Skip to content

Commit 5dff077

Browse files
crisbetodylhunn
authored andcommitted
feat(core): add migration to remove entryComponents (#44308)
Adds an automated migration that will drop any usages of `entryComponents` from `@NgModule` and `@Component`. PR Close #44308
1 parent cd502b7 commit 5dff077

File tree

11 files changed

+595
-0
lines changed

11 files changed

+595
-0
lines changed

packages/core/schematics/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pkg_npm(
1313
],
1414
visibility = ["//packages/core:__pkg__"],
1515
deps = [
16+
"//packages/core/schematics/migrations/entry-components",
1617
"//packages/core/schematics/migrations/router-link-empty-expression",
1718
"//packages/core/schematics/migrations/testbed-teardown",
1819
],

packages/core/schematics/migrations.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@
99
"version": "13.0.0-beta",
1010
"description": "In Angular version 13, the `teardown` flag in `TestBed` will be enabled by default. This migration automatically opts out existing apps from the new teardown behavior.",
1111
"factory": "./migrations/testbed-teardown/index"
12+
},
13+
"migration-v13.1-entry-components": {
14+
"version": "13.1.0-beta",
15+
"description": "As of Angular version 13, `entryComponents` are no longer necessary.",
16+
"factory": "./migrations/entry-components/index"
17+
},
18+
"migration-v14-entry-components": {
19+
"version": "14.0.0-beta",
20+
"description": "As of Angular version 13, `entryComponents` are no longer necessary.",
21+
"factory": "./migrations/entry-components/index"
1222
}
1323
}
1424
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
load("//tools:defaults.bzl", "ts_library")
2+
3+
ts_library(
4+
name = "entry-components",
5+
srcs = glob(["**/*.ts"]),
6+
tsconfig = "//packages/core/schematics:tsconfig.json",
7+
visibility = [
8+
"//packages/core/schematics:__pkg__",
9+
"//packages/core/schematics/migrations/google3:__pkg__",
10+
"//packages/core/schematics/test:__pkg__",
11+
],
12+
deps = [
13+
"//packages/core/schematics/utils",
14+
"@npm//@angular-devkit/schematics",
15+
"@npm//@types/node",
16+
"@npm//typescript",
17+
],
18+
)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
## entryComponents migration
2+
As of Angular version 13, the `entryComponents` option in `@NgModule` and `@Component` isn't
3+
necessary anymore. This migration will automatically remove any usages.
4+
5+
#### Before
6+
```ts
7+
import { NgModule, Component } from '@angular/core';
8+
9+
@Component({selector: 'my-comp', template: ''})
10+
export class MyComp {}
11+
12+
@NgModule({
13+
declarations: [MyComp],
14+
entryComponents: [MyComp],
15+
exports: [MyComp]
16+
})
17+
export class MyModule {}
18+
```
19+
20+
#### After
21+
```ts
22+
import { NgModule, Component } from '@angular/core';
23+
24+
@Component({selector: 'my-comp', template: ''})
25+
export class MyComp {}
26+
27+
@NgModule({
28+
declarations: [MyComp],
29+
exports: [MyComp]
30+
})
31+
export class MyModule {}
32+
```
33+
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {Rule, SchematicsException, Tree} from '@angular-devkit/schematics';
10+
import {relative} from 'path';
11+
import ts from 'typescript';
12+
13+
import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths';
14+
import {canMigrateFile, createMigrationProgram} from '../../utils/typescript/compiler_host';
15+
16+
import {migrateEntryComponentsUsages} from './util';
17+
18+
19+
/** Migration that removes `entryComponents` usages. */
20+
export default function(): Rule {
21+
return async (tree: Tree) => {
22+
const {buildPaths, testPaths} = await getProjectTsConfigPaths(tree);
23+
const basePath = process.cwd();
24+
const allPaths = [...buildPaths, ...testPaths];
25+
26+
if (!allPaths.length) {
27+
throw new SchematicsException(
28+
'Could not find any tsconfig file. Cannot remove `entryComponents`.');
29+
}
30+
31+
for (const tsconfigPath of allPaths) {
32+
runEntryComponentsMigration(tree, tsconfigPath, basePath);
33+
}
34+
};
35+
}
36+
37+
function runEntryComponentsMigration(tree: Tree, tsconfigPath: string, basePath: string) {
38+
const {program} = createMigrationProgram(tree, tsconfigPath, basePath);
39+
const typeChecker = program.getTypeChecker();
40+
const printer = ts.createPrinter();
41+
42+
program.getSourceFiles()
43+
.filter(sourceFile => canMigrateFile(basePath, sourceFile, program))
44+
.forEach(sourceFile => {
45+
const usages = migrateEntryComponentsUsages(typeChecker, printer, sourceFile);
46+
47+
if (usages.length > 0) {
48+
const update = tree.beginUpdate(relative(basePath, sourceFile.fileName));
49+
usages.forEach(usage => {
50+
update.remove(usage.start, usage.length);
51+
update.insertRight(usage.start, usage.replacement);
52+
});
53+
tree.commitUpdate(update);
54+
}
55+
});
56+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import ts from 'typescript';
10+
11+
import {getCallDecoratorImport} from '../../utils/typescript/decorators';
12+
13+
/** Finds and migrates all Angular decorators that pass in `entryComponents`. */
14+
export function migrateEntryComponentsUsages(
15+
typeChecker: ts.TypeChecker, printer: ts.Printer, sourceFile: ts.SourceFile) {
16+
const results: {start: number, length: number, end: number, replacement: string}[] = [];
17+
18+
sourceFile.forEachChild(function walk(node: ts.Node) {
19+
if (ts.isDecorator(node) && ts.isCallExpression(node.expression) &&
20+
node.expression.arguments.length === 1 &&
21+
ts.isObjectLiteralExpression(node.expression.arguments[0])) {
22+
const analysis = getCallDecoratorImport(typeChecker, node);
23+
24+
if (analysis && analysis.importModule === '@angular/core' &&
25+
(analysis.name === 'Component' || analysis.name === 'NgModule')) {
26+
const literal = node.expression.arguments[0];
27+
const entryComponentsProp = literal.properties.find(
28+
property => ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) &&
29+
property.name.text === 'entryComponents');
30+
31+
if (entryComponentsProp) {
32+
const replacementNode = ts.updateObjectLiteral(
33+
literal, literal.properties.filter(prop => prop !== entryComponentsProp));
34+
35+
results.push({
36+
start: literal.getStart(),
37+
length: literal.getWidth(),
38+
end: literal.getEnd(),
39+
replacement: printer.printNode(ts.EmitHint.Unspecified, replacementNode, sourceFile)
40+
});
41+
}
42+
}
43+
}
44+
45+
node.forEachChild(walk);
46+
});
47+
48+
// Sort the operations in reverse order in order to avoid
49+
// issues when migrating multiple usages within the same file.
50+
return results.sort((a, b) => b.start - a.start);
51+
}

packages/core/schematics/migrations/google3/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ ts_library(
66
tsconfig = "//packages/core/schematics:tsconfig.json",
77
visibility = ["//packages/core/schematics/test/google3:__pkg__"],
88
deps = [
9+
"//packages/core/schematics/migrations/entry-components",
910
"//packages/core/schematics/migrations/testbed-teardown",
1011
"//packages/core/schematics/utils",
1112
"//packages/core/schematics/utils/tslint",
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import {Replacement, RuleFailure, Rules} from 'tslint';
10+
import ts from 'typescript';
11+
12+
import {migrateEntryComponentsUsages} from '../../migrations/entry-components/util';
13+
14+
15+
/** TSLint rule that removes usages of `entryComponents`. */
16+
export class Rule extends Rules.TypedRule {
17+
override applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] {
18+
const typeChecker = program.getTypeChecker();
19+
const printer = ts.createPrinter();
20+
21+
return migrateEntryComponentsUsages(typeChecker, printer, sourceFile).map(usage => {
22+
return new RuleFailure(
23+
sourceFile, usage.start, usage.end,
24+
'entryComponents are deprecated and don\'t need to be passed in.', this.ruleName,
25+
new Replacement(usage.start, usage.length, usage.replacement));
26+
});
27+
}
28+
}

packages/core/schematics/test/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ ts_library(
88
"//packages/core/schematics:migrations.json",
99
],
1010
deps = [
11+
"//packages/core/schematics/migrations/entry-components",
1112
"//packages/core/schematics/migrations/router-link-empty-expression",
1213
"//packages/core/schematics/migrations/testbed-teardown",
1314
"//packages/core/schematics/utils",

0 commit comments

Comments
 (0)