refactor(@schematics/angular): remove v19 migrations

These migrations are no longer executed.
This commit is contained in:
Alan Agius 2025-03-06 18:41:30 +00:00
parent b5a86709b7
commit dd7fe11f4b
5 changed files with 1 additions and 364 deletions

View File

@ -1,22 +1,12 @@
{
"schematics": {
"use-application-builder": {
"version": "19.0.0",
"version": "20.0.0",
"factory": "./use-application-builder/migration",
"description": "Migrate application projects to the new build system. Application projects that are using the '@angular-devkit/build-angular' package's 'browser' and/or 'browser-esbuild' builders will be migrated to use the new 'application' builder. You can read more about this, including known issues and limitations, here: https://angular.dev/tools/cli/build-system-migration",
"optional": true,
"recommended": true,
"documentation": "tools/cli/build-system-migration"
},
"update-workspace-config": {
"version": "19.0.0",
"factory": "./update-workspace-config/migration",
"description": "Update the workspace configuration by replacing deprecated options in 'angular.json' for compatibility with the latest Angular CLI changes."
},
"update-ssr-imports": {
"version": "19.0.0",
"factory": "./update-ssr-imports/migration",
"description": "Update '@angular/ssr' import paths to use the new '/node' entry point when 'CommonEngine' is detected."
}
}
}

View File

@ -1,97 +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.dev/license
*/
import { DirEntry, Rule, UpdateRecorder } from '@angular-devkit/schematics';
import * as ts from '../../third_party/github.com/Microsoft/TypeScript/lib/typescript';
import { getPackageJsonDependency } from '../../utility/dependencies';
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('CommonEngine') && !content.includes('@angular/ssr/node')) {
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));
}
}
/**
* Schematics rule that identifies and updates import declarations in TypeScript files.
* Specifically, it modifies imports of '@angular/ssr' by appending '/node' if the
* `CommonEngine` is used from the old entry point.
*
*/
export default function (): Rule {
return (tree) => {
if (!getPackageJsonDependency(tree, '@angular/ssr')) {
return;
}
for (const sourceFile of visit(tree.root)) {
let recorder: UpdateRecorder | undefined;
const allImportDeclarations = sourceFile.statements.filter((n) => ts.isImportDeclaration(n));
if (allImportDeclarations.length === 0) {
continue;
}
const ssrImports = allImportDeclarations.filter(
(n) => ts.isStringLiteral(n.moduleSpecifier) && n.moduleSpecifier.text === '@angular/ssr',
);
for (const ssrImport of ssrImports) {
const ssrNamedBinding = getNamedImports(ssrImport);
if (ssrNamedBinding) {
const isUsingOldEntryPoint = ssrNamedBinding.elements.some((e) =>
e.name.text.startsWith('CommonEngine'),
);
if (!isUsingOldEntryPoint) {
continue;
}
recorder ??= tree.beginUpdate(sourceFile.fileName);
recorder.insertRight(ssrImport.moduleSpecifier.getEnd() - 1, '/node');
}
}
if (recorder) {
tree.commitUpdate(recorder);
}
}
};
}
function getNamedImports(
importDeclaration: ts.ImportDeclaration | undefined,
): ts.NamedImports | undefined {
const namedBindings = importDeclaration?.importClause?.namedBindings;
if (namedBindings && ts.isNamedImports(namedBindings)) {
return namedBindings;
}
return undefined;
}

View File

@ -1,75 +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.dev/license
*/
import { tags } from '@angular-devkit/core';
import { EmptyTree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
describe('CommonEngine migration', () => {
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);
let tree: UnitTestTree;
beforeEach(() => {
tree = new UnitTestTree(new EmptyTree());
tree.create(
'package.json',
JSON.stringify({
dependencies: {
'@angular/ssr': '0.0.0',
},
}),
);
});
function runMigration(): Promise<UnitTestTree> {
return schematicRunner.runSchematic('update-ssr-imports', {}, tree);
}
it(`should replace 'CommonEngine*' imports from '@angular/ssr' to '@angular/ssr/node'`, async () => {
tree.create(
'/index.ts',
tags.stripIndents`
import { CommonEngine } from '@angular/ssr';
import type { CommonEngineOptions, CommonEngineRenderOptions } from '@angular/ssr';
`,
);
const newTree = await runMigration();
expect(newTree.readContent('/index.ts')).toBe(tags.stripIndents`
import { CommonEngine } from '@angular/ssr/node';
import type { CommonEngineOptions, CommonEngineRenderOptions } from '@angular/ssr/node';
`);
});
it(`should not replace 'CommonEngine*' imports from '@angular/ssr/node'`, async () => {
const input = tags.stripIndents`
import { CommonEngine } from '@angular/ssr/node';
import type { CommonEngineOptions, CommonEngineRenderOptions } from '@angular/ssr/node';
`;
tree.create('/index.ts', input);
const newTree = await runMigration();
expect(newTree.readContent('/index.ts')).toBe(input);
});
it(`should not replace 'CommonEngine*' imports from other package`, async () => {
const input = tags.stripIndents`
import { CommonEngine } from 'unknown';
import type { CommonEngineOptions, CommonEngineRenderOptions } from 'unknown';
`;
tree.create('/index.ts', input);
const newTree = await runMigration();
expect(newTree.readContent('/index.ts')).toBe(input);
});
});

View File

@ -1,89 +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.dev/license
*/
import { Rule } from '@angular-devkit/schematics';
import { allTargetOptions, updateWorkspace } from '../../utility/workspace';
import { Builders, ProjectType } from '../../utility/workspace-models';
/**
* Main entry point for the migration rule.
*
* This schematic migration performs updates to the Angular workspace configuration
* to ensure that application projects are properly configured with polyfills
* required for internationalization (`localize`).
*
* It specifically targets application projects that use either the `application`
* or `browser-esbuild` builders.
*
* The migration process involves:
*
* 1. Iterating over all projects in the workspace.
* 2. Checking each project to determine if it is an application-type project.
* 3. For each application project, examining the associated build targets.
* 4. If a build target's `localize` option is enabled but the polyfill
* `@angular/localize/init` is missing from the `polyfills` array, the polyfill
* is automatically added to ensure proper internationalization support.
*
* Additionally, this migration updates projects that use the `dev-server` or `extract-i18n`
* builders to ensure that deprecated `browserTarget` options are migrated to the
* newer `buildTarget` field.
*
*/
export default function (): Rule {
return updateWorkspace((workspace) => {
for (const project of workspace.projects.values()) {
if (project.extensions.projectType !== ProjectType.Application) {
continue;
}
for (const target of project.targets.values()) {
if (target.builder === Builders.DevServer || target.builder === Builders.ExtractI18n) {
// Migrate `browserTarget` to `buildTarget`
for (const [, options] of allTargetOptions(target, false)) {
if (options['browserTarget'] && !options['buildTarget']) {
options['buildTarget'] = options['browserTarget'];
}
delete options['browserTarget'];
}
}
// Check if the target uses application-related builders
if (
target.builder !== Builders.BuildApplication &&
target.builder !== Builders.Application &&
target.builder !== Builders.BrowserEsbuild
) {
continue;
}
// Check if polyfills include '@angular/localize/init'
const polyfills = target.options?.['polyfills'];
if (
Array.isArray(polyfills) &&
polyfills.some(
(polyfill) => typeof polyfill === 'string' && polyfill.startsWith('@angular/localize'),
)
) {
// Skip if the polyfill is already present
continue;
}
// Add '@angular/localize/init' polyfill if localize option is enabled
for (const [, options] of allTargetOptions(target, false)) {
if (options['localize']) {
target.options ??= {};
((target.options['polyfills'] ??= []) as string[]).push('@angular/localize/init');
break;
}
}
}
}
});
}

View File

@ -1,92 +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.dev/license
*/
import { EmptyTree } from '@angular-devkit/schematics';
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
import { ProjectType } from '../../utility/workspace-models';
function createWorkSpaceConfig(tree: UnitTestTree) {
const angularConfig = {
version: 1,
projects: {
app: {
root: '/project/app',
sourceRoot: '/project/app/src',
projectType: ProjectType.Application,
prefix: 'app',
architect: {
build: {
builder: '@angular/build:application',
options: {
localize: true,
polyfills: [],
},
},
},
},
},
};
tree.create('/angular.json', JSON.stringify(angularConfig, undefined, 2));
}
describe(`Migration to update the workspace configuration`, () => {
const schematicName = 'update-workspace-config';
const schematicRunner = new SchematicTestRunner(
'migrations',
require.resolve('../migration-collection.json'),
);
let tree: UnitTestTree;
beforeEach(() => {
tree = new UnitTestTree(new EmptyTree());
createWorkSpaceConfig(tree);
});
it(`should add '@angular/localize/init' to polyfills if localize is enabled`, async () => {
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
const {
projects: { app },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} = newTree.readJson('/angular.json') as any;
expect(app.architect.build.options.polyfills).toContain('@angular/localize/init');
});
it(`should not add '@angular/localize/init' to polyfills if it already exists`, async () => {
// Add '@angular/localize/init' manually
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const config = tree.readJson('/angular.json') as any;
config.projects.app.architect.build.options.polyfills.push('@angular/localize/init');
tree.overwrite('/angular.json', JSON.stringify(config, undefined, 2));
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
const {
projects: { app },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} = newTree.readJson('/angular.json') as any;
const polyfills = app.architect.build.options.polyfills;
expect(polyfills.filter((p: string) => p === '@angular/localize/init').length).toBe(1);
});
it(`should not add polyfills if localize is not enabled`, async () => {
// Disable 'localize'
const config = JSON.parse(tree.readContent('/angular.json'));
config.projects.app.architect.build.options.localize = false;
tree.overwrite('/angular.json', JSON.stringify(config, undefined, 2));
const newTree = await schematicRunner.runSchematic(schematicName, {}, tree);
const {
projects: { app },
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} = newTree.readJson('/angular.json') as any;
expect(app.architect.build.options.polyfills).not.toContain('@angular/localize/init');
});
});