New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: tree shakable output for module library #18272
base: main
Are you sure you want to change the base?
Conversation
For maintainers only:
|
Can you descibe this limitation better and any ideas?
Technically es modules output is experiment and it is fine to make such changes, but yeah some developers can lead on the current output, maybe |
This restriction comes from the test case regression. When there is more than one chunk in a chunkGroup (such as using
Yeah, we could adding a new type and implements features gradually in different PRs. |
tl;drthe modern bundled library idea is built on top of a well-worked concatenate module in top-level, but the concatenate module doesn't always work as expected (we got bailout situations). so we had to add some limitations to ensure consuming a concatenated module as expected. But there are some cases we could not handle for far (described below). detailsAs mentioned above, The problem is there will be an IIFE wrapper for the concatenated module in some specific situations. Currently, there are four situations will lead to IIFE wrapper. // https://github.com/webpack/webpack/blob/main/lib/javascript/JavascriptModulesPlugin.js#L837-L847
let iife = innerStrict
? "it need to be in strict mode."
: inlinedModules.size > 1
? // TODO check globals and top-level declarations of other entries and chunk modules
// to make a better decision
"it need to be isolated against other entry modules."
: chunkModules
? "it need to be isolated against other modules in the chunk."
: exports && !webpackExports
? `it uses a non-standard name for the exports (${m.exportsArgument}).`
: hooks.embedInRuntimeBailout.call(m, renderContext); For each situation:
ideasI still believe that concatenated module is the fundamental foundation principle of this feature. The most ideal situation is that we always can get a concatenated module comprising all modules. Starting from this point, we could add an option for the concatenated module strategy that can tolerate more situations only in ESM format (which means tolerates all the bailout situations), the trade-off might be the correctness in some edge case like esbuild. This approach is also more like other library builders (esbuild/rollup). I am not sure about the feasibility of this idea and would appreciate your feedback. 🙌🙌🙌
|
416c9b4
to
ea522a3
Compare
@fi3ework Sound good for me, can we convert it in checkboxes/tasks to track progress (also we already solved some cases) |
As discussed above, the tree shakable output this PR trying to address is a subset of #17121 target. We're going to add a
That's the TODO remains AFAICR, I'll tackle them in these days. Feel free to add any things I missed. Thanks! 🙌 |
ea522a3
to
b7f59c1
Compare
Updated the PR and clean the tasks remain. There are some known configurations will break the ESM output, however they won't be used in most actual uses, and I think it's acceptable.
Ready to be reviewed, any suggestions are kindly welcomed. |
What kind of change does this PR introduce?
A prototype PR trying to implement part of #17121. This PR is still rough at present, any modification suggestions and implementation ideas are welcomed. 🙌🙌🙌.
We're trying to bundle high-quality output NPM libraries with Webpack / Rspack in ESM format. For now, all exported identifiers are defined and re-declarad from
__webpack_exports__
, which makes live-binding and tree-shaking broken when consuming the ESM package (as described in #2933 (comment)), and that's what this PR trying to tackle.This PR tries to only fix the issue in a limited scene when simultaneously satisfies the following conditions:
output.library.type
is"module"
: The premise of this problem is ESM library.optimization.concatenateModules
istrue
: the modules need to be flatten in the same top-level scope.optimization.runtimeChunk
is unapplicable.I created a gist to demonstrate how this PR changes the output, the file to be bundled are some regular ESM inputs and there's also a CJS input.
The change is quite straightforward:
lib/optimize/ConcatenatedModule.js
and skip defining properties on__webpack_exports__
when the hook returnstrue
.__webpack_exports__
is skipped, the final exports name from ConcatenatedModule will be saved inbuildMeta.exportsFinalName
to letlib/library/ModuleLibraryPlugin.js
consumsing it afterwards.lib/library/ModuleLibraryPlugin.js
will tap the hook foreamentioned and returntrue
to instruct skipping the defining.renderStartup
oflib/library/ModuleLibraryPlugin.js
will try to readbuildMeta.exportsFinalName
from the module and complete the module export with the finalName andexportInfo.name
directly without leveraging__webpack_exports__
.Some unperfect implements and concerns:
output.library.type
ismodule
, skip IIFE wrap inlib/javascript/JavascriptModulesPlugin.js
directly.Did you add tests for your changes?
Yes, consuming a ESM library and the unused exports are tree shaked.
Does this PR introduce a breaking change?
Somehow yes, that would makes live-binding and tree-shaking available, and also skipped IIFE wrapping in some condition. Maybe we could introduce a new
library.type
if we decided to move forward.What needs to be documented once your changes are merged?
Yes, depends on the final implementation.