@angular/cli 22.0.0 → 22.1.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/config/schema.json +4 -6
- package/lib/config/workspace-schema.d.ts +4 -6
- package/lib/config/workspace-schema.js +4 -6
- package/lib/config/workspace-schema.js.map +1 -1
- package/package.json +20 -20
- package/src/commands/add/cli.d.ts +1 -0
- package/src/commands/add/cli.js +56 -1
- package/src/commands/add/cli.js.map +1 -1
- package/src/commands/mcp/resources/best-practices.md +6 -1
- package/src/commands/update/cli.js +96 -104
- package/src/commands/update/cli.js.map +1 -1
- package/src/commands/update/update-resolver.d.ts +62 -0
- package/src/commands/update/update-resolver.js +801 -0
- package/src/commands/update/update-resolver.js.map +1 -0
- package/src/commands/update/utilities/migration.js +5 -2
- package/src/commands/update/utilities/migration.js.map +1 -1
- package/src/package-managers/package-manager.js +64 -0
- package/src/package-managers/package-manager.js.map +1 -1
- package/src/package-managers/parsers.d.ts +0 -5
- package/src/package-managers/parsers.js +43 -1
- package/src/package-managers/parsers.js.map +1 -1
- package/src/utilities/package-metadata.js +0 -13
- package/src/utilities/package-metadata.js.map +1 -1
- package/src/utilities/version.js +1 -1
- package/src/commands/update/schematic/collection.json +0 -9
- package/src/commands/update/schematic/index.d.ts +0 -11
- package/src/commands/update/schematic/index.js +0 -717
- package/src/commands/update/schematic/index.js.map +0 -1
- package/src/commands/update/schematic/schema.d.ts +0 -51
- package/src/commands/update/schematic/schema.js +0 -16
- package/src/commands/update/schematic/schema.js.map +0 -1
- package/src/commands/update/schematic/schema.json +0 -64
|
@@ -1,717 +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.dev/license
|
|
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 () {
|
|
26
|
-
var ownKeys = function(o) {
|
|
27
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
-
var ar = [];
|
|
29
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
-
return ar;
|
|
31
|
-
};
|
|
32
|
-
return ownKeys(o);
|
|
33
|
-
};
|
|
34
|
-
return function (mod) {
|
|
35
|
-
if (mod && mod.__esModule) return mod;
|
|
36
|
-
var result = {};
|
|
37
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
-
__setModuleDefault(result, mod);
|
|
39
|
-
return result;
|
|
40
|
-
};
|
|
41
|
-
})();
|
|
42
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
-
exports.angularMajorCompatGuarantee = angularMajorCompatGuarantee;
|
|
44
|
-
exports.default = default_1;
|
|
45
|
-
const core_1 = require("@angular-devkit/core");
|
|
46
|
-
const schematics_1 = require("@angular-devkit/schematics");
|
|
47
|
-
const npa = __importStar(require("npm-package-arg"));
|
|
48
|
-
const semver = __importStar(require("semver"));
|
|
49
|
-
const package_metadata_1 = require("../../../utilities/package-metadata");
|
|
50
|
-
// Angular guarantees that a major is compatible with its following major (so packages that depend
|
|
51
|
-
// on Angular 5 are also compatible with Angular 6). This is, in code, represented by verifying
|
|
52
|
-
// that all other packages that have a peer dependency of `"@angular/core": "^5.0.0"` actually
|
|
53
|
-
// supports 6.0, by adding that compatibility to the range, so it is `^5.0.0 || ^6.0.0`.
|
|
54
|
-
// We export it to allow for testing.
|
|
55
|
-
function angularMajorCompatGuarantee(range) {
|
|
56
|
-
let newRange = semver.validRange(range);
|
|
57
|
-
if (!newRange) {
|
|
58
|
-
return range;
|
|
59
|
-
}
|
|
60
|
-
let major = 1;
|
|
61
|
-
while (!semver.gtr(major + '.0.0', newRange)) {
|
|
62
|
-
major++;
|
|
63
|
-
if (major >= 99) {
|
|
64
|
-
// Use original range if it supports a major this high
|
|
65
|
-
// Range is most likely unbounded (e.g., >=5.0.0)
|
|
66
|
-
return newRange;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
// Add the major version as compatible with the angular compatible, with all minors. This is
|
|
70
|
-
// already one major above the greatest supported, because we increment `major` before checking.
|
|
71
|
-
// We add minors like this because a minor beta is still compatible with a minor non-beta.
|
|
72
|
-
newRange = range;
|
|
73
|
-
for (let minor = 0; minor < 20; minor++) {
|
|
74
|
-
newRange += ` || ^${major}.${minor}.0-alpha.0 `;
|
|
75
|
-
}
|
|
76
|
-
return semver.validRange(newRange) || range;
|
|
77
|
-
}
|
|
78
|
-
// This is a map of packageGroupName to range extending function. If it isn't found, the range is
|
|
79
|
-
// kept the same.
|
|
80
|
-
const knownPeerCompatibleList = {
|
|
81
|
-
'@angular/core': angularMajorCompatGuarantee,
|
|
82
|
-
};
|
|
83
|
-
function _updatePeerVersion(infoMap, name, range) {
|
|
84
|
-
// Resolve packageGroupName.
|
|
85
|
-
const maybePackageInfo = infoMap.get(name);
|
|
86
|
-
if (!maybePackageInfo) {
|
|
87
|
-
return range;
|
|
88
|
-
}
|
|
89
|
-
if (maybePackageInfo.target) {
|
|
90
|
-
name = maybePackageInfo.target.updateMetadata.packageGroupName || name;
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
name = maybePackageInfo.installed.updateMetadata.packageGroupName || name;
|
|
94
|
-
}
|
|
95
|
-
const maybeTransform = knownPeerCompatibleList[name];
|
|
96
|
-
if (maybeTransform) {
|
|
97
|
-
if (typeof maybeTransform == 'function') {
|
|
98
|
-
return maybeTransform(range);
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
return maybeTransform;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return range;
|
|
105
|
-
}
|
|
106
|
-
function _validateForwardPeerDependencies(name, infoMap, peers, peersMeta, logger, next) {
|
|
107
|
-
let validationFailed = false;
|
|
108
|
-
for (const [peer, range] of Object.entries(peers)) {
|
|
109
|
-
logger.debug(`Checking forward peer ${peer}...`);
|
|
110
|
-
const maybePeerInfo = infoMap.get(peer);
|
|
111
|
-
const isOptional = peersMeta[peer] && !!peersMeta[peer].optional;
|
|
112
|
-
if (!maybePeerInfo) {
|
|
113
|
-
if (!isOptional) {
|
|
114
|
-
logger.warn([
|
|
115
|
-
`Package ${JSON.stringify(name)} has a missing peer dependency of`,
|
|
116
|
-
`${JSON.stringify(peer)} @ ${JSON.stringify(range)}.`,
|
|
117
|
-
].join(' '));
|
|
118
|
-
}
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
const peerVersion = maybePeerInfo.target && maybePeerInfo.target.packageJson.version
|
|
122
|
-
? maybePeerInfo.target.packageJson.version
|
|
123
|
-
: maybePeerInfo.installed.version;
|
|
124
|
-
logger.debug(` Range intersects(${range}, ${peerVersion})...`);
|
|
125
|
-
if (!semver.satisfies(peerVersion, range, { includePrerelease: next || undefined })) {
|
|
126
|
-
logger.error([
|
|
127
|
-
`Package ${JSON.stringify(name)} has an incompatible peer dependency to`,
|
|
128
|
-
`${JSON.stringify(peer)} (requires ${JSON.stringify(range)},`,
|
|
129
|
-
`would install ${JSON.stringify(peerVersion)})`,
|
|
130
|
-
].join(' '));
|
|
131
|
-
validationFailed = true;
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
return validationFailed;
|
|
136
|
-
}
|
|
137
|
-
function _validateReversePeerDependencies(name, version, infoMap, logger, next) {
|
|
138
|
-
for (const [installed, installedInfo] of infoMap.entries()) {
|
|
139
|
-
const installedLogger = logger.createChild(installed);
|
|
140
|
-
installedLogger.debug(`${installed}...`);
|
|
141
|
-
const peers = (installedInfo.target || installedInfo.installed).packageJson.peerDependencies;
|
|
142
|
-
for (const [peer, range] of Object.entries(peers || {})) {
|
|
143
|
-
if (peer != name) {
|
|
144
|
-
// Only check peers to the packages we're updating. We don't care about peers
|
|
145
|
-
// that are unmet but we have no effect on.
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
// Ignore peerDependency mismatches for these packages.
|
|
149
|
-
// They are deprecated and removed via a migration.
|
|
150
|
-
const ignoredPackages = [
|
|
151
|
-
'codelyzer',
|
|
152
|
-
'@schematics/update',
|
|
153
|
-
'@angular-devkit/build-ng-packagr',
|
|
154
|
-
'tsickle',
|
|
155
|
-
'@nguniversal/builders',
|
|
156
|
-
];
|
|
157
|
-
if (ignoredPackages.includes(installed)) {
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
// Override the peer version range if it's known as a compatible.
|
|
161
|
-
const extendedRange = _updatePeerVersion(infoMap, peer, range);
|
|
162
|
-
if (!semver.satisfies(version, extendedRange, { includePrerelease: next || undefined })) {
|
|
163
|
-
logger.error([
|
|
164
|
-
`Package ${JSON.stringify(installed)} has an incompatible peer dependency to`,
|
|
165
|
-
`${JSON.stringify(name)} (requires`,
|
|
166
|
-
`${JSON.stringify(range)}${extendedRange == range ? '' : ' (extended)'},`,
|
|
167
|
-
`would install ${JSON.stringify(version)}).`,
|
|
168
|
-
].join(' '));
|
|
169
|
-
return true;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return false;
|
|
174
|
-
}
|
|
175
|
-
function _validateUpdatePackages(infoMap, force, next, logger) {
|
|
176
|
-
logger.debug('Updating the following packages:');
|
|
177
|
-
infoMap.forEach((info) => {
|
|
178
|
-
if (info.target) {
|
|
179
|
-
logger.debug(` ${info.name} => ${info.target.version}`);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
let peerErrors = false;
|
|
183
|
-
infoMap.forEach((info) => {
|
|
184
|
-
const { name, target } = info;
|
|
185
|
-
if (!target) {
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
const pkgLogger = logger.createChild(name);
|
|
189
|
-
logger.debug(`${name}...`);
|
|
190
|
-
const { peerDependencies = {}, peerDependenciesMeta = {} } = target.packageJson;
|
|
191
|
-
peerErrors =
|
|
192
|
-
_validateForwardPeerDependencies(name, infoMap, peerDependencies, peerDependenciesMeta, pkgLogger, next) || peerErrors;
|
|
193
|
-
peerErrors =
|
|
194
|
-
_validateReversePeerDependencies(name, target.version, infoMap, pkgLogger, next) ||
|
|
195
|
-
peerErrors;
|
|
196
|
-
});
|
|
197
|
-
if (!force && peerErrors) {
|
|
198
|
-
throw new schematics_1.SchematicsException('Incompatible peer dependencies found.\n' +
|
|
199
|
-
'Peer dependency warnings when installing dependencies means that those dependencies might not work correctly together.\n' +
|
|
200
|
-
`You can use the '--force' option to ignore incompatible peer dependencies and instead address these warnings later.`);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
function _performUpdate(tree, context, infoMap, logger, migrateOnly) {
|
|
204
|
-
const packageJsonContent = tree.read('/package.json')?.toString();
|
|
205
|
-
if (!packageJsonContent) {
|
|
206
|
-
throw new schematics_1.SchematicsException('Could not find a package.json. Are you in a Node project?');
|
|
207
|
-
}
|
|
208
|
-
const packageJson = tree.readJson('/package.json');
|
|
209
|
-
const updateDependency = (deps, name, newVersion) => {
|
|
210
|
-
const oldVersion = deps[name];
|
|
211
|
-
// We only respect caret and tilde ranges on update.
|
|
212
|
-
const execResult = /^[\^~]/.exec(oldVersion);
|
|
213
|
-
deps[name] = `${execResult ? execResult[0] : ''}${newVersion}`;
|
|
214
|
-
};
|
|
215
|
-
const toInstall = [...infoMap.values()]
|
|
216
|
-
.map((x) => [x.name, x.target, x.installed])
|
|
217
|
-
.filter(([name, target, installed]) => {
|
|
218
|
-
return !!name && !!target && !!installed;
|
|
219
|
-
});
|
|
220
|
-
toInstall.forEach(([name, target, installed]) => {
|
|
221
|
-
logger.info(`Updating package.json with dependency ${name} ` +
|
|
222
|
-
`@ ${JSON.stringify(target.version)} (was ${JSON.stringify(installed.version)})...`);
|
|
223
|
-
if (packageJson.dependencies && packageJson.dependencies[name]) {
|
|
224
|
-
updateDependency(packageJson.dependencies, name, target.version);
|
|
225
|
-
if (packageJson.devDependencies && packageJson.devDependencies[name]) {
|
|
226
|
-
delete packageJson.devDependencies[name];
|
|
227
|
-
}
|
|
228
|
-
if (packageJson.peerDependencies && packageJson.peerDependencies[name]) {
|
|
229
|
-
delete packageJson.peerDependencies[name];
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
else if (packageJson.devDependencies && packageJson.devDependencies[name]) {
|
|
233
|
-
updateDependency(packageJson.devDependencies, name, target.version);
|
|
234
|
-
if (packageJson.peerDependencies && packageJson.peerDependencies[name]) {
|
|
235
|
-
delete packageJson.peerDependencies[name];
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
else if (packageJson.peerDependencies && packageJson.peerDependencies[name]) {
|
|
239
|
-
updateDependency(packageJson.peerDependencies, name, target.version);
|
|
240
|
-
}
|
|
241
|
-
else {
|
|
242
|
-
logger.warn(`Package ${name} was not found in dependencies.`);
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
const eofMatches = packageJsonContent.match(/\r?\n$/);
|
|
246
|
-
const eof = eofMatches?.[0] ?? '';
|
|
247
|
-
const newContent = JSON.stringify(packageJson, null, 2) + eof;
|
|
248
|
-
if (packageJsonContent != newContent || migrateOnly) {
|
|
249
|
-
if (!migrateOnly) {
|
|
250
|
-
tree.overwrite('/package.json', newContent);
|
|
251
|
-
}
|
|
252
|
-
const externalMigrations = [];
|
|
253
|
-
// Run the migrate schematics with the list of packages to use. The collection contains
|
|
254
|
-
// version information and we need to do this post installation. Please note that the
|
|
255
|
-
// migration COULD fail and leave side effects on disk.
|
|
256
|
-
// Run the schematics task of those packages.
|
|
257
|
-
toInstall.forEach(([name, target, installed]) => {
|
|
258
|
-
if (!target.updateMetadata.migrations) {
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
externalMigrations.push({
|
|
262
|
-
package: name,
|
|
263
|
-
collection: target.updateMetadata.migrations,
|
|
264
|
-
from: installed.version,
|
|
265
|
-
to: target.version,
|
|
266
|
-
});
|
|
267
|
-
return;
|
|
268
|
-
});
|
|
269
|
-
if (externalMigrations.length > 0) {
|
|
270
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
271
|
-
global.externalMigrations = externalMigrations;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
function _getUpdateMetadata(packageJson, logger) {
|
|
276
|
-
const metadata = packageJson['ng-update'];
|
|
277
|
-
const result = {
|
|
278
|
-
packageGroup: {},
|
|
279
|
-
requirements: {},
|
|
280
|
-
};
|
|
281
|
-
if (!metadata || typeof metadata != 'object' || Array.isArray(metadata)) {
|
|
282
|
-
return result;
|
|
283
|
-
}
|
|
284
|
-
if (metadata['packageGroup']) {
|
|
285
|
-
const packageGroup = metadata['packageGroup'];
|
|
286
|
-
// Verify that packageGroup is an array of strings or an map of versions. This is not an error
|
|
287
|
-
// but we still warn the user and ignore the packageGroup keys.
|
|
288
|
-
if (Array.isArray(packageGroup) && packageGroup.every((x) => typeof x == 'string')) {
|
|
289
|
-
result.packageGroup = packageGroup.reduce((group, name) => {
|
|
290
|
-
group[name] = packageJson.version;
|
|
291
|
-
return group;
|
|
292
|
-
}, result.packageGroup);
|
|
293
|
-
}
|
|
294
|
-
else if (typeof packageGroup == 'object' &&
|
|
295
|
-
packageGroup &&
|
|
296
|
-
!Array.isArray(packageGroup) &&
|
|
297
|
-
Object.values(packageGroup).every((x) => typeof x == 'string')) {
|
|
298
|
-
result.packageGroup = packageGroup;
|
|
299
|
-
}
|
|
300
|
-
else {
|
|
301
|
-
logger.warn(`packageGroup metadata of package ${packageJson.name} is malformed. Ignoring.`);
|
|
302
|
-
}
|
|
303
|
-
result.packageGroupName = Object.keys(result.packageGroup)[0];
|
|
304
|
-
}
|
|
305
|
-
if (typeof metadata['packageGroupName'] == 'string') {
|
|
306
|
-
result.packageGroupName = metadata['packageGroupName'];
|
|
307
|
-
}
|
|
308
|
-
if (metadata['migrations']) {
|
|
309
|
-
const migrations = metadata['migrations'];
|
|
310
|
-
if (typeof migrations != 'string') {
|
|
311
|
-
logger.warn(`migrations metadata of package ${packageJson.name} is malformed. Ignoring.`);
|
|
312
|
-
}
|
|
313
|
-
else {
|
|
314
|
-
result.migrations = migrations;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
return result;
|
|
318
|
-
}
|
|
319
|
-
function _usageMessage(options, infoMap, logger) {
|
|
320
|
-
const packageGroups = new Map();
|
|
321
|
-
const packagesToUpdate = [...infoMap.entries()]
|
|
322
|
-
.map(([name, info]) => {
|
|
323
|
-
let tag = options.next
|
|
324
|
-
? info.npmPackageJson['dist-tags']['next']
|
|
325
|
-
? 'next'
|
|
326
|
-
: 'latest'
|
|
327
|
-
: 'latest';
|
|
328
|
-
let version = info.npmPackageJson['dist-tags'][tag];
|
|
329
|
-
let target = info.npmPackageJson.versions[version];
|
|
330
|
-
const versionDiff = semver.diff(info.installed.version, version);
|
|
331
|
-
if (versionDiff !== 'patch' &&
|
|
332
|
-
versionDiff !== 'minor' &&
|
|
333
|
-
/^@(?:angular|nguniversal)\//.test(name)) {
|
|
334
|
-
const installedMajorVersion = semver.parse(info.installed.version)?.major;
|
|
335
|
-
const toInstallMajorVersion = semver.parse(version)?.major;
|
|
336
|
-
if (installedMajorVersion !== undefined &&
|
|
337
|
-
toInstallMajorVersion !== undefined &&
|
|
338
|
-
installedMajorVersion < toInstallMajorVersion - 1) {
|
|
339
|
-
const nextMajorVersion = `${installedMajorVersion + 1}.`;
|
|
340
|
-
const nextMajorVersions = Object.keys(info.npmPackageJson.versions)
|
|
341
|
-
.filter((v) => v.startsWith(nextMajorVersion))
|
|
342
|
-
.sort((a, b) => (a > b ? -1 : 1));
|
|
343
|
-
if (nextMajorVersions.length) {
|
|
344
|
-
version = nextMajorVersions[0];
|
|
345
|
-
target = info.npmPackageJson.versions[version];
|
|
346
|
-
tag = '';
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
return {
|
|
351
|
-
name,
|
|
352
|
-
info,
|
|
353
|
-
version,
|
|
354
|
-
tag,
|
|
355
|
-
target,
|
|
356
|
-
};
|
|
357
|
-
})
|
|
358
|
-
.filter(({ info, version, target }) => target?.['ng-update'] && semver.compare(info.installed.version, version) < 0)
|
|
359
|
-
.map(({ name, info, version, tag, target }) => {
|
|
360
|
-
// Look for packageGroup.
|
|
361
|
-
const ngUpdate = target['ng-update'];
|
|
362
|
-
const packageGroup = ngUpdate?.['packageGroup'];
|
|
363
|
-
if (packageGroup) {
|
|
364
|
-
const packageGroupNames = Array.isArray(packageGroup)
|
|
365
|
-
? packageGroup
|
|
366
|
-
: Object.keys(packageGroup);
|
|
367
|
-
const packageGroupName = ngUpdate?.['packageGroupName'] || packageGroupNames.find((n) => infoMap.has(n));
|
|
368
|
-
if (packageGroupName) {
|
|
369
|
-
if (packageGroups.has(name)) {
|
|
370
|
-
return null;
|
|
371
|
-
}
|
|
372
|
-
for (const groupName of packageGroupNames) {
|
|
373
|
-
packageGroups.set(groupName, packageGroupName);
|
|
374
|
-
}
|
|
375
|
-
packageGroups.set(packageGroupName, packageGroupName);
|
|
376
|
-
name = packageGroupName;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
let command = `ng update ${name}`;
|
|
380
|
-
if (!tag) {
|
|
381
|
-
command += `@${semver.parse(version)?.major || version}`;
|
|
382
|
-
}
|
|
383
|
-
else if (tag == 'next') {
|
|
384
|
-
command += ' --next';
|
|
385
|
-
}
|
|
386
|
-
return [name, `${info.installed.version} -> ${version} `, command];
|
|
387
|
-
})
|
|
388
|
-
.filter((x) => x !== null)
|
|
389
|
-
.sort((a, b) => (a && b ? a[0].localeCompare(b[0]) : 0));
|
|
390
|
-
if (packagesToUpdate.length == 0) {
|
|
391
|
-
logger.info('We analyzed your package.json and everything seems to be in order. Good work!');
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
|
-
logger.info('We analyzed your package.json, there are some packages to update:\n');
|
|
395
|
-
// Find the largest name to know the padding needed.
|
|
396
|
-
let namePad = Math.max(...[...infoMap.keys()].map((x) => x.length)) + 2;
|
|
397
|
-
if (!Number.isFinite(namePad)) {
|
|
398
|
-
namePad = 30;
|
|
399
|
-
}
|
|
400
|
-
const pads = [namePad, 25, 0];
|
|
401
|
-
logger.info(' ' + ['Name', 'Version', 'Command to update'].map((x, i) => x.padEnd(pads[i])).join(''));
|
|
402
|
-
const totalWidth = pads.reduce((sum, width) => sum + width, 20);
|
|
403
|
-
logger.info(` ${'-'.repeat(totalWidth)}`);
|
|
404
|
-
packagesToUpdate.forEach((fields) => {
|
|
405
|
-
if (!fields) {
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
logger.info(' ' + fields.map((x, i) => x.padEnd(pads[i])).join(''));
|
|
409
|
-
});
|
|
410
|
-
logger.info(`\nThere might be additional packages which don't provide 'ng update' capabilities that are outdated.\n` +
|
|
411
|
-
`You can update the additional packages by running the update command of your package manager.`);
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
function _buildPackageInfo(tree, packages, allDependencies, npmPackageJson, logger) {
|
|
415
|
-
const name = npmPackageJson.name;
|
|
416
|
-
const packageJsonRange = allDependencies.get(name);
|
|
417
|
-
if (!packageJsonRange) {
|
|
418
|
-
throw new schematics_1.SchematicsException(`Package ${JSON.stringify(name)} was not found in package.json.`);
|
|
419
|
-
}
|
|
420
|
-
// Find out the currently installed version. Either from the package.json or the node_modules/
|
|
421
|
-
// TODO: figure out a way to read package-lock.json and/or yarn.lock.
|
|
422
|
-
const pkgJsonPath = `/node_modules/${name}/package.json`;
|
|
423
|
-
const pkgJsonExists = tree.exists(pkgJsonPath);
|
|
424
|
-
let installedVersion;
|
|
425
|
-
if (pkgJsonExists) {
|
|
426
|
-
const { version } = tree.readJson(pkgJsonPath);
|
|
427
|
-
installedVersion = version;
|
|
428
|
-
}
|
|
429
|
-
const packageVersionsNonDeprecated = [];
|
|
430
|
-
const packageVersionsDeprecated = [];
|
|
431
|
-
for (const [version, { deprecated }] of Object.entries(npmPackageJson.versions)) {
|
|
432
|
-
if (deprecated) {
|
|
433
|
-
packageVersionsDeprecated.push(version);
|
|
434
|
-
}
|
|
435
|
-
else {
|
|
436
|
-
packageVersionsNonDeprecated.push(version);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
const findSatisfyingVersion = (targetVersion) => (semver.maxSatisfying(packageVersionsNonDeprecated, targetVersion) ??
|
|
440
|
-
semver.maxSatisfying(packageVersionsDeprecated, targetVersion)) ??
|
|
441
|
-
undefined;
|
|
442
|
-
if (!installedVersion) {
|
|
443
|
-
// Find the version from NPM that fits the range to max.
|
|
444
|
-
installedVersion = findSatisfyingVersion(packageJsonRange);
|
|
445
|
-
}
|
|
446
|
-
if (!installedVersion) {
|
|
447
|
-
throw new schematics_1.SchematicsException(`An unexpected error happened; could not determine version for package ${name}.`);
|
|
448
|
-
}
|
|
449
|
-
const installedPackageJson = npmPackageJson.versions[installedVersion] || pkgJsonExists;
|
|
450
|
-
if (!installedPackageJson) {
|
|
451
|
-
throw new schematics_1.SchematicsException(`An unexpected error happened; package ${name} has no version ${installedVersion}.`);
|
|
452
|
-
}
|
|
453
|
-
let targetVersion = packages.get(name);
|
|
454
|
-
if (targetVersion) {
|
|
455
|
-
if (npmPackageJson['dist-tags'][targetVersion]) {
|
|
456
|
-
targetVersion = npmPackageJson['dist-tags'][targetVersion];
|
|
457
|
-
}
|
|
458
|
-
else if (targetVersion == 'next') {
|
|
459
|
-
targetVersion = npmPackageJson['dist-tags']['latest'];
|
|
460
|
-
}
|
|
461
|
-
else {
|
|
462
|
-
targetVersion = findSatisfyingVersion(targetVersion);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
if (targetVersion && semver.lte(targetVersion, installedVersion)) {
|
|
466
|
-
logger.debug(`Package ${name} already satisfied by package.json (${packageJsonRange}).`);
|
|
467
|
-
targetVersion = undefined;
|
|
468
|
-
}
|
|
469
|
-
const target = targetVersion
|
|
470
|
-
? {
|
|
471
|
-
version: targetVersion,
|
|
472
|
-
packageJson: npmPackageJson.versions[targetVersion],
|
|
473
|
-
updateMetadata: _getUpdateMetadata(npmPackageJson.versions[targetVersion], logger),
|
|
474
|
-
}
|
|
475
|
-
: undefined;
|
|
476
|
-
// Check if there's an installed version.
|
|
477
|
-
return {
|
|
478
|
-
name,
|
|
479
|
-
npmPackageJson,
|
|
480
|
-
installed: {
|
|
481
|
-
version: installedVersion,
|
|
482
|
-
packageJson: installedPackageJson,
|
|
483
|
-
updateMetadata: _getUpdateMetadata(installedPackageJson, logger),
|
|
484
|
-
},
|
|
485
|
-
target,
|
|
486
|
-
packageJsonRange,
|
|
487
|
-
};
|
|
488
|
-
}
|
|
489
|
-
function _buildPackageList(options, projectDeps, logger) {
|
|
490
|
-
// Parse the packages options to set the targeted version.
|
|
491
|
-
const packages = new Map();
|
|
492
|
-
const commandLinePackages = options.packages && options.packages.length > 0 ? options.packages : [];
|
|
493
|
-
for (const pkg of commandLinePackages) {
|
|
494
|
-
// Split the version asked on command line.
|
|
495
|
-
const m = pkg.match(/^((?:@[^/]{1,100}\/)?[^@]{1,100})(?:@(.{1,100}))?$/);
|
|
496
|
-
if (!m) {
|
|
497
|
-
logger.warn(`Invalid package argument: ${JSON.stringify(pkg)}. Skipping.`);
|
|
498
|
-
continue;
|
|
499
|
-
}
|
|
500
|
-
const [, npmName, maybeVersion] = m;
|
|
501
|
-
const version = projectDeps.get(npmName);
|
|
502
|
-
if (!version) {
|
|
503
|
-
logger.warn(`Package not installed: ${JSON.stringify(npmName)}. Skipping.`);
|
|
504
|
-
continue;
|
|
505
|
-
}
|
|
506
|
-
packages.set(npmName, (maybeVersion || (options.next ? 'next' : 'latest')));
|
|
507
|
-
}
|
|
508
|
-
return packages;
|
|
509
|
-
}
|
|
510
|
-
function _addPackageGroup(tree, packages, allDependencies, npmPackageJson, logger) {
|
|
511
|
-
const maybePackage = packages.get(npmPackageJson.name);
|
|
512
|
-
if (!maybePackage) {
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
const info = _buildPackageInfo(tree, packages, allDependencies, npmPackageJson, logger);
|
|
516
|
-
const version = (info.target && info.target.version) ||
|
|
517
|
-
npmPackageJson['dist-tags'][maybePackage] ||
|
|
518
|
-
maybePackage;
|
|
519
|
-
if (!npmPackageJson.versions[version]) {
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
const ngUpdateMetadata = npmPackageJson.versions[version]['ng-update'];
|
|
523
|
-
if (!ngUpdateMetadata) {
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
const packageGroup = ngUpdateMetadata['packageGroup'];
|
|
527
|
-
if (!packageGroup) {
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
|
-
let packageGroupNormalized;
|
|
531
|
-
if (Array.isArray(packageGroup) && !packageGroup.some((x) => typeof x != 'string')) {
|
|
532
|
-
packageGroupNormalized = packageGroup.reduce((acc, curr) => {
|
|
533
|
-
acc[curr] = maybePackage;
|
|
534
|
-
return acc;
|
|
535
|
-
}, {});
|
|
536
|
-
}
|
|
537
|
-
else if (typeof packageGroup == 'object' &&
|
|
538
|
-
packageGroup &&
|
|
539
|
-
!Array.isArray(packageGroup) &&
|
|
540
|
-
Object.values(packageGroup).every((x) => typeof x == 'string')) {
|
|
541
|
-
packageGroupNormalized = packageGroup;
|
|
542
|
-
}
|
|
543
|
-
else {
|
|
544
|
-
logger.warn(`packageGroup metadata of package ${npmPackageJson.name} is malformed. Ignoring.`);
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
for (const [name, value] of Object.entries(packageGroupNormalized)) {
|
|
548
|
-
// Don't override names from the command line.
|
|
549
|
-
// Remove packages that aren't installed.
|
|
550
|
-
if (!packages.has(name) && allDependencies.has(name)) {
|
|
551
|
-
packages.set(name, value);
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
/**
|
|
556
|
-
* Add peer dependencies of packages on the command line to the list of packages to update.
|
|
557
|
-
* We don't do verification of the versions here as this will be done by a later step (and can
|
|
558
|
-
* be ignored by the --force flag).
|
|
559
|
-
* @private
|
|
560
|
-
*/
|
|
561
|
-
function _addPeerDependencies(tree, packages, allDependencies, npmPackageJson, npmPackageJsonMap, logger) {
|
|
562
|
-
const maybePackage = packages.get(npmPackageJson.name);
|
|
563
|
-
if (!maybePackage) {
|
|
564
|
-
return;
|
|
565
|
-
}
|
|
566
|
-
const info = _buildPackageInfo(tree, packages, allDependencies, npmPackageJson, logger);
|
|
567
|
-
const version = (info.target && info.target.version) ||
|
|
568
|
-
npmPackageJson['dist-tags'][maybePackage] ||
|
|
569
|
-
maybePackage;
|
|
570
|
-
if (!npmPackageJson.versions[version]) {
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
const packageJson = npmPackageJson.versions[version];
|
|
574
|
-
const error = false;
|
|
575
|
-
for (const [peer, range] of Object.entries(packageJson.peerDependencies || {})) {
|
|
576
|
-
if (packages.has(peer)) {
|
|
577
|
-
continue;
|
|
578
|
-
}
|
|
579
|
-
const peerPackageJson = npmPackageJsonMap.get(peer);
|
|
580
|
-
if (peerPackageJson) {
|
|
581
|
-
const peerInfo = _buildPackageInfo(tree, packages, allDependencies, peerPackageJson, logger);
|
|
582
|
-
if (semver.satisfies(peerInfo.installed.version, range)) {
|
|
583
|
-
continue;
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
packages.set(peer, range);
|
|
587
|
-
}
|
|
588
|
-
if (error) {
|
|
589
|
-
throw new schematics_1.SchematicsException('An error occured, see above.');
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
function _getAllDependencies(tree) {
|
|
593
|
-
const { dependencies, devDependencies, peerDependencies } = tree.readJson('/package.json');
|
|
594
|
-
return [
|
|
595
|
-
...Object.entries(peerDependencies || {}),
|
|
596
|
-
...Object.entries(devDependencies || {}),
|
|
597
|
-
...Object.entries(dependencies || {}),
|
|
598
|
-
];
|
|
599
|
-
}
|
|
600
|
-
function _formatVersion(version) {
|
|
601
|
-
if (version === undefined) {
|
|
602
|
-
return undefined;
|
|
603
|
-
}
|
|
604
|
-
if (!version.match(/^\d{1,30}\.\d{1,30}\.\d{1,30}/)) {
|
|
605
|
-
version += '.0';
|
|
606
|
-
}
|
|
607
|
-
if (!version.match(/^\d{1,30}\.\d{1,30}\.\d{1,30}/)) {
|
|
608
|
-
version += '.0';
|
|
609
|
-
}
|
|
610
|
-
if (!semver.valid(version)) {
|
|
611
|
-
throw new schematics_1.SchematicsException(`Invalid migration version: ${JSON.stringify(version)}`);
|
|
612
|
-
}
|
|
613
|
-
return version;
|
|
614
|
-
}
|
|
615
|
-
/**
|
|
616
|
-
* Returns whether or not the given package specifier (the value string in a
|
|
617
|
-
* `package.json` dependency) is hosted in the NPM registry.
|
|
618
|
-
* @throws When the specifier cannot be parsed.
|
|
619
|
-
*/
|
|
620
|
-
function isPkgFromRegistry(name, specifier) {
|
|
621
|
-
const result = npa.resolve(name, specifier);
|
|
622
|
-
return !!result.registry;
|
|
623
|
-
}
|
|
624
|
-
function default_1(options) {
|
|
625
|
-
if (!options.packages) {
|
|
626
|
-
// We cannot just return this because we need to fetch the packages from NPM still for the
|
|
627
|
-
// help/guide to show.
|
|
628
|
-
options.packages = [];
|
|
629
|
-
}
|
|
630
|
-
else {
|
|
631
|
-
// We split every packages by commas to allow people to pass in multiple and make it an array.
|
|
632
|
-
options.packages = options.packages.reduce((acc, curr) => {
|
|
633
|
-
return acc.concat(curr.split(','));
|
|
634
|
-
}, []);
|
|
635
|
-
}
|
|
636
|
-
if (options.migrateOnly && options.from) {
|
|
637
|
-
if (options.packages.length !== 1) {
|
|
638
|
-
throw new schematics_1.SchematicsException('--from requires that only a single package be passed.');
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
options.from = _formatVersion(options.from);
|
|
642
|
-
options.to = _formatVersion(options.to);
|
|
643
|
-
const usingYarn = options.packageManager === 'yarn';
|
|
644
|
-
return async (tree, context) => {
|
|
645
|
-
const logger = context.logger;
|
|
646
|
-
const npmDeps = new Map(_getAllDependencies(tree).filter(([name, specifier]) => {
|
|
647
|
-
try {
|
|
648
|
-
return isPkgFromRegistry(name, specifier);
|
|
649
|
-
}
|
|
650
|
-
catch {
|
|
651
|
-
logger.warn(`Package ${name} was not found on the registry. Skipping.`);
|
|
652
|
-
return false;
|
|
653
|
-
}
|
|
654
|
-
}));
|
|
655
|
-
const packages = _buildPackageList(options, npmDeps, logger);
|
|
656
|
-
// Grab all package.json from the npm repository. This requires a lot of HTTP calls so we
|
|
657
|
-
// try to parallelize as many as possible.
|
|
658
|
-
const allPackageMetadata = await Promise.all(Array.from(npmDeps.keys()).map((depName) => (0, package_metadata_1.getNpmPackageJson)(depName, logger, {
|
|
659
|
-
registry: options.registry,
|
|
660
|
-
usingYarn,
|
|
661
|
-
verbose: options.verbose,
|
|
662
|
-
})));
|
|
663
|
-
// Build a map of all dependencies and their packageJson.
|
|
664
|
-
const npmPackageJsonMap = allPackageMetadata.reduce((acc, npmPackageJson) => {
|
|
665
|
-
// If the package was not found on the registry. It could be private, so we will just
|
|
666
|
-
// ignore. If the package was part of the list, we will error out, but will simply ignore
|
|
667
|
-
// if it's either not requested (so just part of package.json. silently).
|
|
668
|
-
if (!npmPackageJson.name) {
|
|
669
|
-
if (npmPackageJson.requestedName && packages.has(npmPackageJson.requestedName)) {
|
|
670
|
-
throw new schematics_1.SchematicsException(`Package ${JSON.stringify(npmPackageJson.requestedName)} was not found on the ` +
|
|
671
|
-
'registry. Cannot continue as this may be an error.');
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
else {
|
|
675
|
-
// If a name is present, it is assumed to be fully populated
|
|
676
|
-
acc.set(npmPackageJson.name, npmPackageJson);
|
|
677
|
-
}
|
|
678
|
-
return acc;
|
|
679
|
-
}, new Map());
|
|
680
|
-
// Augment the command line package list with packageGroups and forward peer dependencies.
|
|
681
|
-
// Each added package may uncover new package groups and peer dependencies, so we must
|
|
682
|
-
// repeat this process until the package list stabilizes.
|
|
683
|
-
let lastPackagesSize;
|
|
684
|
-
do {
|
|
685
|
-
lastPackagesSize = packages.size;
|
|
686
|
-
let lastGroupSize;
|
|
687
|
-
do {
|
|
688
|
-
lastGroupSize = packages.size;
|
|
689
|
-
npmPackageJsonMap.forEach((npmPackageJson) => {
|
|
690
|
-
_addPackageGroup(tree, packages, npmDeps, npmPackageJson, logger);
|
|
691
|
-
});
|
|
692
|
-
} while (packages.size > lastGroupSize);
|
|
693
|
-
// This is done in seperate loop to ensure that package groups are added before peer dependencies.
|
|
694
|
-
npmPackageJsonMap.forEach((npmPackageJson) => {
|
|
695
|
-
_addPeerDependencies(tree, packages, npmDeps, npmPackageJson, npmPackageJsonMap, logger);
|
|
696
|
-
});
|
|
697
|
-
} while (packages.size > lastPackagesSize);
|
|
698
|
-
// Build the PackageInfo for each module.
|
|
699
|
-
const packageInfoMap = new Map();
|
|
700
|
-
npmPackageJsonMap.forEach((npmPackageJson) => {
|
|
701
|
-
packageInfoMap.set(npmPackageJson.name, _buildPackageInfo(tree, packages, npmDeps, npmPackageJson, logger));
|
|
702
|
-
});
|
|
703
|
-
// Now that we have all the information, check the flags.
|
|
704
|
-
if (packages.size > 0) {
|
|
705
|
-
if (options.migrateOnly && options.from && options.packages) {
|
|
706
|
-
return;
|
|
707
|
-
}
|
|
708
|
-
const sublog = new core_1.logging.LevelCapLogger('validation', logger.createChild(''), 'warn');
|
|
709
|
-
_validateUpdatePackages(packageInfoMap, !!options.force, !!options.next, sublog);
|
|
710
|
-
_performUpdate(tree, context, packageInfoMap, logger, !!options.migrateOnly);
|
|
711
|
-
}
|
|
712
|
-
else {
|
|
713
|
-
_usageMessage(options, packageInfoMap, logger);
|
|
714
|
-
}
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
//# sourceMappingURL=index.js.map
|