From 9ff4ee9ede77009bac465f8df620d9d711d0c120 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Thu, 6 Aug 2026 07:52:44 +0000 Subject: [PATCH 1/3] fix(@angular/build): prevent syntax corruption in oxc transform Preserve parentheses when unwrapping enum IIFE argument assignments so expressions like Area || (exports.Area = {}) maintain correct operator precedence. (cherry picked from commit 1e9f7c0e39d295a2757802abdb918e07b46ce244) --- .../adjust-typescript-enums_oxc_spec.ts | 20 +++++++++++++++++++ .../src/tools/babel/plugins/oxc-transform.ts | 11 ++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/angular/build/src/tools/babel/plugins/adjust-typescript-enums_oxc_spec.ts b/packages/angular/build/src/tools/babel/plugins/adjust-typescript-enums_oxc_spec.ts index fc01eecf991a..c074f315d35f 100644 --- a/packages/angular/build/src/tools/babel/plugins/adjust-typescript-enums_oxc_spec.ts +++ b/packages/angular/build/src/tools/babel/plugins/adjust-typescript-enums_oxc_spec.ts @@ -280,4 +280,24 @@ describe('adjust-typescript-enums oxc-transform implementation', () => { `, }), ); + + it( + 'handles TypeScript enums with chained exports assignment (angular-split / shared-docs pattern)', + testCase({ + input: ` + var Area; + (function (a1) { + a1[a1["areaAfter"] = 0] = "areaAfter"; + a1[a1["preserveOtherCategoryOrder"] = 1] = "preserveOtherCategoryOrder"; + })(Area || (Area = exports.Area = {})); + `, + expected: ` + var Area = /*#__PURE__*/ (function (a1) { + a1[(a1["areaAfter"] = 0)] = "areaAfter"; + a1[(a1["preserveOtherCategoryOrder"] = 1)] = "preserveOtherCategoryOrder"; + return a1; + })(Area || (exports.Area = {})); + `, + }), + ); }); diff --git a/packages/angular/build/src/tools/babel/plugins/oxc-transform.ts b/packages/angular/build/src/tools/babel/plugins/oxc-transform.ts index 70989a8bc08d..1d14ef50a678 100644 --- a/packages/angular/build/src/tools/babel/plugins/oxc-transform.ts +++ b/packages/angular/build/src/tools/babel/plugins/oxc-transform.ts @@ -423,11 +423,14 @@ export function transform(filename: string, code: string, options: OxcTransformO // 3. Remove `Name = ` assignment in arguments if it's a simple identifier if (rightCallArgument.left.type === 'Identifier') { - s.overwrite( - arg.right.start, - arg.right.end, - code.substring(rightCallArgument.right.start, rightCallArgument.right.end), + let replacement = code.substring( + rightCallArgument.right.start, + rightCallArgument.right.end, ); + if (rightCallArgument.right.type === 'AssignmentExpression') { + replacement = `(${replacement})`; + } + s.overwrite(arg.right.start, arg.right.end, replacement); markEdited(arg.right.start, arg.right.end); } From 6da09e0a975143bcee57dce8834f09fc2f3f55e1 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Thu, 6 Aug 2026 07:52:52 +0000 Subject: [PATCH 2/3] refactor(@angular/build): use unwrapParentheses for oxc AST assignment check (cherry picked from commit 3a79292b2d75f924b3f9d91ad9a26287c3f206d5) --- packages/angular/build/src/tools/babel/plugins/oxc-transform.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/angular/build/src/tools/babel/plugins/oxc-transform.ts b/packages/angular/build/src/tools/babel/plugins/oxc-transform.ts index 1d14ef50a678..ed480ba4d216 100644 --- a/packages/angular/build/src/tools/babel/plugins/oxc-transform.ts +++ b/packages/angular/build/src/tools/babel/plugins/oxc-transform.ts @@ -427,7 +427,7 @@ export function transform(filename: string, code: string, options: OxcTransformO rightCallArgument.right.start, rightCallArgument.right.end, ); - if (rightCallArgument.right.type === 'AssignmentExpression') { + if (unwrapParentheses(rightCallArgument.right).type === 'AssignmentExpression') { replacement = `(${replacement})`; } s.overwrite(arg.right.start, arg.right.end, replacement); From 9899c554a433d165ee1cd9d75d38f16a258aa14f Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Thu, 6 Aug 2026 07:53:05 +0000 Subject: [PATCH 3/3] fix(@angular/build): prevent syntax corruption for Crockford-style enum IIFE When transforming Crockford-style TypeScript enum IIFEs, ensure all leading and trailing parentheses of the expression statement are removed when moving the IIFE call expression to the var initializer. Fixes #33785 (cherry picked from commit e7124ef86ae9443cfa15fbb122be586b51eb1b0a) --- .../adjust-typescript-enums_oxc_spec.ts | 79 +++++++++++++++++++ .../src/tools/babel/plugins/oxc-transform.ts | 6 +- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/packages/angular/build/src/tools/babel/plugins/adjust-typescript-enums_oxc_spec.ts b/packages/angular/build/src/tools/babel/plugins/adjust-typescript-enums_oxc_spec.ts index c074f315d35f..82877cb3b2e3 100644 --- a/packages/angular/build/src/tools/babel/plugins/adjust-typescript-enums_oxc_spec.ts +++ b/packages/angular/build/src/tools/babel/plugins/adjust-typescript-enums_oxc_spec.ts @@ -300,4 +300,83 @@ describe('adjust-typescript-enums oxc-transform implementation', () => { `, }), ); + + it( + 'handles TypeScript enums wrapped in parentheses', + testCase({ + input: ` + var ChangeDetectionStrategy; + ((function (ChangeDetectionStrategy) { + ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush"; + ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default"; + })(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}))); + `, + expected: ` + var ChangeDetectionStrategy = /*#__PURE__*/ (function (ChangeDetectionStrategy) { + ChangeDetectionStrategy[(ChangeDetectionStrategy["OnPush"] = 0)] = "OnPush"; + ChangeDetectionStrategy[(ChangeDetectionStrategy["Default"] = 1)] = "Default"; + return ChangeDetectionStrategy; + })(ChangeDetectionStrategy || {}); + `, + }), + ); + + it( + 'wraps Crockford-style TypeScript enum IIFE without leaving dangling parentheses', + testCase({ + input: ` + var HDirection; + (function (HDirection) { + HDirection[HDirection['Backwards'] = -1] = 'Backwards'; + HDirection[HDirection['Forwards'] = 1] = 'Forwards'; + }(HDirection || (HDirection = {}))); + const nextStatement = true; + `, + expected: ` + var HDirection = /*#__PURE__*/ (function (HDirection) { + HDirection[(HDirection['Backwards'] = -1)] = 'Backwards'; + HDirection[(HDirection['Forwards'] = 1)] = 'Forwards'; + return HDirection; + }(HDirection || {})); + + const nextStatement = true; + `, + }), + ); + + it( + 'wraps TypeScript enum IIFE with multiple nested parentheses', + testCase({ + input: ` + var Foo; + (((function (Foo) { + Foo[Foo['A'] = 0] = 'A'; + }(Foo || (Foo = {}))))); + `, + expected: ` + var Foo = /*#__PURE__*/ (function (Foo) { + Foo[(Foo['A'] = 0)] = 'A'; + return Foo; + }(Foo || {})); + `, + }), + ); + + it( + 'wraps Crockford-style TypeScript enum IIFE with chained export assignments', + testCase({ + input: ` + var Foo; + (function (Foo) { + Foo[Foo['A'] = 0] = 'A'; + }(Foo || (Foo = exports.Foo = {}))); + `, + expected: ` + var Foo = /*#__PURE__*/ (function (Foo) { + Foo[(Foo['A'] = 0)] = 'A'; + return Foo; + }(Foo || (exports.Foo = {}))); + `, + }), + ); }); diff --git a/packages/angular/build/src/tools/babel/plugins/oxc-transform.ts b/packages/angular/build/src/tools/babel/plugins/oxc-transform.ts index ed480ba4d216..569db927f45a 100644 --- a/packages/angular/build/src/tools/babel/plugins/oxc-transform.ts +++ b/packages/angular/build/src/tools/babel/plugins/oxc-transform.ts @@ -414,9 +414,10 @@ export function transform(filename: string, code: string, options: OxcTransformO continue; } - // 1. Remove only the trailing characters/semicolon of the expression statement + // 1. Remove leading/trailing characters/parentheses of the expression statement + s.remove(nextStatement.start, nextExpr.start); s.remove(nextExpr.end, nextStatement.end); - markEdited(nextExpr.end, nextStatement.end); + markEdited(nextStatement.start, nextStatement.end); // 2. Add return statement inside IIFE body s.appendRight(callee.body.end - 1, `; return ${paramName};`); @@ -437,7 +438,6 @@ export function transform(filename: string, code: string, options: OxcTransformO // 4. Move IIFE to the var initializer s.move(nextExpr.start, nextExpr.end, decl.id.end); s.appendLeft(decl.id.end, ' = /*#__PURE__*/ '); - markEdited(nextExpr.start, nextExpr.end); } }