@angular/cli 21.0.0-next.0 → 21.0.0-next.1
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/lib/config/schema.json +4 -5
- package/lib/config/workspace-schema.d.ts +2 -2
- package/lib/config/workspace-schema.js +1 -1
- package/package.json +18 -18
- package/src/command-builder/schematics-command-module.js +7 -2
- package/src/command-builder/utilities/json-schema.js +5 -1
- package/src/command-builder/utilities/schematic-engine-host.js +4 -6
- package/src/commands/add/cli.d.ts +2 -1
- package/src/commands/add/cli.js +114 -69
- package/src/commands/mcp/tools/modernize.js +0 -10
- package/src/commands/update/schematic/schema.d.ts +0 -1
- package/src/commands/update/schematic/schema.js +0 -1
- package/src/commands/update/schematic/schema.json +1 -1
- package/src/utilities/version.js +1 -1
package/lib/config/schema.json
CHANGED
|
@@ -51,7 +51,6 @@
|
|
|
51
51
|
"type": "string",
|
|
52
52
|
"enum": [
|
|
53
53
|
"npm",
|
|
54
|
-
"cnpm",
|
|
55
54
|
"yarn",
|
|
56
55
|
"pnpm",
|
|
57
56
|
"bun"
|
|
@@ -118,7 +117,6 @@
|
|
|
118
117
|
"type": "string",
|
|
119
118
|
"enum": [
|
|
120
119
|
"npm",
|
|
121
|
-
"cnpm",
|
|
122
120
|
"yarn",
|
|
123
121
|
"pnpm",
|
|
124
122
|
"bun"
|
|
@@ -1007,7 +1005,8 @@
|
|
|
1007
1005
|
"css",
|
|
1008
1006
|
"scss",
|
|
1009
1007
|
"sass",
|
|
1010
|
-
"less"
|
|
1008
|
+
"less",
|
|
1009
|
+
"tailwind"
|
|
1011
1010
|
]
|
|
1012
1011
|
},
|
|
1013
1012
|
"skipTests": {
|
|
@@ -1769,7 +1768,8 @@
|
|
|
1769
1768
|
"css",
|
|
1770
1769
|
"scss",
|
|
1771
1770
|
"sass",
|
|
1772
|
-
"less"
|
|
1771
|
+
"less",
|
|
1772
|
+
"tailwind"
|
|
1773
1773
|
]
|
|
1774
1774
|
},
|
|
1775
1775
|
"skipTests": {
|
|
@@ -1800,7 +1800,6 @@
|
|
|
1800
1800
|
"npm",
|
|
1801
1801
|
"yarn",
|
|
1802
1802
|
"pnpm",
|
|
1803
|
-
"cnpm",
|
|
1804
1803
|
"bun"
|
|
1805
1804
|
]
|
|
1806
1805
|
},
|
|
@@ -67,7 +67,6 @@ export declare enum Environment {
|
|
|
67
67
|
*/
|
|
68
68
|
export declare enum PackageManager {
|
|
69
69
|
Bun = "bun",
|
|
70
|
-
Cnpm = "cnpm",
|
|
71
70
|
Npm = "npm",
|
|
72
71
|
Pnpm = "pnpm",
|
|
73
72
|
Yarn = "yarn"
|
|
@@ -196,7 +195,8 @@ export declare enum SchematicsAngularApplicationStyle {
|
|
|
196
195
|
Css = "css",
|
|
197
196
|
Less = "less",
|
|
198
197
|
Sass = "sass",
|
|
199
|
-
Scss = "scss"
|
|
198
|
+
Scss = "scss",
|
|
199
|
+
Tailwind = "tailwind"
|
|
200
200
|
}
|
|
201
201
|
/**
|
|
202
202
|
* Sets the view encapsulation mode for the application's components. This determines how
|
|
@@ -20,7 +20,6 @@ var Environment;
|
|
|
20
20
|
var PackageManager;
|
|
21
21
|
(function (PackageManager) {
|
|
22
22
|
PackageManager["Bun"] = "bun";
|
|
23
|
-
PackageManager["Cnpm"] = "cnpm";
|
|
24
23
|
PackageManager["Npm"] = "npm";
|
|
25
24
|
PackageManager["Pnpm"] = "pnpm";
|
|
26
25
|
PackageManager["Yarn"] = "yarn";
|
|
@@ -36,6 +35,7 @@ var SchematicsAngularApplicationStyle;
|
|
|
36
35
|
SchematicsAngularApplicationStyle["Less"] = "less";
|
|
37
36
|
SchematicsAngularApplicationStyle["Sass"] = "sass";
|
|
38
37
|
SchematicsAngularApplicationStyle["Scss"] = "scss";
|
|
38
|
+
SchematicsAngularApplicationStyle["Tailwind"] = "tailwind";
|
|
39
39
|
})(SchematicsAngularApplicationStyle || (exports.SchematicsAngularApplicationStyle = SchematicsAngularApplicationStyle = {}));
|
|
40
40
|
/**
|
|
41
41
|
* Sets the view encapsulation mode for the application's components. This determines how
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular/cli",
|
|
3
|
-
"version": "21.0.0-next.
|
|
3
|
+
"version": "21.0.0-next.1",
|
|
4
4
|
"description": "CLI tool for Angular",
|
|
5
5
|
"main": "lib/cli/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -25,18 +25,18 @@
|
|
|
25
25
|
},
|
|
26
26
|
"homepage": "https://github.com/angular/angular-cli",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@angular-devkit/architect": "0.2100.0-next.
|
|
29
|
-
"@angular-devkit/core": "21.0.0-next.
|
|
30
|
-
"@angular-devkit/schematics": "21.0.0-next.
|
|
31
|
-
"@inquirer/prompts": "7.8.
|
|
32
|
-
"@listr2/prompt-adapter-inquirer": "3.0.
|
|
33
|
-
"@modelcontextprotocol/sdk": "1.17.
|
|
34
|
-
"@schematics/angular": "21.0.0-next.
|
|
28
|
+
"@angular-devkit/architect": "0.2100.0-next.1",
|
|
29
|
+
"@angular-devkit/core": "21.0.0-next.1",
|
|
30
|
+
"@angular-devkit/schematics": "21.0.0-next.1",
|
|
31
|
+
"@inquirer/prompts": "7.8.4",
|
|
32
|
+
"@listr2/prompt-adapter-inquirer": "3.0.2",
|
|
33
|
+
"@modelcontextprotocol/sdk": "1.17.4",
|
|
34
|
+
"@schematics/angular": "21.0.0-next.1",
|
|
35
35
|
"@yarnpkg/lockfile": "1.1.0",
|
|
36
|
-
"algoliasearch": "5.
|
|
36
|
+
"algoliasearch": "5.36.0",
|
|
37
37
|
"ini": "5.0.0",
|
|
38
38
|
"jsonc-parser": "3.3.1",
|
|
39
|
-
"listr2": "9.0.
|
|
39
|
+
"listr2": "9.0.2",
|
|
40
40
|
"npm-package-arg": "13.0.0",
|
|
41
41
|
"pacote": "21.0.0",
|
|
42
42
|
"resolve": "1.22.10",
|
|
@@ -47,14 +47,14 @@
|
|
|
47
47
|
"ng-update": {
|
|
48
48
|
"migrations": "@schematics/angular/migrations/migration-collection.json",
|
|
49
49
|
"packageGroup": {
|
|
50
|
-
"@angular/cli": "21.0.0-next.
|
|
51
|
-
"@angular/build": "21.0.0-next.
|
|
52
|
-
"@angular/ssr": "21.0.0-next.
|
|
53
|
-
"@angular-devkit/architect": "0.2100.0-next.
|
|
54
|
-
"@angular-devkit/build-angular": "21.0.0-next.
|
|
55
|
-
"@angular-devkit/build-webpack": "0.2100.0-next.
|
|
56
|
-
"@angular-devkit/core": "21.0.0-next.
|
|
57
|
-
"@angular-devkit/schematics": "21.0.0-next.
|
|
50
|
+
"@angular/cli": "21.0.0-next.1",
|
|
51
|
+
"@angular/build": "21.0.0-next.1",
|
|
52
|
+
"@angular/ssr": "21.0.0-next.1",
|
|
53
|
+
"@angular-devkit/architect": "0.2100.0-next.1",
|
|
54
|
+
"@angular-devkit/build-angular": "21.0.0-next.1",
|
|
55
|
+
"@angular-devkit/build-webpack": "0.2100.0-next.1",
|
|
56
|
+
"@angular-devkit/core": "21.0.0-next.1",
|
|
57
|
+
"@angular-devkit/schematics": "21.0.0-next.1"
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
"packageManager": "pnpm@10.15.0",
|
|
@@ -224,13 +224,18 @@ let SchematicsCommandModule = (() => {
|
|
|
224
224
|
? {
|
|
225
225
|
name: item,
|
|
226
226
|
value: item,
|
|
227
|
-
checked:
|
|
227
|
+
checked: definition.multiselect && Array.isArray(definition.default)
|
|
228
|
+
? definition.default?.includes(item)
|
|
229
|
+
: item === definition.default,
|
|
228
230
|
}
|
|
229
231
|
: {
|
|
230
232
|
...item,
|
|
231
233
|
name: item.label,
|
|
232
234
|
value: item.value,
|
|
233
|
-
checked:
|
|
235
|
+
checked: definition.multiselect && Array.isArray(definition.default)
|
|
236
|
+
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
237
|
+
definition.default?.includes(item.value)
|
|
238
|
+
: item.value === definition.default,
|
|
234
239
|
}),
|
|
235
240
|
});
|
|
236
241
|
break;
|
|
@@ -120,11 +120,15 @@ async function parseJsonSchemaToOptions(registry, schema, interactive = true) {
|
|
|
120
120
|
if (current.default !== undefined) {
|
|
121
121
|
switch (types[0]) {
|
|
122
122
|
case 'string':
|
|
123
|
-
case 'array':
|
|
124
123
|
if (typeof current.default == 'string') {
|
|
125
124
|
defaultValue = current.default;
|
|
126
125
|
}
|
|
127
126
|
break;
|
|
127
|
+
case 'array':
|
|
128
|
+
if (Array.isArray(current.default)) {
|
|
129
|
+
defaultValue = current.default;
|
|
130
|
+
}
|
|
131
|
+
break;
|
|
128
132
|
case 'number':
|
|
129
133
|
if (typeof current.default == 'number') {
|
|
130
134
|
defaultValue = current.default;
|
|
@@ -47,11 +47,9 @@ function shouldWrapSchematic(schematicFile, schematicEncapsulation) {
|
|
|
47
47
|
}
|
|
48
48
|
// Check for first-party Angular schematic packages
|
|
49
49
|
// Angular schematics are safe to use in the wrapped VM context
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// Otherwise use the value of the schematic collection's encapsulation option (current default of false)
|
|
54
|
-
return schematicEncapsulation;
|
|
50
|
+
const isFirstParty = /\/node_modules\/@(?:angular|schematics|nguniversal)\//.test(normalizedSchematicFile);
|
|
51
|
+
// Use value of defined option if present, otherwise default to first-party usage.
|
|
52
|
+
return schematicEncapsulation ?? isFirstParty;
|
|
55
53
|
}
|
|
56
54
|
class SchematicEngineHost extends tools_1.NodeModulesEngineHost {
|
|
57
55
|
_resolveReferenceString(refString, parentPath, collectionDescription) {
|
|
@@ -60,7 +58,7 @@ class SchematicEngineHost extends tools_1.NodeModulesEngineHost {
|
|
|
60
58
|
const fullPath = path[0] === '.' ? (0, node_path_1.resolve)(parentPath ?? process.cwd(), path) : path;
|
|
61
59
|
const referenceRequire = (0, node_module_1.createRequire)(__filename);
|
|
62
60
|
const schematicFile = referenceRequire.resolve(fullPath, { paths: [parentPath] });
|
|
63
|
-
if (shouldWrapSchematic(schematicFile,
|
|
61
|
+
if (shouldWrapSchematic(schematicFile, collectionDescription?.encapsulation)) {
|
|
64
62
|
const schematicPath = (0, node_path_1.dirname)(schematicFile);
|
|
65
63
|
const moduleCache = new Map();
|
|
66
64
|
const factoryInitializer = wrap(schematicFile, schematicPath, moduleCache, name || 'default');
|
|
@@ -15,6 +15,7 @@ interface AddCommandArgs extends SchematicsCommandArgs {
|
|
|
15
15
|
'skip-confirmation'?: boolean;
|
|
16
16
|
}
|
|
17
17
|
export default class AddCommandModule extends SchematicsCommandModule implements CommandModuleImplementation<AddCommandArgs> {
|
|
18
|
+
#private;
|
|
18
19
|
command: string;
|
|
19
20
|
describe: string;
|
|
20
21
|
longDescriptionPath: string;
|
|
@@ -33,6 +34,6 @@ export default class AddCommandModule extends SchematicsCommandModule implements
|
|
|
33
34
|
private isPackageInstalled;
|
|
34
35
|
private executeSchematic;
|
|
35
36
|
private findProjectVersion;
|
|
36
|
-
private
|
|
37
|
+
private getPeerDependencyConflicts;
|
|
37
38
|
}
|
|
38
39
|
export {};
|
package/src/commands/add/cli.js
CHANGED
|
@@ -69,6 +69,7 @@ const packageVersionExclusions = {
|
|
|
69
69
|
// @angular/material@7.x versions have unbounded peer dependency ranges (>=7.0.0).
|
|
70
70
|
'@angular/material': '7.x',
|
|
71
71
|
};
|
|
72
|
+
const DEFAULT_CONFLICT_DISPLAY_LIMIT = 5;
|
|
72
73
|
class AddCommandModule extends schematics_command_module_1.SchematicsCommandModule {
|
|
73
74
|
command = 'add <collection>';
|
|
74
75
|
describe = 'Adds support for an external library to your project.';
|
|
@@ -76,6 +77,7 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
|
|
|
76
77
|
allowPrivateSchematics = true;
|
|
77
78
|
schematicName = 'ng-add';
|
|
78
79
|
rootRequire = (0, node_module_1.createRequire)(this.context.root + '/');
|
|
80
|
+
#projectVersionCache = new Map();
|
|
79
81
|
async builder(argv) {
|
|
80
82
|
const localYargs = (await super.builder(argv))
|
|
81
83
|
.positional('collection', {
|
|
@@ -117,6 +119,7 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
|
|
|
117
119
|
return localYargs;
|
|
118
120
|
}
|
|
119
121
|
async run(options) {
|
|
122
|
+
this.#projectVersionCache.clear();
|
|
120
123
|
const { logger } = this.context;
|
|
121
124
|
const { collection, skipConfirmation } = options;
|
|
122
125
|
let packageIdentifier;
|
|
@@ -141,7 +144,8 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
|
|
|
141
144
|
const taskContext = {
|
|
142
145
|
packageIdentifier,
|
|
143
146
|
executeSchematic: this.executeSchematic.bind(this),
|
|
144
|
-
|
|
147
|
+
getPeerDependencyConflicts: this.getPeerDependencyConflicts.bind(this),
|
|
148
|
+
dryRun: options.dryRun,
|
|
145
149
|
};
|
|
146
150
|
const tasks = new listr2_1.Listr([
|
|
147
151
|
{
|
|
@@ -162,11 +166,18 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
|
|
|
162
166
|
},
|
|
163
167
|
{
|
|
164
168
|
title: 'Confirming installation',
|
|
165
|
-
enabled: !skipConfirmation,
|
|
169
|
+
enabled: !skipConfirmation && !options.dryRun,
|
|
166
170
|
task: (context, task) => this.confirmInstallationTask(context, task),
|
|
167
171
|
rendererOptions: { persistentOutput: true },
|
|
168
172
|
},
|
|
169
173
|
{
|
|
174
|
+
title: 'Installing package',
|
|
175
|
+
skip: (context) => {
|
|
176
|
+
if (context.dryRun) {
|
|
177
|
+
return `Skipping package installation. Would install package ${listr2_1.color.blue(context.packageIdentifier.toString())}.`;
|
|
178
|
+
}
|
|
179
|
+
return false;
|
|
180
|
+
},
|
|
170
181
|
task: (context, task) => this.installPackageTask(context, task, options),
|
|
171
182
|
rendererOptions: { bottomBar: Infinity },
|
|
172
183
|
},
|
|
@@ -177,6 +188,10 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
|
|
|
177
188
|
try {
|
|
178
189
|
const result = await tasks.run(taskContext);
|
|
179
190
|
(0, node_assert_1.default)(result.collectionName, 'Collection name should always be available');
|
|
191
|
+
if (options.dryRun) {
|
|
192
|
+
logger.info('The package schematic would be executed next.');
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
180
195
|
return this.executeSchematic({ ...options, collection: result.collectionName });
|
|
181
196
|
}
|
|
182
197
|
catch (e) {
|
|
@@ -210,60 +225,79 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
|
|
|
210
225
|
(0, error_1.assertIsError)(e);
|
|
211
226
|
throw new CommandError(`Unable to load package information from registry: ${e.message}`);
|
|
212
227
|
}
|
|
228
|
+
const rejectionReasons = [];
|
|
213
229
|
// Start with the version tagged as `latest` if it exists
|
|
214
230
|
const latestManifest = packageMetadata.tags['latest'];
|
|
215
231
|
if (latestManifest) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
Object.keys(latestManifest.peerDependencies).length === 0) {
|
|
221
|
-
task.output = `Found compatible package version: ${listr2_1.color.blue(latestManifest.version)}.`;
|
|
222
|
-
}
|
|
223
|
-
else if (!latestManifest || (await context.hasMismatchedPeer(latestManifest))) {
|
|
224
|
-
// 'latest' is invalid so search for most recent matching package
|
|
225
|
-
// Allow prelease versions if the CLI itself is a prerelease
|
|
226
|
-
const allowPrereleases = (0, semver_1.prerelease)(version_1.VERSION.full);
|
|
227
|
-
const versionExclusions = packageVersionExclusions[packageMetadata.name];
|
|
228
|
-
const versionManifests = Object.values(packageMetadata.versions).filter((value) => {
|
|
229
|
-
// Prerelease versions are not stable and should not be considered by default
|
|
230
|
-
if (!allowPrereleases && (0, semver_1.prerelease)(value.version)) {
|
|
231
|
-
return false;
|
|
232
|
-
}
|
|
233
|
-
// Deprecated versions should not be used or considered
|
|
234
|
-
if (value.deprecated) {
|
|
235
|
-
return false;
|
|
236
|
-
}
|
|
237
|
-
// Excluded package versions should not be considered
|
|
238
|
-
if (versionExclusions &&
|
|
239
|
-
(0, semver_1.satisfies)(value.version, versionExclusions, { includePrerelease: true })) {
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
|
-
return true;
|
|
243
|
-
});
|
|
244
|
-
// Sort in reverse SemVer order so that the newest compatible version is chosen
|
|
245
|
-
versionManifests.sort((a, b) => (0, semver_1.compare)(b.version, a.version, true));
|
|
246
|
-
let found = false;
|
|
247
|
-
for (const versionManifest of versionManifests) {
|
|
248
|
-
const mismatch = await context.hasMismatchedPeer(versionManifest);
|
|
249
|
-
if (mismatch) {
|
|
250
|
-
continue;
|
|
251
|
-
}
|
|
252
|
-
context.packageIdentifier = npm_package_arg_1.default.resolve(versionManifest.name, versionManifest.version);
|
|
253
|
-
found = true;
|
|
254
|
-
break;
|
|
255
|
-
}
|
|
256
|
-
if (!found) {
|
|
257
|
-
task.output = "Unable to find compatible package. Using 'latest' tag.";
|
|
232
|
+
const latestConflicts = await this.getPeerDependencyConflicts(latestManifest);
|
|
233
|
+
if (latestConflicts) {
|
|
234
|
+
// 'latest' is invalid so search for most recent matching package
|
|
235
|
+
rejectionReasons.push(...latestConflicts);
|
|
258
236
|
}
|
|
259
237
|
else {
|
|
260
|
-
|
|
238
|
+
context.packageIdentifier = npm_package_arg_1.default.resolve(latestManifest.name, latestManifest.version);
|
|
239
|
+
task.output = `Found compatible package version: ${listr2_1.color.blue(latestManifest.version)}.`;
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// Allow prelease versions if the CLI itself is a prerelease
|
|
244
|
+
const allowPrereleases = !!(0, semver_1.prerelease)(version_1.VERSION.full);
|
|
245
|
+
const versionManifests = this.#getPotentialVersionManifests(packageMetadata, allowPrereleases);
|
|
246
|
+
let found = false;
|
|
247
|
+
for (const versionManifest of versionManifests) {
|
|
248
|
+
// Already checked the 'latest' version
|
|
249
|
+
if (latestManifest?.version === versionManifest.version) {
|
|
250
|
+
continue;
|
|
261
251
|
}
|
|
252
|
+
const conflicts = await this.getPeerDependencyConflicts(versionManifest);
|
|
253
|
+
if (conflicts) {
|
|
254
|
+
if (options.verbose || rejectionReasons.length < DEFAULT_CONFLICT_DISPLAY_LIMIT) {
|
|
255
|
+
rejectionReasons.push(...conflicts);
|
|
256
|
+
}
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
context.packageIdentifier = npm_package_arg_1.default.resolve(versionManifest.name, versionManifest.version);
|
|
260
|
+
found = true;
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
if (!found) {
|
|
264
|
+
let message = `Unable to find compatible package. Using 'latest' tag.`;
|
|
265
|
+
if (rejectionReasons.length > 0) {
|
|
266
|
+
message +=
|
|
267
|
+
'\nThis is often because of incompatible peer dependencies.\n' +
|
|
268
|
+
'These versions were rejected due to the following conflicts:\n' +
|
|
269
|
+
rejectionReasons
|
|
270
|
+
.slice(0, options.verbose ? undefined : DEFAULT_CONFLICT_DISPLAY_LIMIT)
|
|
271
|
+
.map((r) => ` - ${r}`)
|
|
272
|
+
.join('\n');
|
|
273
|
+
}
|
|
274
|
+
task.output = message;
|
|
262
275
|
}
|
|
263
276
|
else {
|
|
264
277
|
task.output = `Found compatible package version: ${listr2_1.color.blue(context.packageIdentifier.toString())}.`;
|
|
265
278
|
}
|
|
266
279
|
}
|
|
280
|
+
#getPotentialVersionManifests(packageMetadata, allowPrereleases) {
|
|
281
|
+
const versionExclusions = packageVersionExclusions[packageMetadata.name];
|
|
282
|
+
const versionManifests = Object.values(packageMetadata.versions).filter((value) => {
|
|
283
|
+
// Prerelease versions are not stable and should not be considered by default
|
|
284
|
+
if (!allowPrereleases && (0, semver_1.prerelease)(value.version)) {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
// Deprecated versions should not be used or considered
|
|
288
|
+
if (value.deprecated) {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
// Excluded package versions should not be considered
|
|
292
|
+
if (versionExclusions &&
|
|
293
|
+
(0, semver_1.satisfies)(value.version, versionExclusions, { includePrerelease: true })) {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
return true;
|
|
297
|
+
});
|
|
298
|
+
// Sort in reverse SemVer order so that the newest compatible version is chosen
|
|
299
|
+
return versionManifests.sort((a, b) => (0, semver_1.compare)(b.version, a.version, true));
|
|
300
|
+
}
|
|
267
301
|
async loadPackageInfoTask(context, task, options) {
|
|
268
302
|
const { logger } = this.context;
|
|
269
303
|
const { verbose, registry } = options;
|
|
@@ -281,7 +315,7 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
|
|
|
281
315
|
}
|
|
282
316
|
context.savePackage = manifest['ng-add']?.save;
|
|
283
317
|
context.collectionName = manifest.name;
|
|
284
|
-
if (await
|
|
318
|
+
if (await this.getPeerDependencyConflicts(manifest)) {
|
|
285
319
|
task.output = listr2_1.color.yellow(listr2_1.figures.warning +
|
|
286
320
|
' Package has unmet peer dependencies. Adding the package may not succeed.');
|
|
287
321
|
}
|
|
@@ -411,6 +445,10 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
|
|
|
411
445
|
}
|
|
412
446
|
}
|
|
413
447
|
async findProjectVersion(name) {
|
|
448
|
+
const cachedVersion = this.#projectVersionCache.get(name);
|
|
449
|
+
if (cachedVersion !== undefined) {
|
|
450
|
+
return cachedVersion;
|
|
451
|
+
}
|
|
414
452
|
const { logger, root } = this.context;
|
|
415
453
|
let installedPackage;
|
|
416
454
|
try {
|
|
@@ -420,6 +458,7 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
|
|
|
420
458
|
if (installedPackage) {
|
|
421
459
|
try {
|
|
422
460
|
const installed = await (0, package_metadata_1.fetchPackageManifest)((0, node_path_1.dirname)(installedPackage), logger);
|
|
461
|
+
this.#projectVersionCache.set(name, installed.version);
|
|
423
462
|
return installed.version;
|
|
424
463
|
}
|
|
425
464
|
catch { }
|
|
@@ -432,44 +471,50 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
|
|
|
432
471
|
if (projectManifest) {
|
|
433
472
|
const version = projectManifest.dependencies?.[name] || projectManifest.devDependencies?.[name];
|
|
434
473
|
if (version) {
|
|
474
|
+
this.#projectVersionCache.set(name, version);
|
|
435
475
|
return version;
|
|
436
476
|
}
|
|
437
477
|
}
|
|
478
|
+
this.#projectVersionCache.set(name, null);
|
|
438
479
|
return null;
|
|
439
480
|
}
|
|
440
|
-
async
|
|
441
|
-
|
|
481
|
+
async getPeerDependencyConflicts(manifest) {
|
|
482
|
+
if (!manifest.peerDependencies) {
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
const checks = Object.entries(manifest.peerDependencies).map(async ([peer, range]) => {
|
|
442
486
|
let peerIdentifier;
|
|
443
487
|
try {
|
|
444
|
-
peerIdentifier = npm_package_arg_1.default.resolve(peer,
|
|
488
|
+
peerIdentifier = npm_package_arg_1.default.resolve(peer, range);
|
|
445
489
|
}
|
|
446
490
|
catch {
|
|
447
491
|
this.context.logger.warn(`Invalid peer dependency ${peer} found in package.`);
|
|
448
|
-
|
|
492
|
+
return null;
|
|
449
493
|
}
|
|
450
|
-
if (peerIdentifier.type
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
return true;
|
|
460
|
-
}
|
|
494
|
+
if (peerIdentifier.type !== 'version' && peerIdentifier.type !== 'range') {
|
|
495
|
+
// type === 'tag' | 'file' | 'directory' | 'remote' | 'git'
|
|
496
|
+
// Cannot accurately compare these as the tag/location may have changed since install.
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
try {
|
|
500
|
+
const version = await this.findProjectVersion(peer);
|
|
501
|
+
if (!version) {
|
|
502
|
+
return null;
|
|
461
503
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
504
|
+
const options = { includePrerelease: true };
|
|
505
|
+
if (!(0, semver_1.intersects)(version, peerIdentifier.rawSpec, options) &&
|
|
506
|
+
!(0, semver_1.satisfies)(version, peerIdentifier.rawSpec, options)) {
|
|
507
|
+
return (`Package "${manifest.name}@${manifest.version}" has an incompatible peer dependency to "` +
|
|
508
|
+
`${peer}@${peerIdentifier.rawSpec}" (requires "${version}" in project).`);
|
|
465
509
|
}
|
|
466
510
|
}
|
|
467
|
-
|
|
468
|
-
//
|
|
469
|
-
// Cannot accurately compare these as the tag/location may have changed since install
|
|
511
|
+
catch {
|
|
512
|
+
// Not found or invalid so ignore
|
|
470
513
|
}
|
|
471
|
-
|
|
472
|
-
|
|
514
|
+
return null;
|
|
515
|
+
});
|
|
516
|
+
const conflicts = (await Promise.all(checks)).filter((result) => !!result);
|
|
517
|
+
return conflicts.length > 0 && conflicts;
|
|
473
518
|
}
|
|
474
519
|
}
|
|
475
520
|
exports.default = AddCommandModule;
|
|
@@ -22,11 +22,6 @@ const TRANSFORMATIONS = [
|
|
|
22
22
|
description: 'Converts tags for elements with no content to be self-closing (e.g., `<app-foo></app-foo>` becomes `<app-foo />`).',
|
|
23
23
|
documentationUrl: 'https://angular.dev/reference/migrations/self-closing-tags',
|
|
24
24
|
},
|
|
25
|
-
{
|
|
26
|
-
name: 'test-bed-get',
|
|
27
|
-
description: 'Updates `TestBed.get` to the preferred and type-safe `TestBed.inject` in TypeScript test files.',
|
|
28
|
-
documentationUrl: 'https://angular.dev/guide/testing/dependency-injection',
|
|
29
|
-
},
|
|
30
25
|
{
|
|
31
26
|
name: 'inject',
|
|
32
27
|
description: 'Converts usages of constructor-based injection to the inject() function.',
|
|
@@ -59,11 +54,6 @@ const TRANSFORMATIONS = [
|
|
|
59
54
|
'3. Run `ng g @angular/core:standalone` and select "Bootstrap the project using standalone APIs"',
|
|
60
55
|
documentationUrl: 'https://angular.dev/reference/migrations/standalone',
|
|
61
56
|
},
|
|
62
|
-
{
|
|
63
|
-
name: 'zoneless',
|
|
64
|
-
description: 'Migrates the application to be zoneless.',
|
|
65
|
-
documentationUrl: 'https://angular.dev/guide/zoneless',
|
|
66
|
-
},
|
|
67
57
|
];
|
|
68
58
|
const modernizeInputSchema = zod_1.z.object({
|
|
69
59
|
// Casting to [string, ...string[]] since the enum definition requires a nonempty array.
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"description": "The preferred package manager configuration files to use for registry settings.",
|
|
58
58
|
"type": "string",
|
|
59
59
|
"default": "npm",
|
|
60
|
-
"enum": ["npm", "yarn", "
|
|
60
|
+
"enum": ["npm", "yarn", "pnpm", "bun"]
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
63
|
"required": []
|
package/src/utilities/version.js
CHANGED