fix: CJS tree-shaking drops exports accessed before deferred require#21123
Conversation
The `const NAME = require(LITERAL)` binding optimization (#21003) missed member accesses that appear lexically before the declaration (e.g. inside hoisted function bodies). The hooks guarded pushes behind `binding.dep &&` which is null until the require() call is walked — but function bodies above are walked first. Fix by operating directly on `binding.referencedExports` (the shared array) instead of going through `binding.dep`. Closes #21122
🦋 Changeset detectedLatest commit: e1de782 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
This PR is packaged and the instant preview is available (1257030). Install it locally:
npm i -D webpack@https://pkg.pr.new/webpack@1257030
yarn add -D webpack@https://pkg.pr.new/webpack@1257030
pnpm add -D webpack@https://pkg.pr.new/webpack@1257030 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #21123 +/- ##
==========================================
+ Coverage 91.98% 92.00% +0.01%
==========================================
Files 581 581
Lines 61441 61445 +4
Branches 16791 16793 +2
==========================================
+ Hits 56519 56534 +15
+ Misses 4922 4911 -11
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Merging this PR will improve performance by ×3.6
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ⚡ | Memory | benchmark "asset-modules-inline", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
1,292.2 KB | 217.1 KB | ×6 |
| ⚡ | Memory | benchmark "react", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' |
330.1 KB | 153 KB | ×2.2 |
Tip
Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.
Comparing fix/cjs-deferred-require-tree-shaking (e1de782) with main (1c36fd6)
Summary
Closes #21122. The
const NAME = require(LITERAL)binding optimization (#21003) missed member accesses appearing lexically before the declaration (e.g. inside hoisted function bodies — the pattern webidl2js generates for whatwg-url). The hooks guarded pushes behindbinding.dep &&which is null until therequire()call is walked, but function bodies above are walked first. Fixed by operating directly onbinding.referencedExports(the shared array that the dep later receives) instead of going throughbinding.dep.What kind of change does this PR introduce?
fix
Did you add tests for your changes?
Yes —
test/cases/cjs-tree-shaking/require-member-access-deferred, reproducing the deferred-require pattern from whatwg-url/webidl2js.Does this PR introduce a breaking change?
No.
If relevant, what needs to be documented once your changes are merged or what have you already documented?
n/a
Use of AI
AI (Claude) was used to trace the root cause through the parser's walk phases and author the fix under human review and direction.