Repository navigation
fix(legacy): minify SystemJS legacy chunks with module: false - #23324
santhiprakash wants to merge 3 commits into
Conversation
When using the oxc minifier (Vite 8 default), legacy chunks are minified with `module: true` because rolldown derives this flag from `output.format` (which is `esm` at minify time). The legacy plugin then converts chunks from ESM to SystemJS in `renderChunk`, which runs AFTER minification. SystemJS output runs in sloppy mode (no "use strict"), where Annex B.3.3 hoists block-level function declarations to the enclosing function scope. With `module: true`, the minifier applies strict-mode semantics and can dead-code-eliminate block-scoped functions that are actually reachable via sloppy-mode hoisting. After mangling, this can cause name collisions that produce TypeErrors and white screens on legacy browsers. The fix disables rolldown's built-in minification for legacy output chunks and instead invokes `minifySync` from `rolldown/experimental` directly in `renderChunk` with `module: false`, ensuring the minifier uses sloppy-mode semantics for the SystemJS output. Adds a unit test that directly verifies `minifySync` with `module: true` eliminates block-scoped functions (strict-mode DCE) while `module: false` preserves them (sloppy-mode safe). Updates the e2e minification test heuristic from terser-specific patterns to SystemJS format detection. fixes #23296
- Escape the literal inside the System.register regex pattern so the regexp/strict ESLint rule accepts it (uses [^\]]* inside a char class). - Convert the rolldown/experimental resolved path to a file:// URL via pathToFileURL before dynamic import, so it works on Windows where bare drive-letter paths (D:\\...\\...) are not accepted by the ESM loader and fail with 'Received protocol d:'.
9799a3e to
7982b9a
Compare
|
Rebased onto current |
The new minify-systemjs.spec.ts introduced in this branch put the 'alphabetically later' vitest import before the rolldown/experimental import. The CI Lint job's 'pnpm format && git diff --exit-code' step fails because oxfmt (sortImports enabled in .oxfmtrc.json) wants them reordered. Swap the two lines so the lint gate can pass on the rebased head. No code or test logic changes - pure formatting.
|
Follow-up fix on top of |
|
This PR has been automatically flagged as likely to be created by a bot, LLM, or agent, and will be automatically closed. These contributions harm the maintenance of the project. Please read our AI policy for more information. |
|
Hi, the close/reopen above was a manual CI retrigger after the Windows |
What is this PR solving?
Fixes #23296
When using the oxc minifier (Vite 8 default, introduced by #22468), legacy chunks are minified with
module: truebecause rolldown derives this flag fromoutput.format(which remainsesmat minify time). The legacy plugin then converts chunks from ESM to SystemJS inrenderChunk, which runs after minification.SystemJS output runs in sloppy mode (no
"use strict"), where Annex B.3.3 hoists block-level function declarations to the enclosing function scope. Withmodule: true, the minifier applies strict-mode semantics and can dead-code-eliminate block-scoped functions that are actually reachable via sloppy-mode hoisting. After mangling, this causes name collisions that produceTypeErrors and white screens on legacy browsers.What changes did you make?
packages/plugin-legacy/src/index.ts: Disable rolldown's built-in minification for legacy output chunks (it always usesmodule: true). Instead, invokeminifySyncfromrolldown/experimentaldirectly inrenderChunkwithmodule: false,compress: { target: 'es2015' }, andmangle: true. This ensures the minifier uses sloppy-mode semantics for SystemJS output. Resolvesrolldownthrough vite's dependency tree since it's not a direct dependency of plugin-legacy.packages/plugin-legacy/src/__tests__/minify-systemjs.spec.ts: New unit tests verifying thatminifySyncwithmodule: trueeliminates block-scoped functions (strict-mode DCE) whilemodule: falsepreserves them (sloppy-mode safe), and thatcompress: { target: 'es2015' }doesn't introduce newer syntax.playground/legacy/__tests__/legacy.spec.ts: Updated the minification heuristic from terser-specific patterns to SystemJS format detection, and added a polyfills-legacy assertion.Alternatives explored
"use strict"injection (closed-unmerged PR fix(legacy): emit legacy chunks as strict mode code #23297): Rejected by maintainer sapphi-red as incomplete — the directive alone doesn't fix the DCE/mangling behavior.minify: { module: false }: Not possible by type — rolldown'sOutputOptions.MinifyOptionsomits themodulefield; it's derived internally fromoutput.format.minify: 'dce-only': Also uses ESM module semantics internally, so it has the same DCE problem. Must usefalse(no built-in minification).Testing
minify-systemjs.spec.tspass (directminifySyncbehavior)The new
sloppy-hoistingplayground test fixture from earlier iterations was removed because the block-scoped function pattern gets eliminated by rolldown's bundler-phase tree-shaking (not just minification), making it unsuitable for e2e. The unit tests cover the minify-level fix directly.AI disclosure
This PR was developed with AI assistance. I reviewed all generated code, verified each change against the root cause analysis, ran all tests, and validated the fix approach against the maintainer's guidance (sapphi-red's feedback on #23297).