@angular/core 21.0.0-next.2 → 21.0.0-next.4

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 (79) hide show
  1. package/api.d.d.ts +1 -1
  2. package/chrome_dev_tools_performance.d.d.ts +1 -1
  3. package/discovery.d.d.ts +44 -18
  4. package/effect.d.d.ts +1 -1
  5. package/event_dispatcher.d.d.ts +1 -1
  6. package/fesm2022/attribute.mjs +1 -1
  7. package/fesm2022/attribute.mjs.map +1 -1
  8. package/fesm2022/core.mjs +253 -87
  9. package/fesm2022/core.mjs.map +1 -1
  10. package/fesm2022/debug_node.mjs +516 -690
  11. package/fesm2022/debug_node.mjs.map +1 -1
  12. package/fesm2022/effect.mjs +1 -1
  13. package/fesm2022/effect.mjs.map +1 -1
  14. package/fesm2022/not_found.mjs +1 -1
  15. package/fesm2022/not_found.mjs.map +1 -1
  16. package/fesm2022/primitives/di.mjs +1 -1
  17. package/fesm2022/primitives/di.mjs.map +1 -1
  18. package/fesm2022/primitives/event-dispatch.mjs +1 -1
  19. package/fesm2022/primitives/event-dispatch.mjs.map +1 -1
  20. package/fesm2022/primitives/signals.mjs +2 -2
  21. package/fesm2022/primitives/signals.mjs.map +1 -1
  22. package/fesm2022/resource.mjs +1 -1
  23. package/fesm2022/resource.mjs.map +1 -1
  24. package/fesm2022/root_effect_scheduler.mjs +55 -39
  25. package/fesm2022/root_effect_scheduler.mjs.map +1 -1
  26. package/fesm2022/rxjs-interop.mjs +3 -1
  27. package/fesm2022/rxjs-interop.mjs.map +1 -1
  28. package/fesm2022/signal.mjs +32 -11
  29. package/fesm2022/signal.mjs.map +1 -1
  30. package/fesm2022/testing.mjs +4 -4
  31. package/fesm2022/testing.mjs.map +1 -1
  32. package/fesm2022/weak_ref.mjs +1 -1
  33. package/fesm2022/weak_ref.mjs.map +1 -1
  34. package/graph.d.d.ts +24 -4
  35. package/index.d.ts +16 -135
  36. package/package.json +2 -2
  37. package/primitives/di/index.d.ts +1 -1
  38. package/primitives/event-dispatch/index.d.ts +1 -1
  39. package/primitives/signals/index.d.ts +2 -2
  40. package/rxjs-interop/index.d.ts +3 -1
  41. package/schematics/bundles/add-bootstrap-context-to-server-main.cjs +135 -0
  42. package/schematics/bundles/application-config-core.cjs +6 -6
  43. package/schematics/bundles/{apply_import_manager-B3czqUhF.cjs → apply_import_manager-DroqamMP.cjs} +3 -3
  44. package/schematics/bundles/bootstrap-options-migration.cjs +715 -0
  45. package/schematics/bundles/cleanup-unused-imports.cjs +5 -5
  46. package/schematics/bundles/{compiler_host-B9qvCnmC.cjs → compiler_host-aKaS4KRz.cjs} +2 -2
  47. package/schematics/bundles/control-flow-migration.cjs +4 -4
  48. package/schematics/bundles/{imports-26VeX8i-.cjs → imports-DwPXlGFl.cjs} +27 -1
  49. package/schematics/bundles/{index-B6-f9bil.cjs → index-BI97t1U8.cjs} +130 -34
  50. package/schematics/bundles/{index-DN8W1c8n.cjs → index-DaB-z4lP.cjs} +4 -4
  51. package/schematics/bundles/inject-migration.cjs +5 -5
  52. package/schematics/bundles/leading_space-D9nQ8UQC.cjs +1 -1
  53. package/schematics/bundles/{migrate_ts_type_references-DawXRJzI.cjs → migrate_ts_type_references-DPuwhGod.cjs} +5 -5
  54. package/schematics/bundles/{ng_component_template-DUAg-x1h.cjs → ng_component_template-CytqBs-q.cjs} +3 -3
  55. package/schematics/bundles/{ng_decorators-CtYwz9Lw.cjs → ng_decorators-BI0uV7KI.cjs} +2 -2
  56. package/schematics/bundles/ngclass-to-class-migration.cjs +9 -9
  57. package/schematics/bundles/ngstyle-to-style-migration.cjs +490 -0
  58. package/schematics/bundles/nodes-B16H9JUd.cjs +1 -1
  59. package/schematics/bundles/output-migration.cjs +6 -6
  60. package/schematics/bundles/parse_html-CeQjkdOK.cjs +132 -0
  61. package/schematics/bundles/{project_paths-D64fJzoa.cjs → project_paths-Cz4x-QiT.cjs} +3 -3
  62. package/schematics/bundles/{project_tsconfig_paths-DZ17BWwk.cjs → project_tsconfig_paths-Clg7WX1w.cjs} +170 -198
  63. package/schematics/bundles/property_name-BBwFuqMe.cjs +1 -1
  64. package/schematics/bundles/route-lazy-loading.cjs +48 -4
  65. package/schematics/bundles/router-current-navigation.cjs +6 -6
  66. package/schematics/bundles/router-last-successful-navigation.cjs +6 -6
  67. package/schematics/bundles/self-closing-tags-migration.cjs +8 -8
  68. package/schematics/bundles/signal-input-migration.cjs +17 -9
  69. package/schematics/bundles/signal-queries-migration.cjs +7 -7
  70. package/schematics/bundles/signals.cjs +7 -7
  71. package/schematics/bundles/standalone-migration.cjs +7 -7
  72. package/schematics/bundles/{symbol-VPWguRxr.cjs → symbol-BObKoqes.cjs} +3 -2
  73. package/schematics/collection.json +6 -0
  74. package/schematics/migrations/ngclass-to-class-migration/schema.json +1 -1
  75. package/schematics/migrations/ngstyle-to-style-migration/schema.json +20 -0
  76. package/schematics/migrations.json +10 -0
  77. package/testing/index.d.ts +1 -1
  78. package/weak_ref.d.d.ts +1 -1
  79. package/schematics/bundles/parse_html-C8TYlOyu.cjs +0 -41
@@ -0,0 +1,490 @@
1
+ 'use strict';
2
+ /**
3
+ * @license Angular v21.0.0-next.4
4
+ * (c) 2010-2025 Google LLC. https://angular.io/
5
+ * License: MIT
6
+ */
7
+ 'use strict';
8
+
9
+ var ts = require('typescript');
10
+ require('os');
11
+ var project_tsconfig_paths = require('./project_tsconfig_paths-Clg7WX1w.cjs');
12
+ require('./index-BI97t1U8.cjs');
13
+ require('path');
14
+ require('node:path');
15
+ var project_paths = require('./project_paths-Cz4x-QiT.cjs');
16
+ var apply_import_manager = require('./apply_import_manager-DroqamMP.cjs');
17
+ var imports = require('./imports-DwPXlGFl.cjs');
18
+ var parse_html = require('./parse_html-CeQjkdOK.cjs');
19
+ var ng_component_template = require('./ng_component_template-CytqBs-q.cjs');
20
+ require('@angular-devkit/core');
21
+ require('node:path/posix');
22
+ require('fs');
23
+ require('module');
24
+ require('url');
25
+ require('@angular-devkit/schematics');
26
+ require('./ng_decorators-BI0uV7KI.cjs');
27
+ require('./property_name-BBwFuqMe.cjs');
28
+
29
+ const ngStyleStr = 'NgStyle';
30
+ const commonModuleStr = '@angular/common';
31
+ const commonModuleImportsStr = 'CommonModule';
32
+ function migrateNgStyleBindings(template, config, componentNode, typeChecker) {
33
+ const parsed = parse_html.parseTemplate(template);
34
+ if (!parsed.tree || !parsed.tree.rootNodes.length) {
35
+ return { migrated: template, changed: false, replacementCount: 0, canRemoveCommonModule: false };
36
+ }
37
+ const visitor = new NgStyleCollector(template, componentNode, typeChecker);
38
+ project_tsconfig_paths.visitAll$1(visitor, parsed.tree.rootNodes, config);
39
+ let newTemplate = template;
40
+ let changedOffset = 0;
41
+ let replacementCount = 0;
42
+ for (const { start, end, replacement } of visitor.replacements) {
43
+ const currentLength = newTemplate.length;
44
+ newTemplate = replaceTemplate(newTemplate, replacement, start, end, changedOffset);
45
+ changedOffset += newTemplate.length - currentLength;
46
+ replacementCount++;
47
+ }
48
+ const changed = newTemplate !== template;
49
+ return {
50
+ migrated: newTemplate,
51
+ changed,
52
+ replacementCount,
53
+ canRemoveCommonModule: changed ? parse_html.canRemoveCommonModule(newTemplate) : false,
54
+ };
55
+ }
56
+ /**
57
+ * Creates a Replacement to remove `NgStyle` from a component's `imports` array.
58
+ * Uses ReflectionHost + PartialEvaluator for robust AST analysis.
59
+ */
60
+ function createNgStyleImportsArrayRemoval(classNode, file, typeChecker, removeCommonModule) {
61
+ const reflector = new project_tsconfig_paths.TypeScriptReflectionHost(typeChecker);
62
+ const decorators = reflector.getDecoratorsOfDeclaration(classNode);
63
+ if (!decorators) {
64
+ return null;
65
+ }
66
+ const componentDecorator = decorators.find((decorator) => decorator.name === 'Component');
67
+ if (!componentDecorator?.node) {
68
+ return null;
69
+ }
70
+ const decoratorNode = componentDecorator.node;
71
+ if (!ts.isDecorator(decoratorNode) ||
72
+ !ts.isCallExpression(decoratorNode.expression) ||
73
+ decoratorNode.expression.arguments.length === 0 ||
74
+ !ts.isObjectLiteralExpression(decoratorNode.expression.arguments[0])) {
75
+ return null;
76
+ }
77
+ const metadata = decoratorNode.expression.arguments[0];
78
+ const importsProperty = metadata.properties.find((p) => ts.isPropertyAssignment(p) && p.name?.getText() === 'imports');
79
+ if (!importsProperty || !ts.isArrayLiteralExpression(importsProperty.initializer)) {
80
+ return null;
81
+ }
82
+ const importsArray = importsProperty.initializer;
83
+ const elementsToRemove = new Set([ngStyleStr]);
84
+ if (removeCommonModule) {
85
+ elementsToRemove.add(commonModuleImportsStr);
86
+ }
87
+ const originalElements = importsArray.elements;
88
+ const filteredElements = originalElements.filter((el) => !ts.isIdentifier(el) || !elementsToRemove.has(el.text));
89
+ if (filteredElements.length === originalElements.length) {
90
+ return null; // No changes needed.
91
+ }
92
+ // If the array becomes empty, remove the entire `imports` property.
93
+ if (filteredElements.length === 0) {
94
+ const removalRange = getPropertyRemovalRange(importsProperty);
95
+ return new project_paths.Replacement(file, new project_paths.TextUpdate({
96
+ position: removalRange.start,
97
+ end: removalRange.end,
98
+ toInsert: '',
99
+ }));
100
+ }
101
+ const printer = ts.createPrinter();
102
+ const newArray = ts.factory.updateArrayLiteralExpression(importsArray, filteredElements);
103
+ const newText = printer.printNode(ts.EmitHint.Unspecified, newArray, classNode.getSourceFile());
104
+ return new project_paths.Replacement(file, new project_paths.TextUpdate({
105
+ position: importsArray.getStart(),
106
+ end: importsArray.getEnd(),
107
+ toInsert: newText,
108
+ }));
109
+ }
110
+ /**
111
+ * Calculates the removal range for a property in an object literal,
112
+ * including the trailing comma if it's not the last property.
113
+ */
114
+ function getPropertyRemovalRange(property) {
115
+ const parent = property.parent;
116
+ if (!ts.isObjectLiteralExpression(parent)) {
117
+ return { start: property.getStart(), end: property.getEnd() };
118
+ }
119
+ const properties = parent.properties;
120
+ const propertyIndex = properties.indexOf(property);
121
+ const end = property.getEnd();
122
+ if (propertyIndex < properties.length - 1) {
123
+ const nextProperty = properties[propertyIndex + 1];
124
+ return { start: property.getStart(), end: nextProperty.getStart() };
125
+ }
126
+ return { start: property.getStart(), end };
127
+ }
128
+ function calculateImportReplacements(info, sourceFiles, filesToRemoveCommonModule) {
129
+ const importReplacements = {};
130
+ const importManager = new project_tsconfig_paths.ImportManager();
131
+ for (const sf of sourceFiles) {
132
+ const file = project_paths.projectFile(sf, info);
133
+ // Always remove NgStyle if it's imported directly.
134
+ importManager.removeImport(sf, ngStyleStr, commonModuleStr);
135
+ // Conditionally remove CommonModule if it's no longer needed.
136
+ if (filesToRemoveCommonModule.has(file.id)) {
137
+ importManager.removeImport(sf, commonModuleImportsStr, commonModuleStr);
138
+ }
139
+ const addRemove = [];
140
+ apply_import_manager.applyImportManagerChanges(importManager, addRemove, [sf], info);
141
+ if (addRemove.length > 0) {
142
+ importReplacements[file.id] = {
143
+ add: [],
144
+ addAndRemove: addRemove,
145
+ };
146
+ }
147
+ }
148
+ return importReplacements;
149
+ }
150
+ function replaceTemplate(template, replaceValue, start, end, offset) {
151
+ return template.slice(0, start + offset) + replaceValue + template.slice(end + offset);
152
+ }
153
+ /**
154
+ * Visitor class that scans Angular templates and collects replacements
155
+ * for [ngStyle] bindings that use static object literals.
156
+ */
157
+ class NgStyleCollector extends project_tsconfig_paths.RecursiveVisitor$1 {
158
+ originalTemplate;
159
+ replacements = [];
160
+ isNgStyleImported = true; // Default to true (permissive)
161
+ constructor(originalTemplate, componentNode, typeChecker) {
162
+ super();
163
+ this.originalTemplate = originalTemplate;
164
+ // If we have enough information, check if NgStyle is actually imported.
165
+ // If not, we can confidently disable the migration for this component.
166
+ if (componentNode && typeChecker) {
167
+ const imports$1 = imports.getImportSpecifiers(componentNode.getSourceFile(), commonModuleStr, [
168
+ ngStyleStr,
169
+ commonModuleImportsStr,
170
+ ]);
171
+ if (imports$1.length === 0) {
172
+ this.isNgStyleImported = false;
173
+ }
174
+ }
175
+ }
176
+ visitElement(element, config) {
177
+ // If NgStyle is not imported, do not attempt to migrate.
178
+ if (!this.isNgStyleImported) {
179
+ return;
180
+ }
181
+ for (const attr of element.attrs) {
182
+ if (attr.name !== '[ngStyle]' && attr.name !== 'ngStyle') {
183
+ continue;
184
+ }
185
+ if (attr.name === '[ngStyle]' && attr.valueSpan) {
186
+ const expr = this.originalTemplate.slice(attr.valueSpan.start.offset, attr.valueSpan.end.offset);
187
+ const staticMatch = parseStaticObjectLiteral(expr);
188
+ if (staticMatch === null) {
189
+ if (config.bestEffortMode && !isObjectLiteralSyntax(expr.trim())) {
190
+ const keyReplacement = this.originalTemplate
191
+ .slice(attr.sourceSpan.start.offset, attr.valueSpan.start.offset)
192
+ .replace('[ngStyle]', '[style]');
193
+ this.replacements.push({
194
+ start: attr.sourceSpan.start.offset,
195
+ end: attr.valueSpan.start.offset,
196
+ replacement: keyReplacement,
197
+ });
198
+ }
199
+ continue;
200
+ }
201
+ let replacement;
202
+ if (staticMatch.length === 0) {
203
+ replacement = '';
204
+ }
205
+ else if (staticMatch.length === 1) {
206
+ const { key, value } = staticMatch[0];
207
+ // Special case: If the key is an empty string, use [style]=""
208
+ if (key === '') {
209
+ replacement = '';
210
+ }
211
+ else {
212
+ // Normal single condition: use [style.styleName]="condition"
213
+ replacement = `[style.${key}]="${value}"`;
214
+ }
215
+ }
216
+ else {
217
+ replacement = `[style]="${expr}"`;
218
+ }
219
+ this.replacements.push({
220
+ start: attr.sourceSpan.start.offset,
221
+ end: attr.sourceSpan.end.offset,
222
+ replacement,
223
+ });
224
+ continue;
225
+ }
226
+ if (attr.name === 'ngStyle' && attr.value) {
227
+ this.replacements.push({
228
+ start: attr.sourceSpan.start.offset,
229
+ end: attr.sourceSpan.end.offset,
230
+ replacement: `style="${attr.value}"`,
231
+ });
232
+ }
233
+ }
234
+ return super.visitElement(element, config);
235
+ }
236
+ }
237
+ function parseStaticObjectLiteral(expr) {
238
+ const trimmedExpr = expr.trim();
239
+ if (trimmedExpr === '{}' || trimmedExpr === '[]') {
240
+ return [];
241
+ }
242
+ if (!isObjectLiteralSyntax(trimmedExpr)) {
243
+ return null;
244
+ }
245
+ const objectLiteral = parseAsObjectLiteral(trimmedExpr);
246
+ if (!objectLiteral) {
247
+ return null;
248
+ }
249
+ return extractStyleBindings(objectLiteral);
250
+ }
251
+ /**
252
+ * Validates basic object literal syntax
253
+ */
254
+ function isObjectLiteralSyntax(expr) {
255
+ return expr.startsWith('{') && expr.endsWith('}');
256
+ }
257
+ /**
258
+ * Parses expression as TypeScript object literal
259
+ */
260
+ function parseAsObjectLiteral(expr) {
261
+ const sourceFile = ts.createSourceFile('temp.ts', `const obj = ${expr}`, ts.ScriptTarget.Latest, true);
262
+ const variableStatement = sourceFile.statements[0];
263
+ if (!ts.isVariableStatement(variableStatement)) {
264
+ return null;
265
+ }
266
+ const declaration = variableStatement.declarationList.declarations[0];
267
+ if (!declaration.initializer || !ts.isObjectLiteralExpression(declaration.initializer)) {
268
+ return null;
269
+ }
270
+ return declaration.initializer;
271
+ }
272
+ function extractStyleBindings(objectLiteral) {
273
+ const result = [];
274
+ for (const property of objectLiteral.properties) {
275
+ if (ts.isShorthandPropertyAssignment(property)) {
276
+ return null;
277
+ }
278
+ else if (ts.isPropertyAssignment(property)) {
279
+ const keyText = extractPropertyKey(property.name);
280
+ const valueText = extractPropertyValue(property.initializer);
281
+ if (keyText === '' && valueText) {
282
+ result.push({ key: '', value: valueText });
283
+ continue;
284
+ }
285
+ if (!keyText || !valueText) {
286
+ return null;
287
+ }
288
+ result.push({ key: keyText, value: valueText });
289
+ }
290
+ else {
291
+ return null;
292
+ }
293
+ }
294
+ return result;
295
+ }
296
+ /**
297
+ * Extracts text from property key (name)
298
+ */
299
+ function extractPropertyKey(name) {
300
+ if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
301
+ return name.text;
302
+ }
303
+ return null;
304
+ }
305
+ /**
306
+ * Extracts text from property value
307
+ */
308
+ function extractPropertyValue(initializer) {
309
+ // String literals: 'value' or "value"
310
+ if (ts.isStringLiteral(initializer)) {
311
+ return `'${initializer.text}'`;
312
+ }
313
+ // Numeric literals: 42, 3.14
314
+ if (ts.isNumericLiteral(initializer) || ts.isIdentifier(initializer)) {
315
+ return initializer.text;
316
+ }
317
+ // Boolean and null keywords
318
+ if (initializer.kind === ts.SyntaxKind.TrueKeyword) {
319
+ return 'true';
320
+ }
321
+ if (initializer.kind === ts.SyntaxKind.FalseKeyword) {
322
+ return 'false';
323
+ }
324
+ if (initializer.kind === ts.SyntaxKind.NullKeyword) {
325
+ return 'null';
326
+ }
327
+ return initializer.getText();
328
+ }
329
+
330
+ class NgStyleMigration extends project_paths.TsurgeFunnelMigration {
331
+ config;
332
+ constructor(config = {}) {
333
+ super();
334
+ this.config = config;
335
+ }
336
+ processTemplate(template, node, file, info, typeChecker) {
337
+ const { migrated, changed, replacementCount, canRemoveCommonModule } = migrateNgStyleBindings(template.content, this.config, node, typeChecker);
338
+ if (!changed) {
339
+ return null;
340
+ }
341
+ const fileToMigrate = template.inline
342
+ ? file
343
+ : project_paths.projectFile(template.filePath, info);
344
+ const end = template.start + template.content.length;
345
+ return {
346
+ replacements: [prepareTextReplacement(fileToMigrate, migrated, template.start, end)],
347
+ replacementCount,
348
+ canRemoveCommonModule,
349
+ };
350
+ }
351
+ async analyze(info) {
352
+ const { sourceFiles, program } = info;
353
+ const typeChecker = program.getTypeChecker();
354
+ const ngStyleReplacements = [];
355
+ const filesWithNgStyleDeclarations = new Set();
356
+ const filesToRemoveCommonModule = new Set();
357
+ for (const sf of sourceFiles) {
358
+ ts.forEachChild(sf, (node) => {
359
+ if (!ts.isClassDeclaration(node)) {
360
+ return;
361
+ }
362
+ const file = project_paths.projectFile(sf, info);
363
+ if (this.config.shouldMigrate && !this.config.shouldMigrate(file)) {
364
+ return;
365
+ }
366
+ const templateVisitor = new ng_component_template.NgComponentTemplateVisitor(typeChecker);
367
+ templateVisitor.visitNode(node);
368
+ const replacementsForStyle = [];
369
+ let replacementCountForStyle = 0;
370
+ let canRemoveCommonModuleForFile = true;
371
+ for (const template of templateVisitor.resolvedTemplates) {
372
+ const result = this.processTemplate(template, node, file, info, typeChecker);
373
+ if (result) {
374
+ replacementsForStyle.push(...result.replacements);
375
+ replacementCountForStyle += result.replacementCount;
376
+ if (!result.canRemoveCommonModule) {
377
+ canRemoveCommonModuleForFile = false;
378
+ }
379
+ }
380
+ }
381
+ if (replacementsForStyle.length > 0) {
382
+ if (canRemoveCommonModuleForFile) {
383
+ filesToRemoveCommonModule.add(file.id);
384
+ }
385
+ // Handle the `@Component({ imports: [...] })` array.
386
+ const importsRemoval = createNgStyleImportsArrayRemoval(node, file, typeChecker, canRemoveCommonModuleForFile);
387
+ if (importsRemoval) {
388
+ replacementsForStyle.push(importsRemoval);
389
+ }
390
+ ngStyleReplacements.push({
391
+ file,
392
+ replacementCount: replacementCountForStyle,
393
+ replacements: replacementsForStyle,
394
+ });
395
+ filesWithNgStyleDeclarations.add(sf);
396
+ }
397
+ });
398
+ }
399
+ const importReplacements = calculateImportReplacements(info, filesWithNgStyleDeclarations, filesToRemoveCommonModule);
400
+ return project_paths.confirmAsSerializable({
401
+ ngStyleReplacements,
402
+ importReplacements,
403
+ });
404
+ }
405
+ async combine(unitA, unitB) {
406
+ const importReplacements = {};
407
+ for (const unit of [unitA, unitB]) {
408
+ for (const fileIDStr of Object.keys(unit.importReplacements)) {
409
+ const fileID = fileIDStr;
410
+ importReplacements[fileID] = unit.importReplacements[fileID];
411
+ }
412
+ }
413
+ return project_paths.confirmAsSerializable({
414
+ ngStyleReplacements: [...unitA.ngStyleReplacements, ...unitB.ngStyleReplacements],
415
+ importReplacements,
416
+ });
417
+ }
418
+ async globalMeta(combinedData) {
419
+ return project_paths.confirmAsSerializable({
420
+ ngStyleReplacements: combinedData.ngStyleReplacements,
421
+ importReplacements: combinedData.importReplacements,
422
+ });
423
+ }
424
+ async stats(globalMetadata) {
425
+ const touchedFilesCount = globalMetadata.ngStyleReplacements.length;
426
+ const replacementCount = globalMetadata.ngStyleReplacements.reduce((acc, cur) => acc + cur.replacementCount, 0);
427
+ return project_paths.confirmAsSerializable({
428
+ touchedFilesCount,
429
+ replacementCount,
430
+ });
431
+ }
432
+ async migrate(globalData) {
433
+ const replacements = [];
434
+ replacements.push(...globalData.ngStyleReplacements.flatMap(({ replacements }) => replacements));
435
+ for (const fileIDStr of Object.keys(globalData.importReplacements)) {
436
+ const fileID = fileIDStr;
437
+ const importReplacements = globalData.importReplacements[fileID];
438
+ replacements.push(...importReplacements.addAndRemove);
439
+ }
440
+ return { replacements };
441
+ }
442
+ }
443
+ function prepareTextReplacement(file, replacement, start, end) {
444
+ return new project_paths.Replacement(file, new project_paths.TextUpdate({
445
+ position: start,
446
+ end: end,
447
+ toInsert: replacement,
448
+ }));
449
+ }
450
+
451
+ function migrate(options) {
452
+ return async (tree, context) => {
453
+ await project_paths.runMigrationInDevkit({
454
+ tree,
455
+ getMigration: (fs) => new NgStyleMigration({
456
+ bestEffortMode: options.bestEffortMode,
457
+ shouldMigrate: (file) => {
458
+ return (file.rootRelativePath.startsWith(fs.normalize(options.path)) &&
459
+ !/(^|\/)node_modules\//.test(file.rootRelativePath));
460
+ },
461
+ }),
462
+ beforeProgramCreation: (tsconfigPath, stage) => {
463
+ if (stage === project_paths.MigrationStage.Analysis) {
464
+ context.logger.info(`Preparing analysis for: ${tsconfigPath}...`);
465
+ }
466
+ else {
467
+ context.logger.info(`Running migration for: ${tsconfigPath}...`);
468
+ }
469
+ },
470
+ beforeUnitAnalysis: (tsconfigPath) => {
471
+ context.logger.info(`Scanning for component tags: ${tsconfigPath}...`);
472
+ },
473
+ afterAllAnalyzed: () => {
474
+ context.logger.info(``);
475
+ context.logger.info(`Processing analysis data between targets...`);
476
+ context.logger.info(``);
477
+ },
478
+ afterAnalysisFailure: () => {
479
+ context.logger.error('Migration failed unexpectedly with no analysis data');
480
+ },
481
+ whenDone: ({ touchedFilesCount, replacementCount, }) => {
482
+ context.logger.info('');
483
+ context.logger.info(`Successfully migrated to style bindings from ngStyle 🎉`);
484
+ context.logger.info(` -> Migrated ${replacementCount} ngStyle to style bindings in ${touchedFilesCount} files.`);
485
+ },
486
+ });
487
+ };
488
+ }
489
+
490
+ exports.migrate = migrate;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v21.0.0-next.2
3
+ * @license Angular v21.0.0-next.4
4
4
  * (c) 2010-2025 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v21.0.0-next.2
3
+ * @license Angular v21.0.0-next.4
4
4
  * (c) 2010-2025 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
@@ -8,13 +8,13 @@
8
8
 
9
9
  var ts = require('typescript');
10
10
  require('os');
11
- var project_tsconfig_paths = require('./project_tsconfig_paths-DZ17BWwk.cjs');
12
- var index$1 = require('./index-B6-f9bil.cjs');
11
+ var project_tsconfig_paths = require('./project_tsconfig_paths-Clg7WX1w.cjs');
12
+ var index$1 = require('./index-BI97t1U8.cjs');
13
13
  require('path');
14
14
  require('node:path');
15
- var project_paths = require('./project_paths-D64fJzoa.cjs');
16
- var apply_import_manager = require('./apply_import_manager-B3czqUhF.cjs');
17
- var index = require('./index-DN8W1c8n.cjs');
15
+ var project_paths = require('./project_paths-Cz4x-QiT.cjs');
16
+ var apply_import_manager = require('./apply_import_manager-DroqamMP.cjs');
17
+ var index = require('./index-DaB-z4lP.cjs');
18
18
  require('@angular-devkit/core');
19
19
  require('node:path/posix');
20
20
  require('fs');
@@ -0,0 +1,132 @@
1
+ 'use strict';
2
+ /**
3
+ * @license Angular v21.0.0-next.4
4
+ * (c) 2010-2025 Google LLC. https://angular.io/
5
+ * License: MIT
6
+ */
7
+ 'use strict';
8
+
9
+ var project_tsconfig_paths = require('./project_tsconfig_paths-Clg7WX1w.cjs');
10
+
11
+ /**
12
+ * parses the template string into the Html AST
13
+ */
14
+ function parseTemplate(template) {
15
+ let parsed;
16
+ try {
17
+ // Note: we use the HtmlParser here, instead of the `parseTemplate` function, because the
18
+ // latter returns an Ivy AST, not an HTML AST. The HTML AST has the advantage of preserving
19
+ // interpolated text as text nodes containing a mixture of interpolation tokens and text tokens,
20
+ // rather than turning them into `BoundText` nodes like the Ivy AST does. This allows us to
21
+ // easily get the text-only ranges without having to reconstruct the original text.
22
+ parsed = new project_tsconfig_paths.HtmlParser().parse(template, '', {
23
+ // Allows for ICUs to be parsed.
24
+ tokenizeExpansionForms: true,
25
+ // Explicitly disable blocks so that their characters are treated as plain text.
26
+ tokenizeBlocks: true,
27
+ preserveLineEndings: true,
28
+ });
29
+ // Don't migrate invalid templates.
30
+ if (parsed.errors && parsed.errors.length > 0) {
31
+ const errors = parsed.errors.map((e) => ({ type: 'parse', error: e }));
32
+ return { tree: undefined, errors };
33
+ }
34
+ }
35
+ catch (e) {
36
+ return { tree: undefined, errors: [{ type: 'parse', error: e }] };
37
+ }
38
+ return { tree: parsed, errors: [] };
39
+ }
40
+ function pipeMatchRegExpFor(name) {
41
+ return new RegExp(`\\|\\s*${name}`);
42
+ }
43
+ const commonModulePipes = [
44
+ 'date',
45
+ 'async',
46
+ 'currency',
47
+ 'number',
48
+ 'i18nPlural',
49
+ 'i18nSelect',
50
+ 'json',
51
+ 'keyvalue',
52
+ 'slice',
53
+ 'lowercase',
54
+ 'uppercase',
55
+ 'titlecase',
56
+ 'percent',
57
+ ].map((name) => pipeMatchRegExpFor(name));
58
+ function allFormsOf(selector) {
59
+ return [selector, `*${selector}`, `[${selector}]`];
60
+ }
61
+ const commonModuleDirectives = new Set([
62
+ ...allFormsOf('ngComponentOutlet'),
63
+ ...allFormsOf('ngTemplateOutlet'),
64
+ ...allFormsOf('ngClass'),
65
+ ...allFormsOf('ngPlural'),
66
+ ...allFormsOf('ngPluralCase'),
67
+ ...allFormsOf('ngStyle'),
68
+ ...allFormsOf('ngTemplateOutlet'),
69
+ ...allFormsOf('ngComponentOutlet'),
70
+ '[NgForOf]',
71
+ '[NgForTrackBy]',
72
+ '[ngIfElse]',
73
+ '[ngIfThenElse]',
74
+ '*ngIf',
75
+ '*ngSwitch',
76
+ '*ngFor',
77
+ ]);
78
+ /**
79
+ * determines if the CommonModule can be safely removed from imports
80
+ */
81
+ function canRemoveCommonModule(template) {
82
+ const parsed = parseTemplate(template);
83
+ let removeCommonModule = false;
84
+ if (parsed.tree !== undefined) {
85
+ const visitor = new CommonCollector();
86
+ project_tsconfig_paths.visitAll$1(visitor, parsed.tree.rootNodes);
87
+ removeCommonModule = visitor.count === 0;
88
+ }
89
+ return removeCommonModule;
90
+ }
91
+ /** Finds all non-control flow elements from common module. */
92
+ class CommonCollector extends project_tsconfig_paths.RecursiveVisitor$1 {
93
+ count = 0;
94
+ visitElement(el) {
95
+ if (el.attrs.length > 0) {
96
+ for (const attr of el.attrs) {
97
+ if (this.hasDirectives(attr.name) || this.hasPipes(attr.value)) {
98
+ this.count++;
99
+ }
100
+ }
101
+ }
102
+ super.visitElement(el, null);
103
+ }
104
+ visitBlock(ast) {
105
+ for (const blockParam of ast.parameters) {
106
+ if (this.hasPipes(blockParam.expression)) {
107
+ this.count++;
108
+ }
109
+ }
110
+ super.visitBlock(ast, null);
111
+ }
112
+ visitText(ast) {
113
+ if (this.hasPipes(ast.value)) {
114
+ this.count++;
115
+ }
116
+ }
117
+ visitLetDeclaration(decl) {
118
+ if (this.hasPipes(decl.value)) {
119
+ this.count++;
120
+ }
121
+ super.visitLetDeclaration(decl, null);
122
+ }
123
+ hasDirectives(input) {
124
+ return commonModuleDirectives.has(input);
125
+ }
126
+ hasPipes(input) {
127
+ return commonModulePipes.some((regexp) => regexp.test(input));
128
+ }
129
+ }
130
+
131
+ exports.canRemoveCommonModule = canRemoveCommonModule;
132
+ exports.parseTemplate = parseTemplate;
@@ -1,18 +1,18 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v21.0.0-next.2
3
+ * @license Angular v21.0.0-next.4
4
4
  * (c) 2010-2025 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
7
7
  'use strict';
8
8
 
9
- var index = require('./index-B6-f9bil.cjs');
9
+ var index = require('./index-BI97t1U8.cjs');
10
10
  var schematics = require('@angular-devkit/schematics');
11
11
  var core = require('@angular-devkit/core');
12
12
  var posixPath = require('node:path/posix');
13
13
  var os = require('os');
14
14
  var ts = require('typescript');
15
- var project_tsconfig_paths = require('./project_tsconfig_paths-DZ17BWwk.cjs');
15
+ var project_tsconfig_paths = require('./project_tsconfig_paths-Clg7WX1w.cjs');
16
16
  require('path');
17
17
  var path = require('node:path');
18
18