From eb22f634f2ec7a5b0bc2f5300682ed8e718b1424 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Tue, 7 Mar 2023 14:47:17 +0000 Subject: [PATCH] fix(@angular-devkit/build-angular): build optimizer support for non spec-compliant ES2022 class static properties The build optimizer's static field pass will now additionally wrap classes that contain side effect free TypeScript ES2022 static class properties. This is needed to update APF to ship ES2022, which have `useDefineForClassFields` set to `false`. (cherry picked from commit 3978891a46e680412d50d670f06c94aa0921f294) --- .../plugins/adjust-static-class-members.ts | 42 +++++++++++++++++++ .../adjust-static-class-members_spec.ts | 36 ++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/packages/angular_devkit/build_angular/src/babel/plugins/adjust-static-class-members.ts b/packages/angular_devkit/build_angular/src/babel/plugins/adjust-static-class-members.ts index 4c9adc23c7bd..ff0dee19e75b 100644 --- a/packages/angular_devkit/build_angular/src/babel/plugins/adjust-static-class-members.ts +++ b/packages/angular_devkit/build_angular/src/babel/plugins/adjust-static-class-members.ts @@ -205,6 +205,7 @@ const exportDefaultAnalysis = new WeakMap 1) { + // Not safe to wrap + shouldWrap = false; + break; + } + + const expression = body.find((n: NodePath) => + n.isExpressionStatement(), + ) as NodePath | undefined; + + const assignmentExpression = expression?.get('expression'); + if (assignmentExpression?.isAssignmentExpression()) { + const left = assignmentExpression.get('left'); + if (!left.isMemberExpression()) { + continue; + } + + if (!left.get('object').isThisExpression()) { + // Not safe to wrap + shouldWrap = false; + break; + } + + const element = left.get('property'); + const right = assignmentExpression.get('right'); + if ( + element.isIdentifier() && + (!right.isExpression() || canWrapProperty(element.node.name, right)) + ) { + shouldWrap = true; + } else { + // Not safe to wrap + shouldWrap = false; + break; + } + } } } if (!shouldWrap) { diff --git a/packages/angular_devkit/build_angular/src/babel/plugins/adjust-static-class-members_spec.ts b/packages/angular_devkit/build_angular/src/babel/plugins/adjust-static-class-members_spec.ts index 4b078a0f02b1..dbb9036e40af 100644 --- a/packages/angular_devkit/build_angular/src/babel/plugins/adjust-static-class-members_spec.ts +++ b/packages/angular_devkit/build_angular/src/babel/plugins/adjust-static-class-members_spec.ts @@ -669,6 +669,42 @@ describe('adjust-static-class-members Babel plugin', () => { }); }); + it('wraps class with Angular ɵfac static block (ES2022 + useDefineForClassFields: false)', () => { + testCase({ + input: ` + class CommonModule { + static { this.ɵfac = function CommonModule_Factory(t) { return new (t || CommonModule)(); }; } + static { this.ɵmod = ɵngcc0.ɵɵdefineNgModule({ type: CommonModule }); } + } + `, + expected: ` + let CommonModule = /*#__PURE__*/ (() => { + class CommonModule { + static { + this.ɵfac = function CommonModule_Factory(t) { + return new (t || CommonModule)(); + }; + } + static { + this.ɵmod = ɵngcc0.ɵɵdefineNgModule({ + type: CommonModule, + }); + } + } + return CommonModule; + })(); + `, + }); + }); + + it('does not wrap class with side effect full static block (ES2022 + useDefineForClassFields: false)', () => { + testCaseNoChange(` + class CommonModule { + static { globalThis.bar = 1 } + } + `); + }); + it('wraps class with Angular ɵmod static field', () => { testCase({ input: `