Skip to content

Commit f4691a5

Browse files
alan-agius4vikerman
authored andcommitted
feat(@schematics/angular): add migration to enable AOT by default
With this change we enable the AOT option for the browser builder when an application will use Ivy as rendering engine.
1 parent 87b01ff commit f4691a5

File tree

4 files changed

+261
-8
lines changed

4 files changed

+261
-8
lines changed

packages/schematics/angular/migrations/update-9/update-workspace-config.ts

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
removePropertyInAstObject,
1616
} from '../../utility/json-utils';
1717
import { Builders } from '../../utility/workspace-models';
18-
import { getAllOptions, getTargets, getWorkspace } from './utils';
18+
import { getAllOptions, getTargets, getWorkspace, isIvyEnabled } from './utils';
1919

2020
export const ANY_COMPONENT_STYLE_BUDGET = {
2121
type: 'anyComponentStyle',
@@ -32,6 +32,7 @@ export function UpdateWorkspaceConfig(): Rule {
3232
updateStyleOrScriptOption('styles', recorder, target);
3333
updateStyleOrScriptOption('scripts', recorder, target);
3434
addAnyComponentStyleBudget(recorder, target);
35+
updateAotOption(tree, recorder, target);
3536
}
3637

3738
for (const { target } of getTargets(workspace, 'test', Builders.Karma)) {
@@ -45,6 +46,41 @@ export function UpdateWorkspaceConfig(): Rule {
4546
};
4647
}
4748

49+
function updateAotOption(tree: Tree, recorder: UpdateRecorder, builderConfig: JsonAstObject) {
50+
const options = findPropertyInAstObject(builderConfig, 'options');
51+
if (!options || options.kind !== 'object') {
52+
return;
53+
}
54+
55+
56+
const tsConfig = findPropertyInAstObject(options, 'tsConfig');
57+
// Do not add aot option if the users already opted out from Ivy.
58+
if (tsConfig && tsConfig.kind === 'string' && !isIvyEnabled(tree, tsConfig.value)) {
59+
return;
60+
}
61+
62+
// Add aot to options.
63+
const aotOption = findPropertyInAstObject(options, 'aot');
64+
65+
if (!aotOption) {
66+
insertPropertyInAstObjectInOrder(recorder, options, 'aot', true, 12);
67+
68+
return;
69+
}
70+
71+
if (aotOption.kind !== 'true') {
72+
const { start, end } = aotOption;
73+
recorder.remove(start.offset, end.offset - start.offset);
74+
recorder.insertLeft(start.offset, 'true');
75+
}
76+
77+
// Remove aot properties from other configurations as they are no redundant
78+
const configOptions = getAllOptions(builderConfig, true);
79+
for (const options of configOptions) {
80+
removePropertyInAstObject(recorder, options, 'aot');
81+
}
82+
}
83+
4884
function updateStyleOrScriptOption(property: 'scripts' | 'styles', recorder: UpdateRecorder, builderConfig: JsonAstObject) {
4985
const options = getAllOptions(builderConfig);
5086

@@ -75,12 +111,6 @@ function addAnyComponentStyleBudget(recorder: UpdateRecorder, builderConfig: Jso
75111
const options = getAllOptions(builderConfig, true);
76112

77113
for (const option of options) {
78-
const aotOption = findPropertyInAstObject(option, 'aot');
79-
if (!aotOption || aotOption.kind !== 'true') {
80-
// AnyComponentStyle only works for AOT
81-
continue;
82-
}
83-
84114
const budgetOption = findPropertyInAstObject(option, 'budgets');
85115
if (!budgetOption) {
86116
// add

packages/schematics/angular/migrations/update-9/update-workspace-config_spec.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,5 +169,84 @@ describe('Migration to version 9', () => {
169169
expect(config.configurations.production.budgets).toEqual([ANY_COMPONENT_STYLE_BUDGET]);
170170
});
171171
});
172+
173+
describe('aot option', () => {
174+
it('should update aot option when false', async () => {
175+
let config = getWorkspaceTargets(tree);
176+
config.build.options.aot = false;
177+
updateWorkspaceTargets(tree, config);
178+
179+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
180+
config = getWorkspaceTargets(tree2).build;
181+
expect(config.options.aot).toBe(true);
182+
});
183+
184+
it('should add aot option when not defined', async () => {
185+
let config = getWorkspaceTargets(tree);
186+
config.build.options.aot = undefined;
187+
updateWorkspaceTargets(tree, config);
188+
189+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
190+
config = getWorkspaceTargets(tree2).build;
191+
expect(config.options.aot).toBe(true);
192+
});
193+
194+
it('should not aot option when opted-out of Ivy', async () => {
195+
const tsConfig = JSON.stringify(
196+
{
197+
extends: './tsconfig.json',
198+
angularCompilerOptions: {
199+
enableIvy: false,
200+
},
201+
},
202+
null,
203+
2,
204+
);
205+
206+
tree.overwrite('/tsconfig.app.json', tsConfig);
207+
208+
let config = getWorkspaceTargets(tree);
209+
config.build.options.aot = false;
210+
updateWorkspaceTargets(tree, config);
211+
212+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
213+
config = getWorkspaceTargets(tree2).build;
214+
expect(config.options.aot).toBe(false);
215+
});
216+
217+
it('should not aot option when opted-out of Ivy in workspace', async () => {
218+
const tsConfig = JSON.stringify(
219+
{
220+
angularCompilerOptions: {
221+
enableIvy: false,
222+
},
223+
},
224+
null,
225+
2,
226+
);
227+
228+
tree.overwrite('/tsconfig.json', tsConfig);
229+
230+
let config = getWorkspaceTargets(tree);
231+
config.build.options.aot = false;
232+
updateWorkspaceTargets(tree, config);
233+
234+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
235+
config = getWorkspaceTargets(tree2).build;
236+
expect(config.options.aot).toBe(false);
237+
});
238+
239+
it('should remove aot option from production configuration', async () => {
240+
let config = getWorkspaceTargets(tree);
241+
config.build.options.aot = false;
242+
config.build.configurations.production.aot = true;
243+
updateWorkspaceTargets(tree, config);
244+
245+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
246+
config = getWorkspaceTargets(tree2).build;
247+
expect(config.options.aot).toBe(true);
248+
expect(config.configurations.production.aot).toBeUndefined();
249+
});
250+
});
172251
});
173252
});

packages/schematics/angular/migrations/update-9/utils.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import { JsonAstObject, JsonParseMode, parseJsonAst } from '@angular-devkit/core';
9+
import { JsonAstObject, JsonParseMode, dirname, normalize, parseJsonAst, resolve } from '@angular-devkit/core';
1010
import { SchematicsException, Tree } from '@angular-devkit/schematics';
1111
import { getWorkspacePath } from '../../utility/config';
1212
import { findPropertyInAstObject } from '../../utility/json-utils';
@@ -81,3 +81,40 @@ export function getWorkspace(host: Tree): JsonAstObject {
8181

8282
return parseJsonAst(content, JsonParseMode.Loose) as JsonAstObject;
8383
}
84+
85+
export function isIvyEnabled(tree: Tree, tsConfigPath: string): boolean {
86+
// In version 9, Ivy is turned on by default
87+
// Ivy is opted out only when 'enableIvy' is set to false.
88+
89+
const buffer = tree.read(tsConfigPath);
90+
if (!buffer) {
91+
return true;
92+
}
93+
94+
const tsCfgAst = parseJsonAst(buffer.toString(), JsonParseMode.Loose);
95+
96+
if (tsCfgAst.kind !== 'object') {
97+
return true;
98+
}
99+
100+
const ngCompilerOptions = findPropertyInAstObject(tsCfgAst, 'angularCompilerOptions');
101+
if (ngCompilerOptions && ngCompilerOptions.kind === 'object') {
102+
const enableIvy = findPropertyInAstObject(ngCompilerOptions, 'enableIvy');
103+
104+
if (enableIvy) {
105+
return !!enableIvy.value;
106+
}
107+
}
108+
109+
const configExtends = findPropertyInAstObject(tsCfgAst, 'extends');
110+
if (configExtends && configExtends.kind === 'string') {
111+
const extendedTsConfigPath = resolve(
112+
dirname(normalize(tsConfigPath)),
113+
normalize(configExtends.value),
114+
);
115+
116+
return isIvyEnabled(tree, extendedTsConfigPath);
117+
}
118+
119+
return true;
120+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
2+
/**
3+
* @license
4+
* Copyright Google Inc. All Rights Reserved.
5+
*
6+
* Use of this source code is governed by an MIT-style license that can be
7+
* found in the LICENSE file at https://angular.io/license
8+
*/
9+
import { HostTree } from '@angular-devkit/schematics';
10+
import { isIvyEnabled } from './utils';
11+
12+
describe('migrations update-9 utils', () => {
13+
describe('isIvyEnabled', () => {
14+
let tree: HostTree;
15+
16+
beforeEach(() => {
17+
tree = new HostTree();
18+
});
19+
20+
it('should return false when disabled in base tsconfig', () => {
21+
tree.create('tsconfig.json', JSON.stringify({
22+
angularCompilerOptions: {
23+
enableIvy: false,
24+
},
25+
}));
26+
27+
tree.create('foo/tsconfig.app.json', JSON.stringify({
28+
extends: '../tsconfig.json',
29+
}));
30+
31+
expect(isIvyEnabled(tree, 'foo/tsconfig.app.json')).toBe(false);
32+
});
33+
34+
it('should return true when enable in child tsconfig but disabled in base tsconfig', () => {
35+
tree.create('tsconfig.json', JSON.stringify({
36+
angularCompilerOptions: {
37+
enableIvy: false,
38+
},
39+
}));
40+
41+
tree.create('foo/tsconfig.app.json', JSON.stringify({
42+
extends: '../tsconfig.json',
43+
angularCompilerOptions: {
44+
enableIvy: true,
45+
},
46+
}));
47+
48+
expect(isIvyEnabled(tree, 'foo/tsconfig.app.json')).toBe(true);
49+
});
50+
51+
it('should return false when disabled in child tsconfig but enabled in base tsconfig', () => {
52+
tree.create('tsconfig.json', JSON.stringify({
53+
angularCompilerOptions: {
54+
enableIvy: true,
55+
},
56+
}));
57+
58+
tree.create('foo/tsconfig.app.json', JSON.stringify({
59+
extends: '../tsconfig.json',
60+
angularCompilerOptions: {
61+
enableIvy: false,
62+
},
63+
}));
64+
65+
expect(isIvyEnabled(tree, 'foo/tsconfig.app.json')).toBe(false);
66+
});
67+
68+
it('should return false when disabled in base with multiple extends', () => {
69+
tree.create('tsconfig.json', JSON.stringify({
70+
angularCompilerOptions: {
71+
enableIvy: false,
72+
},
73+
}));
74+
75+
tree.create('foo/tsconfig.project.json', JSON.stringify({
76+
extends: '../tsconfig.json',
77+
}));
78+
79+
tree.create('foo/tsconfig.app.json', JSON.stringify({
80+
extends: './tsconfig.project.json',
81+
}));
82+
83+
expect(isIvyEnabled(tree, 'foo/tsconfig.app.json')).toBe(false);
84+
});
85+
86+
it('should return true when enable in intermediate tsconfig with multiple extends', () => {
87+
tree.create('tsconfig.json', JSON.stringify({
88+
angularCompilerOptions: {
89+
enableIvy: false,
90+
},
91+
}));
92+
93+
tree.create('foo/tsconfig.project.json', JSON.stringify({
94+
extends: '../tsconfig.json',
95+
angularCompilerOptions: {
96+
enableIvy: true,
97+
},
98+
}));
99+
100+
tree.create('foo/tsconfig.app.json', JSON.stringify({
101+
extends: './tsconfig.project.json',
102+
}));
103+
104+
expect(isIvyEnabled(tree, 'foo/tsconfig.app.json')).toBe(true);
105+
});
106+
});
107+
});

0 commit comments

Comments
 (0)