mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor(core): run the control flow migration during ng update (#60492)
The migration had to be run manually before this change. It is now part of the update process when migrating to v20. PR Close #60492
This commit is contained in:
committed by
Jessica Janiuk
parent
b407157ee8
commit
e40b5c9ebd
@@ -692,7 +692,7 @@ export class NgSwitch {
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<NgSwitch, never>;
|
||||
}
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export class NgSwitchCase implements DoCheck {
|
||||
constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>, ngSwitch: NgSwitch);
|
||||
ngDoCheck(): void;
|
||||
@@ -703,7 +703,7 @@ export class NgSwitchCase implements DoCheck {
|
||||
static ɵfac: i0.ɵɵFactoryDeclaration<NgSwitchCase, [null, null, { optional: true; host: true; }]>;
|
||||
}
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export class NgSwitchDefault {
|
||||
constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>, ngSwitch: NgSwitch);
|
||||
// (undocumented)
|
||||
|
||||
@@ -14,7 +14,6 @@ pkg_npm(
|
||||
"migrations.json",
|
||||
"package.json",
|
||||
"//packages/core/schematics/ng-generate/cleanup-unused-imports:static_files",
|
||||
"//packages/core/schematics/ng-generate/control-flow-migration:static_files",
|
||||
"//packages/core/schematics/ng-generate/inject-migration:static_files",
|
||||
"//packages/core/schematics/ng-generate/output-migration:static_files",
|
||||
"//packages/core/schematics/ng-generate/route-lazy-loading:static_files",
|
||||
@@ -35,7 +34,6 @@ rollup_bundle(
|
||||
name = "bundles",
|
||||
config_file = ":rollup.config.js",
|
||||
entry_points = {
|
||||
"//packages/core/schematics/ng-generate/control-flow-migration:index.ts": "control-flow-migration",
|
||||
"//packages/core/schematics/ng-generate/inject-migration:index.ts": "inject-migration",
|
||||
"//packages/core/schematics/ng-generate/route-lazy-loading:index.ts": "route-lazy-loading",
|
||||
"//packages/core/schematics/ng-generate/standalone-migration:index.ts": "standalone-migration",
|
||||
@@ -47,6 +45,7 @@ rollup_bundle(
|
||||
"//packages/core/schematics/ng-generate/self-closing-tags-migration:index.ts": "self-closing-tags-migration",
|
||||
"//packages/core/schematics/migrations/inject-flags:index.ts": "inject-flags",
|
||||
"//packages/core/schematics/migrations/test-bed-get:index.ts": "test-bed-get",
|
||||
"//packages/core/schematics/migrations/control-flow-migration:index.ts": "control-flow-migration",
|
||||
},
|
||||
format = "cjs",
|
||||
link_workspace_root = True,
|
||||
@@ -56,10 +55,10 @@ rollup_bundle(
|
||||
"//packages/core/schematics/test:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//packages/core/schematics/migrations/control-flow-migration",
|
||||
"//packages/core/schematics/migrations/inject-flags",
|
||||
"//packages/core/schematics/migrations/test-bed-get",
|
||||
"//packages/core/schematics/ng-generate/cleanup-unused-imports",
|
||||
"//packages/core/schematics/ng-generate/control-flow-migration",
|
||||
"//packages/core/schematics/ng-generate/inject-migration",
|
||||
"//packages/core/schematics/ng-generate/output-migration",
|
||||
"//packages/core/schematics/ng-generate/route-lazy-loading",
|
||||
|
||||
@@ -6,12 +6,6 @@
|
||||
"schema": "./ng-generate/standalone-migration/schema.json",
|
||||
"aliases": ["standalone"]
|
||||
},
|
||||
"control-flow-migration": {
|
||||
"description": "Converts the entire application to block control flow syntax",
|
||||
"factory": "./bundles/control-flow-migration#migrate",
|
||||
"schema": "./ng-generate/control-flow-migration/schema.json",
|
||||
"aliases": ["control-flow"]
|
||||
},
|
||||
"inject-migration": {
|
||||
"description": "Converts usages of constructor-based injection to the inject() function",
|
||||
"factory": "./bundles/inject-migration#migrate",
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
"version": "20.0.0",
|
||||
"description": "Replaces usages of the deprecated TestBed.get method with TestBed.inject",
|
||||
"factory": "./bundles/test-bed-get#migrate"
|
||||
},
|
||||
"control-flow-migration": {
|
||||
"version": "20.0.0",
|
||||
"description": "Converts the entire application to block control flow syntax",
|
||||
"factory": "./bundles/control-flow-migration#migrate",
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-53
@@ -7,45 +7,31 @@
|
||||
*/
|
||||
|
||||
import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics';
|
||||
import {join, relative} from 'path';
|
||||
import {relative} from 'path';
|
||||
|
||||
import {normalizePath} from '../../utils/change_tracker';
|
||||
import {canMigrateFile, createMigrationProgram} from '../../utils/typescript/compiler_host';
|
||||
|
||||
import {migrateTemplate} from './migration';
|
||||
import {AnalyzedFile, MigrateError} from './types';
|
||||
import {analyze} from './util';
|
||||
import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths';
|
||||
|
||||
interface Options {
|
||||
path: string;
|
||||
format: boolean;
|
||||
}
|
||||
|
||||
export function migrate(options: Options): Rule {
|
||||
export function migrate(): Rule {
|
||||
return async (tree: Tree, context: SchematicContext) => {
|
||||
const {buildPaths, testPaths} = await getProjectTsConfigPaths(tree);
|
||||
const basePath = process.cwd();
|
||||
const pathToMigrate = normalizePath(join(basePath, options.path));
|
||||
let allPaths = [];
|
||||
if (pathToMigrate.trim() !== '') {
|
||||
allPaths.push(pathToMigrate);
|
||||
}
|
||||
const allPaths = [...buildPaths, ...testPaths];
|
||||
|
||||
if (!allPaths.length) {
|
||||
throw new SchematicsException(
|
||||
'Could not find any tsconfig file. Cannot run the control flow migration.',
|
||||
'Could not find any tsconfig file. Cannot run the http providers migration.',
|
||||
);
|
||||
}
|
||||
|
||||
let errors: string[] = [];
|
||||
|
||||
for (const tsconfigPath of allPaths) {
|
||||
const migrateErrors = runControlFlowMigration(
|
||||
tree,
|
||||
tsconfigPath,
|
||||
basePath,
|
||||
pathToMigrate,
|
||||
options,
|
||||
);
|
||||
const migrateErrors = runControlFlowMigration(tree, tsconfigPath, basePath);
|
||||
errors = [...errors, ...migrateErrors];
|
||||
}
|
||||
|
||||
@@ -58,33 +44,11 @@ export function migrate(options: Options): Rule {
|
||||
};
|
||||
}
|
||||
|
||||
function runControlFlowMigration(
|
||||
tree: Tree,
|
||||
tsconfigPath: string,
|
||||
basePath: string,
|
||||
pathToMigrate: string,
|
||||
schematicOptions: Options,
|
||||
): string[] {
|
||||
if (schematicOptions.path.startsWith('..')) {
|
||||
throw new SchematicsException(
|
||||
'Cannot run control flow migration outside of the current project.',
|
||||
);
|
||||
}
|
||||
|
||||
function runControlFlowMigration(tree: Tree, tsconfigPath: string, basePath: string): string[] {
|
||||
const program = createMigrationProgram(tree, tsconfigPath, basePath);
|
||||
const sourceFiles = program
|
||||
.getSourceFiles()
|
||||
.filter(
|
||||
(sourceFile) =>
|
||||
sourceFile.fileName.startsWith(pathToMigrate) &&
|
||||
canMigrateFile(basePath, sourceFile, program),
|
||||
);
|
||||
|
||||
if (sourceFiles.length === 0) {
|
||||
throw new SchematicsException(
|
||||
`Could not find any files to migrate under the path ${pathToMigrate}. Cannot run the control flow migration.`,
|
||||
);
|
||||
}
|
||||
.filter((sourceFile) => canMigrateFile(basePath, sourceFile, program));
|
||||
|
||||
const analysis = new Map<string, AnalyzedFile>();
|
||||
const migrateErrors = new Map<string, MigrateError[]>();
|
||||
@@ -108,14 +72,7 @@ function runControlFlowMigration(
|
||||
const template = content.slice(start, end);
|
||||
const length = (end ?? content.length) - start;
|
||||
|
||||
const {migrated, errors} = migrateTemplate(
|
||||
template,
|
||||
type,
|
||||
node,
|
||||
file,
|
||||
schematicOptions.format,
|
||||
analysis,
|
||||
);
|
||||
const {migrated, errors} = migrateTemplate(template, type, node, file, true, analysis);
|
||||
|
||||
if (migrated !== null) {
|
||||
update.remove(start, length);
|
||||
@@ -22,7 +22,6 @@ jasmine_node_test(
|
||||
"//packages/core/schematics:collection.json",
|
||||
"//packages/core/schematics:migrations.json",
|
||||
"//packages/core/schematics/ng-generate/cleanup-unused-imports:static_files",
|
||||
"//packages/core/schematics/ng-generate/control-flow-migration:static_files",
|
||||
"//packages/core/schematics/ng-generate/inject-migration:static_files",
|
||||
"//packages/core/schematics/ng-generate/output-migration:static_files",
|
||||
"//packages/core/schematics/ng-generate/route-lazy-loading:static_files",
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('control flow migration', () => {
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
runner = new SchematicTestRunner('test', runfiles.resolvePackageRelative('../collection.json'));
|
||||
runner = new SchematicTestRunner('test', runfiles.resolvePackageRelative('../migrations.json'));
|
||||
host = new TempScopedNodeJsSyncHost();
|
||||
tree = new UnitTestTree(new HostTree(host));
|
||||
|
||||
@@ -67,100 +67,6 @@ describe('control flow migration', () => {
|
||||
shx.rm('-r', tmpDirPath);
|
||||
});
|
||||
|
||||
describe('path', () => {
|
||||
it('should throw an error if no files match the passed-in path', async () => {
|
||||
let error: string | null = null;
|
||||
|
||||
writeFile(
|
||||
'dir.ts',
|
||||
`
|
||||
import {Directive} from '@angular/core';
|
||||
|
||||
@Directive({selector: '[dir]'})
|
||||
export class MyDir {}
|
||||
`,
|
||||
);
|
||||
|
||||
try {
|
||||
await runMigration('./foo');
|
||||
} catch (e: any) {
|
||||
error = e.message;
|
||||
}
|
||||
|
||||
expect(error).toMatch(
|
||||
/Could not find any files to migrate under the path .*\/foo\. Cannot run the control flow migration/,
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an error if a path outside of the project is passed in', async () => {
|
||||
let error: string | null = null;
|
||||
|
||||
writeFile(
|
||||
'dir.ts',
|
||||
`
|
||||
import {Directive} from '@angular/core';
|
||||
|
||||
@Directive({selector: '[dir]'})
|
||||
export class MyDir {}
|
||||
`,
|
||||
);
|
||||
|
||||
try {
|
||||
await runMigration('../foo');
|
||||
} catch (e: any) {
|
||||
error = e.message;
|
||||
}
|
||||
expect(error).toBe('Cannot run control flow migration outside of the current project.');
|
||||
});
|
||||
|
||||
it('should only migrate the paths that were passed in', async () => {
|
||||
writeFile(
|
||||
'comp.ts',
|
||||
`
|
||||
import {Component} from '@angular/core';
|
||||
import {NgIf} from '@angular/common';
|
||||
|
||||
@Component({
|
||||
imports: [NgIf, NgFor,NgSwitch,NgSwitchCase ,NgSwitchDefault],
|
||||
template: \`<div><span *ngIf="toggle">This should be hidden</span></div>\`
|
||||
})
|
||||
class Comp {
|
||||
toggle = false;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
writeFile(
|
||||
'skip.ts',
|
||||
`
|
||||
import {Component} from '@angular/core';
|
||||
import {NgIf} from '@angular/common';
|
||||
|
||||
@Component({
|
||||
imports: [NgIf],
|
||||
template: \`<div *ngIf="show">Show me</div>\`
|
||||
})
|
||||
class Comp {
|
||||
show = false;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
await runMigration('./comp.ts');
|
||||
const migratedContent = tree.readContent('/comp.ts');
|
||||
const skippedContent = tree.readContent('/skip.ts');
|
||||
|
||||
expect(migratedContent).toContain(
|
||||
'template: `<div>@if (toggle) {<span>This should be hidden</span>}</div>`',
|
||||
);
|
||||
expect(migratedContent).toContain('imports: []');
|
||||
expect(migratedContent).not.toContain(`import {NgIf} from '@angular/common';`);
|
||||
expect(skippedContent).toContain('template: `<div *ngIf="show">Show me</div>`');
|
||||
expect(skippedContent).toContain('imports: [NgIf]');
|
||||
expect(skippedContent).toContain(`import {NgIf} from '@angular/common';`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ngIf', () => {
|
||||
it('should migrate an inline template', async () => {
|
||||
writeFile(
|
||||
@@ -5596,48 +5502,6 @@ describe('control flow migration', () => {
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it('should migrate an if else case and not format', async () => {
|
||||
writeFile(
|
||||
'/comp.ts',
|
||||
`
|
||||
import {Component} from '@angular/core';
|
||||
import {NgIf} from '@angular/common';
|
||||
|
||||
@Component({
|
||||
templateUrl: './comp.html'
|
||||
})
|
||||
class Comp {
|
||||
show = false;
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
writeFile(
|
||||
'/comp.html',
|
||||
[
|
||||
`<div>`,
|
||||
`<span *ngIf="show;else elseBlock">Content here</span>`,
|
||||
`<ng-template #elseBlock>Else Content</ng-template>`,
|
||||
`</div>`,
|
||||
].join('\n'),
|
||||
);
|
||||
|
||||
await runMigration(undefined, false);
|
||||
const content = tree.readContent('/comp.html');
|
||||
|
||||
expect(content).toBe(
|
||||
[
|
||||
`<div>`,
|
||||
`@if (show) {`,
|
||||
`<span>Content here</span>`,
|
||||
`} @else {`,
|
||||
`Else Content`,
|
||||
`}\n`,
|
||||
`</div>`,
|
||||
].join('\n'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should ignore formatting on i18n sections', async () => {
|
||||
writeFile(
|
||||
'/comp.ts',
|
||||
|
||||
Reference in New Issue
Block a user