@angular/cli 14.0.0-next.5 → 14.0.0-next.6
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/bin/postinstall/analytics-prompt.js +2 -2
- package/lib/config/schema.json +4 -4
- package/lib/config/workspace-schema.d.ts +1 -1
- package/lib/init.js +5 -1
- package/package.json +13 -13
- package/src/analytics/analytics-collector.js +5 -1
- package/{models/interface.js → src/analytics/analytics-environment-options.d.ts} +2 -2
- package/src/analytics/analytics-environment-options.js +20 -0
- package/src/analytics/analytics.d.ts +10 -23
- package/src/analytics/analytics.js +96 -182
- package/src/command-builder/architect-base-command-module.js +3 -5
- package/src/command-builder/architect-command-module.js +4 -6
- package/src/command-builder/command-module.d.ts +4 -1
- package/src/command-builder/command-module.js +9 -3
- package/src/command-builder/command-runner.js +9 -20
- package/src/command-builder/schematics-command-module.d.ts +18 -5
- package/src/command-builder/schematics-command-module.js +207 -29
- package/src/command-builder/utilities/command.d.ts +13 -0
- package/src/command-builder/utilities/command.js +27 -0
- package/src/command-builder/utilities/json-help.d.ts +16 -14
- package/src/command-builder/utilities/json-help.js +26 -22
- package/{models → src/command-builder/utilities}/schematic-engine-host.d.ts +0 -0
- package/{models → src/command-builder/utilities}/schematic-engine-host.js +0 -0
- package/src/command-builder/utilities/schematic-workflow.d.ts +14 -0
- package/src/command-builder/utilities/schematic-workflow.js +68 -0
- package/src/commands/add/cli.d.ts +11 -1
- package/src/commands/add/cli.js +325 -6
- package/src/commands/analytics/cli.d.ts +5 -10
- package/src/commands/analytics/cli.js +15 -50
- package/src/commands/analytics/info/cli.d.ts +16 -0
- package/src/commands/analytics/info/cli.js +26 -0
- package/src/commands/analytics/settings/cli.d.ts +35 -0
- package/src/commands/analytics/settings/cli.js +61 -0
- package/src/commands/config/cli.d.ts +4 -1
- package/src/commands/config/cli.js +126 -4
- package/src/commands/doc/cli.js +5 -1
- package/src/commands/generate/cli.d.ts +4 -2
- package/src/commands/generate/cli.js +37 -21
- package/src/commands/new/cli.d.ts +5 -3
- package/src/commands/new/cli.js +36 -6
- package/src/commands/update/cli.d.ts +30 -5
- package/src/commands/update/cli.js +680 -9
- package/src/commands/update/schematic/index.js +5 -1
- package/src/commands/version/cli.js +18 -23
- package/src/utilities/color.js +5 -1
- package/src/utilities/config.d.ts +1 -0
- package/src/utilities/config.js +36 -2
- package/src/utilities/find-up.js +5 -1
- package/src/utilities/package-metadata.js +5 -1
- package/src/utilities/package-tree.js +5 -1
- package/src/utilities/project.js +5 -1
- package/src/utilities/prompt.js +5 -1
- package/models/command.d.ts +0 -29
- package/models/command.js +0 -50
- package/models/interface.d.ts +0 -19
- package/models/schematic-command.d.ts +0 -43
- package/models/schematic-command.js +0 -378
- package/src/commands/add/add-impl.d.ts +0 -22
- package/src/commands/add/add-impl.js +0 -331
- package/src/commands/analytics/long-description.md +0 -10
- package/src/commands/config/config-impl.d.ts +0 -17
- package/src/commands/config/config-impl.js +0 -151
- package/src/commands/generate/generate-impl.d.ts +0 -19
- package/src/commands/generate/generate-impl.js +0 -49
- package/src/commands/new/new-impl.d.ts +0 -18
- package/src/commands/new/new-impl.js +0 -38
- package/src/commands/update/update-impl.d.ts +0 -40
- package/src/commands/update/update-impl.js +0 -728
|
@@ -6,25 +6,81 @@
|
|
|
6
6
|
* Use of this source code is governed by an MIT-style license that can be
|
|
7
7
|
* found in the LICENSE file at https://angular.io/license
|
|
8
8
|
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
26
|
+
if (mod && mod.__esModule) return mod;
|
|
27
|
+
var result = {};
|
|
28
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
29
|
+
__setModuleDefault(result, mod);
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
33
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
34
|
+
};
|
|
9
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
36
|
exports.UpdateCommandModule = void 0;
|
|
37
|
+
const schematics_1 = require("@angular-devkit/schematics");
|
|
38
|
+
const tools_1 = require("@angular-devkit/schematics/tools");
|
|
39
|
+
const child_process_1 = require("child_process");
|
|
40
|
+
const fs_1 = require("fs");
|
|
41
|
+
const npm_package_arg_1 = __importDefault(require("npm-package-arg"));
|
|
42
|
+
const npm_pick_manifest_1 = __importDefault(require("npm-pick-manifest"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const path_1 = require("path");
|
|
45
|
+
const semver = __importStar(require("semver"));
|
|
46
|
+
const workspace_schema_1 = require("../../../lib/config/workspace-schema");
|
|
11
47
|
const command_module_1 = require("../../command-builder/command-module");
|
|
12
|
-
const
|
|
48
|
+
const schematic_engine_host_1 = require("../../command-builder/utilities/schematic-engine-host");
|
|
49
|
+
const schematic_workflow_1 = require("../../command-builder/utilities/schematic-workflow");
|
|
50
|
+
const color_1 = require("../../utilities/color");
|
|
51
|
+
const install_package_1 = require("../../utilities/install-package");
|
|
52
|
+
const log_file_1 = require("../../utilities/log-file");
|
|
53
|
+
const package_manager_1 = require("../../utilities/package-manager");
|
|
54
|
+
const package_metadata_1 = require("../../utilities/package-metadata");
|
|
55
|
+
const package_tree_1 = require("../../utilities/package-tree");
|
|
56
|
+
const version_1 = require("../../utilities/version");
|
|
57
|
+
/**
|
|
58
|
+
* Disable CLI version mismatch checks and forces usage of the invoked CLI
|
|
59
|
+
* instead of invoking the local installed version.
|
|
60
|
+
*/
|
|
61
|
+
const disableVersionCheckEnv = process.env['NG_DISABLE_VERSION_CHECK'];
|
|
62
|
+
const disableVersionCheck = disableVersionCheckEnv !== undefined &&
|
|
63
|
+
disableVersionCheckEnv !== '0' &&
|
|
64
|
+
disableVersionCheckEnv.toLowerCase() !== 'false';
|
|
65
|
+
const ANGULAR_PACKAGES_REGEXP = /^@(?:angular|nguniversal)\//;
|
|
66
|
+
const UPDATE_SCHEMATIC_COLLECTION = path.join(__dirname, 'schematic/collection.json');
|
|
13
67
|
class UpdateCommandModule extends command_module_1.CommandModule {
|
|
14
68
|
constructor() {
|
|
15
69
|
super(...arguments);
|
|
70
|
+
this.shouldReportAnalytics = false;
|
|
16
71
|
this.command = 'update [packages..]';
|
|
17
72
|
this.describe = 'Updates your workspace and its dependencies. See https://update.angular.io/.';
|
|
73
|
+
this.longDescriptionPath = (0, path_1.join)(__dirname, 'long-description.md');
|
|
18
74
|
}
|
|
19
75
|
builder(localYargs) {
|
|
20
76
|
return localYargs
|
|
21
77
|
.positional('packages', {
|
|
22
78
|
description: 'The names of package(s) to update.',
|
|
23
|
-
|
|
79
|
+
coerce: (value) => (typeof value === 'string' ? [value] : value),
|
|
24
80
|
})
|
|
25
81
|
.option('force', {
|
|
26
82
|
description: 'Ignore peer dependency version mismatches. ' +
|
|
27
|
-
|
|
83
|
+
`Passes the '--force' flag to the package manager when installing packages.`,
|
|
28
84
|
type: 'boolean',
|
|
29
85
|
default: false,
|
|
30
86
|
})
|
|
@@ -38,20 +94,22 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
38
94
|
type: 'boolean',
|
|
39
95
|
})
|
|
40
96
|
.option('name', {
|
|
41
|
-
description: 'The name of the migration to run.'
|
|
97
|
+
description: 'The name of the migration to run. ' +
|
|
98
|
+
`Only available with a single package being updated, and only with 'migrate-only' option.`,
|
|
42
99
|
type: 'string',
|
|
43
100
|
implies: ['migrate-only'],
|
|
44
101
|
conflicts: ['to', 'from'],
|
|
45
102
|
})
|
|
46
103
|
.option('from', {
|
|
47
|
-
description: 'Version from which to migrate from.
|
|
104
|
+
description: 'Version from which to migrate from. ' +
|
|
105
|
+
`Only available with a single package being updated, and only with 'migrate-only'.`,
|
|
48
106
|
type: 'string',
|
|
49
107
|
implies: ['to', 'migrate-only'],
|
|
50
108
|
conflicts: ['name'],
|
|
51
109
|
})
|
|
52
110
|
.option('to', {
|
|
53
111
|
describe: 'Version up to which to apply migrations. Only available with a single package being updated, ' +
|
|
54
|
-
|
|
112
|
+
`and only with 'migrate-only' option. Requires 'from' to be specified. Default to the installed version detected.`,
|
|
55
113
|
type: 'string',
|
|
56
114
|
implies: ['from', 'migrate-only'],
|
|
57
115
|
conflicts: ['name'],
|
|
@@ -71,13 +129,626 @@ class UpdateCommandModule extends command_module_1.CommandModule {
|
|
|
71
129
|
type: 'boolean',
|
|
72
130
|
alias: ['C'],
|
|
73
131
|
default: false,
|
|
132
|
+
})
|
|
133
|
+
.check(({ packages, next, 'allow-dirty': allowDirty, 'migrate-only': migrateOnly }) => {
|
|
134
|
+
const { logger } = this.context;
|
|
135
|
+
// This allows the user to easily reset any changes from the update.
|
|
136
|
+
if ((packages === null || packages === void 0 ? void 0 : packages.length) && !this.checkCleanGit()) {
|
|
137
|
+
if (allowDirty) {
|
|
138
|
+
logger.warn('Repository is not clean. Update changes will be mixed with pre-existing changes.');
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
throw new command_module_1.CommandModuleError('Repository is not clean. Please commit or stash any changes before updating.');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (migrateOnly) {
|
|
145
|
+
if ((packages === null || packages === void 0 ? void 0 : packages.length) !== 1) {
|
|
146
|
+
throw new command_module_1.CommandModuleError(`A single package must be specified when using the 'migrate-only' option.`);
|
|
147
|
+
}
|
|
148
|
+
if (next) {
|
|
149
|
+
logger.warn(`'next' option has no effect when using 'migrate-only' option.`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return true;
|
|
74
153
|
})
|
|
75
154
|
.strict();
|
|
76
155
|
}
|
|
77
|
-
run(options) {
|
|
78
|
-
|
|
79
|
-
|
|
156
|
+
async run(options) {
|
|
157
|
+
var _a, _b;
|
|
158
|
+
const { logger, packageManager } = this.context;
|
|
159
|
+
await (0, package_manager_1.ensureCompatibleNpm)(this.context.root);
|
|
160
|
+
// Check if the current installed CLI version is older than the latest compatible version.
|
|
161
|
+
if (!disableVersionCheck) {
|
|
162
|
+
const cliVersionToInstall = await this.checkCLIVersion(options.packages, options.verbose, options.next);
|
|
163
|
+
if (cliVersionToInstall) {
|
|
164
|
+
logger.warn('The installed Angular CLI version is outdated.\n' +
|
|
165
|
+
`Installing a temporary Angular CLI versioned ${cliVersionToInstall} to perform the update.`);
|
|
166
|
+
return (0, install_package_1.runTempPackageBin)(`@angular/cli@${cliVersionToInstall}`, packageManager, process.argv.slice(2));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const packages = [];
|
|
170
|
+
for (const request of (_a = options.packages) !== null && _a !== void 0 ? _a : []) {
|
|
171
|
+
try {
|
|
172
|
+
const packageIdentifier = (0, npm_package_arg_1.default)(request);
|
|
173
|
+
// only registry identifiers are supported
|
|
174
|
+
if (!packageIdentifier.registry) {
|
|
175
|
+
logger.error(`Package '${request}' is not a registry package identifer.`);
|
|
176
|
+
return 1;
|
|
177
|
+
}
|
|
178
|
+
if (packages.some((v) => v.name === packageIdentifier.name)) {
|
|
179
|
+
logger.error(`Duplicate package '${packageIdentifier.name}' specified.`);
|
|
180
|
+
return 1;
|
|
181
|
+
}
|
|
182
|
+
if (options.migrateOnly && packageIdentifier.rawSpec) {
|
|
183
|
+
logger.warn('Package specifier has no effect when using "migrate-only" option.');
|
|
184
|
+
}
|
|
185
|
+
// If next option is used and no specifier supplied, use next tag
|
|
186
|
+
if (options.next && !packageIdentifier.rawSpec) {
|
|
187
|
+
packageIdentifier.fetchSpec = 'next';
|
|
188
|
+
}
|
|
189
|
+
packages.push(packageIdentifier);
|
|
190
|
+
}
|
|
191
|
+
catch (e) {
|
|
192
|
+
logger.error(e.message);
|
|
193
|
+
return 1;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
logger.info(`Using package manager: '${packageManager}'`);
|
|
197
|
+
logger.info('Collecting installed dependencies...');
|
|
198
|
+
const rootDependencies = await (0, package_tree_1.getProjectDependencies)(this.context.root);
|
|
199
|
+
logger.info(`Found ${rootDependencies.size} dependencies.`);
|
|
200
|
+
const workflow = new tools_1.NodeWorkflow(this.context.root, {
|
|
201
|
+
packageManager: this.context.packageManager,
|
|
202
|
+
packageManagerForce: options.force,
|
|
203
|
+
// __dirname -> favor @schematics/update from this package
|
|
204
|
+
// Otherwise, use packages from the active workspace (migrations)
|
|
205
|
+
resolvePaths: [__dirname, this.context.root],
|
|
206
|
+
schemaValidation: true,
|
|
207
|
+
engineHostCreator: (options) => new schematic_engine_host_1.SchematicEngineHost(options.resolvePaths),
|
|
208
|
+
});
|
|
209
|
+
if (packages.length === 0) {
|
|
210
|
+
// Show status
|
|
211
|
+
const { success } = await this.executeSchematic(workflow, UPDATE_SCHEMATIC_COLLECTION, 'update', {
|
|
212
|
+
force: options.force,
|
|
213
|
+
next: options.next,
|
|
214
|
+
verbose: options.verbose,
|
|
215
|
+
packageManager,
|
|
216
|
+
packages: [],
|
|
217
|
+
});
|
|
218
|
+
return success ? 0 : 1;
|
|
219
|
+
}
|
|
220
|
+
return options.migrateOnly
|
|
221
|
+
? this.migrateOnly(workflow, ((_b = options.packages) !== null && _b !== void 0 ? _b : [])[0], rootDependencies, options)
|
|
222
|
+
: this.updatePackagesAndMigrate(workflow, rootDependencies, options, packages);
|
|
223
|
+
}
|
|
224
|
+
async executeSchematic(workflow, collection, schematic, options = {}) {
|
|
225
|
+
const { logger } = this.context;
|
|
226
|
+
const workflowSubscription = (0, schematic_workflow_1.subscribeToWorkflow)(workflow, logger);
|
|
227
|
+
// TODO: Allow passing a schematic instance directly
|
|
228
|
+
try {
|
|
229
|
+
await workflow
|
|
230
|
+
.execute({
|
|
231
|
+
collection,
|
|
232
|
+
schematic,
|
|
233
|
+
options,
|
|
234
|
+
logger,
|
|
235
|
+
})
|
|
236
|
+
.toPromise();
|
|
237
|
+
return { success: !workflowSubscription.error, files: workflowSubscription.files };
|
|
238
|
+
}
|
|
239
|
+
catch (e) {
|
|
240
|
+
if (e instanceof schematics_1.UnsuccessfulWorkflowExecution) {
|
|
241
|
+
logger.error(`${color_1.colors.symbols.cross} Migration failed. See above for further details.\n`);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
const logPath = (0, log_file_1.writeErrorToLogFile)(e);
|
|
245
|
+
logger.fatal(`${color_1.colors.symbols.cross} Migration failed: ${e.message}\n` +
|
|
246
|
+
` See "${logPath}" for further details.\n`);
|
|
247
|
+
}
|
|
248
|
+
return { success: false, files: workflowSubscription.files };
|
|
249
|
+
}
|
|
250
|
+
finally {
|
|
251
|
+
workflowSubscription.unsubscribe();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* @return Whether or not the migration was performed successfully.
|
|
256
|
+
*/
|
|
257
|
+
async executeMigration(workflow, packageName, collectionPath, migrationName, commit) {
|
|
258
|
+
const { logger } = this.context;
|
|
259
|
+
const collection = workflow.engine.createCollection(collectionPath);
|
|
260
|
+
const name = collection.listSchematicNames().find((name) => name === migrationName);
|
|
261
|
+
if (!name) {
|
|
262
|
+
logger.error(`Cannot find migration '${migrationName}' in '${packageName}'.`);
|
|
263
|
+
return 1;
|
|
264
|
+
}
|
|
265
|
+
logger.info(color_1.colors.cyan(`** Executing '${migrationName}' of package '${packageName}' **\n`));
|
|
266
|
+
const schematic = workflow.engine.createSchematic(name, collection);
|
|
267
|
+
return this.executePackageMigrations(workflow, [schematic.description], packageName, commit);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* @return Whether or not the migrations were performed successfully.
|
|
271
|
+
*/
|
|
272
|
+
async executeMigrations(workflow, packageName, collectionPath, from, to, commit) {
|
|
273
|
+
const collection = workflow.engine.createCollection(collectionPath);
|
|
274
|
+
const migrationRange = new semver.Range('>' + (semver.prerelease(from) ? from.split('-')[0] + '-0' : from) + ' <=' + to.split('-')[0]);
|
|
275
|
+
const migrations = [];
|
|
276
|
+
for (const name of collection.listSchematicNames()) {
|
|
277
|
+
const schematic = workflow.engine.createSchematic(name, collection);
|
|
278
|
+
const description = schematic.description;
|
|
279
|
+
description.version = coerceVersionNumber(description.version);
|
|
280
|
+
if (!description.version) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (semver.satisfies(description.version, migrationRange, { includePrerelease: true })) {
|
|
284
|
+
migrations.push(description);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (migrations.length === 0) {
|
|
288
|
+
return 0;
|
|
289
|
+
}
|
|
290
|
+
migrations.sort((a, b) => semver.compare(a.version, b.version) || a.name.localeCompare(b.name));
|
|
291
|
+
this.context.logger.info(color_1.colors.cyan(`** Executing migrations of package '${packageName}' **\n`));
|
|
292
|
+
return this.executePackageMigrations(workflow, migrations, packageName, commit);
|
|
293
|
+
}
|
|
294
|
+
async executePackageMigrations(workflow, migrations, packageName, commit = false) {
|
|
295
|
+
const { logger } = this.context;
|
|
296
|
+
for (const migration of migrations) {
|
|
297
|
+
const [title, ...description] = migration.description.split('. ');
|
|
298
|
+
logger.info(color_1.colors.cyan(color_1.colors.symbols.pointer) +
|
|
299
|
+
' ' +
|
|
300
|
+
color_1.colors.bold(title.endsWith('.') ? title : title + '.'));
|
|
301
|
+
if (description.length) {
|
|
302
|
+
logger.info(' ' + description.join('.\n '));
|
|
303
|
+
}
|
|
304
|
+
const result = await this.executeSchematic(workflow, migration.collection.name, migration.name);
|
|
305
|
+
if (!result.success) {
|
|
306
|
+
return 1;
|
|
307
|
+
}
|
|
308
|
+
logger.info(' Migration completed.');
|
|
309
|
+
// Commit migration
|
|
310
|
+
if (commit) {
|
|
311
|
+
const commitPrefix = `${packageName} migration - ${migration.name}`;
|
|
312
|
+
const commitMessage = migration.description
|
|
313
|
+
? `${commitPrefix}\n\n${migration.description}`
|
|
314
|
+
: commitPrefix;
|
|
315
|
+
const committed = this.commit(commitMessage);
|
|
316
|
+
if (!committed) {
|
|
317
|
+
// Failed to commit, something went wrong. Abort the update.
|
|
318
|
+
return 1;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
logger.info(''); // Extra trailing newline.
|
|
322
|
+
}
|
|
323
|
+
return 0;
|
|
324
|
+
}
|
|
325
|
+
async migrateOnly(workflow, packageName, rootDependencies, options) {
|
|
326
|
+
const { logger } = this.context;
|
|
327
|
+
const packageDependency = rootDependencies.get(packageName);
|
|
328
|
+
let packagePath = packageDependency === null || packageDependency === void 0 ? void 0 : packageDependency.path;
|
|
329
|
+
let packageNode = packageDependency === null || packageDependency === void 0 ? void 0 : packageDependency.package;
|
|
330
|
+
if (packageDependency && !packageNode) {
|
|
331
|
+
logger.error('Package found in package.json but is not installed.');
|
|
332
|
+
return 1;
|
|
333
|
+
}
|
|
334
|
+
else if (!packageDependency) {
|
|
335
|
+
// Allow running migrations on transitively installed dependencies
|
|
336
|
+
// There can technically be nested multiple versions
|
|
337
|
+
// TODO: If multiple, this should find all versions and ask which one to use
|
|
338
|
+
const packageJson = (0, package_tree_1.findPackageJson)(this.context.root, packageName);
|
|
339
|
+
if (packageJson) {
|
|
340
|
+
packagePath = path.dirname(packageJson);
|
|
341
|
+
packageNode = await (0, package_tree_1.readPackageJson)(packageJson);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
if (!packageNode || !packagePath) {
|
|
345
|
+
logger.error('Package is not installed.');
|
|
346
|
+
return 1;
|
|
347
|
+
}
|
|
348
|
+
const updateMetadata = packageNode['ng-update'];
|
|
349
|
+
let migrations = updateMetadata === null || updateMetadata === void 0 ? void 0 : updateMetadata.migrations;
|
|
350
|
+
if (migrations === undefined) {
|
|
351
|
+
logger.error('Package does not provide migrations.');
|
|
352
|
+
return 1;
|
|
353
|
+
}
|
|
354
|
+
else if (typeof migrations !== 'string') {
|
|
355
|
+
logger.error('Package contains a malformed migrations field.');
|
|
356
|
+
return 1;
|
|
357
|
+
}
|
|
358
|
+
else if (path.posix.isAbsolute(migrations) || path.win32.isAbsolute(migrations)) {
|
|
359
|
+
logger.error('Package contains an invalid migrations field. Absolute paths are not permitted.');
|
|
360
|
+
return 1;
|
|
361
|
+
}
|
|
362
|
+
// Normalize slashes
|
|
363
|
+
migrations = migrations.replace(/\\/g, '/');
|
|
364
|
+
if (migrations.startsWith('../')) {
|
|
365
|
+
logger.error('Package contains an invalid migrations field. Paths outside the package root are not permitted.');
|
|
366
|
+
return 1;
|
|
367
|
+
}
|
|
368
|
+
// Check if it is a package-local location
|
|
369
|
+
const localMigrations = path.join(packagePath, migrations);
|
|
370
|
+
if ((0, fs_1.existsSync)(localMigrations)) {
|
|
371
|
+
migrations = localMigrations;
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
// Try to resolve from package location.
|
|
375
|
+
// This avoids issues with package hoisting.
|
|
376
|
+
try {
|
|
377
|
+
migrations = require.resolve(migrations, { paths: [packagePath] });
|
|
378
|
+
}
|
|
379
|
+
catch (e) {
|
|
380
|
+
if (e.code === 'MODULE_NOT_FOUND') {
|
|
381
|
+
logger.error('Migrations for package were not found.');
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
logger.error(`Unable to resolve migrations for package. [${e.message}]`);
|
|
385
|
+
}
|
|
386
|
+
return 1;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (options.name) {
|
|
390
|
+
return this.executeMigration(workflow, packageName, migrations, options.name, options.createCommits);
|
|
391
|
+
}
|
|
392
|
+
const from = coerceVersionNumber(options.from);
|
|
393
|
+
if (!from) {
|
|
394
|
+
logger.error(`"from" value [${options.from}] is not a valid version.`);
|
|
395
|
+
return 1;
|
|
396
|
+
}
|
|
397
|
+
return this.executeMigrations(workflow, packageName, migrations, from, options.to || packageNode.version, options.createCommits);
|
|
398
|
+
}
|
|
399
|
+
// eslint-disable-next-line max-lines-per-function
|
|
400
|
+
async updatePackagesAndMigrate(workflow, rootDependencies, options, packages) {
|
|
401
|
+
var _a;
|
|
402
|
+
const { logger } = this.context;
|
|
403
|
+
const logVerbose = (message) => {
|
|
404
|
+
if (options.verbose) {
|
|
405
|
+
logger.info(message);
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
const requests = [];
|
|
409
|
+
// Validate packages actually are part of the workspace
|
|
410
|
+
for (const pkg of packages) {
|
|
411
|
+
const node = rootDependencies.get(pkg.name);
|
|
412
|
+
if (!(node === null || node === void 0 ? void 0 : node.package)) {
|
|
413
|
+
logger.error(`Package '${pkg.name}' is not a dependency.`);
|
|
414
|
+
return 1;
|
|
415
|
+
}
|
|
416
|
+
// If a specific version is requested and matches the installed version, skip.
|
|
417
|
+
if (pkg.type === 'version' && node.package.version === pkg.fetchSpec) {
|
|
418
|
+
logger.info(`Package '${pkg.name}' is already at '${pkg.fetchSpec}'.`);
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
requests.push({ identifier: pkg, node });
|
|
422
|
+
}
|
|
423
|
+
if (requests.length === 0) {
|
|
424
|
+
return 0;
|
|
425
|
+
}
|
|
426
|
+
logger.info('Fetching dependency metadata from registry...');
|
|
427
|
+
const packagesToUpdate = [];
|
|
428
|
+
for (const { identifier: requestIdentifier, node } of requests) {
|
|
429
|
+
const packageName = requestIdentifier.name;
|
|
430
|
+
let metadata;
|
|
431
|
+
try {
|
|
432
|
+
// Metadata requests are internally cached; multiple requests for same name
|
|
433
|
+
// does not result in additional network traffic
|
|
434
|
+
metadata = await (0, package_metadata_1.fetchPackageMetadata)(packageName, logger, {
|
|
435
|
+
verbose: options.verbose,
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
catch (e) {
|
|
439
|
+
logger.error(`Error fetching metadata for '${packageName}': ` + e.message);
|
|
440
|
+
return 1;
|
|
441
|
+
}
|
|
442
|
+
// Try to find a package version based on the user requested package specifier
|
|
443
|
+
// registry specifier types are either version, range, or tag
|
|
444
|
+
let manifest;
|
|
445
|
+
if (requestIdentifier.type === 'version' ||
|
|
446
|
+
requestIdentifier.type === 'range' ||
|
|
447
|
+
requestIdentifier.type === 'tag') {
|
|
448
|
+
try {
|
|
449
|
+
manifest = (0, npm_pick_manifest_1.default)(metadata, requestIdentifier.fetchSpec);
|
|
450
|
+
}
|
|
451
|
+
catch (e) {
|
|
452
|
+
if (e.code === 'ETARGET') {
|
|
453
|
+
// If not found and next was used and user did not provide a specifier, try latest.
|
|
454
|
+
// Package may not have a next tag.
|
|
455
|
+
if (requestIdentifier.type === 'tag' &&
|
|
456
|
+
requestIdentifier.fetchSpec === 'next' &&
|
|
457
|
+
!requestIdentifier.rawSpec) {
|
|
458
|
+
try {
|
|
459
|
+
manifest = (0, npm_pick_manifest_1.default)(metadata, 'latest');
|
|
460
|
+
}
|
|
461
|
+
catch (e) {
|
|
462
|
+
if (e.code !== 'ETARGET' && e.code !== 'ENOVERSIONS') {
|
|
463
|
+
throw e;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
else if (e.code !== 'ENOVERSIONS') {
|
|
469
|
+
throw e;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (!manifest) {
|
|
474
|
+
logger.error(`Package specified by '${requestIdentifier.raw}' does not exist within the registry.`);
|
|
475
|
+
return 1;
|
|
476
|
+
}
|
|
477
|
+
if (manifest.version === ((_a = node.package) === null || _a === void 0 ? void 0 : _a.version)) {
|
|
478
|
+
logger.info(`Package '${packageName}' is already up to date.`);
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
if (node.package && ANGULAR_PACKAGES_REGEXP.test(node.package.name)) {
|
|
482
|
+
const { name, version } = node.package;
|
|
483
|
+
const toBeInstalledMajorVersion = +manifest.version.split('.')[0];
|
|
484
|
+
const currentMajorVersion = +version.split('.')[0];
|
|
485
|
+
if (toBeInstalledMajorVersion - currentMajorVersion > 1) {
|
|
486
|
+
// Only allow updating a single version at a time.
|
|
487
|
+
if (currentMajorVersion < 6) {
|
|
488
|
+
// Before version 6, the major versions were not always sequential.
|
|
489
|
+
// Example @angular/core skipped version 3, @angular/cli skipped versions 2-5.
|
|
490
|
+
logger.error(`Updating multiple major versions of '${name}' at once is not supported. Please migrate each major version individually.\n` +
|
|
491
|
+
`For more information about the update process, see https://update.angular.io/.`);
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
const nextMajorVersionFromCurrent = currentMajorVersion + 1;
|
|
495
|
+
logger.error(`Updating multiple major versions of '${name}' at once is not supported. Please migrate each major version individually.\n` +
|
|
496
|
+
`Run 'ng update ${name}@${nextMajorVersionFromCurrent}' in your workspace directory ` +
|
|
497
|
+
`to update to latest '${nextMajorVersionFromCurrent}.x' version of '${name}'.\n\n` +
|
|
498
|
+
`For more information about the update process, see https://update.angular.io/?v=${currentMajorVersion}.0-${nextMajorVersionFromCurrent}.0`);
|
|
499
|
+
}
|
|
500
|
+
return 1;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
packagesToUpdate.push(requestIdentifier.toString());
|
|
504
|
+
}
|
|
505
|
+
if (packagesToUpdate.length === 0) {
|
|
506
|
+
return 0;
|
|
507
|
+
}
|
|
508
|
+
const { success } = await this.executeSchematic(workflow, UPDATE_SCHEMATIC_COLLECTION, 'update', {
|
|
509
|
+
verbose: options.verbose,
|
|
510
|
+
force: options.force,
|
|
511
|
+
next: options.next,
|
|
512
|
+
packageManager: this.context.packageManager,
|
|
513
|
+
packages: packagesToUpdate,
|
|
514
|
+
});
|
|
515
|
+
if (success) {
|
|
516
|
+
try {
|
|
517
|
+
await fs_1.promises.rm(path.join(this.context.root, 'node_modules'), {
|
|
518
|
+
force: true,
|
|
519
|
+
recursive: true,
|
|
520
|
+
maxRetries: 3,
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
catch { }
|
|
524
|
+
const result = await (0, install_package_1.installAllPackages)(this.context.packageManager, options.force ? ['--force'] : [], this.context.root);
|
|
525
|
+
if (result !== 0) {
|
|
526
|
+
return result;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
if (success && options.createCommits) {
|
|
530
|
+
if (!this.commit(`Angular CLI update for packages - ${packagesToUpdate.join(', ')}`)) {
|
|
531
|
+
return 1;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
// This is a temporary workaround to allow data to be passed back from the update schematic
|
|
535
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
536
|
+
const migrations = global.externalMigrations;
|
|
537
|
+
if (success && migrations) {
|
|
538
|
+
for (const migration of migrations) {
|
|
539
|
+
// Resolve the package from the workspace root, as otherwise it will be resolved from the temp
|
|
540
|
+
// installed CLI version.
|
|
541
|
+
let packagePath;
|
|
542
|
+
logVerbose(`Resolving migration package '${migration.package}' from '${this.context.root}'...`);
|
|
543
|
+
try {
|
|
544
|
+
try {
|
|
545
|
+
packagePath = path.dirname(
|
|
546
|
+
// This may fail if the `package.json` is not exported as an entry point
|
|
547
|
+
require.resolve(path.join(migration.package, 'package.json'), {
|
|
548
|
+
paths: [this.context.root],
|
|
549
|
+
}));
|
|
550
|
+
}
|
|
551
|
+
catch (e) {
|
|
552
|
+
if (e.code === 'MODULE_NOT_FOUND') {
|
|
553
|
+
// Fallback to trying to resolve the package's main entry point
|
|
554
|
+
packagePath = require.resolve(migration.package, { paths: [this.context.root] });
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
throw e;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
catch (e) {
|
|
562
|
+
if (e.code === 'MODULE_NOT_FOUND') {
|
|
563
|
+
logVerbose(e.toString());
|
|
564
|
+
logger.error(`Migrations for package (${migration.package}) were not found.` +
|
|
565
|
+
' The package could not be found in the workspace.');
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
logger.error(`Unable to resolve migrations for package (${migration.package}). [${e.message}]`);
|
|
569
|
+
}
|
|
570
|
+
return 1;
|
|
571
|
+
}
|
|
572
|
+
let migrations;
|
|
573
|
+
// Check if it is a package-local location
|
|
574
|
+
const localMigrations = path.join(packagePath, migration.collection);
|
|
575
|
+
if ((0, fs_1.existsSync)(localMigrations)) {
|
|
576
|
+
migrations = localMigrations;
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
// Try to resolve from package location.
|
|
580
|
+
// This avoids issues with package hoisting.
|
|
581
|
+
try {
|
|
582
|
+
migrations = require.resolve(migration.collection, { paths: [packagePath] });
|
|
583
|
+
}
|
|
584
|
+
catch (e) {
|
|
585
|
+
if (e.code === 'MODULE_NOT_FOUND') {
|
|
586
|
+
logger.error(`Migrations for package (${migration.package}) were not found.`);
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
logger.error(`Unable to resolve migrations for package (${migration.package}). [${e.message}]`);
|
|
590
|
+
}
|
|
591
|
+
return 1;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
const result = await this.executeMigrations(workflow, migration.package, migrations, migration.from, migration.to, options.createCommits);
|
|
595
|
+
if (!result) {
|
|
596
|
+
return 0;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return success ? 0 : 1;
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* @return Whether or not the commit was successful.
|
|
604
|
+
*/
|
|
605
|
+
commit(message) {
|
|
606
|
+
const { logger } = this.context;
|
|
607
|
+
// Check if a commit is needed.
|
|
608
|
+
let commitNeeded;
|
|
609
|
+
try {
|
|
610
|
+
commitNeeded = hasChangesToCommit();
|
|
611
|
+
}
|
|
612
|
+
catch (err) {
|
|
613
|
+
logger.error(` Failed to read Git tree:\n${err.stderr}`);
|
|
614
|
+
return false;
|
|
615
|
+
}
|
|
616
|
+
if (!commitNeeded) {
|
|
617
|
+
logger.info(' No changes to commit after migration.');
|
|
618
|
+
return true;
|
|
619
|
+
}
|
|
620
|
+
// Commit changes and abort on error.
|
|
621
|
+
try {
|
|
622
|
+
createCommit(message);
|
|
623
|
+
}
|
|
624
|
+
catch (err) {
|
|
625
|
+
logger.error(`Failed to commit update (${message}):\n${err.stderr}`);
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
628
|
+
// Notify user of the commit.
|
|
629
|
+
const hash = findCurrentGitSha();
|
|
630
|
+
const shortMessage = message.split('\n')[0];
|
|
631
|
+
if (hash) {
|
|
632
|
+
logger.info(` Committed migration step (${getShortHash(hash)}): ${shortMessage}.`);
|
|
633
|
+
}
|
|
634
|
+
else {
|
|
635
|
+
// Commit was successful, but reading the hash was not. Something weird happened,
|
|
636
|
+
// but nothing that would stop the update. Just log the weirdness and continue.
|
|
637
|
+
logger.info(` Committed migration step: ${shortMessage}.`);
|
|
638
|
+
logger.warn(' Failed to look up hash of most recent commit, continuing anyways.');
|
|
639
|
+
}
|
|
640
|
+
return true;
|
|
641
|
+
}
|
|
642
|
+
checkCleanGit() {
|
|
643
|
+
try {
|
|
644
|
+
const topLevel = (0, child_process_1.execSync)('git rev-parse --show-toplevel', {
|
|
645
|
+
encoding: 'utf8',
|
|
646
|
+
stdio: 'pipe',
|
|
647
|
+
});
|
|
648
|
+
const result = (0, child_process_1.execSync)('git status --porcelain', { encoding: 'utf8', stdio: 'pipe' });
|
|
649
|
+
if (result.trim().length === 0) {
|
|
650
|
+
return true;
|
|
651
|
+
}
|
|
652
|
+
// Only files inside the workspace root are relevant
|
|
653
|
+
for (const entry of result.split('\n')) {
|
|
654
|
+
const relativeEntry = path.relative(path.resolve(this.context.root), path.resolve(topLevel.trim(), entry.slice(3).trim()));
|
|
655
|
+
if (!relativeEntry.startsWith('..') && !path.isAbsolute(relativeEntry)) {
|
|
656
|
+
return false;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
catch { }
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Checks if the current installed CLI version is older or newer than a compatible version.
|
|
665
|
+
* @returns the version to install or null when there is no update to install.
|
|
666
|
+
*/
|
|
667
|
+
async checkCLIVersion(packagesToUpdate, verbose = false, next = false) {
|
|
668
|
+
const { version } = await (0, package_metadata_1.fetchPackageManifest)(`@angular/cli@${this.getCLIUpdateRunnerVersion(typeof packagesToUpdate === 'string' ? [packagesToUpdate] : packagesToUpdate, next)}`, this.context.logger, {
|
|
669
|
+
verbose,
|
|
670
|
+
usingYarn: this.context.packageManager === workspace_schema_1.PackageManager.Yarn,
|
|
671
|
+
});
|
|
672
|
+
return version_1.VERSION.full === version ? null : version;
|
|
673
|
+
}
|
|
674
|
+
getCLIUpdateRunnerVersion(packagesToUpdate, next) {
|
|
675
|
+
var _a, _b;
|
|
676
|
+
if (next) {
|
|
677
|
+
return 'next';
|
|
678
|
+
}
|
|
679
|
+
const updatingAngularPackage = packagesToUpdate === null || packagesToUpdate === void 0 ? void 0 : packagesToUpdate.find((r) => ANGULAR_PACKAGES_REGEXP.test(r));
|
|
680
|
+
if (updatingAngularPackage) {
|
|
681
|
+
// If we are updating any Angular package we can update the CLI to the target version because
|
|
682
|
+
// migrations for @angular/core@13 can be executed using Angular/cli@13.
|
|
683
|
+
// This is same behaviour as `npx @angular/cli@13 update @angular/core@13`.
|
|
684
|
+
// `@angular/cli@13` -> ['', 'angular/cli', '13']
|
|
685
|
+
// `@angular/cli` -> ['', 'angular/cli']
|
|
686
|
+
const tempVersion = coerceVersionNumber(updatingAngularPackage.split('@')[2]);
|
|
687
|
+
return (_b = (_a = semver.parse(tempVersion)) === null || _a === void 0 ? void 0 : _a.major) !== null && _b !== void 0 ? _b : 'latest';
|
|
688
|
+
}
|
|
689
|
+
// When not updating an Angular package we cannot determine which schematic runtime the migration should to be executed in.
|
|
690
|
+
// Typically, we can assume that the `@angular/cli` was updated previously.
|
|
691
|
+
// Example: Angular official packages are typically updated prior to NGRX etc...
|
|
692
|
+
// Therefore, we only update to the latest patch version of the installed major version of the Angular CLI.
|
|
693
|
+
// This is important because we might end up in a scenario where locally Angular v12 is installed, updating NGRX from 11 to 12.
|
|
694
|
+
// We end up using Angular ClI v13 to run the migrations if we run the migrations using the CLI installed major version + 1 logic.
|
|
695
|
+
return version_1.VERSION.major;
|
|
80
696
|
}
|
|
81
697
|
}
|
|
82
698
|
exports.UpdateCommandModule = UpdateCommandModule;
|
|
83
699
|
UpdateCommandModule.scope = command_module_1.CommandScope.In;
|
|
700
|
+
/**
|
|
701
|
+
* @return Whether or not the working directory has Git changes to commit.
|
|
702
|
+
*/
|
|
703
|
+
function hasChangesToCommit() {
|
|
704
|
+
// List all modified files not covered by .gitignore.
|
|
705
|
+
// If any files are returned, then there must be something to commit.
|
|
706
|
+
return (0, child_process_1.execSync)('git ls-files -m -d -o --exclude-standard').toString() !== '';
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Precondition: Must have pending changes to commit, they do not need to be staged.
|
|
710
|
+
* Postcondition: The Git working tree is committed and the repo is clean.
|
|
711
|
+
* @param message The commit message to use.
|
|
712
|
+
*/
|
|
713
|
+
function createCommit(message) {
|
|
714
|
+
// Stage entire working tree for commit.
|
|
715
|
+
(0, child_process_1.execSync)('git add -A', { encoding: 'utf8', stdio: 'pipe' });
|
|
716
|
+
// Commit with the message passed via stdin to avoid bash escaping issues.
|
|
717
|
+
(0, child_process_1.execSync)('git commit --no-verify -F -', { encoding: 'utf8', stdio: 'pipe', input: message });
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* @return The Git SHA hash of the HEAD commit. Returns null if unable to retrieve the hash.
|
|
721
|
+
*/
|
|
722
|
+
function findCurrentGitSha() {
|
|
723
|
+
try {
|
|
724
|
+
return (0, child_process_1.execSync)('git rev-parse HEAD', { encoding: 'utf8', stdio: 'pipe' }).trim();
|
|
725
|
+
}
|
|
726
|
+
catch {
|
|
727
|
+
return null;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
function getShortHash(commitHash) {
|
|
731
|
+
return commitHash.slice(0, 9);
|
|
732
|
+
}
|
|
733
|
+
function coerceVersionNumber(version) {
|
|
734
|
+
var _a;
|
|
735
|
+
if (!version) {
|
|
736
|
+
return undefined;
|
|
737
|
+
}
|
|
738
|
+
if (!/^\d{1,30}\.\d{1,30}\.\d{1,30}/.test(version)) {
|
|
739
|
+
const match = version.match(/^\d{1,30}(\.\d{1,30})*/);
|
|
740
|
+
if (!match) {
|
|
741
|
+
return undefined;
|
|
742
|
+
}
|
|
743
|
+
if (!match[1]) {
|
|
744
|
+
version = version.substring(0, match[0].length) + '.0.0' + version.substring(match[0].length);
|
|
745
|
+
}
|
|
746
|
+
else if (!match[2]) {
|
|
747
|
+
version = version.substring(0, match[0].length) + '.0' + version.substring(match[0].length);
|
|
748
|
+
}
|
|
749
|
+
else {
|
|
750
|
+
return undefined;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
return (_a = semver.valid(version)) !== null && _a !== void 0 ? _a : undefined;
|
|
754
|
+
}
|