@angular/cli 20.2.0 → 21.0.0-next.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.
@@ -4377,6 +4377,10 @@
4377
4377
  "type": "string"
4378
4378
  },
4379
4379
  "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."
4380
+ },
4381
+ "progress": {
4382
+ "type": "boolean",
4383
+ "description": "Log progress to the console while building. Defaults to the build target's progress value."
4380
4384
  }
4381
4385
  },
4382
4386
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular/cli",
3
- "version": "20.2.0",
3
+ "version": "21.0.0-next.0",
4
4
  "description": "CLI tool for Angular",
5
5
  "main": "lib/cli/index.js",
6
6
  "bin": {
@@ -25,13 +25,13 @@
25
25
  },
26
26
  "homepage": "https://github.com/angular/angular-cli",
27
27
  "dependencies": {
28
- "@angular-devkit/architect": "0.2002.0",
29
- "@angular-devkit/core": "20.2.0",
30
- "@angular-devkit/schematics": "20.2.0",
31
- "@inquirer/prompts": "7.8.2",
28
+ "@angular-devkit/architect": "0.2100.0-next.0",
29
+ "@angular-devkit/core": "21.0.0-next.0",
30
+ "@angular-devkit/schematics": "21.0.0-next.0",
31
+ "@inquirer/prompts": "7.8.3",
32
32
  "@listr2/prompt-adapter-inquirer": "3.0.1",
33
33
  "@modelcontextprotocol/sdk": "1.17.3",
34
- "@schematics/angular": "20.2.0",
34
+ "@schematics/angular": "21.0.0-next.0",
35
35
  "@yarnpkg/lockfile": "1.1.0",
36
36
  "algoliasearch": "5.35.0",
37
37
  "ini": "5.0.0",
@@ -47,17 +47,17 @@
47
47
  "ng-update": {
48
48
  "migrations": "@schematics/angular/migrations/migration-collection.json",
49
49
  "packageGroup": {
50
- "@angular/cli": "20.2.0",
51
- "@angular/build": "20.2.0",
52
- "@angular/ssr": "20.2.0",
53
- "@angular-devkit/architect": "0.2002.0",
54
- "@angular-devkit/build-angular": "20.2.0",
55
- "@angular-devkit/build-webpack": "0.2002.0",
56
- "@angular-devkit/core": "20.2.0",
57
- "@angular-devkit/schematics": "20.2.0"
50
+ "@angular/cli": "21.0.0-next.0",
51
+ "@angular/build": "21.0.0-next.0",
52
+ "@angular/ssr": "21.0.0-next.0",
53
+ "@angular-devkit/architect": "0.2100.0-next.0",
54
+ "@angular-devkit/build-angular": "21.0.0-next.0",
55
+ "@angular-devkit/build-webpack": "0.2100.0-next.0",
56
+ "@angular-devkit/core": "21.0.0-next.0",
57
+ "@angular-devkit/schematics": "21.0.0-next.0"
58
58
  }
59
59
  },
60
- "packageManager": "pnpm@10.14.0",
60
+ "packageManager": "pnpm@10.15.0",
61
61
  "engines": {
62
62
  "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
63
63
  "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
@@ -23,6 +23,11 @@ export default class AddCommandModule extends SchematicsCommandModule implements
23
23
  private rootRequire;
24
24
  builder(argv: Argv): Promise<Argv<AddCommandArgs>>;
25
25
  run(options: Options<AddCommandArgs> & OtherOptions): Promise<number | void>;
26
+ private determinePackageManagerTask;
27
+ private findCompatiblePackageVersionTask;
28
+ private loadPackageInfoTask;
29
+ private confirmInstallationTask;
30
+ private installPackageTask;
26
31
  private isProjectVersionValid;
27
32
  private getCollectionName;
28
33
  private isPackageInstalled;
@@ -116,10 +116,9 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
116
116
  }
117
117
  return localYargs;
118
118
  }
119
- // eslint-disable-next-line max-lines-per-function
120
119
  async run(options) {
121
- const { logger, packageManager } = this.context;
122
- const { verbose, registry, collection, skipConfirmation } = options;
120
+ const { logger } = this.context;
121
+ const { collection, skipConfirmation } = options;
123
122
  let packageIdentifier;
124
123
  try {
125
124
  packageIdentifier = (0, npm_package_arg_1.default)(collection);
@@ -147,163 +146,34 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
147
146
  const tasks = new listr2_1.Listr([
148
147
  {
149
148
  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
- },
149
+ task: (context, task) => this.determinePackageManagerTask(context, task),
154
150
  rendererOptions: { persistentOutput: true },
155
151
  },
156
152
  {
157
153
  title: 'Searching for compatible package version',
158
154
  enabled: packageIdentifier.type === 'range' && packageIdentifier.rawSpec === '*',
159
- async task(context, task) {
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
- },
155
+ task: (context, task) => this.findCompatiblePackageVersionTask(context, task, options),
229
156
  rendererOptions: { persistentOutput: true },
230
157
  },
231
158
  {
232
159
  title: 'Loading package information from registry',
233
- async task(context, task) {
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
- },
160
+ task: (context, task) => this.loadPackageInfoTask(context, task, options),
253
161
  rendererOptions: { persistentOutput: true },
254
162
  },
255
163
  {
256
164
  title: 'Confirming installation',
257
165
  enabled: !skipConfirmation,
258
- async task(context, task) {
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
- },
166
+ task: (context, task) => this.confirmInstallationTask(context, task),
277
167
  rendererOptions: { persistentOutput: true },
278
168
  },
279
169
  {
280
- async task(context, task) {
281
- // Only show if installation will actually occur
282
- task.title = 'Installing package';
283
- if (context.savePackage === false) {
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
- }
301
- }
302
- },
170
+ task: (context, task) => this.installPackageTask(context, task, options),
303
171
  rendererOptions: { bottomBar: Infinity },
304
172
  },
305
173
  // TODO: Rework schematic execution as a task and insert here
306
- ]);
174
+ ], {
175
+ /* options */
176
+ });
307
177
  try {
308
178
  const result = await tasks.run(taskContext);
309
179
  (0, node_assert_1.default)(result.collectionName, 'Collection name should always be available');
@@ -311,11 +181,155 @@ class AddCommandModule extends schematics_command_module_1.SchematicsCommandModu
311
181
  }
312
182
  catch (e) {
313
183
  if (e instanceof CommandError) {
184
+ logger.error(e.message);
314
185
  return 1;
315
186
  }
316
187
  throw e;
317
188
  }
318
189
  }
190
+ determinePackageManagerTask(context, task) {
191
+ const { packageManager } = this.context;
192
+ context.usingYarn = packageManager.name === workspace_schema_1.PackageManager.Yarn;
193
+ task.output = `Using package manager: ${listr2_1.color.dim(packageManager.name)}`;
194
+ }
195
+ async findCompatiblePackageVersionTask(context, task, options) {
196
+ const { logger } = this.context;
197
+ const { verbose, registry } = options;
198
+ (0, node_assert_1.default)(context.packageIdentifier.name, 'Registry package identifiers should always have a name.');
199
+ // only package name provided; search for viable version
200
+ // plus special cases for packages that did not have peer deps setup
201
+ let packageMetadata;
202
+ try {
203
+ packageMetadata = await (0, package_metadata_1.fetchPackageMetadata)(context.packageIdentifier.name, logger, {
204
+ registry,
205
+ usingYarn: context.usingYarn,
206
+ verbose,
207
+ });
208
+ }
209
+ catch (e) {
210
+ (0, error_1.assertIsError)(e);
211
+ throw new CommandError(`Unable to load package information from registry: ${e.message}`);
212
+ }
213
+ // Start with the version tagged as `latest` if it exists
214
+ const latestManifest = packageMetadata.tags['latest'];
215
+ if (latestManifest) {
216
+ context.packageIdentifier = npm_package_arg_1.default.resolve(latestManifest.name, latestManifest.version);
217
+ }
218
+ // Adjust the version based on name and peer dependencies
219
+ if (latestManifest?.peerDependencies &&
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.";
258
+ }
259
+ else {
260
+ task.output = `Found compatible package version: ${listr2_1.color.blue(context.packageIdentifier.toString())}.`;
261
+ }
262
+ }
263
+ else {
264
+ task.output = `Found compatible package version: ${listr2_1.color.blue(context.packageIdentifier.toString())}.`;
265
+ }
266
+ }
267
+ async loadPackageInfoTask(context, task, options) {
268
+ const { logger } = this.context;
269
+ const { verbose, registry } = options;
270
+ let manifest;
271
+ try {
272
+ manifest = await (0, package_metadata_1.fetchPackageManifest)(context.packageIdentifier.toString(), logger, {
273
+ registry,
274
+ verbose,
275
+ usingYarn: context.usingYarn,
276
+ });
277
+ }
278
+ catch (e) {
279
+ (0, error_1.assertIsError)(e);
280
+ throw new CommandError(`Unable to fetch package information for '${context.packageIdentifier}': ${e.message}`);
281
+ }
282
+ context.savePackage = manifest['ng-add']?.save;
283
+ context.collectionName = manifest.name;
284
+ if (await context.hasMismatchedPeer(manifest)) {
285
+ task.output = listr2_1.color.yellow(listr2_1.figures.warning +
286
+ ' Package has unmet peer dependencies. Adding the package may not succeed.');
287
+ }
288
+ }
289
+ async confirmInstallationTask(context, task) {
290
+ if (!(0, tty_1.isTTY)()) {
291
+ task.output =
292
+ `'--skip-confirmation' can be used to bypass installation confirmation. ` +
293
+ `Ensure package name is correct prior to '--skip-confirmation' option usage.`;
294
+ throw new CommandError('No terminal detected');
295
+ }
296
+ const { ListrInquirerPromptAdapter } = await Promise.resolve().then(() => __importStar(require('@listr2/prompt-adapter-inquirer')));
297
+ const { confirm } = await Promise.resolve().then(() => __importStar(require('@inquirer/prompts')));
298
+ const shouldProceed = await task.prompt(ListrInquirerPromptAdapter).run(confirm, {
299
+ message: `The package ${listr2_1.color.blue(context.packageIdentifier.toString())} will be installed and executed.\n` +
300
+ 'Would you like to proceed?',
301
+ default: true,
302
+ theme: { prefix: '' },
303
+ });
304
+ if (!shouldProceed) {
305
+ throw new CommandError('Command aborted');
306
+ }
307
+ }
308
+ async installPackageTask(context, task, options) {
309
+ const { packageManager } = this.context;
310
+ const { registry } = options;
311
+ // Only show if installation will actually occur
312
+ task.title = 'Installing package';
313
+ if (context.savePackage === false) {
314
+ task.title += ' in temporary location';
315
+ // Temporary packages are located in a different directory
316
+ // Hence we need to resolve them using the temp path
317
+ const { success, tempNodeModules } = await packageManager.installTemp(context.packageIdentifier.toString(), registry ? [`--registry="${registry}"`] : undefined);
318
+ const tempRequire = (0, node_module_1.createRequire)(tempNodeModules + '/');
319
+ (0, node_assert_1.default)(context.collectionName, 'Collection name should always be available');
320
+ const resolvedCollectionPath = tempRequire.resolve((0, node_path_1.join)(context.collectionName, 'package.json'));
321
+ if (!success) {
322
+ throw new CommandError('Unable to install package');
323
+ }
324
+ context.collectionName = (0, node_path_1.dirname)(resolvedCollectionPath);
325
+ }
326
+ else {
327
+ const success = await packageManager.install(context.packageIdentifier.toString(), context.savePackage, registry ? [`--registry="${registry}"`] : undefined, undefined);
328
+ if (!success) {
329
+ throw new CommandError('Unable to install package');
330
+ }
331
+ }
332
+ }
319
333
  async isProjectVersionValid(packageIdentifier) {
320
334
  if (!packageIdentifier.name) {
321
335
  return false;
@@ -22,4 +22,4 @@ class Version {
22
22
  this.patch = patch;
23
23
  }
24
24
  }
25
- exports.VERSION = new Version('20.2.0');
25
+ exports.VERSION = new Version('21.0.0-next.0');