Skip to content

Commit 8c83fdf

Browse files
committed
fix #2231: log about indirect require usage
1 parent eda0e02 commit 8c83fdf

File tree

5 files changed

+86
-6
lines changed

5 files changed

+86
-6
lines changed

CHANGELOG.md

+19
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,25 @@
2020
// New output (with --minify)
2121
```
2222

23+
* Add log messages for indirect `require` references ([#2231](https://github.com/evanw/esbuild/issues/2231))
24+
25+
A long time ago esbuild used to warn about indirect uses of `require` because they break esbuild's ability to analyze the dependencies of the code and cause dependencies to not be bundled, resulting in a potentially broken bundle. However, this warning was removed because many people wanted the warning to be removed. Some packages have code that uses `require` like this but on a code path that isn't used at run-time, so their code still happens to work even though the bundle is incomplete. For example, the following code will _not_ bundle `bindings`:
26+
27+
```js
28+
// Prevent React Native packager from seeing modules required with this
29+
const nodeRequire = require;
30+
31+
function getRealmConstructor(environment) {
32+
switch (environment) {
33+
case "node.js":
34+
case "electron":
35+
return nodeRequire("bindings")("realm.node").Realm;
36+
}
37+
}
38+
```
39+
40+
Version 0.11.11 of esbuild removed this warning, which means people no longer have a way to know at compile time whether their bundle is broken in this way. Now that esbuild has custom log message levels, this warning can be added back in a way that should make both people happy. With this release, there is now a log message for this that defaults to the `debug` log level, which normally isn't visible. You can either do `--log-override:indirect-require=warning` to make this log message a warning (and therefore visible) or use `--log-level=debug` to see this and all other `debug` log messages.
41+
2342
## 0.14.42
2443

2544
* Fix a parser hang on invalid CSS ([#2276](https://github.com/evanw/esbuild/issues/2276))

internal/bundler/bundler_default_test.go

+30
Original file line numberDiff line numberDiff line change
@@ -6288,3 +6288,33 @@ func TestMangleQuotedPropsMinifySyntax(t *testing.T) {
62886288
},
62896289
})
62906290
}
6291+
6292+
func TestIndirectRequireMessage(t *testing.T) {
6293+
loader_suite.expectBundled(t, bundled{
6294+
files: map[string]string{
6295+
"/array.js": `let x = [require]`,
6296+
"/assign.js": `require = x`,
6297+
"/ident.js": `let x = require`,
6298+
6299+
// These shouldn't log anything: https://github.com/evanw/esbuild/issues/812
6300+
"/dot.js": `let x = require.cache`,
6301+
"/index.js": `let x = require[cache]`,
6302+
},
6303+
entryPaths: []string{
6304+
"/array.js",
6305+
"/assign.js",
6306+
"/dot.js",
6307+
"/ident.js",
6308+
"/index.js",
6309+
},
6310+
options: config.Options{
6311+
Mode: config.ModeBundle,
6312+
AbsOutputDir: "/out",
6313+
},
6314+
debugLogs: true,
6315+
expectedScanLog: `array.js: DEBUG: Indirect calls to "require" will not be bundled
6316+
assign.js: DEBUG: Indirect calls to "require" will not be bundled
6317+
ident.js: DEBUG: Indirect calls to "require" will not be bundled
6318+
`,
6319+
})
6320+
}

internal/bundler/snapshots/snapshots_loader.txt

+18
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,24 @@ a:after {
4040
content: "entry2";
4141
}
4242

43+
================================================================================
44+
TestIndirectRequireMessage
45+
---------- /out/array.js ----------
46+
47+
---------- /out/assign.js ----------
48+
// assign.js
49+
__require = x;
50+
51+
---------- /out/dot.js ----------
52+
// dot.js
53+
var x = __require.cache;
54+
55+
---------- /out/ident.js ----------
56+
57+
---------- /out/index.js ----------
58+
// index.js
59+
var x = __require[cache];
60+
4361
================================================================================
4462
TestJSXPreserveCapitalLetter
4563
---------- /out.js ----------

internal/js_parser/js_parser.go

+14-6
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,13 @@ type parser struct {
143143
// The visit pass binds identifiers to declared symbols, does constant
144144
// folding, substitutes compile-time variable definitions, and lowers certain
145145
// syntactic constructs as appropriate.
146-
stmtExprValue js_ast.E
147-
callTarget js_ast.E
148-
templateTag js_ast.E
149-
deleteTarget js_ast.E
150-
loopBody js_ast.S
151-
moduleScope *js_ast.Scope
146+
stmtExprValue js_ast.E
147+
callTarget js_ast.E
148+
dotOrIndexTarget js_ast.E
149+
templateTag js_ast.E
150+
deleteTarget js_ast.E
151+
loopBody js_ast.S
152+
moduleScope *js_ast.Scope
152153

153154
// This helps recognize the "await import()" pattern. When this is present,
154155
// warnings about non-string import paths will be omitted inside try blocks.
@@ -12619,6 +12620,7 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
1261912620
}
1262012621
}
1262112622

12623+
p.dotOrIndexTarget = e.Target.Data
1262212624
target, out := p.visitExprInOut(e.Target, exprIn{
1262312625
hasChainParent: e.OptionalChain == js_ast.OptionalChainContinue,
1262412626
})
@@ -12703,6 +12705,7 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO
1270312705
}
1270412706
}
1270512707

12708+
p.dotOrIndexTarget = e.Target.Data
1270612709
target, out := p.visitExprInOut(e.Target, exprIn{
1270712710
hasChainParent: e.OptionalChain == js_ast.OptionalChainContinue,
1270812711
})
@@ -14234,6 +14237,11 @@ func (p *parser) handleIdentifier(loc logger.Loc, e *js_ast.EIdentifier, opts id
1423414237

1423514238
// Swap references to the global "require" function with our "__require" stub
1423614239
if ref == p.requireRef && !opts.isCallTarget {
14240+
if p.options.mode == config.ModeBundle && p.source.Index != runtime.SourceIndex && e != p.dotOrIndexTarget {
14241+
p.log.AddID(logger.MsgID_JS_IndirectRequire, logger.Debug, &p.tracker, js_lexer.RangeOfIdentifier(p.source, loc),
14242+
"Indirect calls to \"require\" will not be bundled")
14243+
}
14244+
1423714245
return p.valueToSubstituteForRequire(loc)
1423814246
}
1423914247

internal/logger/msg_ids.go

+5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const (
2525
MsgID_JS_EqualsNewObject
2626
MsgID_JS_HTMLCommentInJS
2727
MsgID_JS_ImpossibleTypeof
28+
MsgID_JS_IndirectRequire
2829
MsgID_JS_PrivateNameWillThrow
2930
MsgID_JS_SemicolonAfterReturn
3031
MsgID_JS_SuspiciousBooleanNot
@@ -110,6 +111,8 @@ func StringToMsgIDs(str string, logLevel LogLevel, overrides map[MsgID]LogLevel)
110111
overrides[MsgID_JS_HTMLCommentInJS] = logLevel
111112
case "impossible-typeof":
112113
overrides[MsgID_JS_ImpossibleTypeof] = logLevel
114+
case "indirect-require":
115+
overrides[MsgID_JS_IndirectRequire] = logLevel
113116
case "private-name-will-throw":
114117
overrides[MsgID_JS_PrivateNameWillThrow] = logLevel
115118
case "semicolon-after-return":
@@ -216,6 +219,8 @@ func MsgIDToString(id MsgID) string {
216219
return "html-comment-in-js"
217220
case MsgID_JS_ImpossibleTypeof:
218221
return "impossible-typeof"
222+
case MsgID_JS_IndirectRequire:
223+
return "indirect-require"
219224
case MsgID_JS_PrivateNameWillThrow:
220225
return "private-name-will-throw"
221226
case MsgID_JS_SemicolonAfterReturn:

0 commit comments

Comments
 (0)