feat: optimize export bindings with value descriptors for static exports#21021
Conversation
🦋 Changeset detectedLatest commit: ed29f43 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 (ed29f43). Install it locally:
npm i -D webpack@https://pkg.pr.new/webpack@ed29f43
yarn add -D webpack@https://pkg.pr.new/webpack@ed29f43
pnpm add -D webpack@https://pkg.pr.new/webpack@ed29f43 |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #21021 +/- ##
==========================================
- Coverage 91.73% 91.72% -0.01%
==========================================
Files 577 580 +3
Lines 60409 60582 +173
Branches 16352 16396 +44
==========================================
+ Hits 55415 55571 +156
- Misses 4994 5011 +17
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
d7fa1f7 to
afd7f8a
Compare
c1631d4 to
62a4692
Compare
|
@xiaoxiaojx Looks like we need to make rebase |
|
@alexander-akait Yeah, let’s merge #20973 first. Looks like both PRs need the ConstValuePlugin logic, so we can reuse it after the merge. |
629658a to
9bf7a90
Compare
|
@xiaoxiaojx merged, feel free to rebase |
670022a to
44012f0
Compare
Use Object.defineProperty with value descriptors instead of getter functions for const exports, reducing runtime overhead. Circular modules are detected via iterative SCC and excluded from the optimization. Gated behind optimization.inlineExports.
44012f0 to
ed29f43
Compare
Types CoverageCoverage after merging feat/static-export-bindings into main will be
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
I'm not sure what 'sloppy mode' is, but (1) can this be toggled on/off with a webpack config setting, and (2) can you detect that it is off and so enable function and class names in this optimization? I feel that many more exports would fall into the class/function buckets and could benefit from this optimization. |
|
Good question! The "sloppy mode" comment in the code is actually slightly misleading — I'll fix that. The real reason is that function fn() { return 1; }
fn = 2; // valid in strict mode
export { fn }; // fn could be 2Only To answer your questions:
|
|
@xiaoxiaojx Let's add a todo for this too in code/issue |
Summary
Resolves #18494
Currently webpack wraps every ES module export in a getter via
__webpack_require__.d():The getter indirection preserves live binding semantics, but
constexports are never reassigned and don't need it. This PR usesObject.definePropertywith value descriptors instead of getters for const exports, reducing runtime overhead.Implementation:
ConstValueParserPlugin(extended frominlineExports) records all top-levelconstdeclaration names (including destructuring patterns) inbuildInfo.constBindingsCircularModulesPlugin+CycleGraphbuilds the synchronous dependency graph and runs iterative Tarjan's SCC, marking circular modules viabuildInfo.isCircularExportBindingInitFragmentgenerates a flat-array__webpack_require__.d()call with0sentinel for value bindings:["key", 0, value]Optimization rules:
constdeclarations are eligible (function/classnames can be reassigned in sloppy mode)undefinedat import time)SideEffectsFlagPlugincan rewire connections)optimization.inlineExports(defaults totruein production)Inspired by web-infra-dev/rspack#14045
What kind of change does this PR introduce?
feat
Did you add tests for your changes?
Yes —
test/configCases/optimization/static-export-bindings/covers const exports (literal, renamed, destructured, array), function exports (getter retained), mutable let exports (getter retained), circular modules, and re-exports.Does this PR introduce a breaking change?
No. The optimization is gated behind the existing
optimization.inlineExportsoption. The runtime__webpack_require__.d()now supports both array and object formats, with the object format retained for backward compatibility.If relevant, what needs to be documented once your changes are merged or what have you already documented?
n/a
Use of AI
Claude Code was used to draft the implementation under human review. All architectural decisions were made through interactive discussion and iterative review.