refactor(@schematics/angular): remove migrations for version 14

This migrations are no longer needed as multiple versions update is not allowed.
This commit is contained in:
Alan Agius 2022-09-12 06:11:58 +00:00 committed by Douglas Parker
parent 4de9045de8
commit 71ff22cc0c
13 changed files with 1 additions and 936 deletions

View File

@ -1,34 +1,3 @@
{
"schematics": {
"update-angular-packages-version-prefix": {
"version": "14.0.0",
"factory": "./update-14/angular-packages-version-prefix",
"description": "Update Angular packages 'dependencies' and 'devDependencies' version prefix to '^' instead of '~'."
},
"update-tsconfig-target": {
"version": "14.0.0",
"factory": "./update-14/update-tsconfig-target",
"description": "Update TypeScript compilation target to 'ES2020'."
},
"remove-show-circular-dependencies-option": {
"version": "14.0.0",
"factory": "./update-14/remove-show-circular-dependencies-option",
"description": "Remove 'showCircularDependencies' option from browser and server builders."
},
"remove-default-project-option": {
"version": "14.0.0",
"factory": "./update-14/remove-default-project-option",
"description": "Remove 'defaultProject' option from workspace configuration. The project to use will be determined from the current working directory."
},
"replace-default-collection-option": {
"version": "14.0.0",
"factory": "./update-14/replace-default-collection-option",
"description": "Replace 'defaultCollection' option in workspace configuration with 'schematicCollections'."
},
"update-libraries-secondary-entrypoints": {
"version": "14.0.0",
"factory": "./update-14/update-libraries-secondary-entrypoints",
"description": "Remove 'package.json' files from library projects secondary entrypoints."
}
}
"schematics": {}
}

View File

@ -1,67 +0,0 @@
/**
* @license
* Copyright Google LLC 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 { Rule } from '@angular-devkit/schematics';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import { NodeDependencyType } from '../../utility/dependencies';
import { JSONFile } from '../../utility/json-file';
const PACKAGES_REGEXP = /^@(?:angular|nguniversal|schematics|angular-devkit)\/|^ng-packagr$/;
/**
* This migrations updates Angular packages 'dependencies' and 'devDependencies' version prefix to '^' instead of '~'.
*
* @example
* **Before**
* ```json
* dependencies: {
* "@angular/animations": "~13.1.0",
* "@angular/common": "~13.1.0"
* }
* ```
*
* **After**
* ```json
* dependencies: {
* "@angular/animations": "^13.1.0",
* "@angular/common": "^13.1.0"
* }
* ```
*/
export default function (): Rule {
return (tree, context) => {
const json = new JSONFile(tree, '/package.json');
updateVersionPrefixToTilde(json, NodeDependencyType.Default);
updateVersionPrefixToTilde(json, NodeDependencyType.Dev);
context.addTask(new NodePackageInstallTask());
};
}
function updateVersionPrefixToTilde(json: JSONFile, dependencyType: NodeDependencyType): void {
const dependencyTypePath = [dependencyType];
const dependencies = json.get(dependencyTypePath);
if (!dependencies || typeof dependencies !== 'object') {
return;
}
const updatedDependencies = new Map<string, string>();
for (const [name, version] of Object.entries(dependencies)) {
if (typeof version === 'string' && version.charAt(0) === '~' && PACKAGES_REGEXP.test(name)) {
updatedDependencies.set(name, `^${version.substring(1)}`);
}
}
if (updatedDependencies.size) {
json.modify(dependencyTypePath, {
...dependencies,
...Object.fromEntries(updatedDependencies),
});
}
}

View File

@ -1,153 +0,0 @@
/**
* @license
* Copyright Google LLC 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 { EmptyTree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
describe('Migration to update Angular packages version prefix to `^` instead of `~`', () => {
const packageJsonPath = '/package.json';
const schematicName = 'update-angular-packages-version-prefix';
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);
let tree: UnitTestTree;
beforeEach(() => {
tree = new UnitTestTree(new EmptyTree());
});
it(`should replace Angular packages versioned with '~' to '^'`, async () => {
tree.create(
packageJsonPath,
JSON.stringify({
dependencies: {
'@angular/animations': '~13.1.0',
'@angular/common': '~13.1.0',
'@angular/compiler': '~13.1.0',
'@angular/core': '~13.1.0',
'@angular/forms': '~13.1.0',
'@angular/platform-browser': '~13.1.0',
'@angular/platform-browser-dynamic': '~13.1.0',
'@angular/router': '~13.1.0',
'@nguniversal/commom': '^13.1.0',
'rxjs': '~7.4.0',
'tslib': '^2.3.0',
'zone.js': '~0.11.4',
},
devDependencies: {
'@angular-devkit/build-angular': '~13.1.3',
'@angular/cli': '~13.1.3',
'@angular/compiler-cli': '~13.1.0',
'@angular/localize': '^13.1.3',
'@types/jasmine': '~3.10.0',
'@types/node': '^14.15.0',
'jasmine-core': '~3.10.0',
'karma': '~6.3.0',
'karma-chrome-launcher': '~3.1.0',
'karma-coverage': '~2.1.0',
'karma-jasmine': '~4.0.0',
'karma-jasmine-html-reporter': '~1.7.0',
'ng-packagr': '~13.1.3',
'typescript': '~4.5.2',
},
}),
);
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const pkg = JSON.parse(newTree.readContent(packageJsonPath));
expect(pkg['dependencies']).toEqual({
'@angular/animations': '^13.1.0',
'@angular/common': '^13.1.0',
'@angular/compiler': '^13.1.0',
'@angular/core': '^13.1.0',
'@angular/forms': '^13.1.0',
'@angular/platform-browser': '^13.1.0',
'@angular/platform-browser-dynamic': '^13.1.0',
'@angular/router': '^13.1.0',
'@nguniversal/commom': '^13.1.0',
'rxjs': '~7.4.0',
'tslib': '^2.3.0',
'zone.js': '~0.11.4',
});
expect(pkg['devDependencies']).toEqual({
'@angular-devkit/build-angular': '^13.1.3',
'@angular/cli': '^13.1.3',
'@angular/compiler-cli': '^13.1.0',
'@angular/localize': '^13.1.3',
'@types/jasmine': '~3.10.0',
'@types/node': '^14.15.0',
'jasmine-core': '~3.10.0',
'karma': '~6.3.0',
'karma-chrome-launcher': '~3.1.0',
'karma-coverage': '~2.1.0',
'karma-jasmine': '~4.0.0',
'karma-jasmine-html-reporter': '~1.7.0',
'ng-packagr': '^13.1.3',
'typescript': '~4.5.2',
});
});
it('should not replace pinned Angular packages versions', async () => {
tree.create(
packageJsonPath,
JSON.stringify({
dependencies: {
'@angular/animations': '13.1.0',
'@angular/core': '~13.1.0',
},
devDependencies: {
'@angular-devkit/build-angular': '13.1.3',
},
}),
);
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const pkg = JSON.parse(newTree.readContent(packageJsonPath));
expect(pkg['dependencies']['@angular/animations']).toBe('13.1.0');
expect(pkg['dependencies']['@angular/core']).toBe('^13.1.0');
expect(pkg['devDependencies']['@angular-devkit/build-angular']).toBe('13.1.3');
});
it('should not error when `dependencies` is missing', async () => {
tree.create(
packageJsonPath,
JSON.stringify({
devDependencies: {
'@angular-devkit/build-angular': '~13.1.3',
},
}),
);
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const pkg = JSON.parse(newTree.readContent(packageJsonPath));
expect(pkg['dependencies']).toBeUndefined();
expect(pkg['devDependencies']['@angular-devkit/build-angular']).toBe('^13.1.3');
});
it('should not error when `devDependencies` is missing', async () => {
tree.create(
packageJsonPath,
JSON.stringify({
dependencies: {
'@angular-devkit/build-angular': '~13.1.3',
},
}),
);
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const pkg = JSON.parse(newTree.readContent(packageJsonPath));
expect(pkg['dependencies']['@angular-devkit/build-angular']).toBe('^13.1.3');
expect(pkg['devDependencies']).toBeUndefined();
});
});

View File

@ -1,17 +0,0 @@
/**
* @license
* Copyright Google LLC 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 { Rule } from '@angular-devkit/schematics';
import { updateWorkspace } from '../../utility/workspace';
/** Migration to remove 'defaultProject' option from angular.json. */
export default function (): Rule {
return updateWorkspace((workspace) => {
delete workspace.extensions['defaultProject'];
});
}

View File

@ -1,51 +0,0 @@
/**
* @license
* Copyright Google LLC 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 { EmptyTree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
import { WorkspaceSchema } from '../../utility/workspace-models';
describe(`Migration to remove 'defaultProject' option.`, () => {
const schematicName = 'remove-default-project-option';
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);
let tree: UnitTestTree;
beforeEach(() => {
tree = new UnitTestTree(new EmptyTree());
});
it(`should remove 'defaultProject'`, async () => {
const angularConfig: WorkspaceSchema = {
version: 1,
projects: {},
defaultProject: 'foo',
};
tree.create('/angular.json', JSON.stringify(angularConfig, undefined, 2));
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const { defaultProject } = JSON.parse(newTree.readContent('/angular.json'));
expect(defaultProject).toBeUndefined();
});
it(`should not error when 'defaultProject' is not defined`, async () => {
const angularConfig: WorkspaceSchema = {
version: 1,
projects: {},
};
tree.create('/angular.json', JSON.stringify(angularConfig, undefined, 2));
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const { defaultProject } = JSON.parse(newTree.readContent('/angular.json'));
expect(defaultProject).toBeUndefined();
});
});

View File

@ -1,28 +0,0 @@
/**
* @license
* Copyright Google LLC 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 { Rule } from '@angular-devkit/schematics';
import { allTargetOptions, updateWorkspace } from '../../utility/workspace';
/** Migration to remove 'showCircularDependencies' option from browser and server builders. */
export default function (): Rule {
return updateWorkspace((workspace) => {
for (const project of workspace.projects.values()) {
for (const target of project.targets.values()) {
if (
target.builder === '@angular-devkit/build-angular:server' ||
target.builder === '@angular-devkit/build-angular:browser'
) {
for (const [, options] of allTargetOptions(target)) {
delete options.showCircularDependencies;
}
}
}
}
});
}

View File

@ -1,82 +0,0 @@
/**
* @license
* Copyright Google LLC 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 { JsonObject } from '@angular-devkit/core';
import { EmptyTree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
import {
BuilderTarget,
Builders,
ProjectType,
WorkspaceSchema,
} from '../../utility/workspace-models';
function getBuildTarget(tree: UnitTestTree): BuilderTarget<Builders.Browser, JsonObject> {
return JSON.parse(tree.readContent('/angular.json')).projects.app.architect.build;
}
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: {
extractCss: false,
showCircularDependencies: true,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any,
configurations: {
one: {
showCircularDependencies: false,
aot: true,
},
two: {
showCircularDependencies: false,
aot: true,
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any,
},
},
},
},
};
tree.create('/angular.json', JSON.stringify(angularConfig, undefined, 2));
}
describe(`Migration to remove 'showCircularDependencies' option.`, () => {
const schematicName = 'remove-show-circular-dependencies-option';
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);
let tree: UnitTestTree;
beforeEach(() => {
tree = new UnitTestTree(new EmptyTree());
createWorkSpaceConfig(tree);
});
it(`should remove 'showCircularDependencies'`, async () => {
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const { options, configurations } = getBuildTarget(newTree);
expect(options.showCircularDependencies).toBeUndefined();
expect(configurations).toBeDefined();
expect(configurations?.one.showCircularDependencies).toBeUndefined();
expect(configurations?.two.showCircularDependencies).toBeUndefined();
});
});

View File

@ -1,35 +0,0 @@
/**
* @license
* Copyright Google LLC 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 { JsonValue, isJsonObject } from '@angular-devkit/core';
import { Rule } from '@angular-devkit/schematics';
import { updateWorkspace } from '../../utility/workspace';
/** Migration to replace 'defaultCollection' option in angular.json. */
export default function (): Rule {
return updateWorkspace((workspace) => {
// workspace level
replaceDefaultCollection(workspace.extensions['cli']);
// Project level
for (const project of workspace.projects.values()) {
replaceDefaultCollection(project.extensions['cli']);
}
});
}
function replaceDefaultCollection(cliExtension: JsonValue | undefined): void {
if (cliExtension && isJsonObject(cliExtension) && cliExtension['defaultCollection']) {
// If `schematicsCollection` defined `defaultCollection` is ignored hence no need to warn.
if (!cliExtension['schematicCollections']) {
cliExtension['schematicCollections'] = [cliExtension['defaultCollection']];
}
delete cliExtension['defaultCollection'];
}
}

View File

@ -1,101 +0,0 @@
/**
* @license
* Copyright Google LLC 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 { EmptyTree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
import { ProjectType, WorkspaceSchema } from '../../utility/workspace-models';
describe(`Migration to replace 'defaultCollection' option.`, () => {
const schematicName = 'replace-default-collection-option';
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);
let tree: UnitTestTree;
beforeEach(() => {
tree = new UnitTestTree(new EmptyTree());
});
it(`should replace 'defaultCollection' with 'schematicCollections' at the root level`, async () => {
const angularConfig: WorkspaceSchema = {
version: 1,
projects: {},
cli: {
defaultCollection: 'foo',
},
};
tree.create('/angular.json', JSON.stringify(angularConfig, undefined, 2));
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const { cli } = JSON.parse(newTree.readContent('/angular.json'));
expect(cli.defaultCollection).toBeUndefined();
expect(cli.schematicCollections).toEqual(['foo']);
});
it(`should not error when 'cli' is not defined`, async () => {
const angularConfig: WorkspaceSchema = {
version: 1,
projects: {},
};
tree.create('/angular.json', JSON.stringify(angularConfig, undefined, 2));
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const { cli } = JSON.parse(newTree.readContent('/angular.json'));
expect(cli).toBeUndefined();
});
it(`should replace 'defaultCollection' with 'schematicCollections' at the project level`, async () => {
const angularConfig: WorkspaceSchema = {
version: 1,
cli: {
defaultCollection: 'foo',
},
projects: {
test: {
sourceRoot: '',
root: '',
prefix: '',
projectType: ProjectType.Application,
cli: {
defaultCollection: 'bar',
},
},
},
};
tree.create('/angular.json', JSON.stringify(angularConfig, undefined, 2));
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const {
projects: { test },
} = JSON.parse(newTree.readContent('/angular.json'));
expect(test.cli.defaultCollection).toBeUndefined();
expect(test.cli.schematicCollections).toEqual(['bar']);
});
it(`should not replace 'defaultCollection' with 'schematicCollections', when it is already defined`, async () => {
const angularConfig: WorkspaceSchema = {
version: 1,
projects: {},
cli: {
defaultCollection: 'foo',
schematicCollections: ['bar'],
},
};
tree.create('/angular.json', JSON.stringify(angularConfig, undefined, 2));
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const { cli } = JSON.parse(newTree.readContent('/angular.json'));
expect(cli.defaultCollection).toBeUndefined();
expect(cli.schematicCollections).toEqual(['bar']);
});
});

View File

@ -1,65 +0,0 @@
/**
* @license
* Copyright Google LLC 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, isJsonObject, join, normalize } from '@angular-devkit/core';
import { DirEntry, Rule } from '@angular-devkit/schematics';
import { getWorkspace } from '../../utility/workspace';
function* visitPackageJsonFiles(
directory: DirEntry,
includedInLookup = false,
): IterableIterator<string> {
if (includedInLookup) {
for (const path of directory.subfiles) {
if (path !== 'package.json') {
continue;
}
yield join(directory.path, path);
}
}
for (const path of directory.subdirs) {
if (path === 'node_modules' || path.startsWith('.')) {
continue;
}
yield* visitPackageJsonFiles(directory.dir(path), true);
}
}
/** Migration to remove secondary entrypoints 'package.json' files and migrate ng-packagr configurations. */
export default function (): Rule {
return async (tree) => {
const workspace = await getWorkspace(tree);
for (const project of workspace.projects.values()) {
if (
project.extensions['projectType'] !== 'library' ||
![...project.targets.values()].some(
({ builder }) => builder === '@angular-devkit/build-angular:ng-packagr',
)
) {
// Project is not a library or doesn't use ng-packagr, skip.
continue;
}
for (const path of visitPackageJsonFiles(tree.getDir(project.root))) {
const json = tree.readJson(path);
if (isJsonObject(json) && json['ngPackage']) {
// Migrate ng-packagr config to an ng-packagr config file.
const configFilePath = join(dirname(normalize(path)), 'ng-package.json');
tree.create(configFilePath, JSON.stringify(json['ngPackage'], undefined, 2));
}
// Delete package.json as it is no longer needed in APF 14.
tree.delete(path);
}
}
};
}

View File

@ -1,122 +0,0 @@
/**
* @license
* Copyright Google LLC 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 { EmptyTree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
import { Builders, ProjectType, WorkspaceSchema } from '../../utility/workspace-models';
function createFileStructure(tree: UnitTestTree) {
const angularConfig: WorkspaceSchema = {
version: 1,
projects: {
foo: {
root: 'projects/foo',
sourceRoot: 'projects/foo/src',
projectType: ProjectType.Library,
prefix: 'lib',
architect: {
build: {
builder: Builders.NgPackagr,
options: {
project: '',
tsConfig: '',
},
},
},
},
bar: {
root: 'projects/bar',
sourceRoot: 'projects/bar/src',
projectType: ProjectType.Library,
prefix: 'lib',
architect: {
test: {
builder: Builders.Karma,
options: {
karmaConfig: '',
tsConfig: '',
},
},
},
},
},
};
tree.create('/angular.json', JSON.stringify(angularConfig, undefined, 2));
// Library foo
tree.create('/projects/foo/package.json', JSON.stringify({ version: '0.0.0' }, undefined, 2));
// Library foo/secondary
tree.create(
'/projects/foo/secondary/package.json',
JSON.stringify(
{ version: '0.0.0', ngPackage: { lib: { entryFile: 'src/public-api.ts' } } },
undefined,
2,
),
);
// Library bar
tree.create('/projects/bar/package.json', JSON.stringify({ version: '0.0.0' }, undefined, 2));
// Library bar/secondary
tree.create(
'/projects/bar/secondary/package.json',
JSON.stringify({ version: '0.0.0' }, undefined, 2),
);
}
describe(`Migration to update Angular libraries secondary entrypoints.`, () => {
const schematicName = 'update-libraries-secondary-entrypoints';
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);
let tree: UnitTestTree;
beforeEach(() => {
tree = new UnitTestTree(new EmptyTree());
createFileStructure(tree);
});
describe(`when library has '@angular-devkit/build-angular:ng-packagr' as a builder`, () => {
it(`should not delete 'package.json' of primary entry-point`, async () => {
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
expect(newTree.exists('/projects/foo/package.json')).toBeTrue();
});
it(`should delete 'package.json' of secondary entry-point`, async () => {
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
expect(newTree.exists('/projects/foo/secondary/package.json')).toBeFalse();
});
it(`should move ng-packagr configuration from 'package.json' to 'ng-package.json'`, async () => {
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
expect(newTree.readJson('projects/foo/secondary/ng-package.json')).toEqual({
lib: { entryFile: 'src/public-api.ts' },
});
});
});
describe(`when library doesn't have '@angular-devkit/build-angular:ng-packagr' as a builder`, () => {
it(`should not delete 'package.json' of primary entry-point`, async () => {
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
expect(newTree.exists('/projects/bar/package.json')).toBeTrue();
});
it(`should not delete 'package.json' of secondary entry-point`, async () => {
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
expect(newTree.exists('/projects/bar/package.json')).toBeTrue();
});
it(`should not create ng-packagr configuration`, async () => {
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
expect(newTree.exists('projects/bar/secondary/ng-package.json')).toBeFalse();
});
});
});

View File

@ -1,56 +0,0 @@
/**
* @license
* Copyright Google LLC 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 { Rule } from '@angular-devkit/schematics';
import { JSONFile } from '../../utility/json-file';
import { getWorkspace } from '../../utility/workspace';
import { Builders } from '../../utility/workspace-models';
/** Migration to update tsconfig compilation target option to es2020. */
export default function (): Rule {
return async (host) => {
/** Builders for which the migration will run. */
const supportedBuilders = [Builders.Karma, Builders.NgPackagr, Builders.Browser];
/** Compilation targets values that should not be amended. */
const skipTargets = ['es2020', 'es2021', 'es2022', 'esnext'];
const uniqueTsConfigs = new Set(['/tsconfig.json']);
// Find all tsconfig files which are refereced by the builders.
const workspace = await getWorkspace(host);
for (const project of workspace.projects.values()) {
for (const target of project.targets.values()) {
if (!supportedBuilders.includes(target.builder as Builders)) {
// Unknown builder.
continue;
}
// Update all other known CLI builders that use a tsconfig.
const allOptions = [target.options ?? {}, ...Object.values(target.configurations ?? {})];
for (const opt of allOptions) {
if (typeof opt?.tsConfig === 'string') {
uniqueTsConfigs.add(opt.tsConfig);
}
}
}
}
// Modify tsconfig files
const targetJsonPath = ['compilerOptions', 'target'];
for (const tsConfigPath of uniqueTsConfigs) {
const json = new JSONFile(host, tsConfigPath);
const target = json.get(targetJsonPath);
// Update compilation target when it's current set lower than es2020.
if (typeof target === 'string' && !skipTargets.includes(target.toLowerCase())) {
json.modify(targetJsonPath, 'es2020');
}
}
};
}

View File

@ -1,127 +0,0 @@
/**
* @license
* Copyright Google LLC 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 { EmptyTree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
import { parse as parseJson } from 'jsonc-parser';
import { Builders, ProjectType, WorkspaceSchema } from '../../utility/workspace-models';
describe('Migration to update target compiler options', () => {
const schematicName = 'update-tsconfig-target';
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));
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function readJsonFile(tree: UnitTestTree, filePath: string): any {
return parseJson(tree.readContent(filePath).toString());
}
let tree: UnitTestTree;
beforeEach(() => {
tree = new UnitTestTree(new EmptyTree());
// Workspace configuration
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',
},
},
server: {
builder: Builders.Server,
options: {
tsConfig: 'src/tsconfig.server.json',
outputPath: '',
main: '',
},
},
},
},
},
};
createJsonFile(tree, 'angular.json', angularConfig);
// Create tsconfigs
const compilerOptions = { target: 'es5', 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 });
// Server
createJsonFile(tree, 'src/tsconfig.server.json', { compilerOptions });
});
it(`should update target in workspace 'tsconfig.json'`, async () => {
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const { target } = readJsonFile(newTree, 'tsconfig.json').compilerOptions;
expect(target).toBe('es2020');
});
it(`should update target in 'tsconfig.json' which is referenced in option`, async () => {
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const { target } = readJsonFile(newTree, 'src/tsconfig.spec.json').compilerOptions;
expect(target).toBe('es2020');
});
it(`should update target in 'tsconfig.json' which is referenced in a configuration`, async () => {
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const { target } = readJsonFile(newTree, 'src/tsconfig.app.prod.json').compilerOptions;
expect(target).toBe('es2020');
});
it(`should not update target in 'tsconfig.server.json'`, async () => {
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const { target } = readJsonFile(newTree, 'src/tsconfig.server.json').compilerOptions;
expect(target).toBe('es5');
});
it('should not update target if it is greater than es2020', async () => {
const tsConfigPath = 'src/tsconfig.app.json';
tree.delete(tsConfigPath);
createJsonFile(tree, tsConfigPath, { compilerOptions: { target: 'es2021' } });
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
const { target } = readJsonFile(newTree, tsConfigPath).compilerOptions;
expect(target).toBe('es2021');
});
});