Skip to content

Commit 4203294

Browse files
alan-agius4vikerman
authored andcommitted
feat(@schematics/angular): add migration for applications tsconfigs
This migration updates the current tsconfig for the applications in two ways. 1) removes `enableIvy: true` option since it's by default true. 2) Amends the files/exclude/include options to only include files that are needed in the compilation.
1 parent 2c8b12f commit 4203294

File tree

5 files changed

+252
-6
lines changed

5 files changed

+252
-6
lines changed

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

+6-4
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,19 @@
88

99
import { Rule, chain } from '@angular-devkit/schematics';
1010
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
11-
import { UpdateLibraries } from './ivy-libraries';
11+
import { updateLibraries } from './ivy-libraries';
1212
import { updateNGSWConfig } from './ngsw-config';
13+
import { updateApplicationTsConfigs } from './update-app-tsconfigs';
1314
import { updateDependencies } from './update-dependencies';
14-
import { UpdateWorkspaceConfig } from './update-workspace-config';
15+
import { updateWorkspaceConfig } from './update-workspace-config';
1516

1617
export default function(): Rule {
1718
return () => {
1819
return chain([
19-
UpdateWorkspaceConfig(),
20-
UpdateLibraries(),
20+
updateWorkspaceConfig(),
21+
updateLibraries(),
2122
updateNGSWConfig(),
23+
updateApplicationTsConfigs(),
2224
updateDependencies(),
2325
(tree, context) => {
2426
const packageChanges = tree.actions.some(a => a.path.endsWith('/package.json'));

packages/schematics/angular/migrations/update-9/ivy-libraries.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { getTargets, getWorkspace } from './utils';
2323
* - Creates a production configuration for VE compilations.
2424
* - Create a prod tsconfig for which disables Ivy and enables VE compilations.
2525
*/
26-
export function UpdateLibraries(): Rule {
26+
export function updateLibraries(): Rule {
2727
return (tree: Tree) => {
2828
const workspacePath = getWorkspacePath(tree);
2929
const workspace = getWorkspace(tree);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import { JsonAstObject, JsonParseMode, parseJsonAst } from '@angular-devkit/core';
9+
import { Rule, Tree, UpdateRecorder } from '@angular-devkit/schematics';
10+
import {
11+
findPropertyInAstObject,
12+
insertPropertyInAstObjectInOrder,
13+
removePropertyInAstObject,
14+
} from '../../utility/json-utils';
15+
import { Builders } from '../../utility/workspace-models';
16+
import { getAllOptions, getTargets, getWorkspace } from './utils';
17+
18+
19+
/**
20+
* Update the tsconfig files for applications
21+
* - Removes enableIvy: true
22+
* - Sets stricter file inclusions
23+
*/
24+
export function updateApplicationTsConfigs(): Rule {
25+
return (tree: Tree) => {
26+
const workspace = getWorkspace(tree);
27+
28+
for (const { target } of getTargets(workspace, 'build', Builders.Browser)) {
29+
updateTsConfig(tree, target, Builders.Browser);
30+
}
31+
32+
for (const { target } of getTargets(workspace, 'server', Builders.Server)) {
33+
updateTsConfig(tree, target, Builders.Server);
34+
}
35+
36+
for (const { target } of getTargets(workspace, 'test', Builders.Karma)) {
37+
updateTsConfig(tree, target, Builders.Karma);
38+
}
39+
40+
return tree;
41+
};
42+
}
43+
44+
function updateTsConfig(tree: Tree, builderConfig: JsonAstObject, builderName: Builders) {
45+
const options = getAllOptions(builderConfig);
46+
for (const option of options) {
47+
let recorder: UpdateRecorder;
48+
const tsConfigOption = findPropertyInAstObject(option, 'tsConfig');
49+
50+
if (!tsConfigOption || tsConfigOption.kind !== 'string') {
51+
continue;
52+
}
53+
54+
const tsConfigPath = tsConfigOption.value;
55+
let tsConfigAst = getTsConfigAst(tree, tsConfigPath);
56+
if (!tsConfigAst) {
57+
continue;
58+
}
59+
60+
// Remove 'enableIvy: true' since this is the default in version 9.
61+
const angularCompilerOptions = findPropertyInAstObject(tsConfigAst, 'angularCompilerOptions');
62+
if (angularCompilerOptions && angularCompilerOptions.kind === 'object') {
63+
const enableIvy = findPropertyInAstObject(angularCompilerOptions, 'enableIvy');
64+
if (enableIvy && enableIvy.kind === 'true') {
65+
recorder = tree.beginUpdate(tsConfigPath);
66+
if (angularCompilerOptions.properties.length === 1) {
67+
// remove entire 'angularCompilerOptions'
68+
removePropertyInAstObject(recorder, tsConfigAst, 'angularCompilerOptions');
69+
} else {
70+
removePropertyInAstObject(recorder, angularCompilerOptions, 'enableIvy');
71+
}
72+
tree.commitUpdate(recorder);
73+
}
74+
}
75+
76+
// Add stricter file inclusions to avoid unused file warning during compilation
77+
if (builderName !== Builders.Karma) {
78+
// Note: we need to re-read the tsconfig after very commit because
79+
// otherwise the updates will be out of sync since we are ammending the same node.
80+
tsConfigAst = getTsConfigAst(tree, tsConfigPath) as JsonAstObject;
81+
const files = findPropertyInAstObject(tsConfigAst, 'files');
82+
const include = findPropertyInAstObject(tsConfigAst, 'include');
83+
84+
if (!files && !include) {
85+
const rootInSrc = tsConfigPath.includes('src/');
86+
const rootSrc = rootInSrc ? '' : 'src/';
87+
const files = builderName === Builders.Server
88+
? [`${rootSrc}main.server.ts`]
89+
: [`${rootSrc}main.ts`, `${rootSrc}polyfills.ts`];
90+
91+
recorder = tree.beginUpdate(tsConfigPath);
92+
insertPropertyInAstObjectInOrder(recorder, tsConfigAst, 'files', files, 2);
93+
tree.commitUpdate(recorder);
94+
95+
if (builderName === Builders.Browser) {
96+
tsConfigAst = getTsConfigAst(tree, tsConfigPath) as JsonAstObject;
97+
recorder = tree.beginUpdate(tsConfigPath);
98+
insertPropertyInAstObjectInOrder(recorder, tsConfigAst, 'include', [`${rootSrc}**/*.d.ts`], 2);
99+
tree.commitUpdate(recorder);
100+
}
101+
102+
tsConfigAst = getTsConfigAst(tree, tsConfigPath) as JsonAstObject;
103+
recorder = tree.beginUpdate(tsConfigPath);
104+
removePropertyInAstObject(recorder, tsConfigAst, 'exclude');
105+
tree.commitUpdate(recorder);
106+
}
107+
}
108+
}
109+
}
110+
111+
function getTsConfigAst(tree: Tree, path: string): JsonAstObject | undefined {
112+
const configBuffer = tree.read(path);
113+
if (!configBuffer) {
114+
return undefined;
115+
}
116+
117+
const content = configBuffer.toString();
118+
const tsConfigAst = parseJsonAst(content, JsonParseMode.Loose);
119+
if (!tsConfigAst || tsConfigAst.kind !== 'object') {
120+
return undefined;
121+
}
122+
123+
return tsConfigAst;
124+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { EmptyTree } from '@angular-devkit/schematics';
10+
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
11+
12+
// tslint:disable-next-line: no-any
13+
function overrideJsonFile(tree: UnitTestTree, path: string, newContent: object) {
14+
tree.overwrite(path, JSON.stringify(newContent, undefined, 2));
15+
}
16+
17+
const defaultTsConfigOptions = {
18+
extends: './tsconfig.json',
19+
compilerOptions: {
20+
outDir: './out-tsc/app',
21+
types: [],
22+
},
23+
exclude: [
24+
'src/test.ts',
25+
'src/**/*.spec.ts',
26+
],
27+
angularCompilerOptions: {
28+
enableIvy: true,
29+
},
30+
};
31+
32+
// tslint:disable:no-big-function
33+
describe('Migration to version 9', () => {
34+
describe('Update applications tsconfig', () => {
35+
const schematicRunner = new SchematicTestRunner(
36+
'migrations',
37+
require.resolve('../migration-collection.json'),
38+
);
39+
40+
let tree: UnitTestTree;
41+
42+
beforeEach(async () => {
43+
tree = new UnitTestTree(new EmptyTree());
44+
tree = await schematicRunner
45+
.runExternalSchematicAsync(
46+
require.resolve('../../collection.json'),
47+
'ng-new',
48+
{
49+
name: 'migration-test',
50+
version: '1.2.3',
51+
directory: '.',
52+
},
53+
tree,
54+
)
55+
.toPromise();
56+
});
57+
58+
it('should update apps tsConfig with stricter files inclusions', async () => {
59+
overrideJsonFile(tree, 'tsconfig.app.json', defaultTsConfigOptions);
60+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
61+
const { exclude, files, include } = JSON.parse(tree2.readContent('tsconfig.app.json'));
62+
expect(exclude).toBeUndefined();
63+
expect(files).toEqual(['src/main.ts', 'src/polyfills.ts']);
64+
expect(include).toEqual(['src/**/*.d.ts']);
65+
});
66+
67+
it('should not update apps tsConfig when tsconfig has include', async () => {
68+
const tsConfigContent = {
69+
...defaultTsConfigOptions,
70+
include: ['foo.ts'],
71+
};
72+
73+
overrideJsonFile(tree, 'tsconfig.app.json', tsConfigContent);
74+
75+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
76+
const { files, include } = JSON.parse(tree2.readContent('tsconfig.app.json'));
77+
expect(files).toEqual(undefined);
78+
expect(include).toEqual(['foo.ts']);
79+
});
80+
81+
it(`should remove angularCompilerOptions when enableIvy is true and it's the only option`, async () => {
82+
overrideJsonFile(tree, 'tsconfig.app.json', defaultTsConfigOptions);
83+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
84+
const { angularCompilerOptions } = JSON.parse(tree2.readContent('tsconfig.app.json'));
85+
expect(angularCompilerOptions).toBeUndefined();
86+
});
87+
88+
it('should remove enableIvy only when true and there are other angularCompilerOptions', async () => {
89+
const tsConfigContent = {
90+
...defaultTsConfigOptions,
91+
angularCompilerOptions: {
92+
enableIvy: true,
93+
fullTemplateTypeCheck: true,
94+
},
95+
};
96+
97+
overrideJsonFile(tree, 'tsconfig.app.json', tsConfigContent);
98+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
99+
const { angularCompilerOptions } = JSON.parse(tree2.readContent('tsconfig.app.json'));
100+
expect(angularCompilerOptions.enableIvy).toBeUndefined();
101+
expect(angularCompilerOptions.fullTemplateTypeCheck).toBe(true);
102+
});
103+
104+
it('should note remove enableIvy is set to false', async () => {
105+
const tsConfigContent = {
106+
...defaultTsConfigOptions,
107+
angularCompilerOptions: {
108+
enableIvy: false,
109+
fullTemplateTypeCheck: true,
110+
},
111+
};
112+
113+
overrideJsonFile(tree, 'tsconfig.app.json', tsConfigContent);
114+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
115+
const { angularCompilerOptions } = JSON.parse(tree2.readContent('tsconfig.app.json'));
116+
expect(angularCompilerOptions.enableIvy).toBe(false);
117+
expect(angularCompilerOptions.fullTemplateTypeCheck).toBe(true);
118+
});
119+
});
120+
});

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const ANY_COMPONENT_STYLE_BUDGET = {
2222
maximumWarning: '6kb',
2323
};
2424

25-
export function UpdateWorkspaceConfig(): Rule {
25+
export function updateWorkspaceConfig(): Rule {
2626
return (tree: Tree) => {
2727
const workspacePath = getWorkspacePath(tree);
2828
const workspace = getWorkspace(tree);

0 commit comments

Comments
 (0)