Skip to content

Commit 1168edb

Browse files
clydinalan-agius4
authored andcommitted
refactor(@angular-devkit/build-angular): add a loose mode for build optimizer enum pass
The `adjust-typescript-enums` plugin now includes an optional loose mode. This mode generates less code for an adjusted enum but differs from the canonical TypeScript enum structure emitted by the TypeScript compiler. This mode is only enabled for packages validated to operate correctly with this structure and is currently limited to first-party Angular packages. The adjusted structure is a variation of the previous build optimizer pass structure but better reflects the runtime behavior of the TypeScript emit structure.
1 parent 5bdd1cb commit 1168edb

File tree

3 files changed

+69
-5
lines changed

3 files changed

+69
-5
lines changed

packages/angular_devkit/build_angular/src/babel/plugins/adjust-typescript-enums.ts

+30-1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ export default function (): PluginObj {
7676
return;
7777
}
7878

79+
// Loose mode rewrites the enum to a shorter but less TypeScript-like form
80+
let enumAssignments: types.ExpressionStatement[] | undefined;
81+
if (loose) {
82+
enumAssignments = [];
83+
}
84+
7985
// Check if all enum member values are pure.
8086
// If not, leave as-is due to potential side efects
8187
let hasElements = false;
@@ -93,6 +99,7 @@ export default function (): PluginObj {
9399
}
94100

95101
hasElements = true;
102+
enumAssignments?.push(enumStatement.node);
96103
}
97104

98105
// If there are no enum elements then there is nothing to wrap
@@ -104,11 +111,33 @@ export default function (): PluginObj {
104111
const enumInitializer = nextExpression.node;
105112
nextExpression.remove();
106113

114+
// Create IIFE block contents
115+
let blockContents;
116+
if (enumAssignments) {
117+
// Loose mode
118+
blockContents = [
119+
types.expressionStatement(
120+
types.assignmentExpression(
121+
'=',
122+
types.cloneNode(declarationId),
123+
types.logicalExpression(
124+
'||',
125+
types.cloneNode(declarationId),
126+
types.objectExpression([]),
127+
),
128+
),
129+
),
130+
...enumAssignments,
131+
];
132+
} else {
133+
blockContents = [types.expressionStatement(enumInitializer)];
134+
}
135+
107136
// Wrap existing enum initializer in a pure annotated IIFE
108137
const container = types.arrowFunctionExpression(
109138
[],
110139
types.blockStatement([
111-
types.expressionStatement(enumInitializer),
140+
...blockContents,
112141
types.returnStatement(types.cloneNode(declarationId)),
113142
]),
114143
);

packages/angular_devkit/build_angular/src/babel/plugins/adjust-typescript-enums_spec.ts

+31-2
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,19 @@ import { default as adjustTypeScriptEnums } from './adjust-typescript-enums';
1212
// eslint-disable-next-line import/no-extraneous-dependencies
1313
const prettier = require('prettier');
1414

15-
function testCase({ input, expected }: { input: string; expected: string }): void {
15+
function testCase({
16+
input,
17+
expected,
18+
options,
19+
}: {
20+
input: string;
21+
expected: string;
22+
options?: { loose?: boolean };
23+
}): void {
1624
const result = transform(input, {
1725
configFile: false,
1826
babelrc: false,
19-
plugins: [adjustTypeScriptEnums],
27+
plugins: [[adjustTypeScriptEnums, options]],
2028
});
2129
if (!result) {
2230
fail('Expected babel to return a transform result.');
@@ -211,4 +219,25 @@ describe('adjust-typescript-enums Babel plugin', () => {
211219
RendererStyleFlags3[RendererStyleFlags3.Important] = 'Important';
212220
`);
213221
});
222+
223+
it('wraps TypeScript enums in loose mode', () => {
224+
testCase({
225+
input: `
226+
var ChangeDetectionStrategy;
227+
(function (ChangeDetectionStrategy) {
228+
ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
229+
ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
230+
})(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
231+
`,
232+
expected: `
233+
var ChangeDetectionStrategy = /*#__PURE__*/ (() => {
234+
ChangeDetectionStrategy = ChangeDetectionStrategy || {};
235+
ChangeDetectionStrategy[(ChangeDetectionStrategy["OnPush"] = 0)] = "OnPush";
236+
ChangeDetectionStrategy[(ChangeDetectionStrategy["Default"] = 1)] = "Default";
237+
return ChangeDetectionStrategy;
238+
})();
239+
`,
240+
options: { loose: true },
241+
});
242+
});
214243
});

packages/angular_devkit/build_angular/src/babel/webpack-loader.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ interface AngularCustomOptions extends Pick<ApplicationPresetOptions, 'angularLi
1515
forceAsyncTransformation: boolean;
1616
forceES5: boolean;
1717
optimize?: {
18+
looseEnums: boolean;
1819
pureTopLevel: boolean;
1920
wrapDecorators: boolean;
2021
};
@@ -85,10 +86,12 @@ export default custom<AngularCustomOptions>(() => {
8586
}
8687

8788
if (optimize) {
89+
const angularPackage = /[\\\/]node_modules[\\\/]@angular[\\\/]/.test(this.resourcePath);
8890
customOptions.optimize = {
8991
// Angular packages provide additional tested side effects guarantees and can use
9092
// otherwise unsafe optimizations.
91-
pureTopLevel: /[\\\/]node_modules[\\\/]@angular[\\\/]/.test(this.resourcePath),
93+
looseEnums: angularPackage,
94+
pureTopLevel: angularPackage,
9295
// JavaScript modules that are marked as side effect free are considered to have
9396
// no decorators that contain non-local effects.
9497
wrapDecorators: !!this._module?.factoryMeta?.sideEffectFree,
@@ -126,7 +129,10 @@ export default custom<AngularCustomOptions>(() => {
126129

127130
plugins.push(
128131
require('./plugins/elide-angular-metadata').default,
129-
require('./plugins/adjust-typescript-enums').default,
132+
[
133+
require('./plugins/adjust-typescript-enums').default,
134+
{ loose: customOptions.optimize.looseEnums },
135+
],
130136
[
131137
require('./plugins/adjust-static-class-members').default,
132138
{ wrapDecorators: customOptions.optimize.wrapDecorators },

0 commit comments

Comments
 (0)