Skip to content

Commit

Permalink
fix #560: avoid a warning for "require.main"
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Nov 25, 2020
1 parent 3b049b2 commit 4e503ba
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

The return value of the `build` API has some optional fields that are undefined unless certain arguments are present. That meant you had to use the `!` null assertion operator to avoid a type error if you have the TypeScript `strictNullChecks` setting enabled in your project. This release adds additional type information so that if the relevant arguments are present, the TypeScript compiler can tell that these optional fields on the return value will never be undefined. This change was contributed by [@lukeed](https://github.com/lukeed).

* Omit a warning about `require.main` when targeting CommonJS ([#560](https://github.com/evanw/esbuild/issues/560))

A common pattern in code that's intended to be run in node is to check if `require.main === module`. That will be true if the current file is being run from the command line but false if the current file is being run because some other code called `require()` on it. Previously esbuild generated a warning about an unexpected use of `require`. Now this warning is no longer generated for `require.main` when the output format is `cjs`.

## 0.8.15

* Allow `paths` without `baseUrl` in `tsconfig.json`
Expand Down
38 changes: 38 additions & 0 deletions internal/bundler/bundler_default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3683,3 +3683,41 @@ func TestConstWithLetNoMangle(t *testing.T) {
},
})
}

func TestRequireMainCommonJS(t *testing.T) {
default_suite.expectBundled(t, bundled{
files: map[string]string{
"/entry.js": `
console.log('is main:', require.main === module)
console.log(require('./is-main'))
`,
"/is-main.js": `
module.exports = require.main === module
`,
},
entryPaths: []string{"/entry.js"},
options: config.Options{
Mode: config.ModeBundle,
AbsOutputFile: "/out.js",
OutputFormat: config.FormatCommonJS,
},
})
}

func TestRequireMainIIFE(t *testing.T) {
default_suite.expectBundled(t, bundled{
files: map[string]string{
"/entry.js": `
console.log('is main:', require.main === module)
`,
},
entryPaths: []string{"/entry.js"},
options: config.Options{
Mode: config.ModeBundle,
AbsOutputFile: "/out.js",
OutputFormat: config.FormatIIFE,
},
expectedScanLog: `/entry.js: warning: Indirect calls to "require" will not be bundled (surround with a try/catch to silence this warning)
`,
})
}
23 changes: 23 additions & 0 deletions internal/bundler/snapshots/snapshots_default.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1818,6 +1818,29 @@ var require_test = __commonJS((exports, module) => {
// /entry.js
console.log(require_test());

================================================================================
TestRequireMainCommonJS
---------- /out.js ----------
// /is-main.js
var require_is_main = __commonJS((exports2, module2) => {
module2.exports = require.main === module2;
});

// /entry.js
console.log("is main:", require.main === module);
console.log(require_is_main());

================================================================================
TestRequireMainIIFE
---------- /out.js ----------
(() => {
// /entry.js
var require_entry = __commonJS((exports, module) => {
console.log("is main:", require.main === module);
});
require_entry();
})();

================================================================================
TestRequireParentDirCommonJS
---------- /out.js ----------
Expand Down
12 changes: 11 additions & 1 deletion internal/js_parser/js_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ type parser struct {
typeofRequireEqualsFn js_ast.E
typeofRequireEqualsFnTarget js_ast.E

// This helps recognize the "require.main" pattern. If this pattern is
// present and the output format is CommonJS, we avoid generating a warning
// about an unbundled use of "require".
cjsDotMainTarget js_ast.E

// This helps recognize calls to "require.resolve()" which may become
// ERequireResolve expressions.
resolveCallTarget js_ast.E
Expand Down Expand Up @@ -9255,6 +9260,11 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
}
}

// Pattern-match "require.main" from node
if p.options.outputFormat == config.FormatCommonJS && e.Name == "main" {
p.cjsDotMainTarget = e.Target.Data
}

isCallTarget := e == p.callTarget
target, out := p.visitExprInOut(e.Target, exprIn{
hasChainParent: e.OptionalChain == js_ast.OptionalChainContinue,
Expand Down Expand Up @@ -9883,7 +9893,7 @@ func (p *parser) handleIdentifier(loc logger.Loc, assignTarget js_ast.AssignTarg
}

// Warn about uses of "require" other than a direct call
if ref == p.requireRef && e != p.callTarget && e != p.typeofTarget && p.fnOrArrowDataVisit.tryBodyCount == 0 {
if ref == p.requireRef && e != p.callTarget && e != p.typeofTarget && e != p.cjsDotMainTarget && p.fnOrArrowDataVisit.tryBodyCount == 0 {
// "typeof require == 'function' && require"
if e == p.typeofRequireEqualsFnTarget {
// Become "false" in the browser and "require" in node
Expand Down

0 comments on commit 4e503ba

Please sign in to comment.