@angular/cli 21.1.0-next.1 → 21.1.0-next.3
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/code-examples.db +0 -0
- package/lib/config/schema.json +1 -2
- package/package.json +17 -17
- package/src/commands/add/cli.js +129 -87
- package/src/commands/add/cli.js.map +1 -1
- package/src/commands/mcp/cli.js +4 -1
- package/src/commands/mcp/cli.js.map +1 -1
- package/src/commands/mcp/{dev-server.d.ts → devserver.d.ts} +4 -4
- package/src/commands/mcp/{dev-server.js → devserver.js} +14 -14
- package/src/commands/mcp/devserver.js.map +1 -0
- package/src/commands/mcp/mcp-server.d.ts +30 -0
- package/src/commands/mcp/mcp-server.js +22 -7
- package/src/commands/mcp/mcp-server.js.map +1 -1
- package/src/commands/mcp/resources/ai-tutor.md +16 -15
- package/src/commands/mcp/tools/best-practices.js.map +1 -1
- package/src/commands/mcp/tools/build.js +2 -2
- package/src/commands/mcp/tools/build.js.map +1 -1
- package/src/commands/mcp/tools/devserver/{start-devserver.d.ts → devserver-start.d.ts} +6 -7
- package/src/commands/mcp/tools/devserver/{start-devserver.js → devserver-start.js} +26 -27
- package/src/commands/mcp/tools/devserver/devserver-start.js.map +1 -0
- package/src/commands/mcp/tools/devserver/{stop-devserver.d.ts → devserver-stop.d.ts} +6 -6
- package/src/commands/mcp/tools/devserver/{stop-devserver.js → devserver-stop.js} +13 -13
- package/src/commands/mcp/tools/devserver/{stop-devserver.js.map → devserver-stop.js.map} +1 -1
- package/src/commands/mcp/tools/devserver/{wait-for-devserver-build.d.ts → devserver-wait-for-build.d.ts} +6 -6
- package/src/commands/mcp/tools/devserver/{wait-for-devserver-build.js → devserver-wait-for-build.js} +16 -16
- package/src/commands/mcp/tools/devserver/{wait-for-devserver-build.js.map → devserver-wait-for-build.js.map} +1 -1
- package/src/commands/mcp/tools/modernize.js +1 -1
- package/src/commands/mcp/tools/modernize.js.map +1 -1
- package/src/commands/mcp/tools/projects.js +47 -8
- package/src/commands/mcp/tools/projects.js.map +1 -1
- package/src/commands/mcp/tools/tool-registry.d.ts +2 -2
- package/src/commands/mcp/tools/tool-registry.js.map +1 -1
- package/src/commands/update/cli.d.ts +0 -24
- package/src/commands/update/cli.js +39 -447
- package/src/commands/update/cli.js.map +1 -1
- package/src/commands/update/schematic/index.js +0 -12
- package/src/commands/update/schematic/index.js.map +1 -1
- package/src/commands/update/utilities/cli-version.d.ts +48 -0
- package/src/commands/update/utilities/cli-version.js +196 -0
- package/src/commands/update/utilities/cli-version.js.map +1 -0
- package/src/commands/update/utilities/constants.d.ts +12 -0
- package/src/commands/update/utilities/constants.js +16 -0
- package/src/commands/update/utilities/constants.js.map +1 -0
- package/src/commands/update/utilities/git.d.ts +36 -0
- package/src/commands/update/utilities/git.js +148 -0
- package/src/commands/update/utilities/git.js.map +1 -0
- package/src/commands/update/utilities/migration.d.ts +32 -0
- package/src/commands/update/utilities/migration.js +257 -0
- package/src/commands/update/utilities/migration.js.map +1 -0
- package/src/package-managers/error.d.ts +12 -0
- package/src/package-managers/host.js +7 -3
- package/src/package-managers/host.js.map +1 -1
- package/src/package-managers/package-manager-descriptor.d.ts +28 -4
- package/src/package-managers/package-manager-descriptor.js +24 -4
- package/src/package-managers/package-manager-descriptor.js.map +1 -1
- package/src/package-managers/package-manager.js +71 -8
- package/src/package-managers/package-manager.js.map +1 -1
- package/src/package-managers/parsers.d.ts +69 -4
- package/src/package-managers/parsers.js +231 -32
- package/src/package-managers/parsers.js.map +1 -1
- package/src/utilities/version.js +1 -1
- package/src/commands/mcp/dev-server.js.map +0 -1
- package/src/commands/mcp/tools/devserver/start-devserver.js.map +0 -1
|
@@ -43,32 +43,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
43
43
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
44
44
|
};
|
|
45
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
-
const schematics_1 = require("@angular-devkit/schematics");
|
|
47
46
|
const tools_1 = require("@angular-devkit/schematics/tools");
|
|
48
47
|
const listr2_1 = require("listr2");
|
|
49
|
-
const node_child_process_1 = require("node:child_process");
|
|
50
48
|
const node_fs_1 = require("node:fs");
|
|
51
49
|
const node_module_1 = require("node:module");
|
|
52
50
|
const path = __importStar(require("node:path"));
|
|
53
|
-
const node_path_1 = require("node:path");
|
|
54
51
|
const npm_package_arg_1 = __importDefault(require("npm-package-arg"));
|
|
55
|
-
const semver = __importStar(require("semver"));
|
|
56
|
-
const workspace_schema_1 = require("../../../lib/config/workspace-schema");
|
|
57
52
|
const command_module_1 = require("../../command-builder/command-module");
|
|
58
53
|
const schematic_engine_host_1 = require("../../command-builder/utilities/schematic-engine-host");
|
|
59
|
-
const
|
|
54
|
+
const package_managers_1 = require("../../package-managers");
|
|
60
55
|
const color_1 = require("../../utilities/color");
|
|
61
56
|
const environment_options_1 = require("../../utilities/environment-options");
|
|
62
57
|
const error_1 = require("../../utilities/error");
|
|
63
|
-
const log_file_1 = require("../../utilities/log-file");
|
|
64
|
-
const package_metadata_1 = require("../../utilities/package-metadata");
|
|
65
58
|
const package_tree_1 = require("../../utilities/package-tree");
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const
|
|
59
|
+
const cli_version_1 = require("./utilities/cli-version");
|
|
60
|
+
const constants_1 = require("./utilities/constants");
|
|
61
|
+
const git_1 = require("./utilities/git");
|
|
62
|
+
const migration_1 = require("./utilities/migration");
|
|
69
63
|
class CommandError extends Error {
|
|
70
64
|
}
|
|
71
|
-
const ANGULAR_PACKAGES_REGEXP = /^@(?:angular|nguniversal)\//;
|
|
72
65
|
const UPDATE_SCHEMATIC_COLLECTION = path.join(__dirname, 'schematic/collection.json');
|
|
73
66
|
class UpdateCommandModule extends command_module_1.CommandModule {
|
|
74
67
|
scope = command_module_1.CommandScope.In;
|
|
@@ -76,7 +69,7 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
76
69
|
resolvePaths = [__dirname, this.context.root];
|
|
77
70
|
command = 'update [packages..]';
|
|
78
71
|
describe = 'Updates your workspace and its dependencies. See https://update.angular.dev/.';
|
|
79
|
-
longDescriptionPath =
|
|
72
|
+
longDescriptionPath = path.join(__dirname, 'long-description.md');
|
|
80
73
|
builder(localYargs) {
|
|
81
74
|
return localYargs
|
|
82
75
|
.positional('packages', {
|
|
@@ -143,7 +136,7 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
143
136
|
.check(({ packages, 'allow-dirty': allowDirty, 'migrate-only': migrateOnly }) => {
|
|
144
137
|
const { logger } = this.context;
|
|
145
138
|
// This allows the user to easily reset any changes from the update.
|
|
146
|
-
if (packages?.length && !
|
|
139
|
+
if (packages?.length && !(0, git_1.checkCleanGit)(this.context.root)) {
|
|
147
140
|
if (allowDirty) {
|
|
148
141
|
logger.warn('Repository is not clean. Update changes will be mixed with pre-existing changes.');
|
|
149
142
|
}
|
|
@@ -161,15 +154,21 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
161
154
|
.strict();
|
|
162
155
|
}
|
|
163
156
|
async run(options) {
|
|
164
|
-
const { logger
|
|
157
|
+
const { logger } = this.context;
|
|
158
|
+
// Instantiate the package manager
|
|
159
|
+
const packageManager = await (0, package_managers_1.createPackageManager)({
|
|
160
|
+
cwd: this.context.root,
|
|
161
|
+
logger,
|
|
162
|
+
configuredPackageManager: this.context.packageManager.name,
|
|
163
|
+
});
|
|
165
164
|
// Check if the current installed CLI version is older than the latest compatible version.
|
|
166
165
|
// Skip when running `ng update` without a package name as this will not trigger an actual update.
|
|
167
166
|
if (!environment_options_1.disableVersionCheck && options.packages?.length) {
|
|
168
|
-
const cliVersionToInstall = await
|
|
167
|
+
const cliVersionToInstall = await (0, cli_version_1.checkCLIVersion)(options.packages, logger, packageManager, options.next);
|
|
169
168
|
if (cliVersionToInstall) {
|
|
170
169
|
logger.warn('The installed Angular CLI version is outdated.\n' +
|
|
171
170
|
`Installing a temporary Angular CLI versioned ${cliVersionToInstall} to perform the update.`);
|
|
172
|
-
return
|
|
171
|
+
return (0, cli_version_1.runTempBinary)(`@angular/cli@${cliVersionToInstall}`, packageManager, process.argv.slice(2));
|
|
173
172
|
}
|
|
174
173
|
}
|
|
175
174
|
const packages = [];
|
|
@@ -208,7 +207,7 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
208
207
|
logger.info(`Found ${rootDependencies.size} dependencies.`);
|
|
209
208
|
const workflow = new tools_1.NodeWorkflow(this.context.root, {
|
|
210
209
|
packageManager: packageManager.name,
|
|
211
|
-
packageManagerForce:
|
|
210
|
+
packageManagerForce: await (0, cli_version_1.shouldForcePackageManager)(packageManager, logger, options.verbose),
|
|
212
211
|
// __dirname -> favor @schematics/update from this package
|
|
213
212
|
// Otherwise, use packages from the active workspace (migrations)
|
|
214
213
|
resolvePaths: this.resolvePaths,
|
|
@@ -217,7 +216,7 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
217
216
|
});
|
|
218
217
|
if (packages.length === 0) {
|
|
219
218
|
// Show status
|
|
220
|
-
const { success } = await
|
|
219
|
+
const { success } = await (0, migration_1.executeSchematic)(workflow, logger, UPDATE_SCHEMATIC_COLLECTION, 'update', {
|
|
221
220
|
force: options.force,
|
|
222
221
|
next: options.next,
|
|
223
222
|
verbose: options.verbose,
|
|
@@ -228,136 +227,7 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
228
227
|
}
|
|
229
228
|
return options.migrateOnly
|
|
230
229
|
? this.migrateOnly(workflow, (options.packages ?? [])[0], rootDependencies, options)
|
|
231
|
-
: this.updatePackagesAndMigrate(workflow, rootDependencies, options, packages);
|
|
232
|
-
}
|
|
233
|
-
async executeSchematic(workflow, collection, schematic, options = {}) {
|
|
234
|
-
const { logger } = this.context;
|
|
235
|
-
const workflowSubscription = (0, schematic_workflow_1.subscribeToWorkflow)(workflow, logger);
|
|
236
|
-
// TODO: Allow passing a schematic instance directly
|
|
237
|
-
try {
|
|
238
|
-
await workflow
|
|
239
|
-
.execute({
|
|
240
|
-
collection,
|
|
241
|
-
schematic,
|
|
242
|
-
options,
|
|
243
|
-
logger,
|
|
244
|
-
})
|
|
245
|
-
.toPromise();
|
|
246
|
-
return { success: !workflowSubscription.error, files: workflowSubscription.files };
|
|
247
|
-
}
|
|
248
|
-
catch (e) {
|
|
249
|
-
if (e instanceof schematics_1.UnsuccessfulWorkflowExecution) {
|
|
250
|
-
logger.error(`${color_1.figures.cross} Migration failed. See above for further details.\n`);
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
(0, error_1.assertIsError)(e);
|
|
254
|
-
const logPath = (0, log_file_1.writeErrorToLogFile)(e);
|
|
255
|
-
logger.fatal(`${color_1.figures.cross} Migration failed: ${e.message}\n` +
|
|
256
|
-
` See "${logPath}" for further details.\n`);
|
|
257
|
-
}
|
|
258
|
-
return { success: false, files: workflowSubscription.files };
|
|
259
|
-
}
|
|
260
|
-
finally {
|
|
261
|
-
workflowSubscription.unsubscribe();
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
/**
|
|
265
|
-
* @return Whether or not the migration was performed successfully.
|
|
266
|
-
*/
|
|
267
|
-
async executeMigration(workflow, packageName, collectionPath, migrationName, commit) {
|
|
268
|
-
const { logger } = this.context;
|
|
269
|
-
const collection = workflow.engine.createCollection(collectionPath);
|
|
270
|
-
const name = collection.listSchematicNames().find((name) => name === migrationName);
|
|
271
|
-
if (!name) {
|
|
272
|
-
logger.error(`Cannot find migration '${migrationName}' in '${packageName}'.`);
|
|
273
|
-
return 1;
|
|
274
|
-
}
|
|
275
|
-
logger.info(color_1.colors.cyan(`** Executing '${migrationName}' of package '${packageName}' **\n`));
|
|
276
|
-
const schematic = workflow.engine.createSchematic(name, collection);
|
|
277
|
-
return this.executePackageMigrations(workflow, [schematic.description], packageName, commit);
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* @return Whether or not the migrations were performed successfully.
|
|
281
|
-
*/
|
|
282
|
-
async executeMigrations(workflow, packageName, collectionPath, from, to, commit) {
|
|
283
|
-
const collection = workflow.engine.createCollection(collectionPath);
|
|
284
|
-
const migrationRange = new semver.Range('>' + (semver.prerelease(from) ? from.split('-')[0] + '-0' : from) + ' <=' + to.split('-')[0]);
|
|
285
|
-
const requiredMigrations = [];
|
|
286
|
-
const optionalMigrations = [];
|
|
287
|
-
for (const name of collection.listSchematicNames()) {
|
|
288
|
-
const schematic = workflow.engine.createSchematic(name, collection);
|
|
289
|
-
const description = schematic.description;
|
|
290
|
-
description.version = coerceVersionNumber(description.version);
|
|
291
|
-
if (!description.version) {
|
|
292
|
-
continue;
|
|
293
|
-
}
|
|
294
|
-
if (semver.satisfies(description.version, migrationRange, { includePrerelease: true })) {
|
|
295
|
-
(description.optional ? optionalMigrations : requiredMigrations).push(description);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
if (requiredMigrations.length === 0 && optionalMigrations.length === 0) {
|
|
299
|
-
return 0;
|
|
300
|
-
}
|
|
301
|
-
// Required migrations
|
|
302
|
-
if (requiredMigrations.length) {
|
|
303
|
-
this.context.logger.info(color_1.colors.cyan(`** Executing migrations of package '${packageName}' **\n`));
|
|
304
|
-
requiredMigrations.sort((a, b) => semver.compare(a.version, b.version) || a.name.localeCompare(b.name));
|
|
305
|
-
const result = await this.executePackageMigrations(workflow, requiredMigrations, packageName, commit);
|
|
306
|
-
if (result === 1) {
|
|
307
|
-
return 1;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
// Optional migrations
|
|
311
|
-
if (optionalMigrations.length) {
|
|
312
|
-
this.context.logger.info(color_1.colors.magenta(`** Optional migrations of package '${packageName}' **\n`));
|
|
313
|
-
optionalMigrations.sort((a, b) => semver.compare(a.version, b.version) || a.name.localeCompare(b.name));
|
|
314
|
-
const migrationsToRun = await this.getOptionalMigrationsToRun(optionalMigrations, packageName);
|
|
315
|
-
if (migrationsToRun?.length) {
|
|
316
|
-
return this.executePackageMigrations(workflow, migrationsToRun, packageName, commit);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
return 0;
|
|
320
|
-
}
|
|
321
|
-
async executePackageMigrations(workflow, migrations, packageName, commit = false) {
|
|
322
|
-
const { logger } = this.context;
|
|
323
|
-
for (const migration of migrations) {
|
|
324
|
-
const { title, description } = getMigrationTitleAndDescription(migration);
|
|
325
|
-
logger.info(color_1.colors.cyan(color_1.figures.pointer) + ' ' + color_1.colors.bold(title));
|
|
326
|
-
if (description) {
|
|
327
|
-
logger.info(' ' + description);
|
|
328
|
-
}
|
|
329
|
-
const { success, files } = await this.executeSchematic(workflow, migration.collection.name, migration.name);
|
|
330
|
-
if (!success) {
|
|
331
|
-
return 1;
|
|
332
|
-
}
|
|
333
|
-
let modifiedFilesText;
|
|
334
|
-
switch (files.size) {
|
|
335
|
-
case 0:
|
|
336
|
-
modifiedFilesText = 'No changes made';
|
|
337
|
-
break;
|
|
338
|
-
case 1:
|
|
339
|
-
modifiedFilesText = '1 file modified';
|
|
340
|
-
break;
|
|
341
|
-
default:
|
|
342
|
-
modifiedFilesText = `${files.size} files modified`;
|
|
343
|
-
break;
|
|
344
|
-
}
|
|
345
|
-
logger.info(` Migration completed (${modifiedFilesText}).`);
|
|
346
|
-
// Commit migration
|
|
347
|
-
if (commit) {
|
|
348
|
-
const commitPrefix = `${packageName} migration - ${migration.name}`;
|
|
349
|
-
const commitMessage = migration.description
|
|
350
|
-
? `${commitPrefix}\n\n${migration.description}`
|
|
351
|
-
: commitPrefix;
|
|
352
|
-
const committed = this.commit(commitMessage);
|
|
353
|
-
if (!committed) {
|
|
354
|
-
// Failed to commit, something went wrong. Abort the update.
|
|
355
|
-
return 1;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
logger.info(''); // Extra trailing newline.
|
|
359
|
-
}
|
|
360
|
-
return 0;
|
|
230
|
+
: this.updatePackagesAndMigrate(workflow, rootDependencies, options, packages, packageManager);
|
|
361
231
|
}
|
|
362
232
|
async migrateOnly(workflow, packageName, rootDependencies, options) {
|
|
363
233
|
const { logger } = this.context;
|
|
@@ -426,17 +296,17 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
426
296
|
}
|
|
427
297
|
}
|
|
428
298
|
if (options.name) {
|
|
429
|
-
return
|
|
299
|
+
return (0, migration_1.executeMigration)(workflow, logger, packageName, migrations, options.name, options.createCommits);
|
|
430
300
|
}
|
|
431
|
-
const from = coerceVersionNumber(options.from);
|
|
301
|
+
const from = (0, cli_version_1.coerceVersionNumber)(options.from);
|
|
432
302
|
if (!from) {
|
|
433
303
|
logger.error(`"from" value [${options.from}] is not a valid version.`);
|
|
434
304
|
return 1;
|
|
435
305
|
}
|
|
436
|
-
return
|
|
306
|
+
return (0, migration_1.executeMigrations)(workflow, logger, packageName, migrations, from, options.to || packageNode.version, options.createCommits);
|
|
437
307
|
}
|
|
438
308
|
// eslint-disable-next-line max-lines-per-function
|
|
439
|
-
async updatePackagesAndMigrate(workflow, rootDependencies, options, packages) {
|
|
309
|
+
async updatePackagesAndMigrate(workflow, rootDependencies, options, packages, packageManager) {
|
|
440
310
|
const { logger } = this.context;
|
|
441
311
|
const logVerbose = (message) => {
|
|
442
312
|
if (options.verbose) {
|
|
@@ -446,6 +316,7 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
446
316
|
const requests = [];
|
|
447
317
|
// Validate packages actually are part of the workspace
|
|
448
318
|
for (const pkg of packages) {
|
|
319
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
449
320
|
const node = rootDependencies.get(pkg.name);
|
|
450
321
|
if (!node?.package) {
|
|
451
322
|
logger.error(`Package '${pkg.name}' is not a dependency.`);
|
|
@@ -465,56 +336,15 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
465
336
|
const packagesToUpdate = [];
|
|
466
337
|
for (const { identifier: requestIdentifier, node } of requests) {
|
|
467
338
|
const packageName = requestIdentifier.name;
|
|
468
|
-
let
|
|
339
|
+
let manifest = null;
|
|
469
340
|
try {
|
|
470
|
-
|
|
471
|
-
// does not result in additional network traffic
|
|
472
|
-
metadata = await (0, package_metadata_1.fetchPackageMetadata)(packageName, logger, {
|
|
473
|
-
verbose: options.verbose,
|
|
474
|
-
});
|
|
341
|
+
manifest = await packageManager.getManifest(requestIdentifier);
|
|
475
342
|
}
|
|
476
343
|
catch (e) {
|
|
477
344
|
(0, error_1.assertIsError)(e);
|
|
478
|
-
logger.error(`Error fetching
|
|
345
|
+
logger.error(`Error fetching manifest for '${packageName}': ` + e.message);
|
|
479
346
|
return 1;
|
|
480
347
|
}
|
|
481
|
-
// Try to find a package version based on the user requested package specifier
|
|
482
|
-
// registry specifier types are either version, range, or tag
|
|
483
|
-
let manifest;
|
|
484
|
-
switch (requestIdentifier.type) {
|
|
485
|
-
case 'tag':
|
|
486
|
-
manifest = metadata.tags[requestIdentifier.fetchSpec];
|
|
487
|
-
// If not found and next option was used and user did not provide a specifier, try latest.
|
|
488
|
-
// Package may not have a next tag.
|
|
489
|
-
if (!manifest &&
|
|
490
|
-
requestIdentifier.fetchSpec === 'next' &&
|
|
491
|
-
requestIdentifier.rawSpec === '*') {
|
|
492
|
-
manifest = metadata.tags['latest'];
|
|
493
|
-
}
|
|
494
|
-
break;
|
|
495
|
-
case 'version':
|
|
496
|
-
manifest = metadata.versions[requestIdentifier.fetchSpec];
|
|
497
|
-
break;
|
|
498
|
-
case 'range':
|
|
499
|
-
for (const potentialManifest of Object.values(metadata.versions)) {
|
|
500
|
-
// Ignore deprecated package versions
|
|
501
|
-
if (potentialManifest.deprecated) {
|
|
502
|
-
continue;
|
|
503
|
-
}
|
|
504
|
-
// Only consider versions that are within the range
|
|
505
|
-
if (!semver.satisfies(potentialManifest.version, requestIdentifier.fetchSpec, {
|
|
506
|
-
loose: true,
|
|
507
|
-
})) {
|
|
508
|
-
continue;
|
|
509
|
-
}
|
|
510
|
-
// Update the used manifest if current potential is newer than existing or there is not one yet
|
|
511
|
-
if (!manifest ||
|
|
512
|
-
semver.gt(potentialManifest.version, manifest.version, { loose: true })) {
|
|
513
|
-
manifest = potentialManifest;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
break;
|
|
517
|
-
}
|
|
518
348
|
if (!manifest) {
|
|
519
349
|
logger.error(`Package specified by '${requestIdentifier.raw}' does not exist within the registry.`);
|
|
520
350
|
return 1;
|
|
@@ -523,7 +353,7 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
523
353
|
logger.info(`Package '${packageName}' is already up to date.`);
|
|
524
354
|
continue;
|
|
525
355
|
}
|
|
526
|
-
if (node.package && ANGULAR_PACKAGES_REGEXP.test(node.package.name)) {
|
|
356
|
+
if (node.package && constants_1.ANGULAR_PACKAGES_REGEXP.test(node.package.name)) {
|
|
527
357
|
const { name, version } = node.package;
|
|
528
358
|
const toBeInstalledMajorVersion = +manifest.version.split('.')[0];
|
|
529
359
|
const currentMajorVersion = +version.split('.')[0];
|
|
@@ -550,7 +380,7 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
550
380
|
if (packagesToUpdate.length === 0) {
|
|
551
381
|
return 0;
|
|
552
382
|
}
|
|
553
|
-
const { success } = await
|
|
383
|
+
const { success } = await (0, migration_1.executeSchematic)(workflow, logger, UPDATE_SCHEMATIC_COLLECTION, 'update', {
|
|
554
384
|
verbose: options.verbose,
|
|
555
385
|
force: options.force,
|
|
556
386
|
next: options.next,
|
|
@@ -558,8 +388,8 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
558
388
|
packages: packagesToUpdate,
|
|
559
389
|
});
|
|
560
390
|
if (success) {
|
|
561
|
-
const { root: commandRoot
|
|
562
|
-
const
|
|
391
|
+
const { root: commandRoot } = this.context;
|
|
392
|
+
const force = await (0, cli_version_1.shouldForcePackageManager)(packageManager, logger, options.verbose);
|
|
563
393
|
const tasks = new listr2_1.Listr([
|
|
564
394
|
{
|
|
565
395
|
title: 'Cleaning node modules directory',
|
|
@@ -582,8 +412,12 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
582
412
|
{
|
|
583
413
|
title: 'Installing packages',
|
|
584
414
|
async task() {
|
|
585
|
-
|
|
586
|
-
|
|
415
|
+
try {
|
|
416
|
+
await packageManager.install({
|
|
417
|
+
force,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
catch (e) {
|
|
587
421
|
throw new CommandError('Unable to install packages');
|
|
588
422
|
}
|
|
589
423
|
},
|
|
@@ -600,7 +434,7 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
600
434
|
}
|
|
601
435
|
}
|
|
602
436
|
if (success && options.createCommits) {
|
|
603
|
-
if (!
|
|
437
|
+
if (!(0, migration_1.commitChanges)(logger, `Angular CLI update for packages - ${packagesToUpdate.join(', ')}`)) {
|
|
604
438
|
return 1;
|
|
605
439
|
}
|
|
606
440
|
}
|
|
@@ -667,7 +501,7 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
667
501
|
return 1;
|
|
668
502
|
}
|
|
669
503
|
}
|
|
670
|
-
const result = await
|
|
504
|
+
const result = await (0, migration_1.executeMigrations)(workflow, logger, migration.package, migrations, migration.from, migration.to, options.createCommits);
|
|
671
505
|
// A non-zero value is a failure for the package's migrations
|
|
672
506
|
if (result !== 0) {
|
|
673
507
|
return result;
|
|
@@ -676,248 +510,6 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
676
510
|
}
|
|
677
511
|
return success ? 0 : 1;
|
|
678
512
|
}
|
|
679
|
-
/**
|
|
680
|
-
* @return Whether or not the commit was successful.
|
|
681
|
-
*/
|
|
682
|
-
commit(message) {
|
|
683
|
-
const { logger } = this.context;
|
|
684
|
-
// Check if a commit is needed.
|
|
685
|
-
let commitNeeded;
|
|
686
|
-
try {
|
|
687
|
-
commitNeeded = hasChangesToCommit();
|
|
688
|
-
}
|
|
689
|
-
catch (err) {
|
|
690
|
-
logger.error(` Failed to read Git tree:\n${err.stderr}`);
|
|
691
|
-
return false;
|
|
692
|
-
}
|
|
693
|
-
if (!commitNeeded) {
|
|
694
|
-
logger.info(' No changes to commit after migration.');
|
|
695
|
-
return true;
|
|
696
|
-
}
|
|
697
|
-
// Commit changes and abort on error.
|
|
698
|
-
try {
|
|
699
|
-
createCommit(message);
|
|
700
|
-
}
|
|
701
|
-
catch (err) {
|
|
702
|
-
logger.error(`Failed to commit update (${message}):\n${err.stderr}`);
|
|
703
|
-
return false;
|
|
704
|
-
}
|
|
705
|
-
// Notify user of the commit.
|
|
706
|
-
const hash = findCurrentGitSha();
|
|
707
|
-
const shortMessage = message.split('\n')[0];
|
|
708
|
-
if (hash) {
|
|
709
|
-
logger.info(` Committed migration step (${getShortHash(hash)}): ${shortMessage}.`);
|
|
710
|
-
}
|
|
711
|
-
else {
|
|
712
|
-
// Commit was successful, but reading the hash was not. Something weird happened,
|
|
713
|
-
// but nothing that would stop the update. Just log the weirdness and continue.
|
|
714
|
-
logger.info(` Committed migration step: ${shortMessage}.`);
|
|
715
|
-
logger.warn(' Failed to look up hash of most recent commit, continuing anyways.');
|
|
716
|
-
}
|
|
717
|
-
return true;
|
|
718
|
-
}
|
|
719
|
-
checkCleanGit() {
|
|
720
|
-
try {
|
|
721
|
-
const topLevel = (0, node_child_process_1.execSync)('git rev-parse --show-toplevel', {
|
|
722
|
-
encoding: 'utf8',
|
|
723
|
-
stdio: 'pipe',
|
|
724
|
-
});
|
|
725
|
-
const result = (0, node_child_process_1.execSync)('git status --porcelain', { encoding: 'utf8', stdio: 'pipe' });
|
|
726
|
-
if (result.trim().length === 0) {
|
|
727
|
-
return true;
|
|
728
|
-
}
|
|
729
|
-
// Only files inside the workspace root are relevant
|
|
730
|
-
for (const entry of result.split('\n')) {
|
|
731
|
-
const relativeEntry = path.relative(path.resolve(this.context.root), path.resolve(topLevel.trim(), entry.slice(3).trim()));
|
|
732
|
-
if (!relativeEntry.startsWith('..') && !path.isAbsolute(relativeEntry)) {
|
|
733
|
-
return false;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
catch { }
|
|
738
|
-
return true;
|
|
739
|
-
}
|
|
740
|
-
/**
|
|
741
|
-
* Checks if the current installed CLI version is older or newer than a compatible version.
|
|
742
|
-
* @returns the version to install or null when there is no update to install.
|
|
743
|
-
*/
|
|
744
|
-
async checkCLIVersion(packagesToUpdate, verbose = false, next = false) {
|
|
745
|
-
const { version } = await (0, package_metadata_1.fetchPackageManifest)(`@angular/cli@${this.getCLIUpdateRunnerVersion(packagesToUpdate, next)}`, this.context.logger, {
|
|
746
|
-
verbose,
|
|
747
|
-
usingYarn: this.context.packageManager.name === workspace_schema_1.PackageManager.Yarn,
|
|
748
|
-
});
|
|
749
|
-
return version_1.VERSION.full === version ? null : version;
|
|
750
|
-
}
|
|
751
|
-
getCLIUpdateRunnerVersion(packagesToUpdate, next) {
|
|
752
|
-
if (next) {
|
|
753
|
-
return 'next';
|
|
754
|
-
}
|
|
755
|
-
const updatingAngularPackage = packagesToUpdate?.find((r) => ANGULAR_PACKAGES_REGEXP.test(r));
|
|
756
|
-
if (updatingAngularPackage) {
|
|
757
|
-
// If we are updating any Angular package we can update the CLI to the target version because
|
|
758
|
-
// migrations for @angular/core@13 can be executed using Angular/cli@13.
|
|
759
|
-
// This is same behaviour as `npx @angular/cli@13 update @angular/core@13`.
|
|
760
|
-
// `@angular/cli@13` -> ['', 'angular/cli', '13']
|
|
761
|
-
// `@angular/cli` -> ['', 'angular/cli']
|
|
762
|
-
const tempVersion = coerceVersionNumber(updatingAngularPackage.split('@')[2]);
|
|
763
|
-
return semver.parse(tempVersion)?.major ?? 'latest';
|
|
764
|
-
}
|
|
765
|
-
// When not updating an Angular package we cannot determine which schematic runtime the migration should to be executed in.
|
|
766
|
-
// Typically, we can assume that the `@angular/cli` was updated previously.
|
|
767
|
-
// Example: Angular official packages are typically updated prior to NGRX etc...
|
|
768
|
-
// Therefore, we only update to the latest patch version of the installed major version of the Angular CLI.
|
|
769
|
-
// This is important because we might end up in a scenario where locally Angular v12 is installed, updating NGRX from 11 to 12.
|
|
770
|
-
// We end up using Angular ClI v13 to run the migrations if we run the migrations using the CLI installed major version + 1 logic.
|
|
771
|
-
return version_1.VERSION.major;
|
|
772
|
-
}
|
|
773
|
-
async runTempBinary(packageName, args = []) {
|
|
774
|
-
const { success, tempNodeModules } = await this.context.packageManager.installTemp(packageName);
|
|
775
|
-
if (!success) {
|
|
776
|
-
return 1;
|
|
777
|
-
}
|
|
778
|
-
// Remove version/tag etc... from package name
|
|
779
|
-
// Ex: @angular/cli@latest -> @angular/cli
|
|
780
|
-
const packageNameNoVersion = packageName.substring(0, packageName.lastIndexOf('@'));
|
|
781
|
-
const pkgLocation = (0, node_path_1.join)(tempNodeModules, packageNameNoVersion);
|
|
782
|
-
const packageJsonPath = (0, node_path_1.join)(pkgLocation, 'package.json');
|
|
783
|
-
// Get a binary location for this package
|
|
784
|
-
let binPath;
|
|
785
|
-
if ((0, node_fs_1.existsSync)(packageJsonPath)) {
|
|
786
|
-
const content = await node_fs_1.promises.readFile(packageJsonPath, 'utf-8');
|
|
787
|
-
if (content) {
|
|
788
|
-
const { bin = {} } = JSON.parse(content);
|
|
789
|
-
const binKeys = Object.keys(bin);
|
|
790
|
-
if (binKeys.length) {
|
|
791
|
-
binPath = (0, node_path_1.resolve)(pkgLocation, bin[binKeys[0]]);
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
if (!binPath) {
|
|
796
|
-
throw new Error(`Cannot locate bin for temporary package: ${packageNameNoVersion}.`);
|
|
797
|
-
}
|
|
798
|
-
const { status, error } = (0, node_child_process_1.spawnSync)(process.execPath, [binPath, ...args], {
|
|
799
|
-
stdio: 'inherit',
|
|
800
|
-
env: {
|
|
801
|
-
...process.env,
|
|
802
|
-
NG_DISABLE_VERSION_CHECK: 'true',
|
|
803
|
-
NG_CLI_ANALYTICS: 'false',
|
|
804
|
-
},
|
|
805
|
-
});
|
|
806
|
-
if (status === null && error) {
|
|
807
|
-
throw error;
|
|
808
|
-
}
|
|
809
|
-
return status ?? 0;
|
|
810
|
-
}
|
|
811
|
-
packageManagerForce(verbose) {
|
|
812
|
-
// npm 7+ can fail due to it incorrectly resolving peer dependencies that have valid SemVer
|
|
813
|
-
// ranges during an update. Update will set correct versions of dependencies within the
|
|
814
|
-
// package.json file. The force option is set to workaround these errors.
|
|
815
|
-
// Example error:
|
|
816
|
-
// npm ERR! Conflicting peer dependency: @angular/compiler-cli@14.0.0-rc.0
|
|
817
|
-
// npm ERR! node_modules/@angular/compiler-cli
|
|
818
|
-
// npm ERR! peer @angular/compiler-cli@"^14.0.0 || ^14.0.0-rc" from @angular-devkit/build-angular@14.0.0-rc.0
|
|
819
|
-
// npm ERR! node_modules/@angular-devkit/build-angular
|
|
820
|
-
// npm ERR! dev @angular-devkit/build-angular@"~14.0.0-rc.0" from the root project
|
|
821
|
-
if (this.context.packageManager.name === workspace_schema_1.PackageManager.Npm &&
|
|
822
|
-
this.context.packageManager.version &&
|
|
823
|
-
semver.gte(this.context.packageManager.version, '7.0.0')) {
|
|
824
|
-
if (verbose) {
|
|
825
|
-
this.context.logger.info('NPM 7+ detected -- enabling force option for package installation');
|
|
826
|
-
}
|
|
827
|
-
return true;
|
|
828
|
-
}
|
|
829
|
-
return false;
|
|
830
|
-
}
|
|
831
|
-
async getOptionalMigrationsToRun(optionalMigrations, packageName) {
|
|
832
|
-
const { logger } = this.context;
|
|
833
|
-
const numberOfMigrations = optionalMigrations.length;
|
|
834
|
-
logger.info(`This package has ${numberOfMigrations} optional migration${numberOfMigrations > 1 ? 's' : ''} that can be executed.`);
|
|
835
|
-
if (!(0, tty_1.isTTY)()) {
|
|
836
|
-
for (const migration of optionalMigrations) {
|
|
837
|
-
const { title } = getMigrationTitleAndDescription(migration);
|
|
838
|
-
logger.info(color_1.colors.cyan(color_1.figures.pointer) + ' ' + color_1.colors.bold(title));
|
|
839
|
-
logger.info(color_1.colors.gray(` ng update ${packageName} --name ${migration.name}`));
|
|
840
|
-
logger.info(''); // Extra trailing newline.
|
|
841
|
-
}
|
|
842
|
-
return undefined;
|
|
843
|
-
}
|
|
844
|
-
logger.info('Optional migrations may be skipped and executed after the update process, if preferred.');
|
|
845
|
-
logger.info(''); // Extra trailing newline.
|
|
846
|
-
const answer = await (0, prompt_1.askChoices)(`Select the migrations that you'd like to run`, optionalMigrations.map((migration) => {
|
|
847
|
-
const { title, documentation } = getMigrationTitleAndDescription(migration);
|
|
848
|
-
return {
|
|
849
|
-
name: `[${color_1.colors.white(migration.name)}] ${title}${documentation ? ` (${documentation})` : ''}`,
|
|
850
|
-
value: migration.name,
|
|
851
|
-
checked: migration.recommended,
|
|
852
|
-
};
|
|
853
|
-
}), null);
|
|
854
|
-
logger.info(''); // Extra trailing newline.
|
|
855
|
-
return optionalMigrations.filter(({ name }) => answer?.includes(name));
|
|
856
|
-
}
|
|
857
513
|
}
|
|
858
514
|
exports.default = UpdateCommandModule;
|
|
859
|
-
/**
|
|
860
|
-
* @return Whether or not the working directory has Git changes to commit.
|
|
861
|
-
*/
|
|
862
|
-
function hasChangesToCommit() {
|
|
863
|
-
// List all modified files not covered by .gitignore.
|
|
864
|
-
// If any files are returned, then there must be something to commit.
|
|
865
|
-
return (0, node_child_process_1.execSync)('git ls-files -m -d -o --exclude-standard').toString() !== '';
|
|
866
|
-
}
|
|
867
|
-
/**
|
|
868
|
-
* Precondition: Must have pending changes to commit, they do not need to be staged.
|
|
869
|
-
* Postcondition: The Git working tree is committed and the repo is clean.
|
|
870
|
-
* @param message The commit message to use.
|
|
871
|
-
*/
|
|
872
|
-
function createCommit(message) {
|
|
873
|
-
// Stage entire working tree for commit.
|
|
874
|
-
(0, node_child_process_1.execSync)('git add -A', { encoding: 'utf8', stdio: 'pipe' });
|
|
875
|
-
// Commit with the message passed via stdin to avoid bash escaping issues.
|
|
876
|
-
(0, node_child_process_1.execSync)('git commit --no-verify -F -', { encoding: 'utf8', stdio: 'pipe', input: message });
|
|
877
|
-
}
|
|
878
|
-
/**
|
|
879
|
-
* @return The Git SHA hash of the HEAD commit. Returns null if unable to retrieve the hash.
|
|
880
|
-
*/
|
|
881
|
-
function findCurrentGitSha() {
|
|
882
|
-
try {
|
|
883
|
-
return (0, node_child_process_1.execSync)('git rev-parse HEAD', { encoding: 'utf8', stdio: 'pipe' }).trim();
|
|
884
|
-
}
|
|
885
|
-
catch {
|
|
886
|
-
return null;
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
function getShortHash(commitHash) {
|
|
890
|
-
return commitHash.slice(0, 9);
|
|
891
|
-
}
|
|
892
|
-
function coerceVersionNumber(version) {
|
|
893
|
-
if (!version) {
|
|
894
|
-
return undefined;
|
|
895
|
-
}
|
|
896
|
-
if (!/^\d{1,30}\.\d{1,30}\.\d{1,30}/.test(version)) {
|
|
897
|
-
const match = version.match(/^\d{1,30}(\.\d{1,30})*/);
|
|
898
|
-
if (!match) {
|
|
899
|
-
return undefined;
|
|
900
|
-
}
|
|
901
|
-
if (!match[1]) {
|
|
902
|
-
version = version.substring(0, match[0].length) + '.0.0' + version.substring(match[0].length);
|
|
903
|
-
}
|
|
904
|
-
else if (!match[2]) {
|
|
905
|
-
version = version.substring(0, match[0].length) + '.0' + version.substring(match[0].length);
|
|
906
|
-
}
|
|
907
|
-
else {
|
|
908
|
-
return undefined;
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
return semver.valid(version) ?? undefined;
|
|
912
|
-
}
|
|
913
|
-
function getMigrationTitleAndDescription(migration) {
|
|
914
|
-
const [title, ...description] = migration.description.split('. ');
|
|
915
|
-
return {
|
|
916
|
-
title: title.endsWith('.') ? title : title + '.',
|
|
917
|
-
description: description.join('.\n '),
|
|
918
|
-
documentation: migration.documentation
|
|
919
|
-
? new URL(migration.documentation, 'https://angular.dev').href
|
|
920
|
-
: undefined,
|
|
921
|
-
};
|
|
922
|
-
}
|
|
923
515
|
//# sourceMappingURL=cli.js.map
|