@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,598 @@
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
+ var property_name = require('./property_name-BBwFuqMe.cjs');
18
+ var imports = require('./imports-DP72APSx.cjs');
19
+ var symbol = require('./symbol-BObKoqes.cjs');
20
+ require('@angular-devkit/schematics');
21
+ require('./project_tsconfig_paths-CDVxT6Ov.cjs');
22
+
23
+ const CORE_PACKAGE = '@angular/core';
24
+ const PROVIDE_ZONE_CHANGE_DETECTION = 'provideZoneChangeDetection';
25
+ const ZONE_CD_PROVIDER = `${PROVIDE_ZONE_CHANGE_DETECTION}()`;
26
+ const SAFE_TO_REMOVE_OPTIONS = [
27
+ 'ignoreChangesOutsideZone',
28
+ 'ngZoneRunCoalescing',
29
+ 'ngZoneEventCoalescing',
30
+ ];
31
+ const BOOTSTRAP_OPTIONS = ['ngZone', ...SAFE_TO_REMOVE_OPTIONS];
32
+ class BootstrapOptionsMigration extends project_paths.TsurgeFunnelMigration {
33
+ async analyze(info) {
34
+ let replacements = [];
35
+ const importManager = new migrations.ImportManager();
36
+ for (const sourceFile of info.sourceFiles) {
37
+ // We need to migration either
38
+ // * `bootstrapApplication(App)
39
+ // * `platformBrowser().bootstrapModule(AppModule)`
40
+ // * `platformBrowserDynamic().bootstrapModule(AppModule)`
41
+ // * `TestBed.initTestEnvironment([AppModule], platformBrowserTesting())`
42
+ // * `getTestBed.initTestEnvironment([AppModule], platformBrowserTesting())`
43
+ const specifiers = getSpecifiers(sourceFile);
44
+ // If none of the imports related to bootstraping are present, we can skip the file.
45
+ if (specifiers === null)
46
+ continue;
47
+ const { bootstrapAppSpecifier, testBedSpecifier, createApplicationSpecifier, getTestBedSpecifier, } = specifiers;
48
+ const typeChecker = info.program.getTypeChecker();
49
+ const isCreateApplicationNode = (node) => {
50
+ return (ts.isCallExpression(node) &&
51
+ createApplicationSpecifier !== null &&
52
+ symbol.isReferenceToImport(typeChecker, node.expression, createApplicationSpecifier));
53
+ };
54
+ const isBootstrapAppNode = (node) => {
55
+ return (ts.isCallExpression(node) &&
56
+ bootstrapAppSpecifier !== null &&
57
+ symbol.isReferenceToImport(typeChecker, node.expression, bootstrapAppSpecifier));
58
+ };
59
+ const isBootstrapModuleNode = (node) => {
60
+ return (ts.isCallExpression(node) &&
61
+ ts.isPropertyAccessExpression(node.expression) &&
62
+ node.expression.name.text === 'bootstrapModule' &&
63
+ node.arguments.length > 0);
64
+ };
65
+ const isTestBedInitEnvironmentNode = (node) => {
66
+ return (ts.isCallExpression(node) &&
67
+ ts.isPropertyAccessExpression(node.expression) &&
68
+ node.expression.name.text === 'initTestEnvironment' &&
69
+ (symbol.isReferenceToImport(typeChecker, node.expression.expression, testBedSpecifier) ||
70
+ symbol.isReferenceToImport(typeChecker, node.expression.expression, getTestBedSpecifier)));
71
+ };
72
+ const reflector = new migrations.TypeScriptReflectionHost(typeChecker);
73
+ const evaluator = new migrations.PartialEvaluator(reflector, typeChecker, null);
74
+ const walk = (node) => {
75
+ if (isBootstrapAppNode(node)) {
76
+ this.analyzeBootstrapApplication(node, sourceFile, info, typeChecker, importManager, replacements);
77
+ }
78
+ else if (isCreateApplicationNode(node)) {
79
+ this.analyzeCreateApplication(node, sourceFile, info, typeChecker, importManager, replacements);
80
+ }
81
+ else if (isBootstrapModuleNode(node)) {
82
+ this.analyzeBootstrapModule(node, sourceFile, reflector, evaluator, info, typeChecker, importManager, replacements);
83
+ }
84
+ else if (isTestBedInitEnvironmentNode(node)) {
85
+ this.analyzeTestBedInitEnvironment(node, sourceFile, info, typeChecker, importManager, replacements);
86
+ }
87
+ node.forEachChild(walk);
88
+ };
89
+ sourceFile.forEachChild(walk);
90
+ }
91
+ apply_import_manager.applyImportManagerChanges(importManager, replacements, info.sourceFiles, info);
92
+ return project_paths.confirmAsSerializable({ replacements });
93
+ }
94
+ async combine(unitA, unitB) {
95
+ const combined = [...unitA.replacements, ...unitB.replacements];
96
+ return project_paths.confirmAsSerializable({ replacements: combined });
97
+ }
98
+ async globalMeta(data) {
99
+ return project_paths.confirmAsSerializable(data);
100
+ }
101
+ async stats(data) {
102
+ return project_paths.confirmAsSerializable({});
103
+ }
104
+ async migrate(data) {
105
+ return { replacements: data.replacements };
106
+ }
107
+ analyzeBootstrapApplication(node, sourceFile, info, typeChecker, importManager, replacements) {
108
+ const hasExistingChangeDetectionProvider = hasChangeDetectionProvider(node, typeChecker);
109
+ if (hasExistingChangeDetectionProvider)
110
+ return;
111
+ const providerFn = 'provideZoneChangeDetection()';
112
+ const optionsNode = node.arguments[1];
113
+ const currentProjectFile = project_paths.projectFile(sourceFile, info);
114
+ if (optionsNode) {
115
+ let optionProjectFile = currentProjectFile;
116
+ let optionLiteral;
117
+ if (ts.isObjectLiteralExpression(optionsNode)) {
118
+ optionLiteral = optionsNode;
119
+ addProvidersToBootstrapOption(optionProjectFile, optionLiteral, providerFn, replacements);
120
+ }
121
+ else if (ts.isIdentifier(optionsNode)) {
122
+ // This case handled both `bootstrapApplication(App, appConfig)` and the server () => bootstrapApplication(App, appConfig)
123
+ // where appConfig is the result of a `mergeApplicationConfig` call.
124
+ // This is tricky case to handle, in G3 we're might not be able to resolve the identifier's value
125
+ // Our best alternative is to assume there is not CD providers set and add the ZoneChangeDetection provider
126
+ // In the cases where it is, we'll just override the zone provider we just set by re-used inthe appConfig providers
127
+ // TODO: Should we insert a TODO to clean this up ?
128
+ const text = `{...${optionsNode.getText()}, providers: [${providerFn}, ...${optionsNode.getText()}.providers]}`;
129
+ replacements.push(new project_paths.Replacement(currentProjectFile, new project_paths.TextUpdate({
130
+ position: optionsNode.getStart(),
131
+ end: optionsNode.getEnd(),
132
+ toInsert: text,
133
+ })));
134
+ }
135
+ else {
136
+ throw new Error('unsupported optionsNode: ' + optionsNode.getText());
137
+ }
138
+ }
139
+ else {
140
+ // No options object, add it.
141
+ const text = `, {providers: [${providerFn}]}`;
142
+ const component = node.arguments[0];
143
+ replacements.push(new project_paths.Replacement(currentProjectFile, new project_paths.TextUpdate({ position: component.getEnd(), end: component.getEnd(), toInsert: text })));
144
+ }
145
+ importManager.addImport({
146
+ exportModuleSpecifier: CORE_PACKAGE,
147
+ exportSymbolName: 'provideZoneChangeDetection',
148
+ requestedFile: sourceFile,
149
+ });
150
+ }
151
+ analyzeCreateApplication(node, sourceFile, info, typeChecker, importManager, replacements) {
152
+ const hasExistingChangeDetectionProvider = hasChangeDetectionProvider(node, typeChecker);
153
+ if (hasExistingChangeDetectionProvider)
154
+ return;
155
+ const providerFn = 'provideZoneChangeDetection()';
156
+ const optionsNode = node.arguments[0];
157
+ const currentProjectFile = project_paths.projectFile(sourceFile, info);
158
+ if (optionsNode) {
159
+ let optionProjectFile = currentProjectFile;
160
+ let optionLiteral;
161
+ if (ts.isObjectLiteralExpression(optionsNode)) {
162
+ optionLiteral = optionsNode;
163
+ addProvidersToBootstrapOption(optionProjectFile, optionLiteral, providerFn, replacements);
164
+ }
165
+ else if (ts.isIdentifier(optionsNode) ||
166
+ ts.isCallExpression(optionsNode) ||
167
+ ts.isPropertyAccessExpression(optionsNode)) {
168
+ // This is tricky case to handle, in G3 we're might not be able to resolve the identifier's value
169
+ // Our best alternative is to assume there is no CD providers set and add the ZoneChangeDetection provider
170
+ // In the cases where it is, we'll just override the zone provider we just set by re-used inthe appConfig providers
171
+ // TODO: Should we insert a TODO to clean this up ?
172
+ const text = `{...${optionsNode.getText()}, providers: [${providerFn}, ...${optionsNode.getText()}.providers]}`;
173
+ replacements.push(new project_paths.Replacement(currentProjectFile, new project_paths.TextUpdate({
174
+ position: optionsNode.getStart(),
175
+ end: optionsNode.getEnd(),
176
+ toInsert: text,
177
+ })));
178
+ }
179
+ else {
180
+ throw new Error('unsupported optionsNode: ' + optionsNode.getText());
181
+ }
182
+ }
183
+ else {
184
+ // No options object, add it.
185
+ const text = `{providers: [${providerFn}]}`;
186
+ replacements.push(new project_paths.Replacement(currentProjectFile, new project_paths.TextUpdate({
187
+ position: node.expression.getEnd() + 1,
188
+ end: node.expression.getEnd() + 1,
189
+ toInsert: text,
190
+ })));
191
+ }
192
+ importManager.addImport({
193
+ exportModuleSpecifier: CORE_PACKAGE,
194
+ exportSymbolName: 'provideZoneChangeDetection',
195
+ requestedFile: sourceFile,
196
+ });
197
+ }
198
+ analyzeBootstrapModule(node, sourceFile, reflector, evaluator, info, typeChecker, importManager, replacements) {
199
+ const moduleIdentifier = node.arguments[0];
200
+ const moduleType = evaluator.evaluate(moduleIdentifier);
201
+ if (!(moduleType instanceof migrations.Reference) || !ts.isClassDeclaration(moduleType.node)) {
202
+ return;
203
+ }
204
+ const moduleClass = moduleType.node;
205
+ const ngModule = findNgModule(moduleClass, reflector);
206
+ if (!ngModule) {
207
+ return;
208
+ }
209
+ const optionsNode = node.arguments[1];
210
+ const file = project_paths.projectFile(sourceFile, info);
211
+ replacements.push(new project_paths.Replacement(file, new project_paths.TextUpdate({
212
+ position: moduleIdentifier.getEnd(),
213
+ end: node.getEnd() - 1,
214
+ toInsert: '',
215
+ })));
216
+ const hasExistingChangeDetectionProvider = hasChangeDetectionProvider(ngModule, typeChecker);
217
+ if (hasExistingChangeDetectionProvider) {
218
+ return;
219
+ }
220
+ // Let's try to understand the bootstrap options.
221
+ let options = optionsNode ? evaluator.evaluate(optionsNode) : null;
222
+ let extraOptions = new Map();
223
+ let zoneCdProvider = ZONE_CD_PROVIDER;
224
+ let zoneInstanceProvider = null;
225
+ if (Array.isArray(options)) {
226
+ const mergedOptions = options.reduce((acc, item) => {
227
+ if (item instanceof Map) {
228
+ for (const [k, v] of item) {
229
+ acc.set(k, v);
230
+ if (!SAFE_TO_REMOVE_OPTIONS.includes(k)) {
231
+ extraOptions.set(k, v);
232
+ }
233
+ }
234
+ }
235
+ return acc;
236
+ }, new Map());
237
+ options = mergedOptions;
238
+ }
239
+ if (options instanceof Map) {
240
+ [...options.entries()].forEach(([k, v]) => {
241
+ if (!BOOTSTRAP_OPTIONS.includes(k) && typeof v !== 'string') {
242
+ extraOptions.set(k, v);
243
+ }
244
+ });
245
+ if (options.has('ngZoneRunCoalescing') || options.has('ngZoneEventCoalescing')) {
246
+ const config = [];
247
+ if (options.get('ngZoneRunCoalescing')) {
248
+ config.push('runCoalescing: true');
249
+ }
250
+ if (options.get('ngZoneEventCoalescing')) {
251
+ config.push('eventCoalescing: true');
252
+ }
253
+ zoneCdProvider = `${PROVIDE_ZONE_CHANGE_DETECTION}(${config.length > 0 ? `{ ${config.join(', ')} }` : ''})`;
254
+ }
255
+ const ngZoneOption = options.get('ngZone');
256
+ if (ngZoneOption instanceof migrations.Reference) {
257
+ const clazz = ngZoneOption.node;
258
+ if (ts.isClassDeclaration(clazz) && clazz.name) {
259
+ zoneInstanceProvider = `{provide: NgZone, useClass: ${clazz.name.text}}`;
260
+ removePropertiesFromLiteral(file, optionsNode, ['ngZone'], replacements);
261
+ }
262
+ }
263
+ else if (typeof ngZoneOption === 'string') {
264
+ if (ngZoneOption === 'noop') {
265
+ return;
266
+ }
267
+ }
268
+ else if (ngZoneOption && typeof ngZoneOption !== 'string') {
269
+ // This is a case where we're not able to migrate automatically
270
+ // The migration fails gracefully, keeps the ngZone option and adds a TODO.
271
+ let ngZoneValue;
272
+ optionsNode.properties.forEach((p) => {
273
+ if (ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === 'ngZone') {
274
+ ngZoneValue = p.initializer.getText();
275
+ }
276
+ else if (ts.isShorthandPropertyAssignment(p) && p.name.text === 'ngZone') {
277
+ ngZoneValue = p.name.text;
278
+ }
279
+ });
280
+ if (ngZoneValue) {
281
+ // We re-add the ngZone option
282
+ extraOptions.set('ngZone', ngZoneValue);
283
+ }
284
+ replacements.push(new project_paths.Replacement(project_paths.projectFile(sourceFile, info), new project_paths.TextUpdate({
285
+ position: node.getStart() - 1,
286
+ end: node.getStart() - 1,
287
+ toInsert: '// TODO: BootstrapOptions are deprecated & ignored. Configure NgZone in the providers array of the application module instead.',
288
+ })));
289
+ }
290
+ }
291
+ const providers = [zoneCdProvider];
292
+ if (zoneInstanceProvider) {
293
+ providers.push(zoneInstanceProvider);
294
+ }
295
+ importManager.addImport({
296
+ exportModuleSpecifier: CORE_PACKAGE,
297
+ exportSymbolName: PROVIDE_ZONE_CHANGE_DETECTION,
298
+ requestedFile: sourceFile,
299
+ });
300
+ // if we only use the key, we use the a shorthand asignment
301
+ const extraOptionsStr = [...extraOptions.entries()]
302
+ .map(([k, v]) => (k != v ? `${k}: ${v},` : `${k},`))
303
+ .join(', ');
304
+ replacements.push(new project_paths.Replacement(file, new project_paths.TextUpdate({
305
+ position: moduleIdentifier.end,
306
+ end: moduleIdentifier.end,
307
+ toInsert: `, { applicationProviders: [${providers.join(', ')}], ${extraOptionsStr}}`,
308
+ })));
309
+ }
310
+ analyzeTestBedInitEnvironment(callExpr, sourceFile, info, typeChecker, importManager, replacements) {
311
+ const hasExistingChangeDetectionProvider = hasChangeDetectionProvider(callExpr, typeChecker);
312
+ if (hasExistingChangeDetectionProvider)
313
+ return;
314
+ const ngModules = callExpr.arguments[0];
315
+ const moduleProjectFile = project_paths.projectFile(sourceFile, info);
316
+ importManager.addImport({
317
+ exportModuleSpecifier: CORE_PACKAGE,
318
+ exportSymbolName: PROVIDE_ZONE_CHANGE_DETECTION,
319
+ requestedFile: sourceFile,
320
+ });
321
+ let tmpNode = callExpr;
322
+ let insertPosition = 0;
323
+ while (tmpNode.parent.kind !== ts.SyntaxKind.SourceFile) {
324
+ insertPosition = tmpNode.parent.getStart(sourceFile, true) - 1;
325
+ tmpNode = tmpNode.parent;
326
+ }
327
+ importManager.addImport({
328
+ exportModuleSpecifier: CORE_PACKAGE,
329
+ exportSymbolName: 'NgModule',
330
+ requestedFile: sourceFile,
331
+ });
332
+ addZoneCDModule(ZONE_CD_PROVIDER, moduleProjectFile, insertPosition, replacements);
333
+ insertZoneCDModule(ngModules, moduleProjectFile, replacements, 'ZoneChangeDetectionModule');
334
+ }
335
+ }
336
+ function addZoneCDModule(providersText, projectFile, location, replacements) {
337
+ const newModuleText = `\n@NgModule({ providers: [ ${providersText} ] })
338
+ export class ZoneChangeDetectionModule {}\n\n`;
339
+ if (replacementsHaveZoneCdModule(projectFile.rootRelativePath, replacements, newModuleText)) {
340
+ return;
341
+ }
342
+ replacements.push(new project_paths.Replacement(projectFile, new project_paths.TextUpdate({
343
+ position: location,
344
+ end: location,
345
+ toInsert: newModuleText,
346
+ })));
347
+ }
348
+ function insertZoneCDModule(node, projectFile, replacements, importedModule) {
349
+ if (ts.isArrayLiteralExpression(node)) {
350
+ const literal = node;
351
+ replacements.push(new project_paths.Replacement(projectFile, new project_paths.TextUpdate({
352
+ position: literal.elements[0]?.getStart() ?? literal.getEnd() - 1,
353
+ end: literal.elements[0]?.getStart() ?? literal.getEnd() - 1,
354
+ toInsert: importedModule + ',',
355
+ })));
356
+ }
357
+ else if (ts.isIdentifier(node)) {
358
+ // This should be a good enough heuristic to determine if the identifier is not array
359
+ let isArray = !node.text.endsWith('Module');
360
+ // Because if it's an array, we need to spread it
361
+ const newImports = `[${importedModule}, ${isArray ? '...' : ''}${node.text}]`;
362
+ replacements.push(new project_paths.Replacement(projectFile, new project_paths.TextUpdate({
363
+ position: node.getStart(),
364
+ end: node.getEnd(),
365
+ toInsert: newImports,
366
+ })));
367
+ }
368
+ else {
369
+ throw new Error('unsupported importsNode: ' + node.getText());
370
+ }
371
+ }
372
+ function addProvidersToBootstrapOption(projectFile, optionsNode, providersText, replacements) {
373
+ const providersProp = property_name.findLiteralProperty(optionsNode, 'providers');
374
+ if (providersProp && ts.isPropertyAssignment(providersProp)) {
375
+ // Can be bootstrap(App, {providers: [...]}), bootstrap(App, {providers}), bootstrap(App, {...appConfig, providers}) etc.
376
+ if (ts.isArrayLiteralExpression(providersProp.initializer)) {
377
+ const initializer = providersProp.initializer;
378
+ const text = `${providersText},`;
379
+ replacements.push(new project_paths.Replacement(projectFile, new project_paths.TextUpdate({
380
+ position: initializer.elements[0]?.getStart() ?? initializer.getEnd() - 1,
381
+ end: initializer.elements[0]?.getStart() ?? initializer.getEnd() - 1,
382
+ toInsert: text,
383
+ })));
384
+ }
385
+ else if (ts.isIdentifier(providersProp.initializer)) {
386
+ const newProviders = `[${providersText}, ...${providersProp.initializer.text}]`;
387
+ replacements.push(new project_paths.Replacement(projectFile, new project_paths.TextUpdate({
388
+ position: providersProp.initializer.getStart(),
389
+ end: providersProp.initializer.getEnd(),
390
+ toInsert: newProviders,
391
+ })));
392
+ }
393
+ else {
394
+ const newProviders = `[${providersText}, ...`;
395
+ replacements.push(new project_paths.Replacement(projectFile, new project_paths.TextUpdate({
396
+ position: providersProp.initializer.getStart(),
397
+ end: providersProp.initializer.getStart(),
398
+ toInsert: newProviders,
399
+ })));
400
+ replacements.push(new project_paths.Replacement(projectFile, new project_paths.TextUpdate({
401
+ position: providersProp.initializer.getEnd(),
402
+ end: providersProp.initializer.getEnd(),
403
+ toInsert: ']',
404
+ })));
405
+ }
406
+ }
407
+ else if (providersProp && ts.isShorthandPropertyAssignment(providersProp)) {
408
+ const newProviders = `providers: [${providersText}, ...${providersProp.name.text}]`;
409
+ replacements.push(new project_paths.Replacement(projectFile, new project_paths.TextUpdate({
410
+ position: providersProp.getStart(),
411
+ end: providersProp.getEnd(),
412
+ toInsert: newProviders,
413
+ })));
414
+ }
415
+ else if (optionsNode.properties.length === 1 &&
416
+ ts.isSpreadAssignment(optionsNode.properties[0])) {
417
+ const spread = optionsNode.properties[0];
418
+ const newProviders = `, providers: [${providersText}, ...${spread.expression.getText()}.providers]`;
419
+ replacements.push(new project_paths.Replacement(projectFile, new project_paths.TextUpdate({
420
+ position: spread.getEnd(),
421
+ end: spread.getEnd(),
422
+ toInsert: newProviders,
423
+ })));
424
+ }
425
+ else {
426
+ const text = `providers: [${providersText}]`;
427
+ let toInsert;
428
+ let position;
429
+ if (optionsNode.properties.length > 0) {
430
+ const lastProperty = optionsNode.properties[optionsNode.properties.length - 1];
431
+ toInsert = `,\n ${text}`;
432
+ position = lastProperty.getEnd();
433
+ }
434
+ else {
435
+ toInsert = `\n ${text}\n`;
436
+ position = optionsNode.getStart() + 1;
437
+ }
438
+ replacements.push(new project_paths.Replacement(projectFile, new project_paths.TextUpdate({
439
+ position,
440
+ end: position,
441
+ toInsert,
442
+ })));
443
+ }
444
+ }
445
+ function findNgModule(node, reflector) {
446
+ const decorators = reflector.getDecoratorsOfDeclaration(node);
447
+ if (decorators) {
448
+ const ngModuleDecorator = migrations.getAngularDecorators(decorators, ['NgModule'], true)[0];
449
+ if (ngModuleDecorator &&
450
+ ngModuleDecorator.args &&
451
+ ngModuleDecorator.args.length > 0 &&
452
+ ts.isObjectLiteralExpression(ngModuleDecorator.args[0])) {
453
+ return ngModuleDecorator.args[0];
454
+ }
455
+ }
456
+ return null;
457
+ }
458
+ function hasChangeDetectionProvider(expression, // either the bootstrapApplication or platformBrowserDynamic().bootstrapModule()
459
+ typeChecker) {
460
+ let literal;
461
+ if (ts.isCallExpression(expression)) {
462
+ let optionsNode = expression.arguments[1];
463
+ if (!optionsNode &&
464
+ symbol.isReferenceToImport(typeChecker, expression.expression, imports.getImportSpecifier(expression.getSourceFile(), '@angular/core', 'createApplication'))) {
465
+ optionsNode = expression.arguments[0];
466
+ }
467
+ if (!optionsNode)
468
+ return false;
469
+ if (ts.isIdentifier(optionsNode)) {
470
+ literal = getObjectLiteralFromIdentifier(optionsNode, typeChecker);
471
+ }
472
+ else {
473
+ literal = optionsNode;
474
+ }
475
+ }
476
+ else {
477
+ literal = expression;
478
+ }
479
+ if (!literal) {
480
+ return false;
481
+ }
482
+ const provideZoneCdSpecifier = imports.getImportSpecifier(literal.getSourceFile(), '@angular/core', 'provideZoneChangeDetection');
483
+ const provideZonelessCdSpecifier = imports.getImportSpecifier(literal.getSourceFile(), '@angular/core', 'provideZonelessChangeDetection');
484
+ if (provideZoneCdSpecifier === null && provideZonelessCdSpecifier === null) {
485
+ return false;
486
+ }
487
+ const found = ts.forEachChild(literal, function walk(node) {
488
+ if (ts.isCallExpression(node)) {
489
+ if (provideZonelessCdSpecifier &&
490
+ node.getText().includes(provideZonelessCdSpecifier.getText())) {
491
+ return true;
492
+ }
493
+ if (provideZoneCdSpecifier && node.getText().includes(provideZoneCdSpecifier.getText())) {
494
+ return true;
495
+ }
496
+ }
497
+ return ts.forEachChild(node, walk);
498
+ });
499
+ return !!found;
500
+ }
501
+ function getObjectLiteralFromIdentifier(identifier, typeChecker) {
502
+ let symbol = typeChecker.getSymbolAtLocation(identifier);
503
+ if (!symbol)
504
+ return;
505
+ // Follow aliases (for imported symbols)
506
+ if ((symbol.flags & ts.SymbolFlags.Alias) !== 0) {
507
+ symbol = typeChecker.getAliasedSymbol(symbol);
508
+ }
509
+ const declarations = symbol.getDeclarations();
510
+ if (!declarations)
511
+ return;
512
+ for (const decl of declarations) {
513
+ if (ts.isVariableDeclaration(decl) &&
514
+ decl.initializer &&
515
+ ts.isObjectLiteralExpression(decl.initializer)) {
516
+ return decl.initializer;
517
+ }
518
+ }
519
+ return;
520
+ }
521
+ /**
522
+ * Extracts the import specifiers related to bootstraping from the source file.
523
+ * Returns null if no relevant specifiers are found.
524
+ */
525
+ function getSpecifiers(sourceFile) {
526
+ const createApplicationSpecifier = imports.getImportSpecifier(sourceFile, '@angular/core', 'createApplication');
527
+ const bootstrapAppSpecifier = imports.getImportSpecifier(sourceFile, '@angular/platform-browser', 'bootstrapApplication');
528
+ const platformBrowserDynamicSpecifier = imports.getImportSpecifier(sourceFile, '@angular/platform-browser-dynamic', 'platformBrowserDynamic');
529
+ const platformBrowserSpecifier = imports.getImportSpecifier(sourceFile, '@angular/platform-browser', 'platformBrowser');
530
+ const testBedSpecifier = imports.getImportSpecifier(sourceFile, '@angular/core/testing', 'TestBed');
531
+ const getTestBedSpecifier = imports.getImportSpecifier(sourceFile, '@angular/core/testing', 'getTestBed');
532
+ const ngModuleSpecifier = imports.getImportSpecifier(sourceFile, '@angular/core', 'NgModule');
533
+ if (!createApplicationSpecifier &&
534
+ !bootstrapAppSpecifier &&
535
+ !platformBrowserSpecifier &&
536
+ !testBedSpecifier &&
537
+ !ngModuleSpecifier &&
538
+ !getTestBedSpecifier) {
539
+ return null;
540
+ }
541
+ return {
542
+ createApplicationSpecifier,
543
+ bootstrapAppSpecifier,
544
+ platformBrowserDynamicSpecifier,
545
+ platformBrowserSpecifier,
546
+ testBedSpecifier,
547
+ ngModuleSpecifier,
548
+ getTestBedSpecifier,
549
+ };
550
+ }
551
+ /**
552
+ * In the case we're looking to insert a new ZoneChangeDetectionModule, we need to check if we already inserted one.
553
+ *
554
+ * This function also checks if the existing one has fewer options (shorter text length), which means the previous migration strategy inserted one
555
+ * but the following one is more complete and we should still add it (the dedup function will take care of the cleanup).
556
+ */
557
+ function replacementsHaveZoneCdModule(rootRelativePath, replacements, text) {
558
+ return replacements.some((replacement) => {
559
+ const exisitingText = replacement.update.data.toInsert;
560
+ const isSameFile = replacement.projectFile.rootRelativePath === rootRelativePath;
561
+ return (isSameFile &&
562
+ text.includes('ZoneChangeDetectionModule') &&
563
+ exisitingText.length >= text.length);
564
+ });
565
+ }
566
+ function removePropertiesFromLiteral(projectFile, literal, propertyNames, replacements) {
567
+ const syntaxList = literal.getChildren().find((ch) => ch.kind === ts.SyntaxKind.SyntaxList);
568
+ const optionsElements = syntaxList.getChildren();
569
+ const optionsToRemove = [];
570
+ optionsElements.forEach((node, i, children) => {
571
+ if (ts.isPropertyAssignment(node) &&
572
+ ts.isIdentifier(node.name) &&
573
+ propertyNames.includes(node.name.text)) {
574
+ // Look ahead for comma
575
+ const next = children[i + 1];
576
+ if (next && next.kind === ts.SyntaxKind.CommaToken) {
577
+ optionsToRemove.push({ start: node.getStart(), end: next.getEnd() });
578
+ }
579
+ else {
580
+ optionsToRemove.push({ start: node.getStart(), end: node.getEnd() });
581
+ }
582
+ }
583
+ });
584
+ optionsToRemove.forEach((toRemove) => {
585
+ replacements.push(new project_paths.Replacement(projectFile, new project_paths.TextUpdate({ position: toRemove.start, end: toRemove.end, toInsert: '' })));
586
+ });
587
+ }
588
+
589
+ function migrate() {
590
+ return async (tree) => {
591
+ await project_paths.runMigrationInDevkit({
592
+ tree,
593
+ getMigration: () => new BootstrapOptionsMigration(),
594
+ });
595
+ };
596
+ }
597
+
598
+ exports.migrate = migrate;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v21.0.0-next.0
3
+ * @license Angular v21.0.0-next.10
4
4
  * (c) 2010-2025 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
@@ -8,18 +8,14 @@
8
8
 
9
9
  require('@angular-devkit/core');
10
10
  require('node:path/posix');
11
- var project_paths = require('./project_paths-DcaODbky.cjs');
11
+ var project_paths = require('./project_paths-DvD50ouC.cjs');
12
12
  var ts = require('typescript');
13
- require('os');
14
- var project_tsconfig_paths = require('./project_tsconfig_paths-CS-eSeHC.cjs');
15
- var index = require('./index-esqfDjNB.cjs');
16
- require('path');
13
+ var compilerCli = require('@angular/compiler-cli');
14
+ var migrations = require('@angular/compiler-cli/private/migrations');
17
15
  require('node:path');
18
- var apply_import_manager = require('./apply_import_manager-DR9xXCle.cjs');
16
+ var apply_import_manager = require('./apply_import_manager-1Zs_gpB6.cjs');
19
17
  require('@angular-devkit/schematics');
20
- require('fs');
21
- require('module');
22
- require('url');
18
+ require('./project_tsconfig_paths-CDVxT6Ov.cjs');
23
19
 
24
20
  /** Migration that cleans up unused imports from a project. */
25
21
  class UnusedImportsMigration extends project_paths.TsurgeFunnelMigration {
@@ -29,7 +25,7 @@ class UnusedImportsMigration extends project_paths.TsurgeFunnelMigration {
29
25
  extendedDiagnostics: {
30
26
  checks: {
31
27
  // Ensure that the diagnostic is enabled.
32
- unusedStandaloneImports: index.DiagnosticCategoryLabel.Warning,
28
+ unusedStandaloneImports: migrations.DiagnosticCategoryLabel.Warning,
33
29
  },
34
30
  },
35
31
  });
@@ -43,7 +39,7 @@ class UnusedImportsMigration extends project_paths.TsurgeFunnelMigration {
43
39
  if (diag.file !== undefined &&
44
40
  diag.start !== undefined &&
45
41
  diag.length !== undefined &&
46
- diag.code === project_tsconfig_paths.ngErrorCode(project_tsconfig_paths.ErrorCode.UNUSED_STANDALONE_IMPORTS)) {
42
+ diag.code === compilerCli.ngErrorCode(compilerCli.ErrorCode.UNUSED_STANDALONE_IMPORTS)) {
47
43
  // Skip files that aren't owned by this compilation unit.
48
44
  if (!info.sourceFiles.includes(diag.file)) {
49
45
  return;
@@ -193,7 +189,7 @@ class UnusedImportsMigration extends project_paths.TsurgeFunnelMigration {
193
189
  generateReplacements(sourceFile, removalLocations, usages, info, replacements) {
194
190
  const { fullRemovals, partialRemovals, allRemovedIdentifiers } = removalLocations;
195
191
  const { importedSymbols, identifierCounts } = usages;
196
- const importManager = new project_tsconfig_paths.ImportManager();
192
+ const importManager = new migrations.ImportManager();
197
193
  const sourceText = sourceFile.getFullText();
198
194
  // Replace full arrays with empty ones. This allows preserves more of the user's formatting.
199
195
  fullRemovals.forEach((node) => {