You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
22.0.9
Description
Since @angular/build 22.1.0, ng build --configuration=production fails with The symbol "X" has already been declared whenever the build graph contains a minified JavaScript file with two or more consecutive top-level classes that qualify for pure-IIFE wrapping.
22.1.0 replaced the Babel-based advancedOptimizations pass with a magic-string + oxc-parser text-splicing implementation in src/tools/babel/plugins/oxc-transform.js. In adjustStaticMembersInStatements, each wrapped class gets:
In minified code two sibling class declarations are directly adjacent, so classA.end === classB.start. At a shared index magic-string emits appendLeft content beforeappendRight content, so class B's opening fragment is inserted ahead of class A's closing fragment. The wrappers interleave and the class escapes the IIFE meant to contain it:
esbuild then reports the duplicated A (the let plus the escaped class) and, in files with an export list, additionally "A" is not declared in this file.
Non-minified files are unaffected, because a newline between the declarations keeps the two insertion points distinct. That is why Angular's own packages, which ship non-minified fesm2022, do not trigger this, while any dependency shipping pre-minified ESM does.
Expected: the build succeeds, as it does on @angular/build 22.0.9. Actual:Application bundle generation failed with the error below.
Verified on the identical app, changing only @angular/build:
@angular/build
Result
22.0.8
success
22.0.9
success
22.1.0
fails
22.1.1
fails
Minimal Reproduction
ng new repro-oxc --defaults --skip-git --style=css --ssr=false
cd repro-oxc
npm i pdfjs-dist@5.6.205
Add one import at the top of src/main.ts:
import'pdfjs-dist/build/pdf.worker.min.mjs';
Remove the default budgets from the production configuration in angular.json (otherwise the build stops on a bundle-size error before reaching this one), then:
ng build --configuration=production
Exception or Error
X [ERROR] The symbol "SYSTEM_FONT_INFO" has already been declared
node_modules/pdfjs-dist/build/pdf.worker.min.mjs:32:11:
32 │ })();class SYSTEM_FONT_INFO{static strings=["css","loadedName","ba...
╵ ~~~~~~~~~~~~~~~~
The symbol "SYSTEM_FONT_INFO" was originally declared here:
node_modules/pdfjs-dist/build/pdf.worker.min.mjs:29:81:
29 │ ...ight","italicAngle"]}let SYSTEM_FONT_INFO = /*#__PURE__*/ (() => {
╵ ~~~~~~~~~~~~~~~~
This appears to have been introduced by feat(@angular/build): migrate advanced optimization Babel plugins to oxc-parser + magic-string #33568 (merged 2026-07-21), which migrated the advanced optimization plugins from Babel to oxc-parser plus magic-string and added oxc-transform.ts. The accompanying adjust-static-class-members_oxc_spec.ts covers the pass fairly thoroughly, but I could not find a case with two adjacent class declarations and no whitespace between them, which is the shape that triggers this. A test on input like class A{static s=[1]}class B{static s=[2]} would cover it.
Production builds only, since advancedOptimizations is gated on !!aot && optimizationOptions.scripts. The dev server and ng-packagr library builds are unaffected.
pdfjs-dist is just a convenient public trigger. Any pre-minified ESM dependency with adjacent wrappable classes hits this, including libraries that ship minified fesm2022 bundles. In our own case a single production build reported corruption in three separate packages.
oxc-transform.js is byte-identical in 22.1.0, 22.1.1 and 22.2.0-next.0, so no released version currently contains a fix.
Possible fix: swap the insertion sides so the closing fragment binds to the preceding chunk and the opening fragment to the following one, that is appendRight for the opener and appendLeft for the closer. I confirmed that with that swap the shared-boundary case emits ... })();let B = ... in the correct order. The isExportDefault, isExportNamed and isVariableClass branches use the same pairing and would need the same treatment.
Command
build
Is this a regression?
The previous version in which this bug was not present was
22.0.9
Description
Since
@angular/build22.1.0,ng build --configuration=productionfails withThe symbol "X" has already been declaredwhenever the build graph contains a minified JavaScript file with two or more consecutive top-level classes that qualify for pure-IIFE wrapping.22.1.0 replaced the Babel-based
advancedOptimizationspass with amagic-string+oxc-parsertext-splicing implementation insrc/tools/babel/plugins/oxc-transform.js. InadjustStaticMembersInStatements, each wrapped class gets:In minified code two sibling class declarations are directly adjacent, so
classA.end === classB.start. At a shared index magic-string emitsappendLeftcontent beforeappendRightcontent, so class B's opening fragment is inserted ahead of class A's closing fragment. The wrappers interleave and the class escapes the IIFE meant to contain it:esbuild then reports the duplicated
A(theletplus the escapedclass) and, in files with an export list, additionally"A" is not declared in this file.Non-minified files are unaffected, because a newline between the declarations keeps the two insertion points distinct. That is why Angular's own packages, which ship non-minified
fesm2022, do not trigger this, while any dependency shipping pre-minified ESM does.Expected: the build succeeds, as it does on
@angular/build22.0.9.Actual:
Application bundle generation failedwith the error below.Verified on the identical app, changing only
@angular/build:@angular/buildMinimal Reproduction
ng new repro-oxc --defaults --skip-git --style=css --ssr=false cd repro-oxc npm i pdfjs-dist@5.6.205Add one import at the top of
src/main.ts:Remove the default
budgetsfrom theproductionconfiguration inangular.json(otherwise the build stops on a bundle-size error before reaching this one), then:Exception or Error
Your Environment
Anything else relevant?
advancedOptimizationsis gated on!!aot && optimizationOptions.scripts. The dev server and ng-packagr library builds are unaffected.pdfjs-distis just a convenient public trigger. Any pre-minified ESM dependency with adjacent wrappable classes hits this, including libraries that ship minifiedfesm2022bundles. In our own case a single production build reported corruption in three separate packages.oxc-transform.jsis byte-identical in 22.1.0, 22.1.1 and 22.2.0-next.0, so no released version currently contains a fix.appendRightfor the opener andappendLeftfor the closer. I confirmed that with that swap the shared-boundary case emits... })();let B = ...in the correct order. TheisExportDefault,isExportNamedandisVariableClassbranches use the same pairing and would need the same treatment.