@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.
- package/fesm2022/_attribute-chunk.mjs +2 -14
- package/fesm2022/_attribute-chunk.mjs.map +1 -1
- package/fesm2022/_debug_node-chunk.mjs +15214 -28375
- package/fesm2022/_debug_node-chunk.mjs.map +1 -1
- package/fesm2022/_effect-chunk.mjs +402 -120
- package/fesm2022/_effect-chunk.mjs.map +1 -1
- package/fesm2022/_effect-chunk2.mjs +2951 -0
- package/fesm2022/_effect-chunk2.mjs.map +1 -0
- package/fesm2022/_not_found-chunk.mjs +18 -35
- package/fesm2022/_not_found-chunk.mjs.map +1 -1
- package/fesm2022/_resource-chunk.mjs +316 -563
- package/fesm2022/_resource-chunk.mjs.map +1 -1
- package/fesm2022/_untracked-chunk.mjs +96 -0
- package/fesm2022/_untracked-chunk.mjs.map +1 -0
- package/fesm2022/_weak_ref-chunk.mjs +2 -4
- package/fesm2022/_weak_ref-chunk.mjs.map +1 -1
- package/fesm2022/core.mjs +2466 -4309
- package/fesm2022/core.mjs.map +1 -1
- package/fesm2022/primitives-di.mjs +9 -9
- package/fesm2022/primitives-di.mjs.map +1 -1
- package/fesm2022/primitives-event-dispatch.mjs +626 -1460
- package/fesm2022/primitives-event-dispatch.mjs.map +1 -1
- package/fesm2022/primitives-signals.mjs +157 -191
- package/fesm2022/primitives-signals.mjs.map +1 -1
- package/fesm2022/rxjs-interop.mjs +208 -308
- package/fesm2022/rxjs-interop.mjs.map +1 -1
- package/fesm2022/testing.mjs +2305 -3164
- package/fesm2022/testing.mjs.map +1 -1
- package/package.json +8 -2
- package/resources/best-practices.md +56 -0
- package/schematics/bundles/add-bootstrap-context-to-server-main.cjs +7 -25
- package/schematics/bundles/application-config-core.cjs +8 -19
- package/schematics/bundles/{apply_import_manager-CBLmogDD.cjs → apply_import_manager-1Zs_gpB6.cjs} +4 -5
- package/schematics/bundles/bootstrap-options-migration.cjs +93 -132
- package/schematics/bundles/cleanup-unused-imports.cjs +9 -13
- package/schematics/bundles/common-to-standalone-migration.cjs +381 -0
- package/schematics/bundles/{compiler_host-T6xncpiw.cjs → compiler_host-DBwYMlTo.cjs} +10 -11
- package/schematics/bundles/control-flow-migration.cjs +29 -31
- package/schematics/bundles/{imports-DwPXlGFl.cjs → imports-DP72APSx.cjs} +1 -23
- package/schematics/bundles/{index-DWSaRJdz.cjs → index-B7I9sIUx.cjs} +36 -37
- package/schematics/bundles/inject-migration.cjs +9 -26
- package/schematics/bundles/leading_space-D9nQ8UQC.cjs +1 -1
- package/schematics/bundles/{migrate_ts_type_references-Cu-FR4L5.cjs → migrate_ts_type_references-UGIUl7En.cjs} +458 -24
- package/schematics/bundles/{ng_component_template-BkWiUuGG.cjs → ng_component_template-Dsuq1Lw7.cjs} +4 -5
- package/schematics/bundles/{ng_decorators-BI0uV7KI.cjs → ng_decorators-DSFlWYQY.cjs} +2 -2
- package/schematics/bundles/ngclass-to-class-migration.cjs +16 -19
- package/schematics/bundles/ngstyle-to-style-migration.cjs +15 -18
- package/schematics/bundles/nodes-B16H9JUd.cjs +1 -1
- package/schematics/bundles/output-migration.cjs +16 -19
- package/schematics/bundles/{parse_html-C97tKKp3.cjs → parse_html-8VLCL37B.cjs} +5 -5
- package/schematics/bundles/{project_paths-C6g3lqjX.cjs → project_paths-DvD50ouC.cjs} +14 -247
- package/schematics/bundles/project_tsconfig_paths-CDVxT6Ov.cjs +90 -0
- package/schematics/bundles/property_name-BBwFuqMe.cjs +1 -1
- package/schematics/bundles/route-lazy-loading.cjs +9 -25
- package/schematics/bundles/router-current-navigation.cjs +6 -17
- package/schematics/bundles/router-last-successful-navigation.cjs +6 -17
- package/schematics/bundles/router-testing-module-migration.cjs +7 -18
- package/schematics/bundles/self-closing-tags-migration.cjs +14 -17
- package/schematics/bundles/signal-input-migration.cjs +23 -26
- package/schematics/bundles/signal-queries-migration.cjs +22 -25
- package/schematics/bundles/signals.cjs +10 -13
- package/schematics/bundles/standalone-migration.cjs +22 -56
- package/schematics/bundles/symbol-BObKoqes.cjs +1 -1
- package/schematics/collection.json +6 -0
- package/schematics/migrations/common-to-standalone-migration/schema.json +14 -0
- package/types/_api-chunk.d.ts +1 -1
- package/types/_chrome_dev_tools_performance-chunk.d.ts +20 -12
- package/types/_discovery-chunk.d.ts +18 -14
- package/types/_effect-chunk.d.ts +1 -1
- package/types/_event_dispatcher-chunk.d.ts +1 -1
- package/types/_formatter-chunk.d.ts +4 -3
- package/types/_weak_ref-chunk.d.ts +1 -1
- package/types/core.d.ts +49 -100
- package/types/primitives-di.d.ts +1 -1
- package/types/primitives-event-dispatch.d.ts +1 -1
- package/types/primitives-signals.d.ts +2 -2
- package/types/rxjs-interop.d.ts +1 -1
- package/types/testing.d.ts +1 -1
- package/fesm2022/_root_effect_scheduler-chunk.mjs +0 -4630
- package/fesm2022/_root_effect_scheduler-chunk.mjs.map +0 -1
- package/fesm2022/_signal-chunk.mjs +0 -581
- package/fesm2022/_signal-chunk.mjs.map +0 -1
- package/schematics/bundles/index-BnmACOsq.cjs +0 -22319
- 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-
|
|
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-
|
|
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-
|
|
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-
|
|
12
|
-
var
|
|
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('
|
|
15
|
-
|
|
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('
|
|
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
|
|
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-
|
|
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
|
|
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-
|
|
17
|
-
var imports = require('./imports-
|
|
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
|
|
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({
|
package/schematics/bundles/{apply_import_manager-CBLmogDD.cjs → apply_import_manager-1Zs_gpB6.cjs}
RENAMED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
/**
|
|
3
|
-
* @license Angular v21.0.0-
|
|
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('
|
|
11
|
-
var
|
|
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(
|
|
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-
|
|
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-
|
|
12
|
-
require('
|
|
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-
|
|
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-
|
|
18
|
+
var imports = require('./imports-DP72APSx.cjs');
|
|
21
19
|
var symbol = require('./symbol-BObKoqes.cjs');
|
|
22
20
|
require('@angular-devkit/schematics');
|
|
23
|
-
require('
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
|
73
|
-
const evaluator = new
|
|
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:
|
|
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
|
|
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
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
226
|
-
|
|
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
|
-
|
|
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
|
-
|
|
244
|
-
|
|
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'
|
|
263
|
-
|
|
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
|
-
|
|
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
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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 =
|
|
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-
|
|
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-
|
|
11
|
+
var project_paths = require('./project_paths-DvD50ouC.cjs');
|
|
12
12
|
var ts = require('typescript');
|
|
13
|
-
require('
|
|
14
|
-
var
|
|
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-
|
|
16
|
+
var apply_import_manager = require('./apply_import_manager-1Zs_gpB6.cjs');
|
|
19
17
|
require('@angular-devkit/schematics');
|
|
20
|
-
require('
|
|
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:
|
|
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 ===
|
|
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
|
|
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) => {
|