refactor(@schematics/angular): remove version 15 migrations

These are no longer used.
This commit is contained in:
Alan Agius 2023-02-17 12:26:41 +00:00 committed by angular-robot[bot]
parent 05d9c91159
commit ada4d694c3
11 changed files with 1 additions and 999 deletions

View File

@ -1,29 +1,5 @@
{
"schematics": {
"remove-browserslist-config": {
"version": "15.0.0",
"factory": "./update-15/remove-browserslist-config",
"description": "Remove Browserslist configuration files that matches the Angular CLI default configuration."
},
"remove-platform-server-exports": {
"version": "15.0.0",
"factory": "./update-15/remove-platform-server-exports",
"description": "Remove exported `@angular/platform-server` `renderModule` method. The `renderModule` method is now exported by the Angular CLI."
},
"update-typescript-target": {
"version": "15.0.0",
"factory": "./update-15/update-typescript-target",
"description": "Update TypeScript compiler `target` and set `useDefineForClassFields`. These changes are for IDE purposes as TypeScript compiler options `target` and `useDefineForClassFields` are set to `ES2022` and `false` respectively by the Angular CLI. To control ECMA version and features use the Browerslist configuration."
},
"update-workspace-config": {
"version": "15.0.0",
"factory": "./update-15/update-workspace-config",
"description": "Remove options from 'angular.json' that are no longer supported by the official builders."
},
"update-karma-main-file": {
"version": "15.0.0",
"factory": "./update-15/update-karma-main-file",
"description": "Remove no longer needed require calls in Karma builder main file."
}
}
}

View File

@ -1,90 +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 { Path, join } from '@angular-devkit/core';
import { DirEntry, Rule } from '@angular-devkit/schematics';
const validBrowserslistConfigFilenames = new Set(['browserslist', '.browserslistrc']);
export const DEFAULT_BROWSERS = [
'last 1 Chrome version',
'last 1 Firefox version',
'last 2 Edge major versions',
'last 2 Safari major versions',
'last 2 iOS major versions',
'Firefox ESR',
];
function* visit(directory: DirEntry): IterableIterator<Path> {
for (const path of directory.subfiles) {
if (validBrowserslistConfigFilenames.has(path)) {
yield join(directory.path, path);
}
}
for (const path of directory.subdirs) {
if (path === 'node_modules') {
continue;
}
yield* visit(directory.dir(path));
}
}
export default function (): Rule {
return async (tree, { logger }) => {
let browserslist: typeof import('browserslist') | undefined;
try {
browserslist = (await import('browserslist')).default;
} catch {
logger.warn('Skipping migration because the "browserslist" package could not be loaded.');
return;
}
// Set the defaults to match the defaults in build-angular.
browserslist.defaults = DEFAULT_BROWSERS;
const defaultSupportedBrowsers = new Set(browserslist(DEFAULT_BROWSERS));
const es5Browsers = new Set(browserslist(['supports es6-module']));
for (const path of visit(tree.root)) {
const { defaults: browsersListConfig, ...otherConfigs } = browserslist.parseConfig(
tree.readText(path),
);
if (Object.keys(otherConfigs).length) {
// The config contains additional sections.
continue;
}
const browserslistInProject = browserslist(
// Exclude from the list ES5 browsers which are not supported.
browsersListConfig.map((s) => `${s} and supports es6-module`),
{
ignoreUnknownVersions: true,
},
);
if (defaultSupportedBrowsers.size !== browserslistInProject.length) {
continue;
}
const shouldDelete = browserslistInProject.every((browser) =>
defaultSupportedBrowsers.has(browser),
);
if (shouldDelete) {
// All browsers are the same as the default config.
// Delete file as it's redundant.
tree.delete(path);
}
}
};
}

View File

@ -1,84 +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 { DEFAULT_BROWSERS } from './remove-browserslist-config';
describe('Migration to delete Browserslist configurations', () => {
const schematicName = 'remove-browserslist-config';
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);
let tree: UnitTestTree;
beforeEach(() => {
tree = new UnitTestTree(new EmptyTree());
});
describe('given the Browserslist config matches the default', () => {
it('should delete ".browserslistrc" file', async () => {
tree.create('/src/app/.browserslistrc', DEFAULT_BROWSERS.join('\n'));
expect(tree.exists('/src/app/.browserslistrc')).toBeTrue();
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
expect(newTree.exists('/src/app/.browserslistrc')).toBeFalse();
});
it(`should not delete "browserslist" in 'node_modules'`, async () => {
tree.create('/node_modules/browserslist', DEFAULT_BROWSERS.join('\n'));
tree.create('/node_modules/.browserslistrc', DEFAULT_BROWSERS.join('\n'));
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
expect(newTree.exists('/node_modules/browserslist')).toBeTrue();
expect(newTree.exists('/node_modules/.browserslistrc')).toBeTrue();
});
});
describe('given the Browserslist config does not match the default', () => {
it('should not delete "browserslist"', async () => {
tree.create('/src/app/browserslist', 'last 1 Chrome version');
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
expect(newTree.exists('/src/app/browserslist')).toBeTrue();
});
it('should not delete ".browserslistrc"', async () => {
tree.create('/src/app/.browserslistrc', 'last 1 Chrome version');
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
expect(newTree.exists('/src/app/.browserslistrc')).toBeTrue();
});
it('should delete ".browserslistrc" file when it only includes non supported ES5 browsers', async () => {
tree.create('/src/app/.browserslistrc', [...DEFAULT_BROWSERS, 'IE 10'].join('\n'));
expect(tree.exists('/src/app/.browserslistrc')).toBeTrue();
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
expect(newTree.exists('/src/app/.browserslistrc')).toBeFalse();
});
it('should not delete ".browserslistrc" file when it includes additional config sections', async () => {
tree.create(
'/src/app/.browserslistrc',
`
${DEFAULT_BROWSERS.join('\n')}
[modern]
last 1 chrome version
`,
);
expect(tree.exists('/src/app/.browserslistrc')).toBeTrue();
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
expect(newTree.exists('/src/app/.browserslistrc')).toBeTrue();
});
});
});

View File

@ -1,99 +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 { DirEntry, Rule, UpdateRecorder } from '@angular-devkit/schematics';
import * as ts from '../../third_party/github.com/Microsoft/TypeScript/lib/typescript';
function* visit(directory: DirEntry): IterableIterator<ts.SourceFile> {
for (const path of directory.subfiles) {
if (path.endsWith('.ts') && !path.endsWith('.d.ts')) {
const entry = directory.file(path);
if (entry) {
const content = entry.content;
if (content.includes('@angular/platform-server') && content.includes('renderModule')) {
const source = ts.createSourceFile(
entry.path,
content.toString().replace(/^\uFEFF/, ''),
ts.ScriptTarget.Latest,
true,
);
yield source;
}
}
}
}
for (const path of directory.subdirs) {
if (path === 'node_modules' || path.startsWith('.')) {
continue;
}
yield* visit(directory.dir(path));
}
}
export default function (): Rule {
return (tree) => {
for (const sourceFile of visit(tree.root)) {
let recorder: UpdateRecorder | undefined;
let printer: ts.Printer | undefined;
ts.forEachChild(sourceFile, function analyze(node) {
if (
!(
ts.isExportDeclaration(node) &&
node.moduleSpecifier &&
ts.isStringLiteral(node.moduleSpecifier) &&
node.moduleSpecifier.text === '@angular/platform-server' &&
node.exportClause &&
ts.isNamedExports(node.exportClause)
)
) {
// Not a @angular/platform-server named export.
return;
}
const exportClause = node.exportClause;
const newElements: ts.ExportSpecifier[] = [];
for (const element of exportClause.elements) {
if (element.name.text !== 'renderModule') {
newElements.push(element);
}
}
if (newElements.length === exportClause.elements.length) {
// No changes
return;
}
recorder ??= tree.beginUpdate(sourceFile.fileName);
if (newElements.length) {
// Update named exports as there are leftovers.
const newExportClause = ts.factory.updateNamedExports(exportClause, newElements);
printer ??= ts.createPrinter();
const fix = printer.printNode(ts.EmitHint.Unspecified, newExportClause, sourceFile);
const index = exportClause.getStart();
const length = exportClause.getWidth();
recorder.remove(index, length).insertLeft(index, fix);
} else {
// Delete export as no exports remain.
recorder.remove(node.getStart(), node.getWidth());
}
ts.forEachChild(node, analyze);
});
if (recorder) {
tree.commitUpdate(recorder);
}
}
};
}

View File

@ -1,85 +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 } from '@angular-devkit/schematics/testing';
describe('Migration to delete platform-server exports', () => {
const schematicName = 'remove-platform-server-exports';
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);
let tree: EmptyTree;
beforeEach(() => {
tree = new EmptyTree();
});
const testTypeScriptFilePath = './test.ts';
describe(`Migration to remove '@angular/platform-server' exports`, () => {
it(`should delete '@angular/platform-server' export when 'renderModule' is the only exported symbol`, async () => {
tree.create(
testTypeScriptFilePath,
`
import { Path, join } from '@angular-devkit/core';
export { renderModule } from '@angular/platform-server';
`,
);
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
const content = newTree.readText(testTypeScriptFilePath);
expect(content).not.toContain('@angular/platform-server');
expect(content).toContain(`import { Path, join } from '@angular-devkit/core';`);
});
it(`should delete only 'renderModule' when there are additional exports`, async () => {
tree.create(
testTypeScriptFilePath,
`
import { Path, join } from '@angular-devkit/core';
export { renderModule, ServerModule } from '@angular/platform-server';
`,
);
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
const content = newTree.readContent(testTypeScriptFilePath);
expect(content).toContain(`import { Path, join } from '@angular-devkit/core';`);
expect(content).toContain(`export { ServerModule } from '@angular/platform-server';`);
});
it(`should not delete 'renderModule' when it's exported from another module`, async () => {
tree.create(
testTypeScriptFilePath,
`
export { renderModule } from '@angular/core';
`,
);
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
const content = newTree.readText(testTypeScriptFilePath);
expect(content).toContain(`export { renderModule } from '@angular/core';`);
});
it(`should not delete 'renderModule' when it's imported from '@angular/platform-server'`, async () => {
tree.create(
testTypeScriptFilePath,
`
import { renderModule } from '@angular/platform-server';
`,
);
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
const content = newTree.readText(testTypeScriptFilePath);
expect(content).toContain(`import { renderModule } from '@angular/platform-server'`);
});
});
});

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 { Rule, Tree } from '@angular-devkit/schematics';
import * as ts from '../../third_party/github.com/Microsoft/TypeScript/lib/typescript';
import { readWorkspace } from '../../utility';
import { allTargetOptions } from '../../utility/workspace';
import { Builders } from '../../utility/workspace-models';
export default function (): Rule {
return async (host) => {
for (const file of await findTestMainFiles(host)) {
updateTestFile(host, file);
}
};
}
async function findTestMainFiles(host: Tree): Promise<Set<string>> {
const testFiles = new Set<string>();
const workspace = await readWorkspace(host);
// find all test.ts files.
for (const project of workspace.projects.values()) {
for (const target of project.targets.values()) {
if (target.builder !== Builders.Karma) {
continue;
}
for (const [, options] of allTargetOptions(target)) {
if (typeof options.main === 'string') {
testFiles.add(options.main);
}
}
}
}
return testFiles;
}
function updateTestFile(host: Tree, file: string): void {
const content = host.readText(file);
if (!content.includes('require.context')) {
return;
}
const sourceFile = ts.createSourceFile(
file,
content.replace(/^\uFEFF/, ''),
ts.ScriptTarget.Latest,
true,
);
const usedVariableNames = new Set<string>();
const recorder = host.beginUpdate(sourceFile.fileName);
ts.forEachChild(sourceFile, (node) => {
if (ts.isVariableStatement(node)) {
const variableDeclaration = node.declarationList.declarations[0];
if (ts.getModifiers(node)?.some((m) => m.kind === ts.SyntaxKind.DeclareKeyword)) {
// `declare const require`
if (variableDeclaration.name.getText() !== 'require') {
return;
}
} else {
// `const context = require.context('./', true, /\.spec\.ts$/);`
if (!variableDeclaration.initializer?.getText().startsWith('require.context')) {
return;
}
// add variable name as used.
usedVariableNames.add(variableDeclaration.name.getText());
}
// Delete node.
recorder.remove(node.getFullStart(), node.getFullWidth());
}
if (
usedVariableNames.size &&
ts.isExpressionStatement(node) && // context.keys().map(context);
ts.isCallExpression(node.expression) && // context.keys().map(context);
ts.isPropertyAccessExpression(node.expression.expression) && // context.keys().map
ts.isCallExpression(node.expression.expression.expression) && // context.keys()
ts.isPropertyAccessExpression(node.expression.expression.expression.expression) && // context.keys
ts.isIdentifier(node.expression.expression.expression.expression.expression) && // context
usedVariableNames.has(node.expression.expression.expression.expression.expression.getText())
) {
// `context.keys().map(context);`
// `context.keys().forEach(context);`
recorder.remove(node.getFullStart(), node.getFullWidth());
}
});
host.commitUpdate(recorder);
}

View File

@ -1,151 +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 { tags } 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';
function createWorkspace(tree: UnitTestTree): void {
const angularConfig: WorkspaceSchema = {
version: 1,
projects: {
app: {
root: '',
sourceRoot: 'src',
projectType: ProjectType.Application,
prefix: 'app',
architect: {
test: {
builder: Builders.Karma,
options: {
main: 'test.ts',
karmaConfig: './karma.config.js',
tsConfig: 'test-spec.json',
},
configurations: {
production: {
main: 'test-multiple-context.ts',
},
},
},
},
},
},
};
tree.create('/angular.json', JSON.stringify(angularConfig, undefined, 2));
tree.create(
'test.ts',
tags.stripIndents`
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(path: string, deep?: boolean, filter?: RegExp): {
<T>(id: string): T;
keys(): string[];
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
`,
);
tree.create(
'test-multiple-context.ts',
tags.stripIndents`
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: {
context(path: string, deep?: boolean, filter?: RegExp): {
<T>(id: string): T;
keys(): string[];
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
);
// Then we find all the tests.
const context1 = require.context('./', true, /\.spec\.ts$/);
const context2 = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context2.keys().forEach(context2);
context1.keys().map(context1);
`,
);
}
describe(`Migration to karma builder main file (test.ts)`, () => {
const schematicName = 'update-karma-main-file';
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);
let tree: UnitTestTree;
beforeEach(() => {
tree = new UnitTestTree(new EmptyTree());
createWorkspace(tree);
});
it(`should remove 'declare const require' and 'require.context' usages`, async () => {
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
expect(newTree.readText('test.ts')).toBe(tags.stripIndents`
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
);
`);
});
it(`should remove multiple 'require.context' usages`, async () => {
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
expect(newTree.readText('test-multiple-context.ts')).toBe(tags.stripIndents`
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
);
`);
});
});

View File

@ -1,87 +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 { Rule, Tree } from '@angular-devkit/schematics';
import { JSONFile } from '../../utility/json-file';
import { getWorkspace } from '../../utility/workspace';
import { Builders } from '../../utility/workspace-models';
export default function (): Rule {
return async (host, context) => {
// Workspace level tsconfig
updateTarget(host, 'tsconfig.json');
const workspace = await getWorkspace(host);
// Find all tsconfig which are refereces used by builders
for (const [, project] of workspace.projects) {
for (const [targetName, target] of project.targets) {
// 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);
for (const tsConfig of uniqueTsConfigs) {
if (host.exists(tsConfig)) {
continue;
}
uniqueTsConfigs.delete(tsConfig);
context.logger.warn(
`'${tsConfig}' referenced in the '${targetName}' target does not exist.`,
);
}
if (!uniqueTsConfigs.size) {
continue;
}
switch (target.builder as Builders) {
case Builders.Server:
case Builders.Karma:
case Builders.Browser:
case Builders.NgPackagr:
for (const tsConfig of uniqueTsConfigs) {
removeOrUpdateTarget(host, tsConfig);
}
break;
}
}
}
};
}
function removeOrUpdateTarget(host: Tree, tsConfigPath: string): void {
const json = new JSONFile(host, tsConfigPath);
if (typeof json.get(['extends']) === 'string') {
json.remove(['compilerOptions', 'target']);
} else {
updateTarget(host, tsConfigPath);
}
}
const ESNEXT_ES2022_REGEXP = /^es(?:next|2022)$/i;
function updateTarget(host: Tree, tsConfigPath: string): void {
const json = new JSONFile(host, tsConfigPath);
const jsonPath = ['compilerOptions'];
const compilerOptions = json.get(jsonPath);
if (compilerOptions && typeof compilerOptions === 'object') {
const { target } = compilerOptions as JsonObject;
if (typeof target === 'string' && !ESNEXT_ES2022_REGEXP.test(target)) {
json.modify(jsonPath, {
...compilerOptions,
'target': 'ES2022',
'useDefineForClassFields': false,
});
}
}
}

View File

@ -1,147 +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 { isJsonObject } 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 add useDefineForClassFields', () => {
const schematicName = 'update-typescript-target';
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);
function createJsonFile(tree: EmptyTree, filePath: string, content: {}): void {
const stringifiedContent = JSON.stringify(content, undefined, 2);
if (tree.exists(filePath)) {
tree.overwrite(filePath, stringifiedContent);
} else {
tree.create(filePath, stringifiedContent);
}
}
function getCompilerOptionsValue(tree: UnitTestTree, filePath: string): Record<string, unknown> {
const json = tree.readJson(filePath);
if (isJsonObject(json) && isJsonObject(json.compilerOptions)) {
return json.compilerOptions;
}
throw new Error(`Cannot retrieve 'compilerOptions'.`);
}
function createWorkSpaceConfig(tree: EmptyTree) {
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',
},
},
},
},
},
};
createJsonFile(tree, 'angular.json', angularConfig);
}
let tree: EmptyTree;
beforeEach(() => {
tree = new EmptyTree();
createWorkSpaceConfig(tree);
// Create tsconfigs
const compilerOptions = { target: 'es2015', module: 'es2020' };
const configWithExtends = { extends: './tsconfig.json', compilerOptions };
// Workspace
createJsonFile(tree, 'tsconfig.json', { compilerOptions });
// Application
createJsonFile(tree, 'src/tsconfig.app.json', configWithExtends);
createJsonFile(tree, 'src/tsconfig.app.prod.json', configWithExtends);
createJsonFile(tree, 'src/tsconfig.spec.json', { compilerOptions });
});
it(`should update target and add useDefineForClassFields in workspace 'tsconfig.json'`, async () => {
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
const compilerOptions = getCompilerOptionsValue(newTree, 'tsconfig.json');
expect(compilerOptions).toEqual(
jasmine.objectContaining({
target: 'ES2022',
useDefineForClassFields: false,
}),
);
});
it(`should remove target value from tsconfig referenced in options and configuration`, async () => {
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
{
const compilerOptions = getCompilerOptionsValue(newTree, 'src/tsconfig.app.prod.json');
expect(compilerOptions['target']).toBeUndefined();
expect(compilerOptions['useDefineForClassFields']).toBeUndefined();
}
{
const compilerOptions = getCompilerOptionsValue(newTree, 'src/tsconfig.app.json');
expect(compilerOptions['target']).toBeUndefined();
expect(compilerOptions['useDefineForClassFields']).toBeUndefined();
}
});
it('should add target and useDefineForClassFields when tsconfig is not extended', async () => {
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
const compilerOptions = getCompilerOptionsValue(newTree, 'src/tsconfig.spec.json');
expect(compilerOptions).toEqual(
jasmine.objectContaining({
target: 'ES2022',
useDefineForClassFields: false,
}),
);
});
it('should not add useDefineForClassFields when tsconfig target is ES2022', async () => {
createJsonFile(tree, 'tsconfig.json', { compilerOptions: { 'target': 'es2022' } });
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
const compilerOptions = getCompilerOptionsValue(newTree, 'tsconfig.json');
expect(compilerOptions).toEqual({ target: 'es2022' });
});
it('should not add useDefineForClassFields when tsconfig target is ESNEXT', async () => {
createJsonFile(tree, 'tsconfig.json', { compilerOptions: { 'target': 'esnext' } });
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
const compilerOptions = getCompilerOptionsValue(newTree, 'tsconfig.json');
expect(compilerOptions).toEqual({ target: 'esnext' });
});
});

View File

@ -1,31 +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';
import { Builders } from '../../utility/workspace-models';
export default function (): Rule {
return updateWorkspace((workspace) => {
for (const project of workspace.projects.values()) {
for (const target of project.targets.values()) {
if (target.builder !== Builders.Server) {
continue;
}
for (const [name, options] of allTargetOptions(target)) {
delete options.bundleDependencies;
if (name === 'development') {
options.vendorChunk ??= true;
}
}
}
}
});
}

View File

@ -1,99 +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 getServerTarget(tree: UnitTestTree): BuilderTarget<Builders.Server, JsonObject> {
const target = (tree.readJson('/angular.json') as unknown as WorkspaceSchema).projects.app
.architect?.server;
return target as unknown as BuilderTarget<Builders.Server, JsonObject>;
}
function createWorkSpaceConfig(tree: UnitTestTree) {
const angularConfig: WorkspaceSchema = {
version: 1,
projects: {
app: {
root: '',
sourceRoot: 'src',
projectType: ProjectType.Application,
prefix: 'app',
architect: {
server: {
builder: Builders.Server,
options: {
main: './server.ts',
bundleDependencies: false,
sourceMaps: true,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any,
configurations: {
one: {
aot: true,
},
two: {
bundleDependencies: true,
aot: true,
},
development: {
bundleDependencies: true,
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any,
},
},
},
},
};
tree.create('/angular.json', JSON.stringify(angularConfig, undefined, 2));
}
const schematicName = 'update-workspace-config';
describe(`Migration to update 'angular.json'. ${schematicName}`, () => {
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);
let tree: UnitTestTree;
beforeEach(() => {
tree = new UnitTestTree(new EmptyTree());
createWorkSpaceConfig(tree);
});
it(`should remove 'bundleDependencies'`, async () => {
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
const { options, configurations } = getServerTarget(newTree);
expect(options.bundleDependencies).toBeUndefined();
expect(configurations).toBeDefined();
expect(configurations?.one.bundleDependencies).toBeUndefined();
expect(configurations?.two.bundleDependencies).toBeUndefined();
});
it(`should add 'vendorChunk: true' to development configuration`, async () => {
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
const { options, configurations } = getServerTarget(newTree);
expect(options.bundleDependencies).toBeUndefined();
expect(configurations).toBeDefined();
expect(configurations?.development.vendorChunk).toBeTrue();
expect(configurations?.one.vendorChunk).toBeUndefined();
});
});