Have you used AI?
Yes
Bug Description
Upgrading from webpack@5.108.4 to 5.109.0 breaks our production build at the Terser minification step. Webpack itself generates syntactically invalid JavaScript (a stray ; inside a comma/sequence expression) when inlining a harmony (ESM) import reference as the non-first element of a SequenceExpression. Webpack never re-parses its own generated output, so the invalid code passes through silently — it only surfaces when a tool that does parse the output (Terser) tries to minify it.
Link to Minimal Reproduction and step to reproduce
I was not able to reduce this to a small standalone repository in the time available. Importing only the single offending source file in isolation (via a trivial webpack config, mode: 'production', default Terser minimizer) built cleanly even on 5.109.0 — whatever triggers the bad code path depends on the surrounding module graph in our real build (a WordPress plugin bundling @arcgis/core via @wordpress/scripts/webpack), likely related to how modules get grouped for concatenation.
What I can provide instead is the exact before/after of webpack's own generated output, captured with optimization.minimize: false at the precise location Terser later failed to parse:
Original library source (@arcgis/core, an ESM dependency, unmodified — a normal comma/sequence expression where n is a locally-aliased import binding):
({densifiedPath:f,distances:d}=G(a,r,P)),n(u),f=m(f,o)
Webpack-generated output on 5.109.0 (pre-minification, i.e. this is what webpack itself emits, before Terser ever sees it):
({densifiedPath:f,distances:d}=G(a,r,P)),;(0,promiseUtils/* throwIfAborted */.Te)(u),f=(0,projectionUtils.project)(f,o)
Webpack inlined the imported reference n(u) into its interop-wrapped form (0, promiseUtils.Te)(u) (the standard "unbind this" wrapper webpack uses when calling an imported function) — but in doing so it also injected a stray ; immediately after the preceding comma, before the wrapped call. expr, ; expr is not valid JavaScript:
new Function("a,b,c","let f; ({d:f}=b), ;(0, c.d)(a), f=1;")
Uncaught SyntaxError: Unexpected token ';'
Steps I used to capture this, against our real project:
- npm run build (via wp-scripts build, wrapping webpack) — fails with the Terser error below.
- Re-run the same webpack config with optimization.minimize: false to get the pre-minification bundle.
- Locate the chunk/line number Terser reported and inspect it — the
,; shape above is present in webpack's own output, with minimize:false, i.e. before Terser touches anything.
Expected Behavior
Webpack's generated (pre-minification) bundle code should always be syntactically valid JavaScript.
Actual Behavior
After upgrading from webpack@5.108.4 to 5.109.0, our production build fails at the Terser minification step with:
ERROR in vendors-....js
Unexpected token: punc (;) [vendors-....js:254,undefined]
at js_error (terser/dist/bundle.min.js:577:11)
at croak (terser/dist/bundle.min.js:1377:9)
at token_error (terser/dist/bundle.min.js:1385:9)
at unexpected (terser/dist/bundle.min.js:1391:9)
at expr_atom (terser/dist/bundle.min.js:2661:25)
at maybe_unary (terser/dist/bundle.min.js:3591:19)
at expr_ops (terser/dist/bundle.min.js:3666:28)
at maybe_conditional (terser/dist/bundle.min.js:3672:20)
at maybe_assign (terser/dist/bundle.min.js:3749:20)
at expression (terser/dist/bundle.min.js:3784:24)
With optimization.minimize: false, the pre-minification generated source at the reported location shows webpack itself emitting invalid JS. The original library source (from @arcgis/core, an ESM dependency) reads:
({densifiedPath:f,distances:d}=G(a,r,P)),n(u),f=m(f,o)
a normal comma (sequence) expression, where n is a locally-aliased import binding. In the 5.109.0-generated bundle, after webpack inlines/interop-wraps the imported reference (n(u) → (0, promiseUtils/* throwIfAborted */.Te)(u), the standard "unbind this" wrapper for calling an imported function), the output becomes:
({densifiedPath:f,distances:d}=G(a,r,P)),;(0,promiseUtils/* throwIfAborted */.Te)(u),f=(0,projectionUtils.project)(f,o)
Note the stray ,; — an extra semicolon injected immediately after the comma, before the wrapped call expression. X, ; Y is not valid JavaScript (confirmed independently: new Function(...) throws Unexpected token ';' on this exact shape), which is why Terser's parser rejects it. Webpack itself never re-parses its own generated output, so this invalid code passes through silently until a tool that does parse it (Terser) chokes on it.
Environment
System:
OS: macOS 26.5.2
CPU: (18) arm64 Apple M5 Pro
Memory: 259.52 MB / 48.00 GB
Binaries:
Node: 25.9.0 - /opt/homebrew/bin/node
npm: 11.12.1 - /opt/homebrew/bin/npm
Browsers:
Chrome: 150.0.7871.184
Safari: 26.5.2
Packages:
webpack: 5.109.0
terser-webpack-plugin: 5.6.1
terser: 5.49.0
webpack-cli: 5.1.4
Is this a regression?
Yes (please specify version below)
Last Working Version
v5.108.4
Additional Context
I bisected every intermediate release between 5.95.0 and 5.109.0 against our real build: everything through 5.108.4 passes, 5.109.0 is the first failing version. It's a single-version regression window with no gap.
The most likely cause, based on timing and the exact shape of the affected construct, is #21453 (refactor: derive ASI positions from source instead of acorn's onInsertedSemicolon), merged into 5.109.0. That PR changed how webpack decides where automatic-semicolon-insertion applies, explicitly around comma-continued sequence elements: "isAsiPosition now treats a statement terminated by a real ; or continued by a , (a sequence element) as non-ASI." That's precisely the code path implicated here — a position immediately following a comma inside a SequenceExpression, where webpack is about to splice in a replacement for an imported identifier reference. The new ASI-position logic appears to misclassify that spot as needing a semicolon. I have not verified this against the PR's diff line-by-line, so treat it as a lead rather than a confirmed cause.
Workaround: pinning webpack to 5.108.4 via npm overrides avoids the issue.
Have you used AI?
Yes
Bug Description
Upgrading from webpack@5.108.4 to 5.109.0 breaks our production build at the Terser minification step. Webpack itself generates syntactically invalid JavaScript (a stray
;inside a comma/sequence expression) when inlining a harmony (ESM) import reference as the non-first element of a SequenceExpression. Webpack never re-parses its own generated output, so the invalid code passes through silently — it only surfaces when a tool that does parse the output (Terser) tries to minify it.Link to Minimal Reproduction and step to reproduce
I was not able to reduce this to a small standalone repository in the time available. Importing only the single offending source file in isolation (via a trivial webpack config, mode: 'production', default Terser minimizer) built cleanly even on 5.109.0 — whatever triggers the bad code path depends on the surrounding module graph in our real build (a WordPress plugin bundling @arcgis/core via @wordpress/scripts/webpack), likely related to how modules get grouped for concatenation.
What I can provide instead is the exact before/after of webpack's own generated output, captured with optimization.minimize: false at the precise location Terser later failed to parse:
Original library source (@arcgis/core, an ESM dependency, unmodified — a normal comma/sequence expression where
nis a locally-aliased import binding):({densifiedPath:f,distances:d}=G(a,r,P)),n(u),f=m(f,o)
Webpack-generated output on 5.109.0 (pre-minification, i.e. this is what webpack itself emits, before Terser ever sees it):
({densifiedPath:f,distances:d}=G(a,r,P)),;(0,promiseUtils/* throwIfAborted */.Te)(u),f=(0,projectionUtils.project)(f,o)
Webpack inlined the imported reference n(u) into its interop-wrapped form (0, promiseUtils.Te)(u) (the standard "unbind this" wrapper webpack uses when calling an imported function) — but in doing so it also injected a stray
;immediately after the preceding comma, before the wrapped call.expr, ; expris not valid JavaScript:Steps I used to capture this, against our real project:
,;shape above is present in webpack's own output, with minimize:false, i.e. before Terser touches anything.Expected Behavior
Webpack's generated (pre-minification) bundle code should always be syntactically valid JavaScript.
Actual Behavior
After upgrading from webpack@5.108.4 to 5.109.0, our production build fails at the Terser minification step with:
ERROR in vendors-....js
Unexpected token: punc (;) [vendors-....js:254,undefined]
at js_error (terser/dist/bundle.min.js:577:11)
at croak (terser/dist/bundle.min.js:1377:9)
at token_error (terser/dist/bundle.min.js:1385:9)
at unexpected (terser/dist/bundle.min.js:1391:9)
at expr_atom (terser/dist/bundle.min.js:2661:25)
at maybe_unary (terser/dist/bundle.min.js:3591:19)
at expr_ops (terser/dist/bundle.min.js:3666:28)
at maybe_conditional (terser/dist/bundle.min.js:3672:20)
at maybe_assign (terser/dist/bundle.min.js:3749:20)
at expression (terser/dist/bundle.min.js:3784:24)
With optimization.minimize: false, the pre-minification generated source at the reported location shows webpack itself emitting invalid JS. The original library source (from @arcgis/core, an ESM dependency) reads:
({densifiedPath:f,distances:d}=G(a,r,P)),n(u),f=m(f,o)
a normal comma (sequence) expression, where n is a locally-aliased import binding. In the 5.109.0-generated bundle, after webpack inlines/interop-wraps the imported reference (n(u) → (0, promiseUtils/* throwIfAborted */.Te)(u), the standard "unbind this" wrapper for calling an imported function), the output becomes:
({densifiedPath:f,distances:d}=G(a,r,P)),;(0,promiseUtils/* throwIfAborted */.Te)(u),f=(0,projectionUtils.project)(f,o)
Note the stray ,; — an extra semicolon injected immediately after the comma, before the wrapped call expression. X, ; Y is not valid JavaScript (confirmed independently: new Function(...) throws Unexpected token ';' on this exact shape), which is why Terser's parser rejects it. Webpack itself never re-parses its own generated output, so this invalid code passes through silently until a tool that does parse it (Terser) chokes on it.
Environment
Is this a regression?
Yes (please specify version below)
Last Working Version
v5.108.4
Additional Context
I bisected every intermediate release between 5.95.0 and 5.109.0 against our real build: everything through 5.108.4 passes, 5.109.0 is the first failing version. It's a single-version regression window with no gap.
The most likely cause, based on timing and the exact shape of the affected construct, is #21453 (refactor: derive ASI positions from source instead of acorn's onInsertedSemicolon), merged into 5.109.0. That PR changed how webpack decides where automatic-semicolon-insertion applies, explicitly around comma-continued sequence elements: "isAsiPosition now treats a statement terminated by a real ; or continued by a , (a sequence element) as non-ASI." That's precisely the code path implicated here — a position immediately following a comma inside a SequenceExpression, where webpack is about to splice in a replacement for an imported identifier reference. The new ASI-position logic appears to misclassify that spot as needing a semicolon. I have not verified this against the PR's diff line-by-line, so treat it as a lead rather than a confirmed cause.
Workaround: pinning webpack to 5.108.4 via npm overrides avoids the issue.