@angular/cli 20.2.1 → 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 +8 -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/commands/add/cli.d.ts +7 -1
- package/src/commands/add/cli.js +222 -163
- 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
|
},
|
|
@@ -4377,6 +4376,10 @@
|
|
|
4377
4376
|
"type": "string"
|
|
4378
4377
|
},
|
|
4379
4378
|
"description": "A list of global setup and configuration files that are included before the test files. The application's polyfills are always included before these files. The Angular Testbed is also initialized prior to the execution of these files."
|
|
4379
|
+
},
|
|
4380
|
+
"progress": {
|
|
4381
|
+
"type": "boolean",
|
|
4382
|
+
"description": "Log progress to the console while building. Defaults to the build target's progress value."
|
|
4380
4383
|
}
|
|
4381
4384
|
},
|
|
4382
4385
|
"additionalProperties": false,
|
|
@@ -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": "
|
|
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.
|
|
29
|
-
"@angular-devkit/core": "
|
|
30
|
-
"@angular-devkit/schematics": "
|
|
31
|
-
"@inquirer/prompts": "7.8.
|
|
32
|
-
"@listr2/prompt-adapter-inquirer": "3.0.
|
|
33
|
-
"@modelcontextprotocol/sdk": "1.17.
|
|
34
|
-
"@schematics/angular": "
|
|
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": "
|
|
51
|
-
"@angular/build": "
|
|
52
|
-
"@angular/ssr": "
|
|
53
|
-
"@angular-devkit/architect": "0.
|
|
54
|
-
"@angular-devkit/build-angular": "
|
|
55
|
-
"@angular-devkit/build-webpack": "0.
|
|
56
|
-
"@angular-devkit/core": "
|
|
57
|
-
"@angular-devkit/schematics": "
|
|
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",
|
|
@@ -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;
|
|
@@ -23,11 +24,16 @@ export default class AddCommandModule extends SchematicsCommandModule implements
|
|
|
23
24
|
private rootRequire;
|
|
24
25
|
builder(argv: Argv): Promise<Argv<AddCommandArgs>>;
|
|
25
26
|
run(options: Options<AddCommandArgs> & OtherOptions): Promise<number | void>;
|
|
27
|
+
private determinePackageManagerTask;
|
|
28
|
+
private findCompatiblePackageVersionTask;
|
|
29
|
+
private loadPackageInfoTask;
|
|
30
|
+
private confirmInstallationTask;
|
|
31
|
+
private installPackageTask;
|
|
26
32
|
private isProjectVersionValid;
|
|
27
33
|
private getCollectionName;
|
|
28
34
|
private isPackageInstalled;
|
|
29
35
|
private executeSchematic;
|
|
30
36
|
private findProjectVersion;
|
|
31
|
-
private
|
|
37
|
+
private getPeerDependencyConflicts;
|
|
32
38
|
}
|
|
33
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', {
|
|
@@ -116,10 +118,10 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
|
|
|
116
118
|
}
|
|
117
119
|
return localYargs;
|
|
118
120
|
}
|
|
119
|
-
// eslint-disable-next-line max-lines-per-function
|
|
120
121
|
async run(options) {
|
|
121
|
-
|
|
122
|
-
const {
|
|
122
|
+
this.#projectVersionCache.clear();
|
|
123
|
+
const { logger } = this.context;
|
|
124
|
+
const { collection, skipConfirmation } = options;
|
|
123
125
|
let packageIdentifier;
|
|
124
126
|
try {
|
|
125
127
|
packageIdentifier = (0, npm_package_arg_1.default)(collection);
|
|
@@ -142,180 +144,226 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
|
|
|
142
144
|
const taskContext = {
|
|
143
145
|
packageIdentifier,
|
|
144
146
|
executeSchematic: this.executeSchematic.bind(this),
|
|
145
|
-
|
|
147
|
+
getPeerDependencyConflicts: this.getPeerDependencyConflicts.bind(this),
|
|
148
|
+
dryRun: options.dryRun,
|
|
146
149
|
};
|
|
147
150
|
const tasks = new listr2_1.Listr([
|
|
148
151
|
{
|
|
149
152
|
title: 'Determining Package Manager',
|
|
150
|
-
task(context, task)
|
|
151
|
-
context.usingYarn = packageManager.name === workspace_schema_1.PackageManager.Yarn;
|
|
152
|
-
task.output = `Using package manager: ${listr2_1.color.dim(packageManager.name)}`;
|
|
153
|
-
},
|
|
153
|
+
task: (context, task) => this.determinePackageManagerTask(context, task),
|
|
154
154
|
rendererOptions: { persistentOutput: true },
|
|
155
155
|
},
|
|
156
156
|
{
|
|
157
157
|
title: 'Searching for compatible package version',
|
|
158
158
|
enabled: packageIdentifier.type === 'range' && packageIdentifier.rawSpec === '*',
|
|
159
|
-
|
|
160
|
-
(0, node_assert_1.default)(context.packageIdentifier.name, 'Registry package identifiers should always have a name.');
|
|
161
|
-
// only package name provided; search for viable version
|
|
162
|
-
// plus special cases for packages that did not have peer deps setup
|
|
163
|
-
let packageMetadata;
|
|
164
|
-
try {
|
|
165
|
-
packageMetadata = await (0, package_metadata_1.fetchPackageMetadata)(context.packageIdentifier.name, logger, {
|
|
166
|
-
registry,
|
|
167
|
-
usingYarn: context.usingYarn,
|
|
168
|
-
verbose,
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
catch (e) {
|
|
172
|
-
(0, error_1.assertIsError)(e);
|
|
173
|
-
throw new CommandError(`Unable to load package information from registry: ${e.message}`);
|
|
174
|
-
}
|
|
175
|
-
// Start with the version tagged as `latest` if it exists
|
|
176
|
-
const latestManifest = packageMetadata.tags['latest'];
|
|
177
|
-
if (latestManifest) {
|
|
178
|
-
context.packageIdentifier = npm_package_arg_1.default.resolve(latestManifest.name, latestManifest.version);
|
|
179
|
-
}
|
|
180
|
-
// Adjust the version based on name and peer dependencies
|
|
181
|
-
if (latestManifest?.peerDependencies &&
|
|
182
|
-
Object.keys(latestManifest.peerDependencies).length === 0) {
|
|
183
|
-
task.output = `Found compatible package version: ${listr2_1.color.blue(latestManifest.version)}.`;
|
|
184
|
-
}
|
|
185
|
-
else if (!latestManifest || (await context.hasMismatchedPeer(latestManifest))) {
|
|
186
|
-
// 'latest' is invalid so search for most recent matching package
|
|
187
|
-
// Allow prelease versions if the CLI itself is a prerelease
|
|
188
|
-
const allowPrereleases = (0, semver_1.prerelease)(version_1.VERSION.full);
|
|
189
|
-
const versionExclusions = packageVersionExclusions[packageMetadata.name];
|
|
190
|
-
const versionManifests = Object.values(packageMetadata.versions).filter((value) => {
|
|
191
|
-
// Prerelease versions are not stable and should not be considered by default
|
|
192
|
-
if (!allowPrereleases && (0, semver_1.prerelease)(value.version)) {
|
|
193
|
-
return false;
|
|
194
|
-
}
|
|
195
|
-
// Deprecated versions should not be used or considered
|
|
196
|
-
if (value.deprecated) {
|
|
197
|
-
return false;
|
|
198
|
-
}
|
|
199
|
-
// Excluded package versions should not be considered
|
|
200
|
-
if (versionExclusions &&
|
|
201
|
-
(0, semver_1.satisfies)(value.version, versionExclusions, { includePrerelease: true })) {
|
|
202
|
-
return false;
|
|
203
|
-
}
|
|
204
|
-
return true;
|
|
205
|
-
});
|
|
206
|
-
// Sort in reverse SemVer order so that the newest compatible version is chosen
|
|
207
|
-
versionManifests.sort((a, b) => (0, semver_1.compare)(b.version, a.version, true));
|
|
208
|
-
let found = false;
|
|
209
|
-
for (const versionManifest of versionManifests) {
|
|
210
|
-
const mismatch = await context.hasMismatchedPeer(versionManifest);
|
|
211
|
-
if (mismatch) {
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
context.packageIdentifier = npm_package_arg_1.default.resolve(versionManifest.name, versionManifest.version);
|
|
215
|
-
found = true;
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
if (!found) {
|
|
219
|
-
task.output = "Unable to find compatible package. Using 'latest' tag.";
|
|
220
|
-
}
|
|
221
|
-
else {
|
|
222
|
-
task.output = `Found compatible package version: ${listr2_1.color.blue(context.packageIdentifier.toString())}.`;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
else {
|
|
226
|
-
task.output = `Found compatible package version: ${listr2_1.color.blue(context.packageIdentifier.toString())}.`;
|
|
227
|
-
}
|
|
228
|
-
},
|
|
159
|
+
task: (context, task) => this.findCompatiblePackageVersionTask(context, task, options),
|
|
229
160
|
rendererOptions: { persistentOutput: true },
|
|
230
161
|
},
|
|
231
162
|
{
|
|
232
163
|
title: 'Loading package information from registry',
|
|
233
|
-
|
|
234
|
-
let manifest;
|
|
235
|
-
try {
|
|
236
|
-
manifest = await (0, package_metadata_1.fetchPackageManifest)(context.packageIdentifier.toString(), logger, {
|
|
237
|
-
registry,
|
|
238
|
-
verbose,
|
|
239
|
-
usingYarn: context.usingYarn,
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
catch (e) {
|
|
243
|
-
(0, error_1.assertIsError)(e);
|
|
244
|
-
throw new CommandError(`Unable to fetch package information for '${context.packageIdentifier}': ${e.message}`);
|
|
245
|
-
}
|
|
246
|
-
context.savePackage = manifest['ng-add']?.save;
|
|
247
|
-
context.collectionName = manifest.name;
|
|
248
|
-
if (await context.hasMismatchedPeer(manifest)) {
|
|
249
|
-
task.output = listr2_1.color.yellow(listr2_1.figures.warning +
|
|
250
|
-
' Package has unmet peer dependencies. Adding the package may not succeed.');
|
|
251
|
-
}
|
|
252
|
-
},
|
|
164
|
+
task: (context, task) => this.loadPackageInfoTask(context, task, options),
|
|
253
165
|
rendererOptions: { persistentOutput: true },
|
|
254
166
|
},
|
|
255
167
|
{
|
|
256
168
|
title: 'Confirming installation',
|
|
257
|
-
enabled: !skipConfirmation,
|
|
258
|
-
|
|
259
|
-
if (!(0, tty_1.isTTY)()) {
|
|
260
|
-
task.output =
|
|
261
|
-
`'--skip-confirmation' can be used to bypass installation confirmation. ` +
|
|
262
|
-
`Ensure package name is correct prior to '--skip-confirmation' option usage.`;
|
|
263
|
-
throw new CommandError('No terminal detected');
|
|
264
|
-
}
|
|
265
|
-
const { ListrInquirerPromptAdapter } = await Promise.resolve().then(() => __importStar(require('@listr2/prompt-adapter-inquirer')));
|
|
266
|
-
const { confirm } = await Promise.resolve().then(() => __importStar(require('@inquirer/prompts')));
|
|
267
|
-
const shouldProceed = await task.prompt(ListrInquirerPromptAdapter).run(confirm, {
|
|
268
|
-
message: `The package ${listr2_1.color.blue(context.packageIdentifier.toString())} will be installed and executed.\n` +
|
|
269
|
-
'Would you like to proceed?',
|
|
270
|
-
default: true,
|
|
271
|
-
theme: { prefix: '' },
|
|
272
|
-
});
|
|
273
|
-
if (!shouldProceed) {
|
|
274
|
-
throw new CommandError('Command aborted');
|
|
275
|
-
}
|
|
276
|
-
},
|
|
169
|
+
enabled: !skipConfirmation && !options.dryRun,
|
|
170
|
+
task: (context, task) => this.confirmInstallationTask(context, task),
|
|
277
171
|
rendererOptions: { persistentOutput: true },
|
|
278
172
|
},
|
|
279
173
|
{
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
task.title += ' in temporary location';
|
|
285
|
-
// Temporary packages are located in a different directory
|
|
286
|
-
// Hence we need to resolve them using the temp path
|
|
287
|
-
const { success, tempNodeModules } = await packageManager.installTemp(context.packageIdentifier.toString(), registry ? [`--registry="${registry}"`] : undefined);
|
|
288
|
-
const tempRequire = (0, node_module_1.createRequire)(tempNodeModules + '/');
|
|
289
|
-
(0, node_assert_1.default)(context.collectionName, 'Collection name should always be available');
|
|
290
|
-
const resolvedCollectionPath = tempRequire.resolve((0, node_path_1.join)(context.collectionName, 'package.json'));
|
|
291
|
-
if (!success) {
|
|
292
|
-
throw new CommandError('Unable to install package');
|
|
293
|
-
}
|
|
294
|
-
context.collectionName = (0, node_path_1.dirname)(resolvedCollectionPath);
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
const success = await packageManager.install(context.packageIdentifier.toString(), context.savePackage, registry ? [`--registry="${registry}"`] : undefined, undefined);
|
|
298
|
-
if (!success) {
|
|
299
|
-
throw new CommandError('Unable to install package');
|
|
300
|
-
}
|
|
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())}.`;
|
|
301
178
|
}
|
|
179
|
+
return false;
|
|
302
180
|
},
|
|
181
|
+
task: (context, task) => this.installPackageTask(context, task, options),
|
|
303
182
|
rendererOptions: { bottomBar: Infinity },
|
|
304
183
|
},
|
|
305
184
|
// TODO: Rework schematic execution as a task and insert here
|
|
306
|
-
]
|
|
185
|
+
], {
|
|
186
|
+
/* options */
|
|
187
|
+
});
|
|
307
188
|
try {
|
|
308
189
|
const result = await tasks.run(taskContext);
|
|
309
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
|
+
}
|
|
310
195
|
return this.executeSchematic({ ...options, collection: result.collectionName });
|
|
311
196
|
}
|
|
312
197
|
catch (e) {
|
|
313
198
|
if (e instanceof CommandError) {
|
|
199
|
+
logger.error(e.message);
|
|
314
200
|
return 1;
|
|
315
201
|
}
|
|
316
202
|
throw e;
|
|
317
203
|
}
|
|
318
204
|
}
|
|
205
|
+
determinePackageManagerTask(context, task) {
|
|
206
|
+
const { packageManager } = this.context;
|
|
207
|
+
context.usingYarn = packageManager.name === workspace_schema_1.PackageManager.Yarn;
|
|
208
|
+
task.output = `Using package manager: ${listr2_1.color.dim(packageManager.name)}`;
|
|
209
|
+
}
|
|
210
|
+
async findCompatiblePackageVersionTask(context, task, options) {
|
|
211
|
+
const { logger } = this.context;
|
|
212
|
+
const { verbose, registry } = options;
|
|
213
|
+
(0, node_assert_1.default)(context.packageIdentifier.name, 'Registry package identifiers should always have a name.');
|
|
214
|
+
// only package name provided; search for viable version
|
|
215
|
+
// plus special cases for packages that did not have peer deps setup
|
|
216
|
+
let packageMetadata;
|
|
217
|
+
try {
|
|
218
|
+
packageMetadata = await (0, package_metadata_1.fetchPackageMetadata)(context.packageIdentifier.name, logger, {
|
|
219
|
+
registry,
|
|
220
|
+
usingYarn: context.usingYarn,
|
|
221
|
+
verbose,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
(0, error_1.assertIsError)(e);
|
|
226
|
+
throw new CommandError(`Unable to load package information from registry: ${e.message}`);
|
|
227
|
+
}
|
|
228
|
+
const rejectionReasons = [];
|
|
229
|
+
// Start with the version tagged as `latest` if it exists
|
|
230
|
+
const latestManifest = packageMetadata.tags['latest'];
|
|
231
|
+
if (latestManifest) {
|
|
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);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
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;
|
|
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;
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
task.output = `Found compatible package version: ${listr2_1.color.blue(context.packageIdentifier.toString())}.`;
|
|
278
|
+
}
|
|
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
|
+
}
|
|
301
|
+
async loadPackageInfoTask(context, task, options) {
|
|
302
|
+
const { logger } = this.context;
|
|
303
|
+
const { verbose, registry } = options;
|
|
304
|
+
let manifest;
|
|
305
|
+
try {
|
|
306
|
+
manifest = await (0, package_metadata_1.fetchPackageManifest)(context.packageIdentifier.toString(), logger, {
|
|
307
|
+
registry,
|
|
308
|
+
verbose,
|
|
309
|
+
usingYarn: context.usingYarn,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
catch (e) {
|
|
313
|
+
(0, error_1.assertIsError)(e);
|
|
314
|
+
throw new CommandError(`Unable to fetch package information for '${context.packageIdentifier}': ${e.message}`);
|
|
315
|
+
}
|
|
316
|
+
context.savePackage = manifest['ng-add']?.save;
|
|
317
|
+
context.collectionName = manifest.name;
|
|
318
|
+
if (await this.getPeerDependencyConflicts(manifest)) {
|
|
319
|
+
task.output = listr2_1.color.yellow(listr2_1.figures.warning +
|
|
320
|
+
' Package has unmet peer dependencies. Adding the package may not succeed.');
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
async confirmInstallationTask(context, task) {
|
|
324
|
+
if (!(0, tty_1.isTTY)()) {
|
|
325
|
+
task.output =
|
|
326
|
+
`'--skip-confirmation' can be used to bypass installation confirmation. ` +
|
|
327
|
+
`Ensure package name is correct prior to '--skip-confirmation' option usage.`;
|
|
328
|
+
throw new CommandError('No terminal detected');
|
|
329
|
+
}
|
|
330
|
+
const { ListrInquirerPromptAdapter } = await Promise.resolve().then(() => __importStar(require('@listr2/prompt-adapter-inquirer')));
|
|
331
|
+
const { confirm } = await Promise.resolve().then(() => __importStar(require('@inquirer/prompts')));
|
|
332
|
+
const shouldProceed = await task.prompt(ListrInquirerPromptAdapter).run(confirm, {
|
|
333
|
+
message: `The package ${listr2_1.color.blue(context.packageIdentifier.toString())} will be installed and executed.\n` +
|
|
334
|
+
'Would you like to proceed?',
|
|
335
|
+
default: true,
|
|
336
|
+
theme: { prefix: '' },
|
|
337
|
+
});
|
|
338
|
+
if (!shouldProceed) {
|
|
339
|
+
throw new CommandError('Command aborted');
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
async installPackageTask(context, task, options) {
|
|
343
|
+
const { packageManager } = this.context;
|
|
344
|
+
const { registry } = options;
|
|
345
|
+
// Only show if installation will actually occur
|
|
346
|
+
task.title = 'Installing package';
|
|
347
|
+
if (context.savePackage === false) {
|
|
348
|
+
task.title += ' in temporary location';
|
|
349
|
+
// Temporary packages are located in a different directory
|
|
350
|
+
// Hence we need to resolve them using the temp path
|
|
351
|
+
const { success, tempNodeModules } = await packageManager.installTemp(context.packageIdentifier.toString(), registry ? [`--registry="${registry}"`] : undefined);
|
|
352
|
+
const tempRequire = (0, node_module_1.createRequire)(tempNodeModules + '/');
|
|
353
|
+
(0, node_assert_1.default)(context.collectionName, 'Collection name should always be available');
|
|
354
|
+
const resolvedCollectionPath = tempRequire.resolve((0, node_path_1.join)(context.collectionName, 'package.json'));
|
|
355
|
+
if (!success) {
|
|
356
|
+
throw new CommandError('Unable to install package');
|
|
357
|
+
}
|
|
358
|
+
context.collectionName = (0, node_path_1.dirname)(resolvedCollectionPath);
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
const success = await packageManager.install(context.packageIdentifier.toString(), context.savePackage, registry ? [`--registry="${registry}"`] : undefined, undefined);
|
|
362
|
+
if (!success) {
|
|
363
|
+
throw new CommandError('Unable to install package');
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
319
367
|
async isProjectVersionValid(packageIdentifier) {
|
|
320
368
|
if (!packageIdentifier.name) {
|
|
321
369
|
return false;
|
|
@@ -397,6 +445,10 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
|
|
|
397
445
|
}
|
|
398
446
|
}
|
|
399
447
|
async findProjectVersion(name) {
|
|
448
|
+
const cachedVersion = this.#projectVersionCache.get(name);
|
|
449
|
+
if (cachedVersion !== undefined) {
|
|
450
|
+
return cachedVersion;
|
|
451
|
+
}
|
|
400
452
|
const { logger, root } = this.context;
|
|
401
453
|
let installedPackage;
|
|
402
454
|
try {
|
|
@@ -406,6 +458,7 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
|
|
|
406
458
|
if (installedPackage) {
|
|
407
459
|
try {
|
|
408
460
|
const installed = await (0, package_metadata_1.fetchPackageManifest)((0, node_path_1.dirname)(installedPackage), logger);
|
|
461
|
+
this.#projectVersionCache.set(name, installed.version);
|
|
409
462
|
return installed.version;
|
|
410
463
|
}
|
|
411
464
|
catch { }
|
|
@@ -418,44 +471,50 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
|
|
|
418
471
|
if (projectManifest) {
|
|
419
472
|
const version = projectManifest.dependencies?.[name] || projectManifest.devDependencies?.[name];
|
|
420
473
|
if (version) {
|
|
474
|
+
this.#projectVersionCache.set(name, version);
|
|
421
475
|
return version;
|
|
422
476
|
}
|
|
423
477
|
}
|
|
478
|
+
this.#projectVersionCache.set(name, null);
|
|
424
479
|
return null;
|
|
425
480
|
}
|
|
426
|
-
async
|
|
427
|
-
|
|
481
|
+
async getPeerDependencyConflicts(manifest) {
|
|
482
|
+
if (!manifest.peerDependencies) {
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
const checks = Object.entries(manifest.peerDependencies).map(async ([peer, range]) => {
|
|
428
486
|
let peerIdentifier;
|
|
429
487
|
try {
|
|
430
|
-
peerIdentifier = npm_package_arg_1.default.resolve(peer,
|
|
488
|
+
peerIdentifier = npm_package_arg_1.default.resolve(peer, range);
|
|
431
489
|
}
|
|
432
490
|
catch {
|
|
433
491
|
this.context.logger.warn(`Invalid peer dependency ${peer} found in package.`);
|
|
434
|
-
|
|
492
|
+
return null;
|
|
435
493
|
}
|
|
436
|
-
if (peerIdentifier.type
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
return true;
|
|
446
|
-
}
|
|
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;
|
|
447
503
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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).`);
|
|
451
509
|
}
|
|
452
510
|
}
|
|
453
|
-
|
|
454
|
-
//
|
|
455
|
-
// Cannot accurately compare these as the tag/location may have changed since install
|
|
511
|
+
catch {
|
|
512
|
+
// Not found or invalid so ignore
|
|
456
513
|
}
|
|
457
|
-
|
|
458
|
-
|
|
514
|
+
return null;
|
|
515
|
+
});
|
|
516
|
+
const conflicts = (await Promise.all(checks)).filter((result) => !!result);
|
|
517
|
+
return conflicts.length > 0 && conflicts;
|
|
459
518
|
}
|
|
460
519
|
}
|
|
461
520
|
exports.default = AddCommandModule;
|
|
@@ -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