@angular/core 21.0.0-next.8 → 21.0.0-rc.0

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 (84) hide show
  1. package/fesm2022/_attribute-chunk.mjs +2 -14
  2. package/fesm2022/_attribute-chunk.mjs.map +1 -1
  3. package/fesm2022/_debug_node-chunk.mjs +15214 -28375
  4. package/fesm2022/_debug_node-chunk.mjs.map +1 -1
  5. package/fesm2022/_effect-chunk.mjs +402 -120
  6. package/fesm2022/_effect-chunk.mjs.map +1 -1
  7. package/fesm2022/_effect-chunk2.mjs +2951 -0
  8. package/fesm2022/_effect-chunk2.mjs.map +1 -0
  9. package/fesm2022/_not_found-chunk.mjs +18 -35
  10. package/fesm2022/_not_found-chunk.mjs.map +1 -1
  11. package/fesm2022/_resource-chunk.mjs +316 -563
  12. package/fesm2022/_resource-chunk.mjs.map +1 -1
  13. package/fesm2022/_untracked-chunk.mjs +96 -0
  14. package/fesm2022/_untracked-chunk.mjs.map +1 -0
  15. package/fesm2022/_weak_ref-chunk.mjs +2 -4
  16. package/fesm2022/_weak_ref-chunk.mjs.map +1 -1
  17. package/fesm2022/core.mjs +2466 -4309
  18. package/fesm2022/core.mjs.map +1 -1
  19. package/fesm2022/primitives-di.mjs +9 -9
  20. package/fesm2022/primitives-di.mjs.map +1 -1
  21. package/fesm2022/primitives-event-dispatch.mjs +626 -1460
  22. package/fesm2022/primitives-event-dispatch.mjs.map +1 -1
  23. package/fesm2022/primitives-signals.mjs +157 -191
  24. package/fesm2022/primitives-signals.mjs.map +1 -1
  25. package/fesm2022/rxjs-interop.mjs +208 -308
  26. package/fesm2022/rxjs-interop.mjs.map +1 -1
  27. package/fesm2022/testing.mjs +2305 -3164
  28. package/fesm2022/testing.mjs.map +1 -1
  29. package/package.json +8 -2
  30. package/resources/best-practices.md +56 -0
  31. package/schematics/bundles/add-bootstrap-context-to-server-main.cjs +7 -25
  32. package/schematics/bundles/application-config-core.cjs +8 -19
  33. package/schematics/bundles/{apply_import_manager-CBLmogDD.cjs → apply_import_manager-1Zs_gpB6.cjs} +4 -5
  34. package/schematics/bundles/bootstrap-options-migration.cjs +93 -132
  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-T6xncpiw.cjs → compiler_host-DBwYMlTo.cjs} +10 -11
  38. package/schematics/bundles/control-flow-migration.cjs +29 -31
  39. package/schematics/bundles/{imports-DwPXlGFl.cjs → imports-DP72APSx.cjs} +1 -23
  40. package/schematics/bundles/{index-DWSaRJdz.cjs → index-B7I9sIUx.cjs} +36 -37
  41. package/schematics/bundles/inject-migration.cjs +9 -26
  42. package/schematics/bundles/leading_space-D9nQ8UQC.cjs +1 -1
  43. package/schematics/bundles/{migrate_ts_type_references-Cu-FR4L5.cjs → migrate_ts_type_references-UGIUl7En.cjs} +458 -24
  44. package/schematics/bundles/{ng_component_template-BkWiUuGG.cjs → ng_component_template-Dsuq1Lw7.cjs} +4 -5
  45. package/schematics/bundles/{ng_decorators-BI0uV7KI.cjs → ng_decorators-DSFlWYQY.cjs} +2 -2
  46. package/schematics/bundles/ngclass-to-class-migration.cjs +16 -19
  47. package/schematics/bundles/ngstyle-to-style-migration.cjs +15 -18
  48. package/schematics/bundles/nodes-B16H9JUd.cjs +1 -1
  49. package/schematics/bundles/output-migration.cjs +16 -19
  50. package/schematics/bundles/{parse_html-C97tKKp3.cjs → parse_html-8VLCL37B.cjs} +5 -5
  51. package/schematics/bundles/{project_paths-C6g3lqjX.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 +9 -25
  55. package/schematics/bundles/router-current-navigation.cjs +6 -17
  56. package/schematics/bundles/router-last-successful-navigation.cjs +6 -17
  57. package/schematics/bundles/router-testing-module-migration.cjs +7 -18
  58. package/schematics/bundles/self-closing-tags-migration.cjs +14 -17
  59. package/schematics/bundles/signal-input-migration.cjs +23 -26
  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 +22 -56
  63. package/schematics/bundles/symbol-BObKoqes.cjs +1 -1
  64. package/schematics/collection.json +6 -0
  65. package/schematics/migrations/common-to-standalone-migration/schema.json +14 -0
  66. package/types/_api-chunk.d.ts +1 -1
  67. package/types/_chrome_dev_tools_performance-chunk.d.ts +20 -12
  68. package/types/_discovery-chunk.d.ts +18 -14
  69. package/types/_effect-chunk.d.ts +1 -1
  70. package/types/_event_dispatcher-chunk.d.ts +1 -1
  71. package/types/_formatter-chunk.d.ts +4 -3
  72. package/types/_weak_ref-chunk.d.ts +1 -1
  73. package/types/core.d.ts +49 -100
  74. package/types/primitives-di.d.ts +1 -1
  75. package/types/primitives-event-dispatch.d.ts +1 -1
  76. package/types/primitives-signals.d.ts +2 -2
  77. package/types/rxjs-interop.d.ts +1 -1
  78. package/types/testing.d.ts +1 -1
  79. package/fesm2022/_root_effect_scheduler-chunk.mjs +0 -4630
  80. package/fesm2022/_root_effect_scheduler-chunk.mjs.map +0 -1
  81. package/fesm2022/_signal-chunk.mjs +0 -581
  82. package/fesm2022/_signal-chunk.mjs.map +0 -1
  83. package/schematics/bundles/index-BnmACOsq.cjs +0 -22319
  84. package/schematics/bundles/project_tsconfig_paths-CdhVNYMk.cjs +0 -51583
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/core",
3
- "version": "21.0.0-next.8",
3
+ "version": "21.0.0-rc.0",
4
4
  "description": "Angular - the core framework",
5
5
  "author": "angular",
6
6
  "license": "MIT",
@@ -46,7 +46,7 @@
46
46
  "tslib": "^2.3.0"
47
47
  },
48
48
  "peerDependencies": {
49
- "@angular/compiler": "21.0.0-next.8",
49
+ "@angular/compiler": "21.0.0-rc.0",
50
50
  "rxjs": "^6.5.3 || ^7.4.0",
51
51
  "zone.js": "~0.15.0"
52
52
  },
@@ -84,6 +84,12 @@
84
84
  "@angular/service-worker"
85
85
  ]
86
86
  },
87
+ "angular": {
88
+ "bestPractices": {
89
+ "format": "markdown",
90
+ "path": "./resources/best-practices.md"
91
+ }
92
+ },
87
93
  "schematics": "./schematics/collection.json",
88
94
  "sideEffects": false,
89
95
  "module": "./fesm2022/core.mjs",
@@ -0,0 +1,56 @@
1
+ You are an expert in TypeScript, Angular, and scalable web application development. You write functional, maintainable, performant, and accessible code following Angular and TypeScript best practices.
2
+
3
+ ## TypeScript Best Practices
4
+
5
+ - Use strict type checking
6
+ - Prefer type inference when the type is obvious
7
+ - Avoid the `any` type; use `unknown` when type is uncertain
8
+
9
+ ## Angular Best Practices
10
+
11
+ - Always use standalone components over NgModules
12
+ - Must NOT set `standalone: true` inside Angular decorators. It's the default in Angular v20+.
13
+ - Use signals for state management
14
+ - Implement lazy loading for feature routes
15
+ - Do NOT use the `@HostBinding` and `@HostListener` decorators. Put host bindings inside the `host` object of the `@Component` or `@Directive` decorator instead
16
+ - Use `NgOptimizedImage` for all static images.
17
+ - `NgOptimizedImage` does not work for inline base64 images.
18
+
19
+ ## Accessibility Requirements
20
+
21
+ - It MUST pass all AXE checks.
22
+ - It MUST follow all WCAG AA minimums, including focus management, color contrast, and ARIA attributes.
23
+
24
+ ### Components
25
+
26
+ - Keep components small and focused on a single responsibility
27
+ - Use `input()` and `output()` functions instead of decorators
28
+ - Use `computed()` for derived state
29
+ - Set `changeDetection: ChangeDetectionStrategy.OnPush` in `@Component` decorator
30
+ - Prefer inline templates for small components
31
+ - Prefer Reactive forms instead of Template-driven ones
32
+ - Do NOT use `ngClass`, use `class` bindings instead
33
+ - Do NOT use `ngStyle`, use `style` bindings instead
34
+ - When using external templates/styles, use paths relative to the component TS file.
35
+
36
+ ## State Management
37
+
38
+ - Use signals for local component state
39
+ - Use `computed()` for derived state
40
+ - Keep state transformations pure and predictable
41
+ - Do NOT use `mutate` on signals, use `update` or `set` instead
42
+
43
+ ## Templates
44
+
45
+ - Keep templates simple and avoid complex logic
46
+ - Use native control flow (`@if`, `@for`, `@switch`) instead of `*ngIf`, `*ngFor`, `*ngSwitch`
47
+ - Use the async pipe to handle observables
48
+ - Do not assume globals like (`new Date()`) are available.
49
+ - Do not write arrow functions in templates (they are not supported).
50
+ - Do not write Regular expressions in templates (they are not supported).
51
+
52
+ ## Services
53
+
54
+ - Design services around a single responsibility
55
+ - Use the `providedIn: 'root'` option for singleton services
56
+ - Use the `inject()` function instead of constructor injection
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v21.0.0-next.8
3
+ * @license Angular v21.0.0-rc.0
4
4
  * (c) 2010-2025 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
@@ -8,26 +8,15 @@
8
8
 
9
9
  require('@angular-devkit/core');
10
10
  require('node:path/posix');
11
- var project_paths = require('./project_paths-C6g3lqjX.cjs');
12
- var project_tsconfig_paths = require('./project_tsconfig_paths-CdhVNYMk.cjs');
11
+ var project_paths = require('./project_paths-DvD50ouC.cjs');
12
+ var migrations = require('@angular/compiler-cli/private/migrations');
13
13
  var ts = require('typescript');
14
- require('os');
15
- var apply_import_manager = require('./apply_import_manager-CBLmogDD.cjs');
16
- require('./index-BnmACOsq.cjs');
17
- require('path');
14
+ var apply_import_manager = require('./apply_import_manager-1Zs_gpB6.cjs');
15
+ require('@angular/compiler-cli');
18
16
  require('node:path');
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
- /*!
25
- * @license
26
- * Copyright Google LLC All Rights Reserved.
27
- *
28
- * Use of this source code is governed by an MIT-style license that can be
29
- * found in the LICENSE file at https://angular.dev/license
30
- */
31
20
  function findArrowFunction(node) {
32
21
  let current = node;
33
22
  while (current) {
@@ -75,7 +64,7 @@ class AddBootstrapContextToServerMainMigration extends project_paths.TsurgeFunne
75
64
  })));
76
65
  }
77
66
  }
78
- importManager ??= new project_tsconfig_paths.ImportManager({
67
+ importManager ??= new migrations.ImportManager({
79
68
  generateUniqueIdentifier: () => null,
80
69
  shouldUseSingleQuotes: () => true,
81
70
  });
@@ -116,13 +105,6 @@ class AddBootstrapContextToServerMainMigration extends project_paths.TsurgeFunne
116
105
  }
117
106
  }
118
107
 
119
- /*!
120
- * @license
121
- * Copyright Google LLC All Rights Reserved.
122
- *
123
- * Use of this source code is governed by an MIT-style license that can be
124
- * found in the LICENSE file at https://angular.dev/license
125
- */
126
108
  function migrate() {
127
109
  return async (tree) => {
128
110
  await project_paths.runMigrationInDevkit({
@@ -1,26 +1,22 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v21.0.0-next.8
3
+ * @license Angular v21.0.0-rc.0
4
4
  * (c) 2010-2025 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
7
7
  'use strict';
8
8
 
9
- var project_tsconfig_paths = require('./project_tsconfig_paths-CdhVNYMk.cjs');
9
+ var migrations = require('@angular/compiler-cli/private/migrations');
10
+ var apply_import_manager = require('./apply_import_manager-1Zs_gpB6.cjs');
11
+ require('@angular/compiler-cli');
10
12
  require('typescript');
11
- require('os');
12
- var apply_import_manager = require('./apply_import_manager-CBLmogDD.cjs');
13
- require('./index-BnmACOsq.cjs');
14
- require('path');
15
13
  require('node:path');
16
- var project_paths = require('./project_paths-C6g3lqjX.cjs');
17
- var imports = require('./imports-DwPXlGFl.cjs');
14
+ var project_paths = require('./project_paths-DvD50ouC.cjs');
15
+ var imports = require('./imports-DP72APSx.cjs');
18
16
  require('@angular-devkit/core');
19
17
  require('node:path/posix');
20
- require('fs');
21
- require('module');
22
- require('url');
23
18
  require('@angular-devkit/schematics');
19
+ require('./project_tsconfig_paths-CDVxT6Ov.cjs');
24
20
 
25
21
  /** Migration that moves the import of `ApplicationConfig` from `platform-browser` to `core`. */
26
22
  class ApplicationConfigCoreMigration extends project_paths.TsurgeFunnelMigration {
@@ -32,7 +28,7 @@ class ApplicationConfigCoreMigration extends project_paths.TsurgeFunnelMigration
32
28
  if (!specifier) {
33
29
  continue;
34
30
  }
35
- importManager ??= new project_tsconfig_paths.ImportManager({
31
+ importManager ??= new migrations.ImportManager({
36
32
  // Prevent the manager from trying to generate a non-conflicting import.
37
33
  generateUniqueIdentifier: () => null,
38
34
  shouldUseSingleQuotes: () => true,
@@ -76,13 +72,6 @@ class ApplicationConfigCoreMigration extends project_paths.TsurgeFunnelMigration
76
72
  }
77
73
  }
78
74
 
79
- /*!
80
- * @license
81
- * Copyright Google LLC All Rights Reserved.
82
- *
83
- * Use of this source code is governed by an MIT-style license that can be
84
- * found in the LICENSE file at https://angular.dev/license
85
- */
86
75
  function migrate() {
87
76
  return async (tree) => {
88
77
  await project_paths.runMigrationInDevkit({
@@ -1,15 +1,14 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v21.0.0-next.8
3
+ * @license Angular v21.0.0-rc.0
4
4
  * (c) 2010-2025 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
7
7
  'use strict';
8
8
 
9
9
  var ts = require('typescript');
10
- require('os');
11
- var project_tsconfig_paths = require('./project_tsconfig_paths-CdhVNYMk.cjs');
12
- var project_paths = require('./project_paths-C6g3lqjX.cjs');
10
+ var compilerCli = require('@angular/compiler-cli');
11
+ var project_paths = require('./project_paths-DvD50ouC.cjs');
13
12
 
14
13
  /**
15
14
  * Applies import manager changes, and writes them as replacements the
@@ -23,7 +22,7 @@ function applyImportManagerChanges(importManager, replacements, sourceFiles, inf
23
22
  newImports.forEach((newImports, fileName) => {
24
23
  newImports.forEach((newImport) => {
25
24
  const printedImport = printer.printNode(ts.EmitHint.Unspecified, newImport, pathToFile.get(fileName));
26
- replacements.push(new project_paths.Replacement(project_paths.projectFile(project_tsconfig_paths.absoluteFrom(fileName), info), new project_paths.TextUpdate({ position: 0, end: 0, toInsert: `${printedImport}\n` })));
25
+ replacements.push(new project_paths.Replacement(project_paths.projectFile(compilerCli.absoluteFrom(fileName), info), new project_paths.TextUpdate({ position: 0, end: 0, toInsert: `${printedImport}\n` })));
27
26
  });
28
27
  });
29
28
  // Capture updated imports
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v21.0.0-next.8
3
+ * @license Angular v21.0.0-rc.0
4
4
  * (c) 2010-2025 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
@@ -8,29 +8,31 @@
8
8
 
9
9
  require('@angular-devkit/core');
10
10
  require('node:path/posix');
11
- var project_paths = require('./project_paths-C6g3lqjX.cjs');
12
- require('os');
11
+ var project_paths = require('./project_paths-DvD50ouC.cjs');
12
+ require('@angular/compiler-cli');
13
+ var migrations = require('@angular/compiler-cli/private/migrations');
13
14
  var ts = require('typescript');
14
- var project_tsconfig_paths = require('./project_tsconfig_paths-CdhVNYMk.cjs');
15
- var index = require('./index-BnmACOsq.cjs');
16
- require('path');
17
15
  require('node:path');
18
- var apply_import_manager = require('./apply_import_manager-CBLmogDD.cjs');
16
+ var apply_import_manager = require('./apply_import_manager-1Zs_gpB6.cjs');
19
17
  var property_name = require('./property_name-BBwFuqMe.cjs');
20
- var imports = require('./imports-DwPXlGFl.cjs');
18
+ var imports = require('./imports-DP72APSx.cjs');
21
19
  var symbol = require('./symbol-BObKoqes.cjs');
22
20
  require('@angular-devkit/schematics');
23
- require('fs');
24
- require('module');
25
- require('url');
21
+ require('./project_tsconfig_paths-CDVxT6Ov.cjs');
26
22
 
27
23
  const CORE_PACKAGE = '@angular/core';
28
24
  const PROVIDE_ZONE_CHANGE_DETECTION = 'provideZoneChangeDetection';
29
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];
30
32
  class BootstrapOptionsMigration extends project_paths.TsurgeFunnelMigration {
31
33
  async analyze(info) {
32
34
  let replacements = [];
33
- const importManager = new project_tsconfig_paths.ImportManager();
35
+ const importManager = new migrations.ImportManager();
34
36
  for (const sourceFile of info.sourceFiles) {
35
37
  // We need to migration either
36
38
  // * `bootstrapApplication(App)
@@ -42,7 +44,7 @@ class BootstrapOptionsMigration extends project_paths.TsurgeFunnelMigration {
42
44
  // If none of the imports related to bootstraping are present, we can skip the file.
43
45
  if (specifiers === null)
44
46
  continue;
45
- const { bootstrapAppSpecifier, platformBrowserDynamicSpecifier, platformBrowserSpecifier, testBedSpecifier, createApplicationSpecifier, getTestBedSpecifier, } = specifiers;
47
+ const { bootstrapAppSpecifier, testBedSpecifier, createApplicationSpecifier, getTestBedSpecifier, } = specifiers;
46
48
  const typeChecker = info.program.getTypeChecker();
47
49
  const isCreateApplicationNode = (node) => {
48
50
  return (ts.isCallExpression(node) &&
@@ -58,9 +60,7 @@ class BootstrapOptionsMigration extends project_paths.TsurgeFunnelMigration {
58
60
  return (ts.isCallExpression(node) &&
59
61
  ts.isPropertyAccessExpression(node.expression) &&
60
62
  node.expression.name.text === 'bootstrapModule' &&
61
- ts.isCallExpression(node.expression.expression) &&
62
- (symbol.isReferenceToImport(typeChecker, node.expression.expression.expression, platformBrowserSpecifier) ||
63
- symbol.isReferenceToImport(typeChecker, node.expression.expression.expression, platformBrowserDynamicSpecifier)));
63
+ node.arguments.length > 0);
64
64
  };
65
65
  const isTestBedInitEnvironmentNode = (node) => {
66
66
  return (ts.isCallExpression(node) &&
@@ -69,8 +69,8 @@ class BootstrapOptionsMigration extends project_paths.TsurgeFunnelMigration {
69
69
  (symbol.isReferenceToImport(typeChecker, node.expression.expression, testBedSpecifier) ||
70
70
  symbol.isReferenceToImport(typeChecker, node.expression.expression, getTestBedSpecifier)));
71
71
  };
72
- const reflector = new project_tsconfig_paths.TypeScriptReflectionHost(typeChecker);
73
- const evaluator = new index.PartialEvaluator(reflector, typeChecker, null);
72
+ const reflector = new migrations.TypeScriptReflectionHost(typeChecker);
73
+ const evaluator = new migrations.PartialEvaluator(reflector, typeChecker, null);
74
74
  const walk = (node) => {
75
75
  if (isBootstrapAppNode(node)) {
76
76
  this.analyzeBootstrapApplication(node, sourceFile, info, typeChecker, importManager, replacements);
@@ -88,15 +88,12 @@ class BootstrapOptionsMigration extends project_paths.TsurgeFunnelMigration {
88
88
  };
89
89
  sourceFile.forEachChild(walk);
90
90
  }
91
- // The combine method might not run when there is a single target.
92
- // So we deduplicate here
93
- replacements = deduplicateReplacements(replacements);
94
91
  apply_import_manager.applyImportManagerChanges(importManager, replacements, info.sourceFiles, info);
95
92
  return project_paths.confirmAsSerializable({ replacements });
96
93
  }
97
94
  async combine(unitA, unitB) {
98
95
  const combined = [...unitA.replacements, ...unitB.replacements];
99
- return project_paths.confirmAsSerializable({ replacements: deduplicateReplacements(combined) });
96
+ return project_paths.confirmAsSerializable({ replacements: combined });
100
97
  }
101
98
  async globalMeta(data) {
102
99
  return project_paths.confirmAsSerializable(data);
@@ -201,7 +198,7 @@ class BootstrapOptionsMigration extends project_paths.TsurgeFunnelMigration {
201
198
  analyzeBootstrapModule(node, sourceFile, reflector, evaluator, info, typeChecker, importManager, replacements) {
202
199
  const moduleIdentifier = node.arguments[0];
203
200
  const moduleType = evaluator.evaluate(moduleIdentifier);
204
- if (!(moduleType instanceof project_tsconfig_paths.Reference) || !ts.isClassDeclaration(moduleType.node)) {
201
+ if (!(moduleType instanceof migrations.Reference) || !ts.isClassDeclaration(moduleType.node)) {
205
202
  return;
206
203
  }
207
204
  const moduleClass = moduleType.node;
@@ -209,27 +206,42 @@ class BootstrapOptionsMigration extends project_paths.TsurgeFunnelMigration {
209
206
  if (!ngModule) {
210
207
  return;
211
208
  }
212
- const moduleSourceFile = moduleClass.getSourceFile();
213
- const moduleProjectFile = project_paths.projectFile(moduleSourceFile, info);
214
- if (moduleSourceFile.getText().includes('ZoneChangeDetectionModule')) {
215
- // If the file already contains the ZoneChangeDetectionModule, we can skip it.
216
- return;
217
- }
218
- // Always remove the options argument
219
- replacements.push(new project_paths.Replacement(project_paths.projectFile(sourceFile, info), new project_paths.TextUpdate({ position: moduleIdentifier.getEnd(), end: node.getEnd() - 1, toInsert: '' })));
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
+ })));
220
216
  const hasExistingChangeDetectionProvider = hasChangeDetectionProvider(ngModule, typeChecker);
221
217
  if (hasExistingChangeDetectionProvider) {
222
218
  return;
223
219
  }
224
220
  // Let's try to understand the bootstrap options.
225
- const optionsNode = node.arguments[1];
226
- const options = optionsNode && ts.isObjectLiteralExpression(optionsNode)
227
- ? evaluator.evaluate(optionsNode)
228
- : null;
221
+ let options = optionsNode ? evaluator.evaluate(optionsNode) : null;
222
+ let extraOptions = new Map();
229
223
  let zoneCdProvider = ZONE_CD_PROVIDER;
230
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
+ }
231
239
  if (options instanceof Map) {
232
- const ngZoneOption = options.get('ngZone');
240
+ [...options.entries()].forEach(([k, v]) => {
241
+ if (!BOOTSTRAP_OPTIONS.includes(k) && typeof v !== 'string') {
242
+ extraOptions.set(k, v);
243
+ }
244
+ });
233
245
  if (options.has('ngZoneRunCoalescing') || options.has('ngZoneEventCoalescing')) {
234
246
  const config = [];
235
247
  if (options.get('ngZoneRunCoalescing')) {
@@ -240,27 +252,18 @@ class BootstrapOptionsMigration extends project_paths.TsurgeFunnelMigration {
240
252
  }
241
253
  zoneCdProvider = `${PROVIDE_ZONE_CHANGE_DETECTION}(${config.length > 0 ? `{ ${config.join(', ')} }` : ''})`;
242
254
  }
243
- if (ngZoneOption instanceof project_tsconfig_paths.Reference) {
244
- importManager.addImport({
245
- exportModuleSpecifier: CORE_PACKAGE,
246
- exportSymbolName: 'NgZone',
247
- requestedFile: moduleSourceFile,
248
- });
255
+ const ngZoneOption = options.get('ngZone');
256
+ if (ngZoneOption instanceof migrations.Reference) {
249
257
  const clazz = ngZoneOption.node;
250
258
  if (ts.isClassDeclaration(clazz) && clazz.name) {
251
- const customZoneSourceFile = clazz.getSourceFile();
252
- const exportModuleSpecifier = ngZoneOption.bestGuessOwningModule?.specifier ??
253
- imports.getRelativePath(moduleSourceFile.fileName, customZoneSourceFile.fileName);
254
- importManager.addImport({
255
- exportModuleSpecifier,
256
- exportSymbolName: clazz.name.text,
257
- requestedFile: moduleSourceFile,
258
- });
259
259
  zoneInstanceProvider = `{provide: NgZone, useClass: ${clazz.name.text}}`;
260
+ removePropertiesFromLiteral(file, optionsNode, ['ngZone'], replacements);
260
261
  }
261
262
  }
262
- else if (typeof ngZoneOption === 'string' && ngZoneOption === 'noop') {
263
- return;
263
+ else if (typeof ngZoneOption === 'string') {
264
+ if (ngZoneOption === 'noop') {
265
+ return;
266
+ }
264
267
  }
265
268
  else if (ngZoneOption && typeof ngZoneOption !== 'string') {
266
269
  // This is a case where we're not able to migrate automatically
@@ -276,13 +279,8 @@ class BootstrapOptionsMigration extends project_paths.TsurgeFunnelMigration {
276
279
  });
277
280
  if (ngZoneValue) {
278
281
  // We re-add the ngZone option
279
- replacements.push(new project_paths.Replacement(project_paths.projectFile(sourceFile, info), new project_paths.TextUpdate({
280
- position: moduleIdentifier.getEnd(),
281
- end: node.getEnd() - 1,
282
- toInsert: `, {ngZone: ${ngZoneValue}}`,
283
- })));
282
+ extraOptions.set('ngZone', ngZoneValue);
284
283
  }
285
- // And add the TODO
286
284
  replacements.push(new project_paths.Replacement(project_paths.projectFile(sourceFile, info), new project_paths.TextUpdate({
287
285
  position: node.getStart() - 1,
288
286
  end: node.getStart() - 1,
@@ -294,14 +292,20 @@ class BootstrapOptionsMigration extends project_paths.TsurgeFunnelMigration {
294
292
  if (zoneInstanceProvider) {
295
293
  providers.push(zoneInstanceProvider);
296
294
  }
297
- if (providers.length > 0) {
298
- importManager.addImport({
299
- exportModuleSpecifier: CORE_PACKAGE,
300
- exportSymbolName: PROVIDE_ZONE_CHANGE_DETECTION,
301
- requestedFile: moduleSourceFile,
302
- });
303
- addProvidersToNgModule(moduleProjectFile, moduleSourceFile, ngModule, providers.join(',\n'), replacements);
304
- }
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
+ })));
305
309
  }
306
310
  analyzeTestBedInitEnvironment(callExpr, sourceFile, info, typeChecker, importManager, replacements) {
307
311
  const hasExistingChangeDetectionProvider = hasChangeDetectionProvider(callExpr, typeChecker);
@@ -329,30 +333,6 @@ class BootstrapOptionsMigration extends project_paths.TsurgeFunnelMigration {
329
333
  insertZoneCDModule(ngModules, moduleProjectFile, replacements, 'ZoneChangeDetectionModule');
330
334
  }
331
335
  }
332
- function addProvidersToNgModule(projectFile, moduleSourceFile, ngModule, providersText, replacements) {
333
- // ObjLiteral => callExp => Decorator => ClassExpression
334
- const moduleClassDeclaration = ngModule.parent.parent.parent;
335
- const insertPosition = moduleClassDeclaration.getStart(moduleSourceFile, true) - 1;
336
- addZoneCDModule(providersText, projectFile, insertPosition, replacements);
337
- const importsNode = property_name.findLiteralProperty(ngModule, 'imports');
338
- if (importsNode && ts.isPropertyAssignment(importsNode)) {
339
- insertZoneCDModule(importsNode.initializer, projectFile, replacements, 'ZoneChangeDetectionModule');
340
- }
341
- else {
342
- const text = `imports: [ZoneChangeDetectionModule]`;
343
- const toInsert = `${text},\n`;
344
- let position = ngModule.getStart() + 1;
345
- if (ngModule.properties.length > 0) {
346
- const firstProperty = ngModule.properties[0];
347
- position = firstProperty.getStart();
348
- }
349
- replacements.push(new project_paths.Replacement(projectFile, new project_paths.TextUpdate({
350
- position,
351
- end: position,
352
- toInsert,
353
- })));
354
- }
355
- }
356
336
  function addZoneCDModule(providersText, projectFile, location, replacements) {
357
337
  const newModuleText = `\n@NgModule({ providers: [ ${providersText} ] })
358
338
  export class ZoneChangeDetectionModule {}\n\n`;
@@ -465,7 +445,7 @@ function addProvidersToBootstrapOption(projectFile, optionsNode, providersText,
465
445
  function findNgModule(node, reflector) {
466
446
  const decorators = reflector.getDecoratorsOfDeclaration(node);
467
447
  if (decorators) {
468
- const ngModuleDecorator = project_tsconfig_paths.getAngularDecorators(decorators, ['NgModule'], true)[0];
448
+ const ngModuleDecorator = migrations.getAngularDecorators(decorators, ['NgModule'], true)[0];
469
449
  if (ngModuleDecorator &&
470
450
  ngModuleDecorator.args &&
471
451
  ngModuleDecorator.args.length > 0 &&
@@ -552,7 +532,6 @@ function getSpecifiers(sourceFile) {
552
532
  const ngModuleSpecifier = imports.getImportSpecifier(sourceFile, '@angular/core', 'NgModule');
553
533
  if (!createApplicationSpecifier &&
554
534
  !bootstrapAppSpecifier &&
555
- !platformBrowserDynamicSpecifier &&
556
535
  !platformBrowserSpecifier &&
557
536
  !testBedSpecifier &&
558
537
  !ngModuleSpecifier &&
@@ -569,46 +548,6 @@ function getSpecifiers(sourceFile) {
569
548
  getTestBedSpecifier,
570
549
  };
571
550
  }
572
- /**
573
- * Removes duplicate replacements and for replacements at the same position, takes the longest one.
574
- */
575
- function deduplicateReplacements(replacements) {
576
- if (replacements.length <= 1) {
577
- return replacements;
578
- }
579
- // Group replacements by file and position
580
- const groupedByFileAndPosition = new Map();
581
- for (const replacement of replacements) {
582
- const fileKey = replacement.projectFile.id;
583
- const position = replacement.update.data.position;
584
- if (!groupedByFileAndPosition.has(fileKey)) {
585
- groupedByFileAndPosition.set(fileKey, new Map());
586
- }
587
- const fileReplacements = groupedByFileAndPosition.get(fileKey);
588
- if (!fileReplacements.has(position)) {
589
- fileReplacements.set(position, []);
590
- }
591
- fileReplacements.get(position).push(replacement);
592
- }
593
- const result = [];
594
- for (const fileReplacements of groupedByFileAndPosition.values()) {
595
- for (const positionReplacements of fileReplacements.values()) {
596
- if (positionReplacements.length === 1) {
597
- result.push(positionReplacements[0]);
598
- }
599
- else {
600
- // For multiple replacements at the same position, take the one with the longest content
601
- const longestReplacement = positionReplacements.reduce((longest, current) => {
602
- const longestLength = longest.update.data.toInsert.length;
603
- const currentLength = current.update.data.toInsert.length;
604
- return currentLength > longestLength ? current : longest;
605
- });
606
- result.push(longestReplacement);
607
- }
608
- }
609
- }
610
- return result;
611
- }
612
551
  /**
613
552
  * In the case we're looking to insert a new ZoneChangeDetectionModule, we need to check if we already inserted one.
614
553
  *
@@ -624,6 +563,28 @@ function replacementsHaveZoneCdModule(rootRelativePath, replacements, text) {
624
563
  exisitingText.length >= text.length);
625
564
  });
626
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
+ }
627
588
 
628
589
  function migrate() {
629
590
  return async (tree) => {
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v21.0.0-next.8
3
+ * @license Angular v21.0.0-rc.0
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-C6g3lqjX.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-CdhVNYMk.cjs');
15
- var index = require('./index-BnmACOsq.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-CBLmogDD.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) => {