Skip to content

Commit ac70f86

Browse files
committed
fix(@angular-devkit/build-angular): do not mark standalone expression statements as pure
Top-level call and new expressions that appear directly as expression statements are executed solely for their side effects, such as class property initialization, static blocks, and helper calls. When targeting browsers requiring class static block or property lowering (e.g., Chrome < 94), Babel's preset-env lowers self-referencing static properties into zero-argument IIFEs (and static blocks into _staticBlock() calls). Because pure-toplevel-functions previously only skipped IIFEs if they had arguments, zero-argument IIFE statements were marked as pure. Downstream minifiers (such as esbuild) then eliminated these standalone pure statements as dead code, dropping essential class members like Injector.ɵprov and causing NG0201 at bootstrap. This change ensures that call and new expressions that are directly expression statements are never annotated as pure, avoiding accidental elimination of side-effecting statements without requiring hardcoded whitelists of helper function names. Fixes #34017 (cherry picked from commit dd68d3a)
1 parent 90ee193 commit ac70f86

2 files changed

Lines changed: 36 additions & 0 deletions

File tree

‎packages/angular_devkit/build_angular/src/tools/babel/plugins/pure-toplevel-functions.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ export default function (): PluginObject {
7171
return;
7272
}
7373

74+
// Do not annotate standalone expression statements.
75+
// Standalone expression statements are executed solely for their side effects;
76+
// marking them pure causes minifiers to drop them as dead code.
77+
if (path.parentPath.isExpressionStatement()) {
78+
return;
79+
}
80+
7481
const callee = path.get('callee');
7582
if (
7683
(callee.isFunctionExpression() || callee.isArrowFunctionExpression()) &&
@@ -96,6 +103,13 @@ export default function (): PluginObject {
96103
return;
97104
}
98105

106+
// Do not annotate standalone expression statements.
107+
// Standalone expression statements are executed solely for their side effects;
108+
// marking them pure causes minifiers to drop them as dead code.
109+
if (path.parentPath.isExpressionStatement()) {
110+
return;
111+
}
112+
99113
const { topLevelSafeMode = false } = state.opts;
100114

101115
if (!topLevelSafeMode) {

‎packages/angular_devkit/build_angular/src/tools/babel/plugins/pure-toplevel-functions_spec.ts‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,28 @@ describe('pure-toplevel-functions Babel plugin', () => {
154154
`),
155155
);
156156

157+
it('does not annotate top-level call expression statements', testCaseNoChange('someCall();'));
158+
159+
it(
160+
'does not annotate top-level IIFE expression statements',
161+
testCaseNoChange(`
162+
(() => {
163+
_defineProperty(_Injector, 'ɵprov', ɵɵdefineInjectable({ token: _Injector }));
164+
_defineProperty(_Injector, '__NG_ELEMENT_ID__', -1);
165+
})();
166+
`),
167+
);
168+
169+
it(
170+
'does not annotate top-level static block helper calls',
171+
testCaseNoChange(`
172+
_staticBlock();
173+
_staticBlock2();
174+
`),
175+
);
176+
177+
it('does not annotate top-level new expression statements', testCaseNoChange('new SomeClass();'));
178+
157179
describe('topLevelSafeMode: true', () => {
158180
it(
159181
'annotates top-level `new InjectionToken` expressions',

0 commit comments

Comments
 (0)