@angular/core 19.0.0-next.2 → 19.0.0-next.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,151 @@
1
+ 'use strict';
2
+ /**
3
+ * @license Angular v19.0.0-next.3
4
+ * (c) 2010-2024 Google LLC. https://angular.io/
5
+ * License: MIT
6
+ */
7
+ 'use strict';
8
+
9
+ var ts = require('typescript');
10
+
11
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
12
+
13
+ var ts__default = /*#__PURE__*/_interopDefaultLegacy(ts);
14
+
15
+ /** Gets import information about the specified identifier by using the Type checker. */
16
+ function getImportOfIdentifier(typeChecker, node) {
17
+ const symbol = typeChecker.getSymbolAtLocation(node);
18
+ if (!symbol || symbol.declarations === undefined || !symbol.declarations.length) {
19
+ return null;
20
+ }
21
+ const decl = symbol.declarations[0];
22
+ if (!ts__default["default"].isImportSpecifier(decl)) {
23
+ return null;
24
+ }
25
+ const importDecl = decl.parent.parent.parent;
26
+ if (!ts__default["default"].isImportDeclaration(importDecl) || !ts__default["default"].isStringLiteral(importDecl.moduleSpecifier)) {
27
+ return null;
28
+ }
29
+ return {
30
+ // Handles aliased imports: e.g. "import {Component as myComp} from ...";
31
+ name: decl.propertyName ? decl.propertyName.text : decl.name.text,
32
+ importModule: importDecl.moduleSpecifier.text,
33
+ node: importDecl,
34
+ };
35
+ }
36
+ /**
37
+ * Gets a top-level import specifier with a specific name that is imported from a particular module.
38
+ * E.g. given a file that looks like:
39
+ *
40
+ * ```
41
+ * import { Component, Directive } from '@angular/core';
42
+ * import { Foo } from './foo';
43
+ * ```
44
+ *
45
+ * Calling `getImportSpecifier(sourceFile, '@angular/core', 'Directive')` will yield the node
46
+ * referring to `Directive` in the top import.
47
+ *
48
+ * @param sourceFile File in which to look for imports.
49
+ * @param moduleName Name of the import's module.
50
+ * @param specifierName Original name of the specifier to look for. Aliases will be resolved to
51
+ * their original name.
52
+ */
53
+ function getImportSpecifier(sourceFile, moduleName, specifierName) {
54
+ return getImportSpecifiers(sourceFile, moduleName, specifierName)[0] ?? null;
55
+ }
56
+ function getImportSpecifiers(sourceFile, moduleName, specifierOrSpecifiers) {
57
+ const matches = [];
58
+ for (const node of sourceFile.statements) {
59
+ if (!ts__default["default"].isImportDeclaration(node) || !ts__default["default"].isStringLiteral(node.moduleSpecifier)) {
60
+ continue;
61
+ }
62
+ const namedBindings = node.importClause?.namedBindings;
63
+ const isMatch = typeof moduleName === 'string'
64
+ ? node.moduleSpecifier.text === moduleName
65
+ : moduleName.test(node.moduleSpecifier.text);
66
+ if (!isMatch || !namedBindings || !ts__default["default"].isNamedImports(namedBindings)) {
67
+ continue;
68
+ }
69
+ if (typeof specifierOrSpecifiers === 'string') {
70
+ const match = findImportSpecifier(namedBindings.elements, specifierOrSpecifiers);
71
+ if (match) {
72
+ matches.push(match);
73
+ }
74
+ }
75
+ else {
76
+ for (const specifierName of specifierOrSpecifiers) {
77
+ const match = findImportSpecifier(namedBindings.elements, specifierName);
78
+ if (match) {
79
+ matches.push(match);
80
+ }
81
+ }
82
+ }
83
+ }
84
+ return matches;
85
+ }
86
+ function getNamedImports(sourceFile, moduleName) {
87
+ for (const node of sourceFile.statements) {
88
+ if (ts__default["default"].isImportDeclaration(node) && ts__default["default"].isStringLiteral(node.moduleSpecifier)) {
89
+ const isMatch = typeof moduleName === 'string'
90
+ ? node.moduleSpecifier.text === moduleName
91
+ : moduleName.test(node.moduleSpecifier.text);
92
+ const namedBindings = node.importClause?.namedBindings;
93
+ if (isMatch && namedBindings && ts__default["default"].isNamedImports(namedBindings)) {
94
+ return namedBindings;
95
+ }
96
+ }
97
+ }
98
+ return null;
99
+ }
100
+ /** Finds an import specifier with a particular name. */
101
+ function findImportSpecifier(nodes, specifierName) {
102
+ return nodes.find((element) => {
103
+ const { name, propertyName } = element;
104
+ return propertyName ? propertyName.text === specifierName : name.text === specifierName;
105
+ });
106
+ }
107
+
108
+ function getCallDecoratorImport(typeChecker, decorator) {
109
+ // Note that this does not cover the edge case where decorators are called from
110
+ // a namespace import: e.g. "@core.Component()". This is not handled by Ngtsc either.
111
+ if (!ts__default["default"].isCallExpression(decorator.expression) ||
112
+ !ts__default["default"].isIdentifier(decorator.expression.expression)) {
113
+ return null;
114
+ }
115
+ const identifier = decorator.expression.expression;
116
+ return getImportOfIdentifier(typeChecker, identifier);
117
+ }
118
+
119
+ /**
120
+ * Gets all decorators which are imported from an Angular package (e.g. "@angular/core")
121
+ * from a list of decorators.
122
+ */
123
+ function getAngularDecorators(typeChecker, decorators) {
124
+ return decorators
125
+ .map((node) => ({ node, importData: getCallDecoratorImport(typeChecker, node) }))
126
+ .filter(({ importData }) => importData && importData.importModule.startsWith('@angular/'))
127
+ .map(({ node, importData }) => ({
128
+ node: node,
129
+ name: importData.name,
130
+ moduleName: importData.importModule,
131
+ importNode: importData.node,
132
+ }));
133
+ }
134
+
135
+ /** Find the closest parent node of a particular kind. */
136
+ function closestNode(node, predicate) {
137
+ let current = node.parent;
138
+ while (current && !ts__default["default"].isSourceFile(current)) {
139
+ if (predicate(current)) {
140
+ return current;
141
+ }
142
+ current = current.parent;
143
+ }
144
+ return null;
145
+ }
146
+
147
+ exports.closestNode = closestNode;
148
+ exports.getAngularDecorators = getAngularDecorators;
149
+ exports.getImportOfIdentifier = getImportOfIdentifier;
150
+ exports.getImportSpecifier = getImportSpecifier;
151
+ exports.getNamedImports = getNamedImports;
@@ -0,0 +1,90 @@
1
+ 'use strict';
2
+ /**
3
+ * @license Angular v19.0.0-next.3
4
+ * (c) 2010-2024 Google LLC. https://angular.io/
5
+ * License: MIT
6
+ */
7
+ 'use strict';
8
+
9
+ var core = require('@angular-devkit/core');
10
+
11
+ /**
12
+ * Gets all tsconfig paths from a CLI project by reading the workspace configuration
13
+ * and looking for common tsconfig locations.
14
+ */
15
+ async function getProjectTsConfigPaths(tree) {
16
+ // Start with some tsconfig paths that are generally used within CLI projects. Note
17
+ // that we are not interested in IDE-specific tsconfig files (e.g. /tsconfig.json)
18
+ const buildPaths = new Set();
19
+ const testPaths = new Set();
20
+ const workspace = await getWorkspace(tree);
21
+ for (const [, project] of workspace.projects) {
22
+ for (const [name, target] of project.targets) {
23
+ if (name !== 'build' && name !== 'test') {
24
+ continue;
25
+ }
26
+ for (const [, options] of allTargetOptions(target)) {
27
+ const tsConfig = options['tsConfig'];
28
+ // Filter out tsconfig files that don't exist in the CLI project.
29
+ if (typeof tsConfig !== 'string' || !tree.exists(tsConfig)) {
30
+ continue;
31
+ }
32
+ if (name === 'build') {
33
+ buildPaths.add(core.normalize(tsConfig));
34
+ }
35
+ else {
36
+ testPaths.add(core.normalize(tsConfig));
37
+ }
38
+ }
39
+ }
40
+ }
41
+ return {
42
+ buildPaths: [...buildPaths],
43
+ testPaths: [...testPaths],
44
+ };
45
+ }
46
+ /** Get options for all configurations for the passed builder target. */
47
+ function* allTargetOptions(target) {
48
+ if (target.options) {
49
+ yield [undefined, target.options];
50
+ }
51
+ if (!target.configurations) {
52
+ return;
53
+ }
54
+ for (const [name, options] of Object.entries(target.configurations)) {
55
+ if (options) {
56
+ yield [name, options];
57
+ }
58
+ }
59
+ }
60
+ function createHost(tree) {
61
+ return {
62
+ async readFile(path) {
63
+ const data = tree.read(path);
64
+ if (!data) {
65
+ throw new Error('File not found.');
66
+ }
67
+ return core.virtualFs.fileBufferToString(data);
68
+ },
69
+ async writeFile(path, data) {
70
+ return tree.overwrite(path, data);
71
+ },
72
+ async isDirectory(path) {
73
+ // Approximate a directory check.
74
+ // We don't need to consider empty directories and hence this is a good enough approach.
75
+ // This is also per documentation, see:
76
+ // https://angular.dev/tools/cli/schematics-for-libraries#get-the-project-configuration
77
+ return !tree.exists(path) && tree.getDir(path).subfiles.length > 0;
78
+ },
79
+ async isFile(path) {
80
+ return tree.exists(path);
81
+ },
82
+ };
83
+ }
84
+ async function getWorkspace(tree) {
85
+ const host = createHost(tree);
86
+ const { workspace } = await core.workspaces.readWorkspace('/', host);
87
+ return workspace;
88
+ }
89
+
90
+ exports.getProjectTsConfigPaths = getProjectTsConfigPaths;
@@ -0,0 +1,411 @@
1
+ 'use strict';
2
+ /**
3
+ * @license Angular v19.0.0-next.3
4
+ * (c) 2010-2024 Google LLC. https://angular.io/
5
+ * License: MIT
6
+ */
7
+ 'use strict';
8
+
9
+ Object.defineProperty(exports, '__esModule', { value: true });
10
+
11
+ var schematics = require('@angular-devkit/schematics');
12
+ var fs = require('fs');
13
+ var p = require('path');
14
+ var compiler_host = require('./compiler_host-bbb5d8fd.js');
15
+ var project_tsconfig_paths = require('./project_tsconfig_paths-e9ccccbf.js');
16
+ var ts = require('typescript');
17
+ require('os');
18
+ require('module');
19
+ require('url');
20
+ require('@angular-devkit/core');
21
+
22
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
23
+
24
+ var ts__default = /*#__PURE__*/_interopDefaultLegacy(ts);
25
+
26
+ /**
27
+ * Finds the class declaration that is being referred to by a node.
28
+ * @param reference Node referring to a class declaration.
29
+ * @param typeChecker
30
+ */
31
+ function findClassDeclaration(reference, typeChecker) {
32
+ return (typeChecker
33
+ .getTypeAtLocation(reference)
34
+ .getSymbol()
35
+ ?.declarations?.find(ts__default["default"].isClassDeclaration) || null);
36
+ }
37
+
38
+ /** Finds a property with a specific name in an object literal expression. */
39
+ function findLiteralProperty(literal, name) {
40
+ return literal.properties.find((prop) => prop.name && ts__default["default"].isIdentifier(prop.name) && prop.name.text === name);
41
+ }
42
+
43
+ /*!
44
+ * @license
45
+ * Copyright Google LLC All Rights Reserved.
46
+ *
47
+ * Use of this source code is governed by an MIT-style license that can be
48
+ * found in the LICENSE file at https://angular.dev/license
49
+ */
50
+ /**
51
+ * Checks whether a component is standalone.
52
+ * @param node Class being checked.
53
+ */
54
+ function isStandaloneComponent(node) {
55
+ const decorator = node.modifiers?.find((m) => m.kind === ts__default["default"].SyntaxKind.Decorator);
56
+ if (!decorator) {
57
+ return false;
58
+ }
59
+ if (ts__default["default"].isCallExpression(decorator.expression)) {
60
+ const arg = decorator.expression.arguments[0];
61
+ if (ts__default["default"].isObjectLiteralExpression(arg)) {
62
+ const property = findLiteralProperty(arg, 'standalone');
63
+ return property ? property.initializer.getText() === 'true' : false;
64
+ }
65
+ }
66
+ return false;
67
+ }
68
+ /**
69
+ * Checks whether a node is variable declaration of type Routes or Route[] and comes from @angular/router
70
+ * @param node Variable declaration being checked.
71
+ * @param typeChecker
72
+ */
73
+ function isAngularRoutesArray(node, typeChecker) {
74
+ if (ts__default["default"].isVariableDeclaration(node)) {
75
+ const type = typeChecker.getTypeAtLocation(node);
76
+ if (type && typeChecker.isArrayType(type)) {
77
+ // Route[] is an array type
78
+ const typeArguments = typeChecker.getTypeArguments(type);
79
+ const symbol = typeArguments[0]?.getSymbol();
80
+ return (symbol?.name === 'Route' &&
81
+ symbol?.declarations?.some((decl) => {
82
+ return decl.getSourceFile().fileName.includes('@angular/router');
83
+ }));
84
+ }
85
+ }
86
+ return false;
87
+ }
88
+ /**
89
+ * Checks whether a node is a call expression to a router module method.
90
+ * Examples:
91
+ * - RouterModule.forRoot(routes)
92
+ * - RouterModule.forChild(routes)
93
+ */
94
+ function isRouterModuleCallExpression(node, typeChecker) {
95
+ if (ts__default["default"].isPropertyAccessExpression(node.expression)) {
96
+ const propAccess = node.expression;
97
+ const moduleSymbol = typeChecker.getSymbolAtLocation(propAccess.expression);
98
+ return (moduleSymbol?.name === 'RouterModule' &&
99
+ (propAccess.name.text === 'forRoot' || propAccess.name.text === 'forChild'));
100
+ }
101
+ return false;
102
+ }
103
+ /**
104
+ * Checks whether a node is a call expression to a router method.
105
+ * Example: this.router.resetConfig(routes)
106
+ */
107
+ function isRouterCallExpression(node, typeChecker) {
108
+ if (ts__default["default"].isCallExpression(node) &&
109
+ ts__default["default"].isPropertyAccessExpression(node.expression) &&
110
+ node.expression.name.text === 'resetConfig') {
111
+ const calleeExpression = node.expression.expression;
112
+ const symbol = typeChecker.getSymbolAtLocation(calleeExpression);
113
+ if (symbol) {
114
+ const type = typeChecker.getTypeOfSymbolAtLocation(symbol, calleeExpression);
115
+ // if type of router is Router, then it is a router call expression
116
+ return type.aliasSymbol?.escapedName === 'Router';
117
+ }
118
+ }
119
+ return false;
120
+ }
121
+ /**
122
+ * Checks whether a node is a call expression to router provide function.
123
+ * Example: provideRoutes(routes)
124
+ */
125
+ function isRouterProviderCallExpression(node, typeChecker) {
126
+ if (ts__default["default"].isIdentifier(node.expression)) {
127
+ const moduleSymbol = typeChecker.getSymbolAtLocation(node.expression);
128
+ return moduleSymbol && moduleSymbol.name === 'provideRoutes';
129
+ }
130
+ return false;
131
+ }
132
+ /**
133
+ * Checks whether a node is a call expression to provideRouter function.
134
+ * Example: provideRouter(routes)
135
+ */
136
+ function isProvideRoutesCallExpression(node, typeChecker) {
137
+ if (ts__default["default"].isIdentifier(node.expression)) {
138
+ const moduleSymbol = typeChecker.getSymbolAtLocation(node.expression);
139
+ return moduleSymbol && moduleSymbol.name === 'provideRouter';
140
+ }
141
+ return false;
142
+ }
143
+
144
+ /*!
145
+ * @license
146
+ * Copyright Google LLC All Rights Reserved.
147
+ *
148
+ * Use of this source code is governed by an MIT-style license that can be
149
+ * found in the LICENSE file at https://angular.dev/license
150
+ */
151
+ /**
152
+ * Converts all application routes that are using standalone components to be lazy loaded.
153
+ * @param sourceFile File that should be migrated.
154
+ * @param program
155
+ */
156
+ function migrateFileToLazyRoutes(sourceFile, program) {
157
+ const typeChecker = program.getTypeChecker();
158
+ const printer = ts__default["default"].createPrinter();
159
+ const tracker = new compiler_host.ChangeTracker(printer);
160
+ const routeArraysToMigrate = findRoutesArrayToMigrate(sourceFile, typeChecker);
161
+ if (routeArraysToMigrate.length === 0) {
162
+ return { pendingChanges: [], skippedRoutes: [], migratedRoutes: [] };
163
+ }
164
+ const { skippedRoutes, migratedRoutes } = migrateRoutesArray(routeArraysToMigrate, typeChecker, tracker);
165
+ return {
166
+ pendingChanges: tracker.recordChanges().get(sourceFile) || [],
167
+ skippedRoutes,
168
+ migratedRoutes,
169
+ };
170
+ }
171
+ /** Finds route object that can be migrated */
172
+ function findRoutesArrayToMigrate(sourceFile, typeChecker) {
173
+ const routesArrays = [];
174
+ sourceFile.forEachChild(function walk(node) {
175
+ if (ts__default["default"].isCallExpression(node)) {
176
+ if (isRouterModuleCallExpression(node, typeChecker) ||
177
+ isRouterProviderCallExpression(node, typeChecker) ||
178
+ isRouterCallExpression(node, typeChecker) ||
179
+ isProvideRoutesCallExpression(node, typeChecker)) {
180
+ const arg = node.arguments[0]; // ex: RouterModule.forRoot(routes) or provideRouter(routes)
181
+ const routeFileImports = sourceFile.statements.filter(ts__default["default"].isImportDeclaration);
182
+ if (ts__default["default"].isArrayLiteralExpression(arg) && arg.elements.length > 0) {
183
+ // ex: inline routes array: RouterModule.forRoot([{ path: 'test', component: TestComponent }])
184
+ routesArrays.push({
185
+ routeFilePath: sourceFile.fileName,
186
+ array: arg,
187
+ routeFileImports,
188
+ });
189
+ }
190
+ else if (ts__default["default"].isIdentifier(arg)) {
191
+ // ex: reference to routes array: RouterModule.forRoot(routes)
192
+ // RouterModule.forRoot(routes), provideRouter(routes), provideRoutes(routes)
193
+ const symbol = typeChecker.getSymbolAtLocation(arg);
194
+ if (!symbol?.declarations)
195
+ return;
196
+ for (const declaration of symbol.declarations) {
197
+ if (ts__default["default"].isVariableDeclaration(declaration)) {
198
+ const initializer = declaration.initializer;
199
+ if (initializer && ts__default["default"].isArrayLiteralExpression(initializer)) {
200
+ // ex: const routes = [{ path: 'test', component: TestComponent }];
201
+ routesArrays.push({
202
+ routeFilePath: sourceFile.fileName,
203
+ array: initializer,
204
+ routeFileImports,
205
+ });
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
211
+ }
212
+ if (ts__default["default"].isVariableDeclaration(node)) {
213
+ if (isAngularRoutesArray(node, typeChecker)) {
214
+ const initializer = node.initializer;
215
+ if (initializer &&
216
+ ts__default["default"].isArrayLiteralExpression(initializer) &&
217
+ initializer.elements.length > 0) {
218
+ // ex: const routes: Routes = [{ path: 'test', component: TestComponent }];
219
+ if (routesArrays.find((x) => x.array === initializer)) {
220
+ // already exists
221
+ return;
222
+ }
223
+ routesArrays.push({
224
+ routeFilePath: sourceFile.fileName,
225
+ array: initializer,
226
+ routeFileImports: sourceFile.statements.filter(ts__default["default"].isImportDeclaration),
227
+ });
228
+ }
229
+ }
230
+ }
231
+ node.forEachChild(walk);
232
+ });
233
+ return routesArrays;
234
+ }
235
+ /** Migrate a routes object standalone components to be lazy loaded. */
236
+ function migrateRoutesArray(routesArray, typeChecker, tracker) {
237
+ const migratedRoutes = [];
238
+ const skippedRoutes = [];
239
+ const importsToRemove = [];
240
+ for (const route of routesArray) {
241
+ route.array.elements.forEach((element) => {
242
+ if (ts__default["default"].isObjectLiteralExpression(element)) {
243
+ const { migratedRoutes: migrated, skippedRoutes: toBeSkipped, importsToRemove: toBeRemoved, } = migrateRoute(element, route, typeChecker, tracker);
244
+ migratedRoutes.push(...migrated);
245
+ skippedRoutes.push(...toBeSkipped);
246
+ importsToRemove.push(...toBeRemoved);
247
+ }
248
+ });
249
+ }
250
+ for (const importToRemove of importsToRemove) {
251
+ tracker.removeNode(importToRemove);
252
+ }
253
+ return { migratedRoutes, skippedRoutes };
254
+ }
255
+ /**
256
+ * Migrates a single route object and returns the results of the migration
257
+ * It recursively migrates the children routes if they exist
258
+ */
259
+ function migrateRoute(element, route, typeChecker, tracker) {
260
+ const skippedRoutes = [];
261
+ const migratedRoutes = [];
262
+ const importsToRemove = [];
263
+ const component = findLiteralProperty(element, 'component');
264
+ // this can be empty string or a variable that is not a string, or not present at all
265
+ const routePath = findLiteralProperty(element, 'path')?.getText() ?? '';
266
+ const children = findLiteralProperty(element, 'children');
267
+ // recursively migrate children routes first if they exist
268
+ if (children && ts__default["default"].isArrayLiteralExpression(children.initializer)) {
269
+ for (const childRoute of children.initializer.elements) {
270
+ if (ts__default["default"].isObjectLiteralExpression(childRoute)) {
271
+ const { migratedRoutes: migrated, skippedRoutes: toBeSkipped, importsToRemove: toBeRemoved, } = migrateRoute(childRoute, route, typeChecker, tracker);
272
+ migratedRoutes.push(...migrated);
273
+ skippedRoutes.push(...toBeSkipped);
274
+ importsToRemove.push(...toBeRemoved);
275
+ }
276
+ }
277
+ }
278
+ const routeMigrationResults = { migratedRoutes, skippedRoutes, importsToRemove };
279
+ if (!component) {
280
+ return routeMigrationResults;
281
+ }
282
+ const componentDeclaration = findClassDeclaration(component, typeChecker);
283
+ if (!componentDeclaration) {
284
+ return routeMigrationResults;
285
+ }
286
+ // if component is not a standalone component, skip it
287
+ if (!isStandaloneComponent(componentDeclaration)) {
288
+ skippedRoutes.push({ path: routePath, file: route.routeFilePath });
289
+ return routeMigrationResults;
290
+ }
291
+ const componentClassName = componentDeclaration.name && ts__default["default"].isIdentifier(componentDeclaration.name)
292
+ ? componentDeclaration.name.text
293
+ : null;
294
+ if (!componentClassName) {
295
+ return routeMigrationResults;
296
+ }
297
+ // if component is in the same file as the routes array, skip it
298
+ if (componentDeclaration.getSourceFile().fileName === route.routeFilePath) {
299
+ return routeMigrationResults;
300
+ }
301
+ const componentImport = route.routeFileImports.find((importDecl) => importDecl.importClause?.getText().includes(componentClassName));
302
+ // remove single and double quotes from the import path
303
+ let componentImportPath = ts__default["default"].isStringLiteral(componentImport?.moduleSpecifier)
304
+ ? componentImport.moduleSpecifier.text
305
+ : null;
306
+ // if the import path is not a string literal, skip it
307
+ if (!componentImportPath) {
308
+ skippedRoutes.push({ path: routePath, file: route.routeFilePath });
309
+ return routeMigrationResults;
310
+ }
311
+ const isDefaultExport = componentDeclaration.modifiers?.some((x) => x.kind === ts__default["default"].SyntaxKind.DefaultKeyword) ?? false;
312
+ const loadComponent = createLoadComponentPropertyAssignment(componentImportPath, componentClassName, isDefaultExport);
313
+ tracker.replaceNode(component, loadComponent);
314
+ // Add the import statement for the standalone component
315
+ if (!importsToRemove.includes(componentImport)) {
316
+ importsToRemove.push(componentImport);
317
+ }
318
+ migratedRoutes.push({ path: routePath, file: route.routeFilePath });
319
+ // the component was migrated, so we return the results
320
+ return routeMigrationResults;
321
+ }
322
+ /**
323
+ * Generates the loadComponent property assignment for a given component.
324
+ *
325
+ * Example:
326
+ * loadComponent: () => import('./path').then(m => m.componentName)
327
+ * or
328
+ * loadComponent: () => import('./path') // when isDefaultExport is true
329
+ */
330
+ function createLoadComponentPropertyAssignment(componentImportPath, componentDeclarationName, isDefaultExport) {
331
+ return ts__default["default"].factory.createPropertyAssignment('loadComponent', ts__default["default"].factory.createArrowFunction(undefined, undefined, [], undefined, ts__default["default"].factory.createToken(ts__default["default"].SyntaxKind.EqualsGreaterThanToken), isDefaultExport
332
+ ? createImportCallExpression(componentImportPath) // will generate import('./path) and will skip the then() call
333
+ : ts__default["default"].factory.createCallExpression(
334
+ // will generate import('./path).then(m => m.componentName)
335
+ ts__default["default"].factory.createPropertyAccessExpression(createImportCallExpression(componentImportPath), 'then'), undefined, [createImportThenCallExpression(componentDeclarationName)])));
336
+ }
337
+ // import('./path)
338
+ const createImportCallExpression = (componentImportPath) => ts__default["default"].factory.createCallExpression(ts__default["default"].factory.createIdentifier('import'), undefined, [
339
+ ts__default["default"].factory.createStringLiteral(componentImportPath, true),
340
+ ]);
341
+ // m => m.componentName
342
+ const createImportThenCallExpression = (componentDeclarationName) => ts__default["default"].factory.createArrowFunction(undefined, undefined, [ts__default["default"].factory.createParameterDeclaration(undefined, undefined, 'm', undefined, undefined)], undefined, ts__default["default"].factory.createToken(ts__default["default"].SyntaxKind.EqualsGreaterThanToken), ts__default["default"].factory.createPropertyAccessExpression(ts__default["default"].factory.createIdentifier('m'), componentDeclarationName));
343
+
344
+ function migrate(options) {
345
+ return async (tree, context) => {
346
+ const { buildPaths } = await project_tsconfig_paths.getProjectTsConfigPaths(tree);
347
+ const basePath = process.cwd();
348
+ // TS and Schematic use paths in POSIX format even on Windows. This is needed as otherwise
349
+ // string matching such as `sourceFile.fileName.startsWith(pathToMigrate)` might not work.
350
+ const pathToMigrate = compiler_host.normalizePath(p.join(basePath, options.path));
351
+ if (!buildPaths.length) {
352
+ throw new schematics.SchematicsException('Could not find any tsconfig file. Cannot run the route lazy loading migration.');
353
+ }
354
+ let migratedRoutes = [];
355
+ let skippedRoutes = [];
356
+ for (const tsconfigPath of buildPaths) {
357
+ const { migratedRoutes: migrated, skippedRoutes: skipped } = standaloneRoutesMigration(tree, tsconfigPath, basePath, pathToMigrate, options);
358
+ migratedRoutes.push(...migrated);
359
+ skippedRoutes.push(...skipped);
360
+ }
361
+ if (migratedRoutes.length === 0 && skippedRoutes.length === 0) {
362
+ throw new schematics.SchematicsException(`Could not find any files to migrate under the path ${pathToMigrate}.`);
363
+ }
364
+ context.logger.info('🎉 Automated migration step has finished! 🎉');
365
+ context.logger.info(`Number of updated routes: ${migratedRoutes.length}`);
366
+ context.logger.info(`Number of skipped routes: ${skippedRoutes.length}`);
367
+ if (skippedRoutes.length > 0) {
368
+ context.logger.info(`Note: this migration was unable to optimize the following routes, since they use components declared in NgModules:`);
369
+ for (const route of skippedRoutes) {
370
+ context.logger.info(`- \`${route.path}\` path at \`${route.file}\``);
371
+ }
372
+ context.logger.info(`Consider making those components standalone and run this migration again. More information about standalone migration can be found at https://angular.dev/reference/migrations/standalone`);
373
+ }
374
+ context.logger.info('IMPORTANT! Please verify manually that your application builds and behaves as expected.');
375
+ context.logger.info(`See https://angular.dev/reference/migrations/route-lazy-loading for more information.`);
376
+ };
377
+ }
378
+ function standaloneRoutesMigration(tree, tsconfigPath, basePath, pathToMigrate, schematicOptions) {
379
+ if (schematicOptions.path.startsWith('..')) {
380
+ throw new schematics.SchematicsException('Cannot run route lazy loading migration outside of the current project.');
381
+ }
382
+ if (fs.existsSync(pathToMigrate) && !fs.statSync(pathToMigrate).isDirectory()) {
383
+ throw new schematics.SchematicsException(`Migration path ${pathToMigrate} has to be a directory. Cannot run the route lazy loading migration.`);
384
+ }
385
+ const program = compiler_host.createMigrationProgram(tree, tsconfigPath, basePath);
386
+ const sourceFiles = program
387
+ .getSourceFiles()
388
+ .filter((sourceFile) => sourceFile.fileName.startsWith(pathToMigrate) &&
389
+ compiler_host.canMigrateFile(basePath, sourceFile, program));
390
+ const migratedRoutes = [];
391
+ const skippedRoutes = [];
392
+ if (sourceFiles.length === 0) {
393
+ return { migratedRoutes, skippedRoutes };
394
+ }
395
+ for (const sourceFile of sourceFiles) {
396
+ const { pendingChanges, skippedRoutes: skipped, migratedRoutes: migrated, } = migrateFileToLazyRoutes(sourceFile, program);
397
+ skippedRoutes.push(...skipped);
398
+ migratedRoutes.push(...migrated);
399
+ const update = tree.beginUpdate(p.relative(basePath, sourceFile.fileName));
400
+ pendingChanges.forEach((change) => {
401
+ if (change.removeLength != null) {
402
+ update.remove(change.start, change.removeLength);
403
+ }
404
+ update.insertRight(change.start, change.text);
405
+ });
406
+ tree.commitUpdate(update);
407
+ }
408
+ return { migratedRoutes, skippedRoutes };
409
+ }
410
+
411
+ exports.migrate = migrate;