evanw / evanw/esbuild

"use strict;" directive sometimes not included when using a custom plugin

Open
#3,368 0 comments 2 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
40.1k
Forks
1.3k
PR merge metrics
No merged PRs in 30d

Description

## Issue

"use strict;" directive sometimes not included when using a custom plugin.

If the custom plugin resolves an`entry-point` into a javascript file which imports the `entry-point`, the "strict" flag seems to be dropped and "use strict" is not emitted.

Here's a walkthru of the issue: https://share.commandbar.com/CP3F2nMN

## Steps to reproduce

### Sample project
Here's a sample project that reproduces the issue (download and run "yarn && yarn node esbuild.js" then examine dist/bundle*.js): [esbuild-bug-onresolve-plugin.tgz](https://github.com/evanw/esbuild/files/12529869/esbuild-bug-onresolve-plugin.tgz)

### Summary
Consider this build file with a plugin:
```js
//// esbuild.js
const esbuild = require('esbuild');
const path = require("path");

function myPlugin() {
const pluginName = 'myPlugin';

return {
name: pluginName,

setup({ onLoad, onResolve, resolve }) {

onResolve({ filter: /.*/ }, async (args) => {
if (args.kind !== "entry-point") {
return;
} else {
return {
pluginName,
path: path.join(args.resolveDir, args.path), // note: unused but will show up in metafile.json,
namespace: '__my_plugin_namespace__',
pluginData: {
originalPath: args.path,
originalResolveDir: args.resolveDir,
},

// add a suffix; will cause esbuild to re-resolve the path when we reimport it below
suffix: "?__my_plugin_suffix__=true",
};
}
});

onLoad({ filter: /.*/, namespace: '__my_plugin_namespace__' }, (args) => {
const originalPath = args.pluginData.originalPath;
const originalResolveDir = args.pluginData.originalResolveDir;

return {
loader: "js",
pluginName,
contents: `import ${JSON.stringify(originalPath)};`,
resolveDir: originalResolveDir,
};
});

}

};
}

esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/bundle.js',
plugins: [
myPlugin(),
]
})
```
```js
///// src/index.ts
function fn() { console.log("`this` should be 'undefined'", this===undefined, this); }
fn();

export default "Hello World!";

```
```js
//// tsconfig.json
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
```

## Expected result:

Because the `tsconfig.json` file includes "strict: true", `esbuild` should include "use strict;" somewhere in the built file so that "fn" is executed in strict mode.

## Actual result:

With "myPlugin" installed, the emitted file looks like this:
```js
(() => {
// src/index.ts
function fn() {
console.log("`this` should be 'undefined'", this === void 0, this);
}
fn();
})();
```

Without "myPlugin" installed, we get the "use strict" directive at the top of the file as expected:
```js
"use strict";
(() => {
// src/index.ts
function fn() {
console.log("`this` should be 'undefined'", this === void 0, this);
}
fn();
var src_default = "Hello World!";
})();
```

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the supplied esbuild.js plugin reproduction, run "yarn && yarn node esbuild.js", and compare dist/bundle*.js with and without the plugin. Trace how src/index.ts, tsconfig.json, and the plugin's re-resolved entry point affect strict-mode output; done means the bundled code preserves the expected "use strict" directive when the plugin is enabled.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, javascript, typescript
Domain
build-system, compilers, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.