Skip to content

Commit a154db8

Browse files
crisbetothePunderWoman
authored andcommitted
feat(core): add ng generate schematic to convert declarations to standalone (#48790)
Implements a new `ng generate @angular/core:standalone` schematic that allows the user to convert all the declarations in a set of NgModules to standalone. PR Close #48790
1 parent b37a624 commit a154db8

File tree

10 files changed

+2117
-1
lines changed

10 files changed

+2117
-1
lines changed

packages/core/schematics/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ pkg_npm(
1212
"collection.json",
1313
"migrations.json",
1414
"package.json",
15+
"//packages/core/schematics/ng-generate/standalone-migration:static_files",
1516
],
1617
validate = False,
1718
visibility = ["//packages/core:__pkg__"],
1819
deps = [
1920
"//packages/core/schematics/migrations/relative-link-resolution:bundle",
2021
"//packages/core/schematics/migrations/router-link-with-href:bundle",
22+
"//packages/core/schematics/ng-generate/standalone-migration:bundle",
2123
],
2224
)
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
{
2-
"schematics": {}
2+
"schematics": {
3+
"standalone-migration": {
4+
"description": "Converts the entire application or a part of it to standalone",
5+
"factory": "./ng-generate/standalone-migration/bundle",
6+
"schema": "./ng-generate/standalone-migration/schema.json",
7+
"aliases": ["standalone"]
8+
}
9+
}
310
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
load("//tools:defaults.bzl", "esbuild", "ts_library")
2+
3+
package(
4+
default_visibility = [
5+
"//packages/core/schematics:__pkg__",
6+
"//packages/core/schematics/migrations/google3:__pkg__",
7+
"//packages/core/schematics/test:__pkg__",
8+
],
9+
)
10+
11+
filegroup(
12+
name = "static_files",
13+
srcs = ["schema.json"],
14+
)
15+
16+
ts_library(
17+
name = "standalone-migration",
18+
srcs = glob(["**/*.ts"]),
19+
tsconfig = "//packages/core/schematics:tsconfig.json",
20+
deps = [
21+
"//packages/compiler-cli",
22+
"//packages/compiler-cli/private",
23+
"//packages/core/schematics/utils",
24+
"@npm//@angular-devkit/schematics",
25+
"@npm//@types/node",
26+
"@npm//typescript",
27+
],
28+
)
29+
30+
esbuild(
31+
name = "bundle",
32+
entry_point = ":index.ts",
33+
external = [
34+
"@angular-devkit/*",
35+
"typescript",
36+
],
37+
format = "cjs",
38+
platform = "node",
39+
deps = [":standalone-migration"],
40+
)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Standalone migration
2+
TODO
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
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 {createProgram, NgtscProgram} from '@angular/compiler-cli';
11+
import {existsSync, statSync} from 'fs';
12+
import {join, relative} from 'path';
13+
import ts from 'typescript';
14+
15+
import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths';
16+
import {canMigrateFile, createProgramOptions} from '../../utils/typescript/compiler_host';
17+
18+
import {toStandalone} from './to-standalone';
19+
import {ChangesByFile} from './util';
20+
21+
enum MigrationMode {
22+
toStandalone = 'convert-to-standalone',
23+
}
24+
25+
interface Options {
26+
path: string;
27+
mode: MigrationMode;
28+
}
29+
30+
export default function(options: Options): Rule {
31+
return async (tree) => {
32+
const {buildPaths, testPaths} = await getProjectTsConfigPaths(tree);
33+
const basePath = process.cwd();
34+
const allPaths = [...buildPaths, ...testPaths];
35+
36+
if (!allPaths.length) {
37+
throw new SchematicsException(
38+
'Could not find any tsconfig file. Cannot run the standalone migration.');
39+
}
40+
41+
for (const tsconfigPath of allPaths) {
42+
standaloneMigration(tree, tsconfigPath, basePath, options);
43+
}
44+
};
45+
}
46+
47+
function standaloneMigration(tree: Tree, tsconfigPath: string, basePath: string, options: Options) {
48+
if (options.path.startsWith('..')) {
49+
throw new SchematicsException(
50+
'Cannot run standalone migration outside of the current project.');
51+
}
52+
53+
const {host, rootNames} = createProgramOptions(tree, tsconfigPath, basePath);
54+
const program = createProgram({
55+
rootNames,
56+
host,
57+
options: {_enableTemplateTypeChecker: true, compileNonExportedClasses: true}
58+
}) as NgtscProgram;
59+
const printer = ts.createPrinter();
60+
const pathToMigrate = join(basePath, options.path);
61+
62+
if (existsSync(pathToMigrate) && !statSync(pathToMigrate).isDirectory()) {
63+
throw new SchematicsException(`Migration path ${
64+
pathToMigrate} has to be a directory. Cannot run the standalone migration.`);
65+
}
66+
67+
const sourceFiles = program.getTsProgram().getSourceFiles().filter(sourceFile => {
68+
return sourceFile.fileName.startsWith(pathToMigrate) &&
69+
canMigrateFile(basePath, sourceFile, program.getTsProgram());
70+
});
71+
72+
if (sourceFiles.length === 0) {
73+
throw new SchematicsException(`Could not find any files to migrate under the path ${
74+
pathToMigrate}. Cannot run the standalone migration.`);
75+
}
76+
77+
let pendingChanges: ChangesByFile;
78+
79+
if (options.mode === MigrationMode.toStandalone) {
80+
pendingChanges = toStandalone(sourceFiles, program, printer);
81+
} else {
82+
throw new SchematicsException(
83+
`Unknown schematic mode ${options.mode}. Cannot run the standalone migration.`);
84+
}
85+
86+
for (const [file, changes] of pendingChanges.entries()) {
87+
const update = tree.beginUpdate(relative(basePath, file.fileName));
88+
89+
changes.forEach(change => {
90+
if (change.removeLength != null) {
91+
update.remove(change.start, change.removeLength);
92+
}
93+
update.insertRight(change.start, change.text);
94+
});
95+
96+
tree.commitUpdate(update);
97+
}
98+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema",
3+
"$id": "AngularStandaloneMigration",
4+
"title": "Angular Standalone Migration Schema",
5+
"type": "object",
6+
"properties": {
7+
"mode": {
8+
"description": "Operation that should be performed by the migrator",
9+
"type": "string",
10+
"default": "convert-to-standalone",
11+
"x-prompt": {
12+
"message": "Choose the type of migration:",
13+
"type": "list",
14+
"items": [
15+
{
16+
"value": "convert-to-standalone",
17+
"label": "Convert all components, directives and pipes to standalone"
18+
}
19+
]
20+
}
21+
},
22+
"path": {
23+
"type": "string",
24+
"$default": {
25+
"$source": "workingDirectory"
26+
},
27+
"description": "Path relative to the project root in which to look for declarations to migrate to standalone",
28+
"x-prompt": "Which path in your project should be migrated to standalone?",
29+
"default": "./"
30+
}
31+
},
32+
"required": ["mode", "path"]
33+
}

0 commit comments

Comments
 (0)