Skip to content

Commit bcd6d05

Browse files
alan-agius4vikerman
authored andcommitted
fix(@schematics/angular): migrate module compiler option
This migration coverts the TypeScript `module` compiler option to `esnext` or `commonjs` which is required when using `import()`. Fixes: angular#16094
1 parent 1941762 commit bcd6d05

File tree

2 files changed

+124
-6
lines changed

2 files changed

+124
-6
lines changed

packages/schematics/angular/migrations/update-9/update-app-tsconfigs.ts

+48-6
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,26 @@ import { getAllOptions, getTargets, getWorkspace, readJsonFileAsAstObject } from
2020
* Update the tsconfig files for applications
2121
* - Removes enableIvy: true
2222
* - Sets stricter file inclusions
23+
* - Sets module compiler option to esnext or commonjs
2324
*/
2425
export function updateApplicationTsConfigs(): Rule {
2526
return (tree, context) => {
2627
const workspace = getWorkspace(tree);
28+
const logger = context.logger;
29+
30+
// Add `module` option in the workspace tsconfig
31+
updateModuleCompilerOption(tree, '/tsconfig.json');
2732

2833
for (const { target } of getTargets(workspace, 'build', Builders.Browser)) {
29-
updateTsConfig(tree, target, Builders.Browser, context.logger);
34+
updateTsConfig(tree, target, Builders.Browser, logger);
3035
}
3136

3237
for (const { target } of getTargets(workspace, 'server', Builders.Server)) {
33-
updateTsConfig(tree, target, Builders.Server, context.logger);
38+
updateTsConfig(tree, target, Builders.Server, logger);
3439
}
3540

3641
for (const { target } of getTargets(workspace, 'test', Builders.Karma)) {
37-
updateTsConfig(tree, target, Builders.Karma, context.logger);
42+
updateTsConfig(tree, target, Builders.Karma, logger);
3843
}
3944

4045
return tree;
@@ -74,14 +79,17 @@ function updateTsConfig(tree: Tree, builderConfig: JsonAstObject, builderName: B
7479
}
7580
}
7681

82+
// Update 'module' compilerOption
83+
updateModuleCompilerOption(tree, tsConfigPath, builderName);
84+
7785
// Add stricter file inclusions to avoid unused file warning during compilation
7886
if (builderName !== Builders.Karma) {
7987
// Note: we need to re-read the tsconfig after very commit because
8088
// otherwise the updates will be out of sync since we are ammending the same node.
8189

8290
// we are already checking that tsconfig exists above!
8391
// tslint:disable-next-line: no-non-null-assertion
84-
tsConfigAst = readJsonFileAsAstObject(tree, tsConfigPath) !;
92+
tsConfigAst = readJsonFileAsAstObject(tree, tsConfigPath)!;
8593
const include = findPropertyInAstObject(tsConfigAst, 'include');
8694

8795
if (include && include.kind === 'array') {
@@ -113,17 +121,51 @@ function updateTsConfig(tree: Tree, builderConfig: JsonAstObject, builderName: B
113121
if (newFiles.length) {
114122
recorder = tree.beginUpdate(tsConfigPath);
115123
// tslint:disable-next-line: no-non-null-assertion
116-
tsConfigAst = readJsonFileAsAstObject(tree, tsConfigPath) !;
124+
tsConfigAst = readJsonFileAsAstObject(tree, tsConfigPath)!;
117125
insertPropertyInAstObjectInOrder(recorder, tsConfigAst, 'files', newFiles, 2);
118126
tree.commitUpdate(recorder);
119127
}
120128

121129
recorder = tree.beginUpdate(tsConfigPath);
122130
// tslint:disable-next-line: no-non-null-assertion
123-
tsConfigAst = readJsonFileAsAstObject(tree, tsConfigPath) !;
131+
tsConfigAst = readJsonFileAsAstObject(tree, tsConfigPath)!;
124132
removePropertyInAstObject(recorder, tsConfigAst, 'exclude');
125133
tree.commitUpdate(recorder);
126134
}
127135
}
128136
}
129137
}
138+
139+
function updateModuleCompilerOption(tree: Tree, tsConfigPath: string, builderName?: Builders) {
140+
const tsConfigAst = readJsonFileAsAstObject(tree, tsConfigPath);
141+
142+
if (!tsConfigAst) {
143+
return;
144+
}
145+
146+
const compilerOptions = findPropertyInAstObject(tsConfigAst, 'compilerOptions');
147+
if (!compilerOptions || compilerOptions.kind !== 'object') {
148+
return;
149+
}
150+
151+
const configExtends = findPropertyInAstObject(tsConfigAst, 'extends');
152+
const isExtendedConfig = configExtends && configExtends.kind === 'string';
153+
const recorder = tree.beginUpdate(tsConfigPath);
154+
155+
// Server tsconfig should have a module of commonjs
156+
const moduleType = builderName === Builders.Server ? 'commonjs' : 'esnext';
157+
if (isExtendedConfig && builderName !== Builders.Server) {
158+
removePropertyInAstObject(recorder, compilerOptions, 'module');
159+
} else {
160+
const scriptModule = findPropertyInAstObject(compilerOptions, 'module');
161+
if (!scriptModule) {
162+
insertPropertyInAstObjectInOrder(recorder, compilerOptions, 'module', moduleType, 4);
163+
} else if (scriptModule.value !== moduleType) {
164+
const { start, end } = scriptModule;
165+
recorder.remove(start.offset, end.offset - start.offset);
166+
recorder.insertLeft(start.offset, `"${moduleType}"`);
167+
}
168+
}
169+
170+
tree.commitUpdate(recorder);
171+
}

packages/schematics/angular/migrations/update-9/update-app-tsconfigs_spec.ts

+76
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ function overrideJsonFile(tree: UnitTestTree, path: string, newContent: object)
1717
const defaultTsConfigOptions = {
1818
extends: './tsconfig.json',
1919
compilerOptions: {
20+
module: 'es2015',
2021
outDir: './out-tsc/app',
2122
types: [],
2223
},
@@ -146,5 +147,80 @@ describe('Migration to version 9', () => {
146147
expect(angularCompilerOptions.enableIvy).toBe(false);
147148
expect(angularCompilerOptions.fullTemplateTypeCheck).toBe(true);
148149
});
150+
151+
it(`should remove 'module' if in an extended configuration`, async () => {
152+
const tsConfigContent = {
153+
...defaultTsConfigOptions,
154+
angularCompilerOptions: {
155+
enableIvy: true,
156+
fullTemplateTypeCheck: true,
157+
},
158+
};
159+
160+
overrideJsonFile(tree, 'tsconfig.app.json', tsConfigContent);
161+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
162+
const { compilerOptions } = JSON.parse(tree2.readContent('tsconfig.app.json'));
163+
expect(compilerOptions.module).toBeUndefined();
164+
165+
const { compilerOptions: workspaceCompilerOptions } = JSON.parse(tree2.readContent('tsconfig.json'));
166+
expect(workspaceCompilerOptions.module).toBe('esnext');
167+
});
168+
169+
it(`should set 'module' to 'esnext' if app tsconfig is not extended`, async () => {
170+
const tsConfigContent = {
171+
...defaultTsConfigOptions,
172+
extends: undefined,
173+
angularCompilerOptions: {
174+
enableIvy: true,
175+
fullTemplateTypeCheck: true,
176+
},
177+
};
178+
179+
overrideJsonFile(tree, 'tsconfig.app.json', tsConfigContent);
180+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
181+
const { compilerOptions } = JSON.parse(tree2.readContent('tsconfig.app.json'));
182+
expect(compilerOptions.module).toBe('esnext');
183+
});
184+
185+
it(`should set 'module' to 'commonjs' in server tsconfig`, async () => {
186+
const tsConfigContent = {
187+
...defaultTsConfigOptions,
188+
compilerOptions: {
189+
module: 'es2015',
190+
outDir: './out-tsc/server',
191+
},
192+
angularCompilerOptions: {
193+
enableIvy: true,
194+
},
195+
};
196+
197+
tree = await schematicRunner
198+
.runExternalSchematicAsync(
199+
require.resolve('../../collection.json'),
200+
'universal',
201+
{
202+
clientProject: 'migration-test',
203+
},
204+
tree,
205+
)
206+
.toPromise();
207+
208+
overrideJsonFile(tree, 'tsconfig.server.json', tsConfigContent);
209+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
210+
const { compilerOptions } = JSON.parse(tree2.readContent('tsconfig.server.json'));
211+
expect(compilerOptions.module).toBe('commonjs');
212+
});
213+
214+
it(`should set 'module' to 'esnext' in workspace tsconfig`, async () => {
215+
const tsConfigContent = {
216+
...defaultTsConfigOptions,
217+
extends: undefined,
218+
};
219+
220+
overrideJsonFile(tree, 'tsconfig.json', tsConfigContent);
221+
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
222+
const { compilerOptions } = JSON.parse(tree2.readContent('tsconfig.json'));
223+
expect(compilerOptions.module).toBe('esnext');
224+
});
149225
});
150226
});

0 commit comments

Comments
 (0)