Skip to content

Commit 3978891

Browse files
alan-agius4angular-robot[bot]
authored andcommitted
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`.
1 parent d15d44d commit 3978891

File tree

2 files changed

+78
-0
lines changed

2 files changed

+78
-0
lines changed

packages/angular_devkit/build_angular/src/babel/plugins/adjust-static-class-members.ts

+42
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ const exportDefaultAnalysis = new WeakMap<types.Class, ReturnType<typeof analyze
205205
*
206206
* @returns A babel plugin object instance.
207207
*/
208+
// eslint-disable-next-line max-lines-per-function
208209
export default function (): PluginObj {
209210
return {
210211
visitor: {
@@ -278,6 +279,47 @@ export default function (): PluginObj {
278279
shouldWrap = false;
279280
break;
280281
}
282+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
283+
} else if ((element as any).isStaticBlock()) {
284+
// Only need to analyze static blocks
285+
const body = element.get('body');
286+
287+
if (Array.isArray(body) && body.length > 1) {
288+
// Not safe to wrap
289+
shouldWrap = false;
290+
break;
291+
}
292+
293+
const expression = body.find((n: NodePath<types.Node>) =>
294+
n.isExpressionStatement(),
295+
) as NodePath<types.ExpressionStatement> | undefined;
296+
297+
const assignmentExpression = expression?.get('expression');
298+
if (assignmentExpression?.isAssignmentExpression()) {
299+
const left = assignmentExpression.get('left');
300+
if (!left.isMemberExpression()) {
301+
continue;
302+
}
303+
304+
if (!left.get('object').isThisExpression()) {
305+
// Not safe to wrap
306+
shouldWrap = false;
307+
break;
308+
}
309+
310+
const element = left.get('property');
311+
const right = assignmentExpression.get('right');
312+
if (
313+
element.isIdentifier() &&
314+
(!right.isExpression() || canWrapProperty(element.node.name, right))
315+
) {
316+
shouldWrap = true;
317+
} else {
318+
// Not safe to wrap
319+
shouldWrap = false;
320+
break;
321+
}
322+
}
281323
}
282324
}
283325
if (!shouldWrap) {

packages/angular_devkit/build_angular/src/babel/plugins/adjust-static-class-members_spec.ts

+36
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,42 @@ describe('adjust-static-class-members Babel plugin', () => {
669669
});
670670
});
671671

672+
it('wraps class with Angular ɵfac static block (ES2022 + useDefineForClassFields: false)', () => {
673+
testCase({
674+
input: `
675+
class CommonModule {
676+
static { this.ɵfac = function CommonModule_Factory(t) { return new (t || CommonModule)(); }; }
677+
static { this.ɵmod = ɵngcc0.ɵɵdefineNgModule({ type: CommonModule }); }
678+
}
679+
`,
680+
expected: `
681+
let CommonModule = /*#__PURE__*/ (() => {
682+
class CommonModule {
683+
static {
684+
this.ɵfac = function CommonModule_Factory(t) {
685+
return new (t || CommonModule)();
686+
};
687+
}
688+
static {
689+
this.ɵmod = ɵngcc0.ɵɵdefineNgModule({
690+
type: CommonModule,
691+
});
692+
}
693+
}
694+
return CommonModule;
695+
})();
696+
`,
697+
});
698+
});
699+
700+
it('does not wrap class with side effect full static block (ES2022 + useDefineForClassFields: false)', () => {
701+
testCaseNoChange(`
702+
class CommonModule {
703+
static { globalThis.bar = 1 }
704+
}
705+
`);
706+
});
707+
672708
it('wraps class with Angular ɵmod static field', () => {
673709
testCase({
674710
input: `

0 commit comments

Comments
 (0)