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