diff --git a/integration/angular_cli/e2e/tsconfig.json b/integration/angular_cli/e2e/tsconfig.json index 39b800f78961..c92199cfd63f 100644 --- a/integration/angular_cli/e2e/tsconfig.json +++ b/integration/angular_cli/e2e/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "outDir": "../out-tsc/e2e", "module": "commonjs", - "target": "es5", + "target": "es2018", "types": [ "jasmine", "jasminewd2", diff --git a/integration/angular_cli/tsconfig.json b/integration/angular_cli/tsconfig.json index 8c4ef3bbae3a..ac05639a2820 100644 --- a/integration/angular_cli/tsconfig.json +++ b/integration/angular_cli/tsconfig.json @@ -7,7 +7,7 @@ "declaration": false, "downlevelIteration": true, "experimentalDecorators": true, - "module": "esnext", + "module": "es2020", "moduleResolution": "node", "importHelpers": true, "target": "es2015", diff --git a/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts b/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts index b7561498d3d9..dfb0b6e72eea 100644 --- a/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts +++ b/packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/common.ts @@ -193,7 +193,7 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { if (profilingEnabled) { extraPlugins.push( new debug.ProfilingPlugin({ - outputPath: path.resolve(root, `chrome-profiler-events${targetInFileName}.json`), + outputPath: path.resolve(root, 'chrome-profiler-events.json'), }), ); } @@ -302,7 +302,7 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration { apply(compiler: Compiler) { compiler.hooks.emit.tap('angular-cli-stats', compilation => { const data = JSON.stringify(compilation.getStats().toJson('verbose')); - compilation.assets[`stats${targetInFileName}.json`] = new RawSource(data); + compilation.assets['stats.json'] = new RawSource(data); }); } })(), diff --git a/packages/angular_devkit/build_angular/src/angular-cli-files/utilities/bundle-calculator.ts b/packages/angular_devkit/build_angular/src/angular-cli-files/utilities/bundle-calculator.ts index 8e380442a5c4..cffdf5d47eb6 100644 --- a/packages/angular_devkit/build_angular/src/angular-cli-files/utilities/bundle-calculator.ts +++ b/packages/angular_devkit/build_angular/src/angular-cli-files/utilities/bundle-calculator.ts @@ -32,6 +32,7 @@ export enum ThresholdSeverity { } enum DifferentialBuildType { + // FIXME: this should match the actual file suffix and not hardcoded. ORIGINAL = 'es2015', DOWNLEVEL = 'es5', } diff --git a/packages/angular_devkit/build_angular/test/hello-world-app/e2e/tsconfig.e2e.json b/packages/angular_devkit/build_angular/test/hello-world-app/e2e/tsconfig.e2e.json index 39b800f78961..c92199cfd63f 100644 --- a/packages/angular_devkit/build_angular/test/hello-world-app/e2e/tsconfig.e2e.json +++ b/packages/angular_devkit/build_angular/test/hello-world-app/e2e/tsconfig.e2e.json @@ -3,7 +3,7 @@ "compilerOptions": { "outDir": "../out-tsc/e2e", "module": "commonjs", - "target": "es5", + "target": "es2018", "types": [ "jasmine", "jasminewd2", diff --git a/packages/angular_devkit/build_angular/test/hello-world-app/tsconfig.json b/packages/angular_devkit/build_angular/test/hello-world-app/tsconfig.json index ea3c9bcf3ffd..0e3088946b77 100644 --- a/packages/angular_devkit/build_angular/test/hello-world-app/tsconfig.json +++ b/packages/angular_devkit/build_angular/test/hello-world-app/tsconfig.json @@ -10,7 +10,7 @@ "emitDecoratorMetadata": true, "experimentalDecorators": true, "target": "es2015", - "module": "esnext", + "module": "es2020", "typeRoots": [ "../node_modules/@types" ], diff --git a/packages/angular_devkit/build_ng_packagr/test/ng-packaged/tsconfig.json b/packages/angular_devkit/build_ng_packagr/test/ng-packaged/tsconfig.json index d9a4c4b50abe..2c5d470a7258 100644 --- a/packages/angular_devkit/build_ng_packagr/test/ng-packaged/tsconfig.json +++ b/packages/angular_devkit/build_ng_packagr/test/ng-packaged/tsconfig.json @@ -8,7 +8,7 @@ "moduleResolution": "node", "experimentalDecorators": true, "target": "es2015", - "module": "esnext", + "module": "es2020", "typeRoots": [ "node_modules/@types" ], diff --git a/packages/angular_devkit/build_webpack/test/angular-app/src/tsconfig.app.json b/packages/angular_devkit/build_webpack/test/angular-app/src/tsconfig.app.json index b90ffb0148a4..40a1cd9e3ec6 100644 --- a/packages/angular_devkit/build_webpack/test/angular-app/src/tsconfig.app.json +++ b/packages/angular_devkit/build_webpack/test/angular-app/src/tsconfig.app.json @@ -2,7 +2,7 @@ "extends": "../tsconfig.json", "compilerOptions": { "outDir": "../out-tsc/app", - "module": "es2015", + "module": "es2020", "types": [] }, "exclude": [ diff --git a/packages/schematics/angular/e2e/files/tsconfig.json.template b/packages/schematics/angular/e2e/files/tsconfig.json.template index cc3e6d1cb952..bb011f580373 100644 --- a/packages/schematics/angular/e2e/files/tsconfig.json.template +++ b/packages/schematics/angular/e2e/files/tsconfig.json.template @@ -3,7 +3,7 @@ "compilerOptions": { "outDir": "<%= relativePathToWorkspaceRoot %>/out-tsc/e2e", "module": "commonjs", - "target": "es5", + "target": "es2018", "types": [ "jasmine", "jasminewd2", diff --git a/packages/schematics/angular/migrations/migration-collection.json b/packages/schematics/angular/migrations/migration-collection.json index cd51f7fcca06..c8702d82b872 100644 --- a/packages/schematics/angular/migrations/migration-collection.json +++ b/packages/schematics/angular/migrations/migration-collection.json @@ -89,6 +89,11 @@ "version": "10.0.0-beta.3", "factory": "./update-10/side-effects-package-json", "description": "Create a special 'package.json' file that is used to tell the tools and bundlers whether the code under the app directory is free of code with non-local side-effect." + }, + "update-module-and-target-compiler-options": { + "version": "10.0.0-beta.3", + "factory": "./update-10/update-module-and-target-compiler-options", + "description": "Update 'module' and 'target' TypeScript compiler options." } } } diff --git a/packages/schematics/angular/migrations/update-10/update-module-and-target-compiler-options.ts b/packages/schematics/angular/migrations/update-10/update-module-and-target-compiler-options.ts new file mode 100644 index 000000000000..61b09054f3f8 --- /dev/null +++ b/packages/schematics/angular/migrations/update-10/update-module-and-target-compiler-options.ts @@ -0,0 +1,124 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import { dirname, join, normalize } from '@angular-devkit/core'; +import { Rule, Tree } from '@angular-devkit/schematics'; +import { findPropertyInAstObject, removePropertyInAstObject } from '../../utility/json-utils'; +import { getWorkspace } from '../../utility/workspace'; +import { Builders } from '../../utility/workspace-models'; +import { readJsonFileAsAstObject } from '../update-9/utils'; + + +interface ModuleAndTargetReplamenent { + oldModule?: string; + newModule?: string | false; + oldTarget?: string; + newTarget?: string; +} + +export default function (): Rule { + return async host => { + // Workspace level tsconfig + updateModuleAndTarget(host, 'tsconfig.json', { + oldModule: 'esnext', + newModule: 'es2020', + }); + + const workspace = await getWorkspace(host); + // Find all tsconfig which are refereces used by builders + for (const [, project] of workspace.projects) { + for (const [, target] of project.targets) { + // E2E builder doesn't reference a tsconfig but it uses one found in the root folder. + if (target.builder === Builders.Protractor && typeof target.options?.protractorConfig === 'string') { + const tsConfigPath = join(dirname(normalize(target.options.protractorConfig)), 'tsconfig.json'); + + updateModuleAndTarget(host, tsConfigPath, { + oldTarget: 'es5', + newTarget: 'es2018', + }); + + continue; + } + + // Update all other known CLI builders that use a tsconfig + const tsConfigs = [ + target.options || {}, + ...Object.values(target.configurations || {}), + ] + .filter(opt => typeof opt?.tsConfig === 'string') + .map(opt => (opt as { tsConfig: string }).tsConfig); + + const uniqueTsConfigs = [...new Set(tsConfigs)]; + + if (uniqueTsConfigs.length < 1) { + continue; + } + + switch (target.builder as Builders) { + case Builders.Server: + uniqueTsConfigs.forEach(p => { + updateModuleAndTarget(host, p, { + oldModule: 'commonjs', + // False will remove the module + // NB: For server we no longer use commonjs because it is bundled using webpack which has it's own module system. + // This ensures that lazy-loaded works on the server. + newModule: false, + }); + }); + break; + case Builders.Karma: + case Builders.Browser: + case Builders.NgPackagr: + uniqueTsConfigs.forEach(p => { + updateModuleAndTarget(host, p, { + oldModule: 'esnext', + newModule: 'es2020', + }); + }); + break; + } + } + } + }; +} + +function updateModuleAndTarget(host: Tree, tsConfigPath: string, replacements: ModuleAndTargetReplamenent) { + const jsonAst = readJsonFileAsAstObject(host, tsConfigPath); + if (!jsonAst) { + return; + } + + const compilerOptionsAst = findPropertyInAstObject(jsonAst, 'compilerOptions'); + if (compilerOptionsAst?.kind !== 'object') { + return; + } + + const { oldTarget, newTarget, newModule, oldModule } = replacements; + + const recorder = host.beginUpdate(tsConfigPath); + if (newTarget) { + const targetAst = findPropertyInAstObject(compilerOptionsAst, 'target'); + if (targetAst?.kind === 'string' && oldTarget === targetAst.value.toLowerCase()) { + const offset = targetAst.start.offset + 1; + recorder.remove(offset, targetAst.value.length); + recorder.insertLeft(offset, newTarget); + } + } + + if (newModule === false) { + removePropertyInAstObject(recorder, compilerOptionsAst, 'module'); + } else if (newModule) { + const moduleAst = findPropertyInAstObject(compilerOptionsAst, 'module'); + if (moduleAst?.kind === 'string' && oldModule === moduleAst.value.toLowerCase()) { + const offset = moduleAst.start.offset + 1; + recorder.remove(offset, moduleAst.value.length); + recorder.insertLeft(offset, newModule); + } + } + + host.commitUpdate(recorder); +} diff --git a/packages/schematics/angular/migrations/update-10/update-module-and-target-compiler-options_spec.ts b/packages/schematics/angular/migrations/update-10/update-module-and-target-compiler-options_spec.ts new file mode 100644 index 000000000000..058869d2d69c --- /dev/null +++ b/packages/schematics/angular/migrations/update-10/update-module-and-target-compiler-options_spec.ts @@ -0,0 +1,141 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import { JsonParseMode, parseJson } from '@angular-devkit/core'; +import { EmptyTree } from '@angular-devkit/schematics'; +import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'; +import { Builders, ProjectType, WorkspaceSchema } from '../../utility/workspace-models'; + +describe('Migration to update target and module compiler options', () => { + const schematicName = 'update-module-and-target-compiler-options'; + + const schematicRunner = new SchematicTestRunner( + 'migrations', + require.resolve('../migration-collection.json'), + ); + + function createJsonFile(tree: UnitTestTree, filePath: string, content: {}) { + tree.create(filePath, JSON.stringify(content, undefined, 2)); + } + + // tslint:disable-next-line: no-any + function readJsonFile(tree: UnitTestTree, filePath: string): any { + // tslint:disable-next-line: no-any + return parseJson(tree.readContent(filePath).toString(), JsonParseMode.Loose) as any; + } + + function createWorkSpaceConfig(tree: UnitTestTree) { + const angularConfig: WorkspaceSchema = { + version: 1, + projects: { + app: { + root: '', + sourceRoot: 'src', + projectType: ProjectType.Application, + prefix: 'app', + architect: { + build: { + builder: Builders.Browser, + options: { + tsConfig: 'src/tsconfig.app.json', + main: '', + polyfills: '', + }, + configurations: { + production: { + tsConfig: 'src/tsconfig.app.prod.json', + }, + }, + }, + test: { + builder: Builders.Karma, + options: { + karmaConfig: '', + tsConfig: 'src/tsconfig.spec.json', + }, + }, + e2e: { + builder: Builders.Protractor, + options: { + protractorConfig: 'src/e2e/protractor.conf.js', + devServerTarget: '', + }, + }, + server: { + builder: Builders.Server, + options: { + tsConfig: 'src/tsconfig.server.json', + outputPath: '', + main: '', + }, + }, + }, + }, + }, + }; + + createJsonFile(tree, 'angular.json', angularConfig); + } + + + let tree: UnitTestTree; + beforeEach(() => { + tree = new UnitTestTree(new EmptyTree()); + createWorkSpaceConfig(tree); + + // Create tsconfigs + const compilerOptions = { target: 'es2015', module: 'esnext' }; + + // Workspace + createJsonFile(tree, 'tsconfig.json', { compilerOptions }); + + // Application + createJsonFile(tree, 'src/tsconfig.app.json', { compilerOptions }); + createJsonFile(tree, 'src/tsconfig.app.prod.json', { compilerOptions }); + createJsonFile(tree, 'src/tsconfig.spec.json', { compilerOptions }); + + // E2E + createJsonFile(tree, 'src/e2e/protractor.conf.js', ''); + createJsonFile(tree, 'src/e2e/tsconfig.json', { compilerOptions: { module: 'commonjs', target: 'es5' } }); + + // Universal + createJsonFile(tree, 'src/tsconfig.server.json', { compilerOptions: { module: 'commonjs' } }); + }); + + it(`should update module and target in workspace 'tsconfig.json'`, async () => { + const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise(); + const { module } = readJsonFile(newTree, 'tsconfig.json').compilerOptions; + expect(module).toBe('es2020'); + }); + + it(`should update module and target in 'tsconfig.json' which is referenced in option`, async () => { + const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise(); + const { module } = readJsonFile(newTree, 'src/tsconfig.spec.json').compilerOptions; + expect(module).toBe('es2020'); + }); + + it(`should update module and target in 'tsconfig.json' which is referenced in a configuration`, async () => { + const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise(); + const { module } = readJsonFile(newTree, 'src/tsconfig.app.prod.json').compilerOptions; + expect(module).toBe('es2020'); + }); + + it(`should update target to es2018 in E2E 'tsconfig.json'`, async () => { + const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise(); + const { module, target } = readJsonFile(newTree, 'src/e2e/tsconfig.json').compilerOptions; + expect(module).toBe('commonjs'); + expect(target).toBe('es2018'); + }); + + + it(`should remove module in 'tsconfig.server.json'`, async () => { + const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise(); + const { module, target } = readJsonFile(newTree, 'src/tsconfig.server.json').compilerOptions; + expect(module).toBeUndefined(); + expect(target).toBeUndefined(); + }); +}); diff --git a/packages/schematics/angular/workspace/files/tsconfig.json.template b/packages/schematics/angular/workspace/files/tsconfig.json.template index ea8a5c4530a1..c92051232587 100644 --- a/packages/schematics/angular/workspace/files/tsconfig.json.template +++ b/packages/schematics/angular/workspace/files/tsconfig.json.template @@ -11,7 +11,7 @@ "declaration": false, "downlevelIteration": true, "experimentalDecorators": true, - "module": "esnext", + "module": "es2020", "moduleResolution": "node", "importHelpers": true, "target": "es2015", diff --git a/tests/legacy-cli/e2e/assets/1.7-project/e2e/tsconfig.e2e.json b/tests/legacy-cli/e2e/assets/1.7-project/e2e/tsconfig.e2e.json index 1d9e5edf0965..78b5000e2870 100644 --- a/tests/legacy-cli/e2e/assets/1.7-project/e2e/tsconfig.e2e.json +++ b/tests/legacy-cli/e2e/assets/1.7-project/e2e/tsconfig.e2e.json @@ -4,7 +4,7 @@ "outDir": "../out-tsc/e2e", "baseUrl": "./", "module": "commonjs", - "target": "es5", + "target": "es2018", "types": [ "jasmine", "jasminewd2", diff --git a/tests/legacy-cli/e2e/assets/7.0-project/e2e/tsconfig.e2e.json b/tests/legacy-cli/e2e/assets/7.0-project/e2e/tsconfig.e2e.json index a6dd62202824..db33721a1918 100644 --- a/tests/legacy-cli/e2e/assets/7.0-project/e2e/tsconfig.e2e.json +++ b/tests/legacy-cli/e2e/assets/7.0-project/e2e/tsconfig.e2e.json @@ -3,7 +3,7 @@ "compilerOptions": { "outDir": "../out-tsc/app", "module": "commonjs", - "target": "es5", + "target": "es2018", "types": [ "jasmine", "jasminewd2",