Skip to content

Commit

Permalink
fix(@schematics/angular): migrate module compiler option
Browse files Browse the repository at this point in the history
This migration coverts the TypeScript `module` compiler option to `esnext` or `commonjs` which is required when using `import()`.

Fixes: #16094
  • Loading branch information
alan-agius4 authored and vikerman committed Nov 7, 2019
1 parent 0def8ff commit 39c56cf
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,26 @@ import { getAllOptions, getTargets, getWorkspace, readJsonFileAsAstObject } from
* Update the tsconfig files for applications
* - Removes enableIvy: true
* - Sets stricter file inclusions
* - Sets module compiler option to esnext or commonjs
*/
export function updateApplicationTsConfigs(): Rule {
return (tree, context) => {
const workspace = getWorkspace(tree);
const logger = context.logger;

// Add `module` option in the workspace tsconfig
updateModuleCompilerOption(tree, '/tsconfig.json');

for (const { target } of getTargets(workspace, 'build', Builders.Browser)) {
updateTsConfig(tree, target, Builders.Browser, context.logger);
updateTsConfig(tree, target, Builders.Browser, logger);
}

for (const { target } of getTargets(workspace, 'server', Builders.Server)) {
updateTsConfig(tree, target, Builders.Server, context.logger);
updateTsConfig(tree, target, Builders.Server, logger);
}

for (const { target } of getTargets(workspace, 'test', Builders.Karma)) {
updateTsConfig(tree, target, Builders.Karma, context.logger);
updateTsConfig(tree, target, Builders.Karma, logger);
}

return tree;
Expand Down Expand Up @@ -74,14 +79,17 @@ function updateTsConfig(tree: Tree, builderConfig: JsonAstObject, builderName: B
}
}

// Update 'module' compilerOption
updateModuleCompilerOption(tree, tsConfigPath, builderName);

// Add stricter file inclusions to avoid unused file warning during compilation
if (builderName !== Builders.Karma) {
// Note: we need to re-read the tsconfig after very commit because
// otherwise the updates will be out of sync since we are ammending the same node.

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

if (include && include.kind === 'array') {
Expand Down Expand Up @@ -113,17 +121,51 @@ function updateTsConfig(tree: Tree, builderConfig: JsonAstObject, builderName: B
if (newFiles.length) {
recorder = tree.beginUpdate(tsConfigPath);
// tslint:disable-next-line: no-non-null-assertion
tsConfigAst = readJsonFileAsAstObject(tree, tsConfigPath) !;
tsConfigAst = readJsonFileAsAstObject(tree, tsConfigPath)!;
insertPropertyInAstObjectInOrder(recorder, tsConfigAst, 'files', newFiles, 2);
tree.commitUpdate(recorder);
}

recorder = tree.beginUpdate(tsConfigPath);
// tslint:disable-next-line: no-non-null-assertion
tsConfigAst = readJsonFileAsAstObject(tree, tsConfigPath) !;
tsConfigAst = readJsonFileAsAstObject(tree, tsConfigPath)!;
removePropertyInAstObject(recorder, tsConfigAst, 'exclude');
tree.commitUpdate(recorder);
}
}
}
}

function updateModuleCompilerOption(tree: Tree, tsConfigPath: string, builderName?: Builders) {
const tsConfigAst = readJsonFileAsAstObject(tree, tsConfigPath);

if (!tsConfigAst) {
return;
}

const compilerOptions = findPropertyInAstObject(tsConfigAst, 'compilerOptions');
if (!compilerOptions || compilerOptions.kind !== 'object') {
return;
}

const configExtends = findPropertyInAstObject(tsConfigAst, 'extends');
const isExtendedConfig = configExtends && configExtends.kind === 'string';
const recorder = tree.beginUpdate(tsConfigPath);

// Server tsconfig should have a module of commonjs
const moduleType = builderName === Builders.Server ? 'commonjs' : 'esnext';
if (isExtendedConfig && builderName !== Builders.Server) {
removePropertyInAstObject(recorder, compilerOptions, 'module');
} else {
const scriptModule = findPropertyInAstObject(compilerOptions, 'module');
if (!scriptModule) {
insertPropertyInAstObjectInOrder(recorder, compilerOptions, 'module', moduleType, 4);
} else if (scriptModule.value !== moduleType) {
const { start, end } = scriptModule;
recorder.remove(start.offset, end.offset - start.offset);
recorder.insertLeft(start.offset, `"${moduleType}"`);
}
}

tree.commitUpdate(recorder);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ function overrideJsonFile(tree: UnitTestTree, path: string, newContent: object)
const defaultTsConfigOptions = {
extends: './tsconfig.json',
compilerOptions: {
module: 'es2015',
outDir: './out-tsc/app',
types: [],
},
Expand Down Expand Up @@ -146,5 +147,80 @@ describe('Migration to version 9', () => {
expect(angularCompilerOptions.enableIvy).toBe(false);
expect(angularCompilerOptions.fullTemplateTypeCheck).toBe(true);
});

it(`should remove 'module' if in an extended configuration`, async () => {
const tsConfigContent = {
...defaultTsConfigOptions,
angularCompilerOptions: {
enableIvy: true,
fullTemplateTypeCheck: true,
},
};

overrideJsonFile(tree, 'tsconfig.app.json', tsConfigContent);
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
const { compilerOptions } = JSON.parse(tree2.readContent('tsconfig.app.json'));
expect(compilerOptions.module).toBeUndefined();

const { compilerOptions: workspaceCompilerOptions } = JSON.parse(tree2.readContent('tsconfig.json'));
expect(workspaceCompilerOptions.module).toBe('esnext');
});

it(`should set 'module' to 'esnext' if app tsconfig is not extended`, async () => {
const tsConfigContent = {
...defaultTsConfigOptions,
extends: undefined,
angularCompilerOptions: {
enableIvy: true,
fullTemplateTypeCheck: true,
},
};

overrideJsonFile(tree, 'tsconfig.app.json', tsConfigContent);
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
const { compilerOptions } = JSON.parse(tree2.readContent('tsconfig.app.json'));
expect(compilerOptions.module).toBe('esnext');
});

it(`should set 'module' to 'commonjs' in server tsconfig`, async () => {
const tsConfigContent = {
...defaultTsConfigOptions,
compilerOptions: {
module: 'es2015',
outDir: './out-tsc/server',
},
angularCompilerOptions: {
enableIvy: true,
},
};

tree = await schematicRunner
.runExternalSchematicAsync(
require.resolve('../../collection.json'),
'universal',
{
clientProject: 'migration-test',
},
tree,
)
.toPromise();

overrideJsonFile(tree, 'tsconfig.server.json', tsConfigContent);
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
const { compilerOptions } = JSON.parse(tree2.readContent('tsconfig.server.json'));
expect(compilerOptions.module).toBe('commonjs');
});

it(`should set 'module' to 'esnext' in workspace tsconfig`, async () => {
const tsConfigContent = {
...defaultTsConfigOptions,
extends: undefined,
};

overrideJsonFile(tree, 'tsconfig.json', tsConfigContent);
const tree2 = await schematicRunner.runSchematicAsync('migration-09', {}, tree.branch()).toPromise();
const { compilerOptions } = JSON.parse(tree2.readContent('tsconfig.json'));
expect(compilerOptions.module).toBe('esnext');
});
});
});

0 comments on commit 39c56cf

Please sign in to comment.