@angular/core 21.0.0-next.0 → 21.0.0-next.10

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.
Files changed (106) hide show
  1. package/fesm2022/_attribute-chunk.mjs +12 -0
  2. package/fesm2022/_attribute-chunk.mjs.map +1 -0
  3. package/fesm2022/_debug_node-chunk.mjs +18469 -0
  4. package/fesm2022/_debug_node-chunk.mjs.map +1 -0
  5. package/fesm2022/_effect-chunk.mjs +423 -0
  6. package/fesm2022/_effect-chunk.mjs.map +1 -0
  7. package/fesm2022/_effect-chunk2.mjs +2951 -0
  8. package/fesm2022/_effect-chunk2.mjs.map +1 -0
  9. package/fesm2022/_not_found-chunk.mjs +39 -0
  10. package/fesm2022/_not_found-chunk.mjs.map +1 -0
  11. package/fesm2022/_resource-chunk.mjs +378 -0
  12. package/fesm2022/_resource-chunk.mjs.map +1 -0
  13. package/fesm2022/_untracked-chunk.mjs +96 -0
  14. package/fesm2022/_untracked-chunk.mjs.map +1 -0
  15. package/fesm2022/_weak_ref-chunk.mjs +10 -0
  16. package/fesm2022/_weak_ref-chunk.mjs.map +1 -0
  17. package/fesm2022/core.mjs +2499 -4185
  18. package/fesm2022/core.mjs.map +1 -1
  19. package/fesm2022/primitives-di.mjs +23 -0
  20. package/fesm2022/primitives-di.mjs.map +1 -0
  21. package/fesm2022/primitives-event-dispatch.mjs +788 -0
  22. package/fesm2022/primitives-event-dispatch.mjs.map +1 -0
  23. package/fesm2022/primitives-signals.mjs +187 -0
  24. package/fesm2022/primitives-signals.mjs.map +1 -0
  25. package/fesm2022/rxjs-interop.mjs +210 -308
  26. package/fesm2022/rxjs-interop.mjs.map +1 -1
  27. package/fesm2022/testing.mjs +2309 -3170
  28. package/fesm2022/testing.mjs.map +1 -1
  29. package/package.json +18 -12
  30. package/resources/best-practices.md +56 -0
  31. package/schematics/bundles/add-bootstrap-context-to-server-main.cjs +117 -0
  32. package/schematics/bundles/application-config-core.cjs +84 -0
  33. package/schematics/bundles/{apply_import_manager-DR9xXCle.cjs → apply_import_manager-1Zs_gpB6.cjs} +4 -5
  34. package/schematics/bundles/bootstrap-options-migration.cjs +598 -0
  35. package/schematics/bundles/cleanup-unused-imports.cjs +9 -13
  36. package/schematics/bundles/common-to-standalone-migration.cjs +381 -0
  37. package/schematics/bundles/{compiler_host-BXBP7CE2.cjs → compiler_host-DBwYMlTo.cjs} +10 -11
  38. package/schematics/bundles/control-flow-migration.cjs +122 -119
  39. package/schematics/bundles/{imports-CIX-JgAN.cjs → imports-DP72APSx.cjs} +6 -1
  40. package/schematics/bundles/{index-CfTQUOiz.cjs → index-B7I9sIUx.cjs} +36 -39
  41. package/schematics/bundles/inject-migration.cjs +148 -70
  42. package/schematics/bundles/leading_space-D9nQ8UQC.cjs +1 -1
  43. package/schematics/bundles/{migrate_ts_type_references-6NtAj-Wk.cjs → migrate_ts_type_references-UGIUl7En.cjs} +500 -24
  44. package/schematics/bundles/ng_component_template-Dsuq1Lw7.cjs +185 -0
  45. package/schematics/bundles/{ng_decorators-B5HCqr20.cjs → ng_decorators-DSFlWYQY.cjs} +2 -2
  46. package/schematics/bundles/ngclass-to-class-migration.cjs +542 -0
  47. package/schematics/bundles/ngstyle-to-style-migration.cjs +487 -0
  48. package/schematics/bundles/nodes-B16H9JUd.cjs +1 -1
  49. package/schematics/bundles/output-migration.cjs +16 -19
  50. package/schematics/bundles/parse_html-8VLCL37B.cjs +132 -0
  51. package/schematics/bundles/{project_paths-DcaODbky.cjs → project_paths-DvD50ouC.cjs} +14 -247
  52. package/schematics/bundles/project_tsconfig_paths-CDVxT6Ov.cjs +90 -0
  53. package/schematics/bundles/property_name-BBwFuqMe.cjs +1 -1
  54. package/schematics/bundles/route-lazy-loading.cjs +54 -26
  55. package/schematics/bundles/router-current-navigation.cjs +7 -18
  56. package/schematics/bundles/router-last-successful-navigation.cjs +7 -18
  57. package/schematics/bundles/router-testing-module-migration.cjs +502 -0
  58. package/schematics/bundles/self-closing-tags-migration.cjs +17 -216
  59. package/schematics/bundles/signal-input-migration.cjs +93 -29
  60. package/schematics/bundles/signal-queries-migration.cjs +22 -25
  61. package/schematics/bundles/signals.cjs +10 -13
  62. package/schematics/bundles/standalone-migration.cjs +135 -102
  63. package/schematics/bundles/{symbol-VPWguRxr.cjs → symbol-BObKoqes.cjs} +3 -2
  64. package/schematics/collection.json +23 -0
  65. package/schematics/migrations/common-to-standalone-migration/schema.json +14 -0
  66. package/schematics/migrations/ngclass-to-class-migration/schema.json +20 -0
  67. package/schematics/migrations/ngstyle-to-style-migration/schema.json +20 -0
  68. package/schematics/migrations/router-testing-module-migration/schema.json +14 -0
  69. package/schematics/migrations.json +16 -2
  70. package/{api.d.d.ts → types/_api-chunk.d.ts} +9 -6
  71. package/{chrome_dev_tools_performance.d.d.ts → types/_chrome_dev_tools_performance-chunk.d.ts} +26 -31
  72. package/{discovery.d.d.ts → types/_discovery-chunk.d.ts} +135 -98
  73. package/{signal.d.d.ts → types/_effect-chunk.d.ts} +14 -5
  74. package/{event_dispatcher.d.d.ts → types/_event_dispatcher-chunk.d.ts} +2 -2
  75. package/{graph.d.d.ts → types/_formatter-chunk.d.ts} +40 -7
  76. package/{weak_ref.d.d.ts → types/_weak_ref-chunk.d.ts} +2 -2
  77. package/{index.d.ts → types/core.d.ts} +233 -305
  78. package/{primitives/di/index.d.ts → types/primitives-di.d.ts} +2 -2
  79. package/{primitives/event-dispatch/index.d.ts → types/primitives-event-dispatch.d.ts} +4 -4
  80. package/{primitives/signals/index.d.ts → types/primitives-signals.d.ts} +7 -8
  81. package/{rxjs-interop/index.d.ts → types/rxjs-interop.d.ts} +8 -6
  82. package/{testing/index.d.ts → types/testing.d.ts} +7 -7
  83. package/fesm2022/attribute.mjs +0 -24
  84. package/fesm2022/attribute.mjs.map +0 -1
  85. package/fesm2022/debug_node.mjs +0 -31833
  86. package/fesm2022/debug_node.mjs.map +0 -1
  87. package/fesm2022/not_found.mjs +0 -56
  88. package/fesm2022/not_found.mjs.map +0 -1
  89. package/fesm2022/primitives/di.mjs +0 -23
  90. package/fesm2022/primitives/di.mjs.map +0 -1
  91. package/fesm2022/primitives/event-dispatch.mjs +0 -1622
  92. package/fesm2022/primitives/event-dispatch.mjs.map +0 -1
  93. package/fesm2022/primitives/signals.mjs +0 -89
  94. package/fesm2022/primitives/signals.mjs.map +0 -1
  95. package/fesm2022/resource.mjs +0 -633
  96. package/fesm2022/resource.mjs.map +0 -1
  97. package/fesm2022/root_effect_scheduler.mjs +0 -4007
  98. package/fesm2022/root_effect_scheduler.mjs.map +0 -1
  99. package/fesm2022/signal.mjs +0 -560
  100. package/fesm2022/signal.mjs.map +0 -1
  101. package/fesm2022/untracked.mjs +0 -117
  102. package/fesm2022/untracked.mjs.map +0 -1
  103. package/fesm2022/weak_ref.mjs +0 -12
  104. package/fesm2022/weak_ref.mjs.map +0 -1
  105. package/schematics/bundles/index-esqfDjNB.cjs +0 -22074
  106. package/schematics/bundles/project_tsconfig_paths-CS-eSeHC.cjs +0 -51062
@@ -0,0 +1,502 @@
1
+ 'use strict';
2
+ /**
3
+ * @license Angular v21.0.0-next.10
4
+ * (c) 2010-2025 Google LLC. https://angular.io/
5
+ * License: MIT
6
+ */
7
+ 'use strict';
8
+
9
+ require('@angular-devkit/core');
10
+ require('node:path/posix');
11
+ var project_paths = require('./project_paths-DvD50ouC.cjs');
12
+ require('@angular/compiler-cli');
13
+ var migrations = require('@angular/compiler-cli/private/migrations');
14
+ var ts = require('typescript');
15
+ require('node:path');
16
+ var apply_import_manager = require('./apply_import_manager-1Zs_gpB6.cjs');
17
+ require('@angular-devkit/schematics');
18
+ require('./project_tsconfig_paths-CDVxT6Ov.cjs');
19
+
20
+ const ROUTER_TESTING_MODULE = 'RouterTestingModule';
21
+ const SPY_LOCATION = 'SpyLocation';
22
+ const ROUTER_MODULE = 'RouterModule';
23
+ const PROVIDE_LOCATION_MOCKS = 'provideLocationMocks';
24
+ const ANGULAR_ROUTER_TESTING = '@angular/router/testing';
25
+ const ANGULAR_ROUTER = '@angular/router';
26
+ const ANGULAR_COMMON_TESTING = '@angular/common/testing';
27
+ const IMPORTS_PROPERTY = 'imports';
28
+ const PROVIDERS_PROPERTY = 'providers';
29
+ const WITH_ROUTES_STATIC_METHOD = 'withRoutes';
30
+ const TESTBED_IDENTIFIER = 'TestBed';
31
+ const CONFIGURE_TESTING_MODULE = 'configureTestingModule';
32
+ function hasImportFromModule(sourceFile, modulePath, ...symbolNames) {
33
+ const symbolSet = new Set(symbolNames);
34
+ let hasImport = false;
35
+ ts.forEachChild(sourceFile, (node) => {
36
+ if (ts.isImportDeclaration(node) &&
37
+ ts.isStringLiteral(node.moduleSpecifier) &&
38
+ node.moduleSpecifier.text === modulePath &&
39
+ node.importClause?.namedBindings &&
40
+ ts.isNamedImports(node.importClause.namedBindings)) {
41
+ for (const element of node.importClause.namedBindings.elements) {
42
+ if (symbolSet.has(element.name.text)) {
43
+ hasImport = true;
44
+ break;
45
+ }
46
+ }
47
+ }
48
+ });
49
+ return hasImport;
50
+ }
51
+ function detectSpyLocationUrlChangesUsage(sourceFile) {
52
+ const hasSpyLocationImport = hasImportFromModule(sourceFile, ANGULAR_COMMON_TESTING, SPY_LOCATION);
53
+ let usesUrlChangesFeature = false;
54
+ function walk(node) {
55
+ if (usesUrlChangesFeature) {
56
+ return;
57
+ }
58
+ if (ts.isPropertyAccessExpression(node) &&
59
+ ts.isIdentifier(node.name) &&
60
+ node.name.text === 'urlChanges') {
61
+ usesUrlChangesFeature = true;
62
+ return;
63
+ }
64
+ node.forEachChild(walk);
65
+ }
66
+ walk(sourceFile);
67
+ return hasSpyLocationImport && usesUrlChangesFeature;
68
+ }
69
+ function createArrayLiteralReplacement(file, arrayLiteral, newElements, sourceFile) {
70
+ const elementNodes = newElements.map((element) => {
71
+ if (typeof element === 'string') {
72
+ return parseStringToExpression(element);
73
+ }
74
+ return element;
75
+ });
76
+ const newArray = ts.factory.updateArrayLiteralExpression(arrayLiteral, elementNodes);
77
+ const printer = ts.createPrinter({
78
+ newLine: ts.NewLineKind.LineFeed,
79
+ });
80
+ const newText = printer.printNode(ts.EmitHint.Unspecified, newArray, sourceFile);
81
+ return new project_paths.Replacement(file, new project_paths.TextUpdate({
82
+ position: arrayLiteral.getStart(),
83
+ end: arrayLiteral.getEnd(),
84
+ toInsert: newText,
85
+ }));
86
+ }
87
+ function createImportRemovalReplacement(file, importDeclaration, namedBindings, symbolToRemove, sourceFile) {
88
+ const otherImports = namedBindings.elements.filter((el) => el.name.text !== symbolToRemove);
89
+ if (otherImports.length === 0) {
90
+ return new project_paths.Replacement(file, new project_paths.TextUpdate({
91
+ position: importDeclaration.getStart(),
92
+ end: importDeclaration.getEnd() + 1,
93
+ toInsert: '',
94
+ }));
95
+ }
96
+ else {
97
+ const newNamedBindings = ts.factory.updateNamedImports(namedBindings, otherImports);
98
+ const printer = ts.createPrinter();
99
+ const newText = printer.printNode(ts.EmitHint.Unspecified, newNamedBindings, sourceFile);
100
+ return new project_paths.Replacement(file, new project_paths.TextUpdate({
101
+ position: namedBindings.getStart(),
102
+ end: namedBindings.getEnd(),
103
+ toInsert: newText,
104
+ }));
105
+ }
106
+ }
107
+ function isEmptyArrayExpression(expression) {
108
+ return ts.isArrayLiteralExpression(expression) && expression.elements.length === 0;
109
+ }
110
+ function getRoutesArgumentForMigration(routesNode, optionsNode) {
111
+ if (!routesNode) {
112
+ return undefined;
113
+ }
114
+ if (!isEmptyArrayExpression(routesNode)) {
115
+ return routesNode;
116
+ }
117
+ return optionsNode ? routesNode : undefined;
118
+ }
119
+ function createRouterModuleExpression(routesArg, optionsArg) {
120
+ const routerModuleIdentifier = ts.factory.createIdentifier(ROUTER_MODULE);
121
+ if (routesArg) {
122
+ // Build args list and include options if present
123
+ const args = [routesArg];
124
+ if (optionsArg) {
125
+ args.push(optionsArg);
126
+ }
127
+ // Create RouterModule.forRoot(routes, options?) expression
128
+ return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(routerModuleIdentifier, ts.factory.createIdentifier('forRoot')), undefined, args);
129
+ }
130
+ return routerModuleIdentifier;
131
+ }
132
+ function createProviderCallExpression(functionName, argument) {
133
+ return ts.factory.createCallExpression(ts.factory.createIdentifier(functionName), undefined, []);
134
+ }
135
+ function createArrayLiteralFromExpressions(expressions) {
136
+ return ts.factory.createArrayLiteralExpression(Array.from(expressions), true);
137
+ }
138
+ function parseStringToExpression(text) {
139
+ const wrapped = `(${text})`;
140
+ const sourceFile = ts.createSourceFile('temp.ts', wrapped, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
141
+ if (sourceFile.statements.length === 1) {
142
+ const statement = sourceFile.statements[0];
143
+ if (ts.isExpressionStatement(statement) && ts.isParenthesizedExpression(statement.expression)) {
144
+ return statement.expression.expression;
145
+ }
146
+ }
147
+ return parseExpressionWithPatternRecognition(text);
148
+ }
149
+ function parseExpressionWithPatternRecognition(text) {
150
+ const callPattern = analyzeCallPattern(text);
151
+ if (callPattern) {
152
+ return ts.factory.createCallExpression(callPattern.expression, undefined, Array.from(callPattern.arguments));
153
+ }
154
+ const arrayPattern = analyzeArrayPattern(text);
155
+ if (arrayPattern) {
156
+ return ts.factory.createArrayLiteralExpression(Array.from(arrayPattern.elements), true);
157
+ }
158
+ const literalPattern = analyzeLiteralPattern(text);
159
+ if (literalPattern) {
160
+ return literalPattern.expression;
161
+ }
162
+ return ts.factory.createIdentifier(text);
163
+ }
164
+ function analyzeCallPattern(text) {
165
+ const testExpression = `(${text})`;
166
+ const sourceFile = ts.createSourceFile('temp.ts', testExpression, ts.ScriptTarget.Latest, true);
167
+ if (sourceFile.statements.length === 1) {
168
+ const statement = sourceFile.statements[0];
169
+ if (ts.isExpressionStatement(statement) &&
170
+ ts.isParenthesizedExpression(statement.expression) &&
171
+ ts.isCallExpression(statement.expression.expression)) {
172
+ const callExpr = statement.expression.expression;
173
+ return {
174
+ type: 'call',
175
+ expression: callExpr.expression,
176
+ arguments: callExpr.arguments,
177
+ };
178
+ }
179
+ }
180
+ return null;
181
+ }
182
+ function analyzeArrayPattern(text) {
183
+ const sourceFile = ts.createSourceFile('temp.ts', text, ts.ScriptTarget.Latest, true);
184
+ if (sourceFile.statements.length === 1) {
185
+ const statement = sourceFile.statements[0];
186
+ if (ts.isExpressionStatement(statement) && ts.isArrayLiteralExpression(statement.expression)) {
187
+ return {
188
+ type: 'array',
189
+ elements: statement.expression.elements,
190
+ };
191
+ }
192
+ }
193
+ return null;
194
+ }
195
+ function analyzeLiteralPattern(text) {
196
+ const sourceFile = ts.createSourceFile('temp.ts', text, ts.ScriptTarget.Latest, true);
197
+ if (sourceFile.statements.length === 1) {
198
+ const statement = sourceFile.statements[0];
199
+ if (ts.isExpressionStatement(statement)) {
200
+ const expr = statement.expression;
201
+ if (ts.isStringLiteral(expr) || ts.isNumericLiteral(expr) || ts.isLiteralExpression(expr)) {
202
+ return {
203
+ type: 'literal',
204
+ expression: expr,
205
+ };
206
+ }
207
+ }
208
+ }
209
+ return null;
210
+ }
211
+ function removeRouterTestingModuleImport(sourceFile, file, replacements) {
212
+ ts.forEachChild(sourceFile, (node) => {
213
+ if (ts.isImportDeclaration(node) &&
214
+ ts.isStringLiteral(node.moduleSpecifier) &&
215
+ node.moduleSpecifier.text === ANGULAR_ROUTER_TESTING &&
216
+ node.importClause?.namedBindings &&
217
+ ts.isNamedImports(node.importClause.namedBindings)) {
218
+ const namedBindings = node.importClause.namedBindings;
219
+ replacements.push(createImportRemovalReplacement(file, node, namedBindings, ROUTER_TESTING_MODULE, sourceFile));
220
+ }
221
+ });
222
+ }
223
+ function migrateToRouterModule(usage, file, routesNode, optionsNode, replacements) {
224
+ const neededImportsExpressions = new Set();
225
+ const neededProvidersExpressions = new Set();
226
+ const optionsExpression = optionsNode ? optionsNode : undefined;
227
+ const routesExpression = getRoutesArgumentForMigration(routesNode, optionsNode);
228
+ const routerModuleExpression = createRouterModuleExpression(routesExpression, optionsExpression);
229
+ neededImportsExpressions.add(routerModuleExpression);
230
+ if (usage.usesSpyLocationUrlChanges) {
231
+ const provideLocationMocksExpression = createProviderCallExpression(PROVIDE_LOCATION_MOCKS);
232
+ neededProvidersExpressions.add(provideLocationMocksExpression);
233
+ }
234
+ if (usage.importsProperty && ts.isArrayLiteralExpression(usage.importsProperty.initializer)) {
235
+ const importsArray = usage.importsProperty.initializer;
236
+ const otherImportExpressions = usage.importsArrayElements.filter((el) => el !== usage.routerTestingModuleElement);
237
+ const allImportExpressions = [
238
+ ...otherImportExpressions,
239
+ ...Array.from(neededImportsExpressions),
240
+ ];
241
+ replacements.push(createArrayLiteralReplacement(file, importsArray, allImportExpressions, usage.sourceFile));
242
+ }
243
+ if (neededProvidersExpressions.size > 0) {
244
+ if (usage.providersProperty &&
245
+ ts.isArrayLiteralExpression(usage.providersProperty.initializer)) {
246
+ const existingProvidersArray = usage.providersProperty.initializer;
247
+ const allProviderExpressions = [
248
+ ...existingProvidersArray.elements,
249
+ ...Array.from(neededProvidersExpressions),
250
+ ];
251
+ replacements.push(createArrayLiteralReplacement(file, existingProvidersArray, allProviderExpressions, usage.sourceFile));
252
+ }
253
+ else {
254
+ const providersArray = createArrayLiteralFromExpressions(neededProvidersExpressions);
255
+ const printer = ts.createPrinter();
256
+ const providersText = printer.printNode(ts.EmitHint.Unspecified, providersArray, usage.sourceFile);
257
+ const insertPosition = usage.importsProperty.getEnd();
258
+ replacements.push(new project_paths.Replacement(file, new project_paths.TextUpdate({
259
+ position: insertPosition,
260
+ end: insertPosition,
261
+ toInsert: `,\n ${PROVIDERS_PROPERTY}: ${providersText}`,
262
+ })));
263
+ }
264
+ }
265
+ }
266
+ function analyzeRouterTestingModuleUsage(usage) {
267
+ const neededProviders = new Set();
268
+ const neededImports = new Set();
269
+ let hasLocationMocks = false;
270
+ const optionsExpression = usage.optionsNode ? usage.optionsNode : undefined;
271
+ const routesExpression = getRoutesArgumentForMigration(usage.routesNode, usage.optionsNode);
272
+ // Add RouterModule to imports (preserve options when present)
273
+ const routerModuleExpression = createRouterModuleExpression(routesExpression, optionsExpression);
274
+ neededImports.add(routerModuleExpression);
275
+ // Add location mocks ONLY if:
276
+ // 1. SpyLocation is imported from @angular/common/testing, AND
277
+ // 2. urlChanges property is accessed in the test
278
+ // 3. provideLocationMocks() is not already present
279
+ if (usage.usesSpyLocationUrlChanges) {
280
+ const provideLocationMocksExpression = createProviderCallExpression(PROVIDE_LOCATION_MOCKS);
281
+ neededProviders.add(provideLocationMocksExpression);
282
+ hasLocationMocks = true;
283
+ }
284
+ return {
285
+ neededProviders,
286
+ neededImports,
287
+ canRemoveRouterTestingModule: true,
288
+ replacementCount: 1,
289
+ hasLocationMocks,
290
+ };
291
+ }
292
+ function findRouterTestingModuleUsages(sourceFile) {
293
+ const usages = [];
294
+ const hasRouterTestingModule = hasImportFromModule(sourceFile, ANGULAR_ROUTER_TESTING, ROUTER_TESTING_MODULE);
295
+ const usesSpyLocationUrlChanges = detectSpyLocationUrlChangesUsage(sourceFile);
296
+ if (!hasRouterTestingModule) {
297
+ return usages;
298
+ }
299
+ function walk(node) {
300
+ if (ts.isCallExpression(node) &&
301
+ ts.isPropertyAccessExpression(node.expression) &&
302
+ node.expression.name.text === CONFIGURE_TESTING_MODULE &&
303
+ ts.isIdentifier(node.expression.expression) &&
304
+ node.expression.expression.text === TESTBED_IDENTIFIER &&
305
+ node.arguments.length > 0 &&
306
+ ts.isObjectLiteralExpression(node.arguments[0])) {
307
+ const config = node.arguments[0];
308
+ let importsProperty = null;
309
+ let providersProperty = null;
310
+ for (const prop of config.properties) {
311
+ if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
312
+ if (prop.name.text === IMPORTS_PROPERTY) {
313
+ importsProperty = prop;
314
+ }
315
+ else if (prop.name.text === PROVIDERS_PROPERTY) {
316
+ providersProperty = prop;
317
+ }
318
+ }
319
+ }
320
+ if (!importsProperty || !ts.isArrayLiteralExpression(importsProperty.initializer)) {
321
+ node.forEachChild(walk);
322
+ return;
323
+ }
324
+ const importsArray = importsProperty.initializer;
325
+ let routerTestingModuleElement = null;
326
+ let routesNode = null;
327
+ let optionsNode = null;
328
+ for (const element of importsArray.elements) {
329
+ if (ts.isIdentifier(element) && element.text === ROUTER_TESTING_MODULE) {
330
+ routerTestingModuleElement = element;
331
+ break;
332
+ }
333
+ else if (ts.isCallExpression(element) &&
334
+ ts.isPropertyAccessExpression(element.expression) &&
335
+ ts.isIdentifier(element.expression.expression) &&
336
+ element.expression.expression.text === ROUTER_TESTING_MODULE &&
337
+ element.expression.name.text === WITH_ROUTES_STATIC_METHOD) {
338
+ routerTestingModuleElement = element;
339
+ if (element.arguments.length > 0) {
340
+ routesNode = element.arguments[0];
341
+ }
342
+ if (element.arguments.length > 1) {
343
+ optionsNode = element.arguments[1];
344
+ }
345
+ break;
346
+ }
347
+ }
348
+ if (routerTestingModuleElement) {
349
+ usages.push({
350
+ sourceFile,
351
+ configObject: config,
352
+ importsProperty,
353
+ providersProperty,
354
+ routerTestingModuleElement,
355
+ routesNode,
356
+ optionsNode,
357
+ importsArrayElements: Array.from(importsArray.elements),
358
+ usesSpyLocationUrlChanges,
359
+ });
360
+ }
361
+ }
362
+ node.forEachChild(walk);
363
+ }
364
+ walk(sourceFile);
365
+ return usages;
366
+ }
367
+ function processRouterTestingModuleUsage(usage, sourceFile, info, importManager, replacements) {
368
+ const file = project_paths.projectFile(sourceFile, info);
369
+ const routesNode = usage.routesNode;
370
+ const optionsNode = usage.optionsNode;
371
+ const analysis = analyzeRouterTestingModuleUsage(usage);
372
+ migrateToRouterModule(usage, file, routesNode, optionsNode, replacements);
373
+ importManager.addImport({
374
+ exportModuleSpecifier: ANGULAR_ROUTER,
375
+ exportSymbolName: ROUTER_MODULE,
376
+ requestedFile: sourceFile,
377
+ });
378
+ if (analysis.hasLocationMocks) {
379
+ importManager.addImport({
380
+ exportModuleSpecifier: ANGULAR_COMMON_TESTING,
381
+ exportSymbolName: PROVIDE_LOCATION_MOCKS,
382
+ requestedFile: sourceFile,
383
+ });
384
+ }
385
+ removeRouterTestingModuleImport(sourceFile, file, replacements);
386
+ }
387
+
388
+ /**
389
+ * Migration that converts RouterTestingModule usages to the recommended API:
390
+ * - Replace RouterTestingModule with RouterModule for all tests (respecting existing imports)
391
+ * - Adds provideLocationMocks only when needed and not conflicting
392
+ */
393
+ class RouterTestingModuleMigration extends project_paths.TsurgeFunnelMigration {
394
+ config;
395
+ constructor(config = {}) {
396
+ super();
397
+ this.config = config;
398
+ }
399
+ async analyze(info) {
400
+ const replacements = [];
401
+ const migratedUsages = [];
402
+ const filesWithLocationMocks = new Map();
403
+ const importManager = new migrations.ImportManager({
404
+ shouldUseSingleQuotes: () => true,
405
+ });
406
+ for (const sourceFile of info.sourceFiles) {
407
+ const file = project_paths.projectFile(sourceFile, info);
408
+ if (this.config.shouldMigrate && !this.config.shouldMigrate(file)) {
409
+ continue;
410
+ }
411
+ const usages = findRouterTestingModuleUsages(sourceFile);
412
+ for (const usage of usages) {
413
+ processRouterTestingModuleUsage(usage, sourceFile, info, importManager, replacements);
414
+ migratedUsages.push(usage);
415
+ if (usage.usesSpyLocationUrlChanges) {
416
+ filesWithLocationMocks.set(sourceFile.fileName, true);
417
+ }
418
+ }
419
+ }
420
+ apply_import_manager.applyImportManagerChanges(importManager, replacements, info.sourceFiles, info);
421
+ return project_paths.confirmAsSerializable({
422
+ replacements,
423
+ migratedUsages,
424
+ filesWithLocationMocks,
425
+ });
426
+ }
427
+ async migrate(globalData) {
428
+ return {
429
+ replacements: globalData.replacements,
430
+ };
431
+ }
432
+ async combine(unitA, unitB) {
433
+ const combinedFilesWithLocationMocks = new Map(unitA.filesWithLocationMocks);
434
+ for (const [fileName, hasLocationMocks] of unitB.filesWithLocationMocks) {
435
+ combinedFilesWithLocationMocks.set(fileName, hasLocationMocks || combinedFilesWithLocationMocks.get(fileName) || false);
436
+ }
437
+ return project_paths.confirmAsSerializable({
438
+ replacements: [...unitA.replacements, ...unitB.replacements],
439
+ migratedUsages: [...unitA.migratedUsages, ...unitB.migratedUsages],
440
+ filesWithLocationMocks: combinedFilesWithLocationMocks,
441
+ });
442
+ }
443
+ async globalMeta(combinedData) {
444
+ return project_paths.confirmAsSerializable(combinedData);
445
+ }
446
+ async stats(globalMetadata) {
447
+ const stats = {
448
+ counters: {
449
+ replacements: globalMetadata.replacements.length,
450
+ migratedUsages: globalMetadata.migratedUsages.length,
451
+ filesWithLocationMocks: globalMetadata.filesWithLocationMocks.size,
452
+ totalFiles: new Set(globalMetadata.migratedUsages.map((usage) => usage.sourceFile.fileName))
453
+ .size,
454
+ },
455
+ };
456
+ return stats;
457
+ }
458
+ }
459
+
460
+ function migrate(options) {
461
+ return async (tree, context) => {
462
+ await project_paths.runMigrationInDevkit({
463
+ tree,
464
+ getMigration: (fs) => new RouterTestingModuleMigration({
465
+ shouldMigrate: (file) => {
466
+ return (file.rootRelativePath.startsWith(fs.normalize(options.path)) &&
467
+ !/(^|\/)node_modules\//.test(file.rootRelativePath) &&
468
+ /\.spec\.ts$/.test(file.rootRelativePath));
469
+ },
470
+ }),
471
+ beforeProgramCreation: (tsconfigPath, stage) => {
472
+ if (stage === project_paths.MigrationStage.Analysis) {
473
+ context.logger.info(`Preparing analysis for: ${tsconfigPath}...`);
474
+ }
475
+ else {
476
+ context.logger.info(`Running migration for: ${tsconfigPath}...`);
477
+ }
478
+ },
479
+ beforeUnitAnalysis: (tsconfigPath) => {
480
+ context.logger.info(`Scanning for RouterTestingModule usage: ${tsconfigPath}...`);
481
+ },
482
+ afterAllAnalyzed: () => {
483
+ context.logger.info(``);
484
+ context.logger.info(`Processing analysis data between targets...`);
485
+ context.logger.info(``);
486
+ },
487
+ afterAnalysisFailure: () => {
488
+ context.logger.error('Migration failed unexpectedly with no analysis data');
489
+ },
490
+ whenDone: (stats) => {
491
+ context.logger.info('');
492
+ context.logger.info(`Successfully migrated RouterTestingModule to RouterModule 🎉`);
493
+ context.logger.info(` -> Migrated ${stats.counters.migratedUsages} RouterTestingModule usages in ${stats.counters.totalFiles} test files.`);
494
+ if (stats.counters.filesWithLocationMocks > 0) {
495
+ context.logger.info(` -> Added provideLocationMocks() to ${stats.counters.filesWithLocationMocks} files with SpyLocation.urlChanges usage.`);
496
+ }
497
+ },
498
+ });
499
+ };
500
+ }
501
+
502
+ exports.migrate = migrate;