@depup/angular__cli 22.0.8-depup.2 → 22.1.0-depup.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/README.md +5 -12
- package/changes.json +4 -32
- package/lib/config/schema.json +4 -6
- package/lib/config/workspace-schema.d.ts +5 -7
- package/lib/config/workspace-schema.js +4 -6
- package/lib/config/workspace-schema.js.map +1 -1
- package/package.json +22 -54
- package/src/commands/cache/utilities.js +41 -3
- package/src/commands/cache/utilities.js.map +1 -1
- package/src/commands/mcp/resources/best-practices.md +2 -0
- package/src/commands/mcp/tools/devserver/devserver-start.js +1 -1
- package/src/commands/mcp/tools/devserver/devserver-start.js.map +1 -1
- package/src/commands/mcp/tools/devserver/devserver-stop.js +1 -1
- package/src/commands/mcp/tools/devserver/devserver-stop.js.map +1 -1
- package/src/commands/mcp/tools/doc-search.js +51 -46
- package/src/commands/mcp/tools/doc-search.js.map +1 -1
- package/src/commands/update/cli.js +118 -105
- package/src/commands/update/cli.js.map +1 -1
- package/src/commands/update/update-resolver.d.ts +75 -0
- package/src/commands/update/update-resolver.js +919 -0
- package/src/commands/update/update-resolver.js.map +1 -0
- package/src/package-managers/host.js +18 -7
- package/src/package-managers/host.js.map +1 -1
- package/src/package-managers/package-manager-descriptor.d.ts +11 -1
- package/src/package-managers/package-manager-descriptor.js +6 -0
- package/src/package-managers/package-manager-descriptor.js.map +1 -1
- package/src/package-managers/package-manager.d.ts +5 -0
- package/src/package-managers/package-manager.js +29 -0
- package/src/package-managers/package-manager.js.map +1 -1
- package/src/package-managers/package-metadata.d.ts +1 -1
- package/src/package-managers/parsers.d.ts +20 -5
- package/src/package-managers/parsers.js +129 -1
- package/src/package-managers/parsers.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 -885
- package/src/commands/update/schematic/index.js.map +0 -1
- package/src/commands/update/schematic/schema.d.ts +0 -55
- 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 -68
- package/src/utilities/package-metadata.d.ts +0 -48
- package/src/utilities/package-metadata.js +0 -203
- package/src/utilities/package-metadata.js.map +0 -1
|
@@ -0,0 +1,919 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
43
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
44
|
+
};
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.RegistryClient = void 0;
|
|
47
|
+
exports.getSatisfyingVersion = getSatisfyingVersion;
|
|
48
|
+
exports.angularMajorCompatGuarantee = angularMajorCompatGuarantee;
|
|
49
|
+
exports.isPnpActive = isPnpActive;
|
|
50
|
+
exports.findPackageJson = findPackageJson;
|
|
51
|
+
exports.resolveUserUpdatePlan = resolveUserUpdatePlan;
|
|
52
|
+
exports.printUpdateUsageMessage = printUpdateUsageMessage;
|
|
53
|
+
exports.applyUpdatePlan = applyUpdatePlan;
|
|
54
|
+
const core_1 = require("@angular-devkit/core");
|
|
55
|
+
const node_fs_1 = require("node:fs");
|
|
56
|
+
const node_module_1 = require("node:module");
|
|
57
|
+
const path = __importStar(require("node:path"));
|
|
58
|
+
const npm_package_arg_1 = __importDefault(require("npm-package-arg"));
|
|
59
|
+
const semver = __importStar(require("semver"));
|
|
60
|
+
class RegistryClient {
|
|
61
|
+
packageManager;
|
|
62
|
+
logger;
|
|
63
|
+
minReleaseAge;
|
|
64
|
+
getRegistryName;
|
|
65
|
+
metadataCache = new Map();
|
|
66
|
+
manifestCache = new Map();
|
|
67
|
+
constructor(packageManager, logger, minReleaseAge = 0, getRegistryName) {
|
|
68
|
+
this.packageManager = packageManager;
|
|
69
|
+
this.logger = logger;
|
|
70
|
+
this.minReleaseAge = minReleaseAge;
|
|
71
|
+
this.getRegistryName = getRegistryName;
|
|
72
|
+
}
|
|
73
|
+
async getMetadata(packageName) {
|
|
74
|
+
const registryName = this.getRegistryName ? this.getRegistryName(packageName) : packageName;
|
|
75
|
+
let promise = this.metadataCache.get(registryName);
|
|
76
|
+
if (!promise) {
|
|
77
|
+
promise = this.packageManager.getRegistryMetadata(registryName).catch((e) => {
|
|
78
|
+
this.metadataCache.delete(registryName);
|
|
79
|
+
throw e;
|
|
80
|
+
});
|
|
81
|
+
this.metadataCache.set(registryName, promise);
|
|
82
|
+
}
|
|
83
|
+
const metadata = await promise;
|
|
84
|
+
if (metadata && registryName !== packageName) {
|
|
85
|
+
return { ...metadata, name: packageName };
|
|
86
|
+
}
|
|
87
|
+
return metadata;
|
|
88
|
+
}
|
|
89
|
+
async getManifest(packageName, version) {
|
|
90
|
+
const registryName = this.getRegistryName ? this.getRegistryName(packageName) : packageName;
|
|
91
|
+
const key = `${registryName}@${version}`;
|
|
92
|
+
let promise = this.manifestCache.get(key);
|
|
93
|
+
if (!promise) {
|
|
94
|
+
promise = this.packageManager.getRegistryManifest(registryName, version).catch((e) => {
|
|
95
|
+
this.manifestCache.delete(key);
|
|
96
|
+
throw e;
|
|
97
|
+
});
|
|
98
|
+
this.manifestCache.set(key, promise);
|
|
99
|
+
}
|
|
100
|
+
const manifest = await promise;
|
|
101
|
+
if (manifest && registryName !== packageName) {
|
|
102
|
+
return { ...manifest, name: packageName };
|
|
103
|
+
}
|
|
104
|
+
return manifest;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.RegistryClient = RegistryClient;
|
|
108
|
+
function isReleaseAgeSatisfied(registryClient, metadata, version) {
|
|
109
|
+
const minReleaseAge = registryClient.minReleaseAge;
|
|
110
|
+
if (!minReleaseAge || !metadata.time) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
const publishTimeStr = metadata.time[version];
|
|
114
|
+
if (!publishTimeStr) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
const publishTime = Date.parse(publishTimeStr);
|
|
118
|
+
if (isNaN(publishTime)) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
return Date.now() - publishTime >= minReleaseAge;
|
|
122
|
+
}
|
|
123
|
+
async function getSatisfyingVersion(registryClient, metadata, range, next) {
|
|
124
|
+
const options = { includePrerelease: next || undefined };
|
|
125
|
+
let candidates = metadata.versions.filter((v) => semver.satisfies(v, range, options));
|
|
126
|
+
candidates = candidates.filter((version) => isReleaseAgeSatisfied(registryClient, metadata, version));
|
|
127
|
+
const sorted = semver.rsort(candidates);
|
|
128
|
+
for (const version of sorted) {
|
|
129
|
+
const manifest = await registryClient.getManifest(metadata.name, version);
|
|
130
|
+
if (manifest && !manifest.deprecated) {
|
|
131
|
+
return version;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Fallback to deprecated versions if no non-deprecated version satisfies
|
|
135
|
+
for (const version of sorted) {
|
|
136
|
+
const manifest = await registryClient.getManifest(metadata.name, version);
|
|
137
|
+
if (manifest) {
|
|
138
|
+
return version;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
function angularMajorCompatGuarantee(range) {
|
|
144
|
+
let newRange = semver.validRange(range);
|
|
145
|
+
if (!newRange) {
|
|
146
|
+
return range;
|
|
147
|
+
}
|
|
148
|
+
let major = 1;
|
|
149
|
+
while (!semver.gtr(major + '.0.0', newRange)) {
|
|
150
|
+
major++;
|
|
151
|
+
if (major >= 99) {
|
|
152
|
+
return newRange;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
newRange = range;
|
|
156
|
+
for (let minor = 0; minor < 20; minor++) {
|
|
157
|
+
newRange += ` || ^${major}.${minor}.0-alpha.0 `;
|
|
158
|
+
}
|
|
159
|
+
return semver.validRange(newRange) || range;
|
|
160
|
+
}
|
|
161
|
+
const knownPeerCompatibleList = {
|
|
162
|
+
'@angular/core': angularMajorCompatGuarantee,
|
|
163
|
+
};
|
|
164
|
+
function _updatePeerVersion(infoMap, name, range) {
|
|
165
|
+
const maybePackageInfo = infoMap.get(name);
|
|
166
|
+
if (!maybePackageInfo) {
|
|
167
|
+
return range;
|
|
168
|
+
}
|
|
169
|
+
if (maybePackageInfo.target) {
|
|
170
|
+
name = maybePackageInfo.target.updateMetadata.packageGroupName || name;
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
name = maybePackageInfo.installed.updateMetadata.packageGroupName || name;
|
|
174
|
+
}
|
|
175
|
+
const maybeTransform = knownPeerCompatibleList[name];
|
|
176
|
+
if (maybeTransform) {
|
|
177
|
+
if (typeof maybeTransform == 'function') {
|
|
178
|
+
return maybeTransform(range);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
return maybeTransform;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return range;
|
|
185
|
+
}
|
|
186
|
+
function _validateForwardPeerDependencies(name, infoMap, logger) {
|
|
187
|
+
let error = false;
|
|
188
|
+
const info = infoMap.get(name);
|
|
189
|
+
if (!info || !info.target) {
|
|
190
|
+
return error;
|
|
191
|
+
}
|
|
192
|
+
const peerDependencies = info.target.packageJson.peerDependencies || {};
|
|
193
|
+
const peerDependenciesMeta = info.target.packageJson.peerDependenciesMeta || {};
|
|
194
|
+
for (const [peer, range] of Object.entries(peerDependencies)) {
|
|
195
|
+
const peerInfo = infoMap.get(peer);
|
|
196
|
+
if (!peerInfo) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const isOptional = !!peerDependenciesMeta[peer]?.optional;
|
|
200
|
+
const resolvedRange = _updatePeerVersion(infoMap, peer, range);
|
|
201
|
+
const resolvedVersion = peerInfo.target ? peerInfo.target.version : peerInfo.installed.version;
|
|
202
|
+
if (!semver.satisfies(resolvedVersion, resolvedRange, { includePrerelease: true })) {
|
|
203
|
+
logger.error(`Package ${JSON.stringify(name)} has an incompatible peer dependency to ` +
|
|
204
|
+
`${JSON.stringify(peer)} (requires ${JSON.stringify(range)}, ` +
|
|
205
|
+
`would install ${JSON.stringify(resolvedVersion)}).`);
|
|
206
|
+
error = error || !isOptional;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return error;
|
|
210
|
+
}
|
|
211
|
+
function _validateReversePeerDependencies(name, version, infoMap, logger, next) {
|
|
212
|
+
let error = false;
|
|
213
|
+
for (const [installed, installedInfo] of infoMap.entries()) {
|
|
214
|
+
const installedLogger = logger.createChild(installed);
|
|
215
|
+
installedLogger.debug(`${installed}...`);
|
|
216
|
+
const peers = (installedInfo.target || installedInfo.installed).packageJson.peerDependencies;
|
|
217
|
+
const peersMeta = (installedInfo.target || installedInfo.installed).packageJson
|
|
218
|
+
.peerDependenciesMeta;
|
|
219
|
+
for (const [peer, range] of Object.entries(peers || {})) {
|
|
220
|
+
if (peer !== name) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
const isOptional = !!peersMeta?.[peer]?.optional;
|
|
224
|
+
const resolvedRange = _updatePeerVersion(infoMap, name, range);
|
|
225
|
+
if (!semver.satisfies(version, resolvedRange, { includePrerelease: next || undefined })) {
|
|
226
|
+
logger.error(`Package ${JSON.stringify(installed)} has an incompatible peer dependency to ` +
|
|
227
|
+
`${JSON.stringify(name)} (requires ${JSON.stringify(range)}, ` +
|
|
228
|
+
`would install ${JSON.stringify(version)}).`);
|
|
229
|
+
error = error || !isOptional;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return error;
|
|
234
|
+
}
|
|
235
|
+
function _validateUpdatePackages(infoMap, force, next, logger) {
|
|
236
|
+
logger.debug('Validating peer dependencies...');
|
|
237
|
+
let error = false;
|
|
238
|
+
for (const name of infoMap.keys()) {
|
|
239
|
+
const info = infoMap.get(name);
|
|
240
|
+
if (!info || !info.target) {
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
logger.debug(`Checking ${name}...`);
|
|
244
|
+
error = _validateForwardPeerDependencies(name, infoMap, logger) || error;
|
|
245
|
+
error =
|
|
246
|
+
_validateReversePeerDependencies(name, info.target.version, infoMap, logger, next) || error;
|
|
247
|
+
}
|
|
248
|
+
if (error && !force) {
|
|
249
|
+
throw new Error('Incompatible peer dependencies found. See above for details. ' +
|
|
250
|
+
'You can bypass this check using the --force option.');
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function _getUpdateMetadata(packageJson, logger) {
|
|
254
|
+
const metadata = packageJson['ng-update'];
|
|
255
|
+
const result = {
|
|
256
|
+
packageGroup: {},
|
|
257
|
+
requirements: {},
|
|
258
|
+
};
|
|
259
|
+
if (!metadata || typeof metadata != 'object' || Array.isArray(metadata)) {
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
262
|
+
if (metadata['packageGroup']) {
|
|
263
|
+
const packageGroup = metadata['packageGroup'];
|
|
264
|
+
if (Array.isArray(packageGroup) && packageGroup.every((x) => typeof x == 'string')) {
|
|
265
|
+
result.packageGroup = packageGroup.reduce((group, name) => {
|
|
266
|
+
group[name] = packageJson.version;
|
|
267
|
+
return group;
|
|
268
|
+
}, {});
|
|
269
|
+
}
|
|
270
|
+
else if (typeof packageGroup == 'object' && packageGroup !== null) {
|
|
271
|
+
result.packageGroup = Object.entries(packageGroup).reduce((group, [name, version]) => {
|
|
272
|
+
if (typeof version == 'string') {
|
|
273
|
+
group[name] = version;
|
|
274
|
+
}
|
|
275
|
+
return group;
|
|
276
|
+
}, {});
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
logger.warn(`PackageGroup metadata for ${packageJson.name} is malformed. Ignoring.`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (typeof metadata['packageGroupName'] == 'string') {
|
|
283
|
+
result.packageGroupName = metadata['packageGroupName'];
|
|
284
|
+
}
|
|
285
|
+
if (typeof metadata['migrations'] == 'string') {
|
|
286
|
+
result.migrations = metadata['migrations'];
|
|
287
|
+
}
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
290
|
+
function isPnpActive(workspaceRoot) {
|
|
291
|
+
return (process.versions.pnp !== undefined ||
|
|
292
|
+
(0, node_fs_1.existsSync)(path.join(workspaceRoot, '.pnp.cjs')) ||
|
|
293
|
+
(0, node_fs_1.existsSync)(path.join(workspaceRoot, '.pnp.js')));
|
|
294
|
+
}
|
|
295
|
+
function findPackageJson(workspaceDir, packageName) {
|
|
296
|
+
if (isPnpActive(workspaceDir)) {
|
|
297
|
+
try {
|
|
298
|
+
const workspaceRequire = (0, node_module_1.createRequire)(path.join(workspaceDir, 'package.json'));
|
|
299
|
+
return workspaceRequire.resolve(`${packageName}/package.json`);
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
let currentDir = workspaceDir;
|
|
306
|
+
while (true) {
|
|
307
|
+
const candidatePath = path.join(currentDir, 'node_modules', packageName, 'package.json');
|
|
308
|
+
if ((0, node_fs_1.existsSync)(candidatePath)) {
|
|
309
|
+
return (0, node_fs_1.realpathSync)(candidatePath);
|
|
310
|
+
}
|
|
311
|
+
const parentDir = path.dirname(currentDir);
|
|
312
|
+
if (parentDir === currentDir) {
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
currentDir = parentDir;
|
|
316
|
+
}
|
|
317
|
+
return undefined;
|
|
318
|
+
}
|
|
319
|
+
function getInstalledPackageJson(packageName, workspaceRoot) {
|
|
320
|
+
try {
|
|
321
|
+
const manifestPath = findPackageJson(workspaceRoot, packageName);
|
|
322
|
+
if (manifestPath) {
|
|
323
|
+
const content = (0, node_fs_1.readFileSync)(manifestPath, 'utf8');
|
|
324
|
+
return JSON.parse(content);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
catch { }
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
function getInstalledVersion(packageName, workspaceRoot) {
|
|
331
|
+
const pkgJson = getInstalledPackageJson(packageName, workspaceRoot);
|
|
332
|
+
return pkgJson?.version ?? null;
|
|
333
|
+
}
|
|
334
|
+
function _buildLocalPackageInfo(name, allDependencies, workspaceRoot) {
|
|
335
|
+
const packageJsonRange = allDependencies.get(name);
|
|
336
|
+
if (!packageJsonRange) {
|
|
337
|
+
throw new Error(`Package ${JSON.stringify(name)} was not found in package.json.`);
|
|
338
|
+
}
|
|
339
|
+
const localPkgJson = getInstalledPackageJson(name, workspaceRoot);
|
|
340
|
+
if (!localPkgJson) {
|
|
341
|
+
throw new Error(`Package ${name} is not installed.`);
|
|
342
|
+
}
|
|
343
|
+
const installedVersion = localPkgJson.version;
|
|
344
|
+
const npmPackageJson = {
|
|
345
|
+
name,
|
|
346
|
+
versions: [installedVersion],
|
|
347
|
+
'dist-tags': {},
|
|
348
|
+
};
|
|
349
|
+
const logger = new core_1.logging.NullLogger();
|
|
350
|
+
return {
|
|
351
|
+
name,
|
|
352
|
+
npmPackageJson,
|
|
353
|
+
installed: {
|
|
354
|
+
version: installedVersion,
|
|
355
|
+
packageJson: localPkgJson,
|
|
356
|
+
updateMetadata: _getUpdateMetadata(localPkgJson, logger),
|
|
357
|
+
},
|
|
358
|
+
packageJsonRange,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
async function _buildPackageInfo(packages, allDependencies, npmPackageJson, workspaceRoot, registryClient, logger) {
|
|
362
|
+
const name = npmPackageJson.name;
|
|
363
|
+
const packageJsonRange = allDependencies.get(name);
|
|
364
|
+
if (!packageJsonRange) {
|
|
365
|
+
throw new Error(`Package ${JSON.stringify(name)} was not found in package.json.`);
|
|
366
|
+
}
|
|
367
|
+
const localPkgJson = getInstalledPackageJson(name, workspaceRoot);
|
|
368
|
+
let installedVersion = localPkgJson?.version;
|
|
369
|
+
if (!installedVersion) {
|
|
370
|
+
installedVersion = (await getSatisfyingVersion(registryClient, npmPackageJson, packageJsonRange));
|
|
371
|
+
}
|
|
372
|
+
if (!installedVersion) {
|
|
373
|
+
throw new Error(`An unexpected error happened; could not determine version for package ${name}.`);
|
|
374
|
+
}
|
|
375
|
+
const installedPackageJson = localPkgJson || (await registryClient.getManifest(name, installedVersion));
|
|
376
|
+
if (!installedPackageJson) {
|
|
377
|
+
throw new Error(`An unexpected error happened; package ${name} has no version ${installedVersion}.`);
|
|
378
|
+
}
|
|
379
|
+
let targetVersion = packages.get(name);
|
|
380
|
+
if (targetVersion) {
|
|
381
|
+
const distTags = npmPackageJson['dist-tags'] ?? {};
|
|
382
|
+
let resolvedVersion = distTags[targetVersion] ?? (targetVersion === 'next' ? distTags['latest'] : undefined);
|
|
383
|
+
if (resolvedVersion &&
|
|
384
|
+
!isReleaseAgeSatisfied(registryClient, npmPackageJson, resolvedVersion)) {
|
|
385
|
+
resolvedVersion = undefined;
|
|
386
|
+
}
|
|
387
|
+
if (resolvedVersion) {
|
|
388
|
+
targetVersion = resolvedVersion;
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
targetVersion = (await getSatisfyingVersion(registryClient, npmPackageJson, distTags[targetVersion] || targetVersion === 'next' ? '*' : targetVersion));
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (targetVersion && semver.lte(targetVersion, installedVersion)) {
|
|
395
|
+
logger.debug(`Package ${name} already satisfied by package.json (${packageJsonRange}).`);
|
|
396
|
+
targetVersion = undefined;
|
|
397
|
+
}
|
|
398
|
+
let target;
|
|
399
|
+
if (targetVersion) {
|
|
400
|
+
const targetPackageJson = await registryClient.getManifest(name, targetVersion);
|
|
401
|
+
if (targetPackageJson) {
|
|
402
|
+
target = {
|
|
403
|
+
version: targetVersion,
|
|
404
|
+
packageJson: targetPackageJson,
|
|
405
|
+
updateMetadata: _getUpdateMetadata(targetPackageJson, logger),
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return {
|
|
410
|
+
name,
|
|
411
|
+
npmPackageJson,
|
|
412
|
+
installed: {
|
|
413
|
+
version: installedVersion,
|
|
414
|
+
packageJson: installedPackageJson,
|
|
415
|
+
updateMetadata: _getUpdateMetadata(installedPackageJson, logger),
|
|
416
|
+
},
|
|
417
|
+
target,
|
|
418
|
+
packageJsonRange,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
function splitPackageName(pkg) {
|
|
422
|
+
let name = pkg;
|
|
423
|
+
let version;
|
|
424
|
+
if (pkg.startsWith('@')) {
|
|
425
|
+
const parts = pkg.split('@');
|
|
426
|
+
name = '@' + parts[1];
|
|
427
|
+
version = parts[2];
|
|
428
|
+
}
|
|
429
|
+
else if (pkg.includes('@')) {
|
|
430
|
+
const parts = pkg.split('@');
|
|
431
|
+
name = parts[0];
|
|
432
|
+
version = parts[1];
|
|
433
|
+
}
|
|
434
|
+
return { name, version };
|
|
435
|
+
}
|
|
436
|
+
function _buildPackageList(options, allDependencies, logger) {
|
|
437
|
+
const packages = new Map();
|
|
438
|
+
const inputPackages = options.packages ?? [];
|
|
439
|
+
if (inputPackages.length === 0) {
|
|
440
|
+
return packages;
|
|
441
|
+
}
|
|
442
|
+
for (const pkg of inputPackages) {
|
|
443
|
+
const { name: pkgName, version: pkgVersion } = splitPackageName(pkg);
|
|
444
|
+
if (!allDependencies.has(pkgName)) {
|
|
445
|
+
throw new Error(`Package ${JSON.stringify(pkgName)} is not in package.json.`);
|
|
446
|
+
}
|
|
447
|
+
let targetVersion = pkgVersion;
|
|
448
|
+
if (options.migrateOnly && !targetVersion && options.from) {
|
|
449
|
+
targetVersion = options.from;
|
|
450
|
+
}
|
|
451
|
+
packages.set(pkgName, (targetVersion || (options.next ? 'next' : 'latest')));
|
|
452
|
+
}
|
|
453
|
+
return packages;
|
|
454
|
+
}
|
|
455
|
+
async function resolvePackageVersion(registryClient, metadata, range, next = false) {
|
|
456
|
+
const distTags = metadata['dist-tags'] ?? {};
|
|
457
|
+
let resolvedVersion = distTags[range] ?? (range === 'next' ? distTags['latest'] : undefined);
|
|
458
|
+
if (resolvedVersion && !isReleaseAgeSatisfied(registryClient, metadata, resolvedVersion)) {
|
|
459
|
+
resolvedVersion = undefined;
|
|
460
|
+
}
|
|
461
|
+
if (resolvedVersion) {
|
|
462
|
+
return resolvedVersion;
|
|
463
|
+
}
|
|
464
|
+
return getSatisfyingVersion(registryClient, metadata, distTags[range] || range === 'next' ? '*' : range, next);
|
|
465
|
+
}
|
|
466
|
+
async function _addPackageGroup(packages, allDependencies, metadata, registryClient, logger) {
|
|
467
|
+
const maybePackage = packages.get(metadata.name);
|
|
468
|
+
if (!maybePackage) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
const distTags = metadata['dist-tags'] ?? {};
|
|
472
|
+
let version = maybePackage;
|
|
473
|
+
let resolvedVersion = distTags[version] ?? (version === 'next' ? distTags['latest'] : undefined);
|
|
474
|
+
if (resolvedVersion && !isReleaseAgeSatisfied(registryClient, metadata, resolvedVersion)) {
|
|
475
|
+
resolvedVersion = undefined;
|
|
476
|
+
}
|
|
477
|
+
if (resolvedVersion) {
|
|
478
|
+
version = resolvedVersion;
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
version =
|
|
482
|
+
(await getSatisfyingVersion(registryClient, metadata, distTags[version] || version === 'next' ? '*' : version)) ?? version;
|
|
483
|
+
}
|
|
484
|
+
const packageJson = await registryClient.getManifest(metadata.name, version);
|
|
485
|
+
if (!packageJson) {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const ngUpdateMetadata = packageJson['ng-update'];
|
|
489
|
+
if (!ngUpdateMetadata) {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
const packageGroup = ngUpdateMetadata['packageGroup'];
|
|
493
|
+
if (!packageGroup) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
let packageGroupNormalized;
|
|
497
|
+
if (Array.isArray(packageGroup) && !packageGroup.some((x) => typeof x != 'string')) {
|
|
498
|
+
packageGroupNormalized = packageGroup.reduce((acc, curr) => {
|
|
499
|
+
acc[curr] = version;
|
|
500
|
+
return acc;
|
|
501
|
+
}, {});
|
|
502
|
+
}
|
|
503
|
+
else if (typeof packageGroup === 'object' && packageGroup !== null) {
|
|
504
|
+
packageGroupNormalized = Object.entries(packageGroup).reduce((acc, [name, v]) => {
|
|
505
|
+
if (typeof v === 'string') {
|
|
506
|
+
acc[name] = v;
|
|
507
|
+
}
|
|
508
|
+
return acc;
|
|
509
|
+
}, {});
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
logger.warn(`PackageGroup metadata for ${metadata.name} is malformed. Ignoring.`);
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
for (const [member, memberVersion] of Object.entries(packageGroupNormalized)) {
|
|
516
|
+
if (packages.has(member)) {
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
if (allDependencies.has(member)) {
|
|
520
|
+
packages.set(member, memberVersion);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
async function _addPeerDependencies(packages, allDependencies, npmPackageJson, workspaceRoot, registryClient, logger) {
|
|
525
|
+
const maybePackage = packages.get(npmPackageJson.name);
|
|
526
|
+
if (!maybePackage) {
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
const distTags = npmPackageJson['dist-tags'] ?? {};
|
|
530
|
+
const version = distTags[maybePackage] || maybePackage;
|
|
531
|
+
const packageJson = await registryClient.getManifest(npmPackageJson.name, version);
|
|
532
|
+
if (!packageJson) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
for (const [peer, range] of Object.entries(packageJson.peerDependencies || {})) {
|
|
536
|
+
if (packages.has(peer)) {
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
const installedVersion = getInstalledVersion(peer, workspaceRoot);
|
|
540
|
+
if (installedVersion) {
|
|
541
|
+
if (semver.satisfies(installedVersion, range)) {
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
const packageJsonRange = allDependencies.get(peer);
|
|
547
|
+
if (packageJsonRange) {
|
|
548
|
+
const peerMetadata = await registryClient.getMetadata(peer);
|
|
549
|
+
if (peerMetadata) {
|
|
550
|
+
const resolvedInstalledVersion = await getSatisfyingVersion(registryClient, peerMetadata, packageJsonRange);
|
|
551
|
+
if (resolvedInstalledVersion && semver.satisfies(resolvedInstalledVersion, range)) {
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
packages.set(peer, range);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
function _formatVersion(v) {
|
|
561
|
+
if (v === undefined) {
|
|
562
|
+
return v;
|
|
563
|
+
}
|
|
564
|
+
if (semver.valid(v)) {
|
|
565
|
+
return v;
|
|
566
|
+
}
|
|
567
|
+
const coerced = semver.coerce(v);
|
|
568
|
+
return coerced ? coerced.toString() : undefined;
|
|
569
|
+
}
|
|
570
|
+
function getRegistryNameAndRange(name, specifier) {
|
|
571
|
+
try {
|
|
572
|
+
const result = npm_package_arg_1.default.resolve(name, specifier);
|
|
573
|
+
if (result.type === 'alias' && result.subSpec) {
|
|
574
|
+
return {
|
|
575
|
+
name: result.subSpec.name ?? name,
|
|
576
|
+
range: result.subSpec.fetchSpec ?? specifier,
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
catch { }
|
|
581
|
+
return { name, range: specifier };
|
|
582
|
+
}
|
|
583
|
+
function isPkgFromRegistry(name, specifier) {
|
|
584
|
+
const result = npm_package_arg_1.default.resolve(name, specifier);
|
|
585
|
+
return !!result.registry;
|
|
586
|
+
}
|
|
587
|
+
async function checkCatalogUpdates(normalizedPackages, packageJsonContent, registryClient, workspaceRoot, options) {
|
|
588
|
+
const catalogUpdates = [];
|
|
589
|
+
for (const requestedPkg of normalizedPackages) {
|
|
590
|
+
const { name: pkgName } = splitPackageName(requestedPkg);
|
|
591
|
+
const specifier = packageJsonContent.dependencies?.[pkgName] ||
|
|
592
|
+
packageJsonContent.devDependencies?.[pkgName] ||
|
|
593
|
+
packageJsonContent.peerDependencies?.[pkgName];
|
|
594
|
+
if (specifier?.startsWith('catalog:')) {
|
|
595
|
+
const current = getInstalledVersion(pkgName, workspaceRoot) ?? 'unknown';
|
|
596
|
+
let target = 'latest';
|
|
597
|
+
try {
|
|
598
|
+
const metadata = await registryClient.getMetadata(pkgName);
|
|
599
|
+
if (metadata) {
|
|
600
|
+
const resolved = await resolvePackageVersion(registryClient, metadata, options.next ? 'next' : 'latest', !!options.next);
|
|
601
|
+
target = resolved ?? 'latest';
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
catch {
|
|
605
|
+
// Fallback to 'latest' tag
|
|
606
|
+
}
|
|
607
|
+
catalogUpdates.push({ name: pkgName, current, target, specifier });
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
if (catalogUpdates.length > 0) {
|
|
611
|
+
const packageManagerName = options.packageManager ?? 'your package manager';
|
|
612
|
+
const installCmd = packageManagerName === 'yarn' ? 'yarn install' : 'pnpm install';
|
|
613
|
+
const updatesList = catalogUpdates
|
|
614
|
+
.map((pkg) => ` - ${pkg.name} (${pkg.specifier}) -> Target version: ${pkg.target}`)
|
|
615
|
+
.join('\n');
|
|
616
|
+
const migrationCommands = catalogUpdates
|
|
617
|
+
.map((pkg) => {
|
|
618
|
+
const fromVer = pkg.current === 'unknown' ? '<current-version>' : pkg.current;
|
|
619
|
+
return ` ng update ${pkg.name} --migrate-only --from ${fromVer}`;
|
|
620
|
+
})
|
|
621
|
+
.join('\n');
|
|
622
|
+
throw new Error(`The following packages to update are configured to use \`catalog:\`:\n` +
|
|
623
|
+
`${updatesList}\n\n` +
|
|
624
|
+
`Because catalogs are shared across the monorepo, 'ng update' cannot modify them directly.\n` +
|
|
625
|
+
`Please perform the following steps to update:\n` +
|
|
626
|
+
` 1. Manually update the versions for these packages in your catalog configuration file ` +
|
|
627
|
+
`(e.g., pnpm-workspace.yaml or .yarnrc.yml).\n` +
|
|
628
|
+
` 2. Run '${installCmd}' to install the updated versions.\n` +
|
|
629
|
+
` 3. Run the following command(s) from the workspace root to execute the migration schematics:\n` +
|
|
630
|
+
`${migrationCommands}`);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
async function resolveUserUpdatePlan(options, packageManager, logger) {
|
|
634
|
+
const workspaceRoot = options.workspaceRoot ?? process.cwd();
|
|
635
|
+
const packageJsonPath = path.join(workspaceRoot, 'package.json');
|
|
636
|
+
if (!(0, node_fs_1.existsSync)(packageJsonPath)) {
|
|
637
|
+
throw new Error('Could not find a package.json. Are you in a Node project?');
|
|
638
|
+
}
|
|
639
|
+
const rawJson = (0, node_fs_1.readFileSync)(packageJsonPath, 'utf8');
|
|
640
|
+
const packageJsonContent = JSON.parse(rawJson);
|
|
641
|
+
const getDependencies = (deps) => Object.entries(deps ?? {}).map(([name, range]) => [name, range]);
|
|
642
|
+
const allRawDeps = [
|
|
643
|
+
...getDependencies(packageJsonContent.dependencies),
|
|
644
|
+
...getDependencies(packageJsonContent.devDependencies),
|
|
645
|
+
...getDependencies(packageJsonContent.peerDependencies),
|
|
646
|
+
];
|
|
647
|
+
const npmDeps = new Map(allRawDeps.filter(([name, specifier]) => {
|
|
648
|
+
try {
|
|
649
|
+
return isPkgFromRegistry(name, specifier);
|
|
650
|
+
}
|
|
651
|
+
catch {
|
|
652
|
+
logger.warn(`Package ${name} was not found on the registry. Skipping.`);
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
}));
|
|
656
|
+
const packagesOption = options.packages ?? [];
|
|
657
|
+
const normalizedPackages = packagesOption.reduce((acc, curr) => {
|
|
658
|
+
return acc.concat(curr.split(','));
|
|
659
|
+
}, []);
|
|
660
|
+
options.packages = normalizedPackages;
|
|
661
|
+
if (options.migrateOnly && options.from) {
|
|
662
|
+
if (options.packages.length !== 1) {
|
|
663
|
+
throw new Error('--from requires that only a single package be passed.');
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
options.from = _formatVersion(options.from);
|
|
667
|
+
options.to = _formatVersion(options.to);
|
|
668
|
+
const usingYarn = options.packageManager === 'yarn';
|
|
669
|
+
const minReleaseAge = await packageManager.getMinimumReleaseAge();
|
|
670
|
+
const getRegistryName = (name) => {
|
|
671
|
+
const specifier = npmDeps.get(name);
|
|
672
|
+
if (specifier) {
|
|
673
|
+
return getRegistryNameAndRange(name, specifier).name;
|
|
674
|
+
}
|
|
675
|
+
return name;
|
|
676
|
+
};
|
|
677
|
+
const registryClient = new RegistryClient(packageManager, logger, minReleaseAge, getRegistryName);
|
|
678
|
+
await checkCatalogUpdates(normalizedPackages, packageJsonContent, registryClient, workspaceRoot, options);
|
|
679
|
+
const packages = _buildPackageList(options, npmDeps, logger);
|
|
680
|
+
const getOrFetchPackageMetadata = async (packageName) => {
|
|
681
|
+
return registryClient.getMetadata(packageName);
|
|
682
|
+
};
|
|
683
|
+
if (packages.size === 0) {
|
|
684
|
+
await Promise.all(Array.from(npmDeps.keys()).map(async (depName) => {
|
|
685
|
+
await getOrFetchPackageMetadata(depName);
|
|
686
|
+
}));
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
let lastPackagesSize;
|
|
690
|
+
do {
|
|
691
|
+
lastPackagesSize = packages.size;
|
|
692
|
+
let lastGroupSize;
|
|
693
|
+
do {
|
|
694
|
+
lastGroupSize = packages.size;
|
|
695
|
+
for (const name of Array.from(packages.keys())) {
|
|
696
|
+
const metadata = await getOrFetchPackageMetadata(name);
|
|
697
|
+
const spec = packages.get(name);
|
|
698
|
+
if (metadata && spec) {
|
|
699
|
+
const resolvedVersion = await resolvePackageVersion(registryClient, metadata, spec, !!options.next);
|
|
700
|
+
if (resolvedVersion) {
|
|
701
|
+
packages.set(name, resolvedVersion);
|
|
702
|
+
}
|
|
703
|
+
await _addPackageGroup(packages, npmDeps, metadata, registryClient, logger);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
} while (packages.size > lastGroupSize);
|
|
707
|
+
for (const name of Array.from(packages.keys())) {
|
|
708
|
+
const metadata = await getOrFetchPackageMetadata(name);
|
|
709
|
+
const spec = packages.get(name);
|
|
710
|
+
if (metadata && spec) {
|
|
711
|
+
const resolvedVersion = await resolvePackageVersion(registryClient, metadata, spec, !!options.next);
|
|
712
|
+
if (resolvedVersion) {
|
|
713
|
+
packages.set(name, resolvedVersion);
|
|
714
|
+
}
|
|
715
|
+
await _addPeerDependencies(packages, npmDeps, metadata, workspaceRoot, registryClient, logger);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
} while (packages.size > lastPackagesSize);
|
|
719
|
+
}
|
|
720
|
+
const packageInfoEntries = await Promise.all(Array.from(npmDeps.keys(), async (depName) => {
|
|
721
|
+
const isUpdating = packages.has(depName);
|
|
722
|
+
const localPkgJson = getInstalledPackageJson(depName, workspaceRoot);
|
|
723
|
+
if (isUpdating || !localPkgJson) {
|
|
724
|
+
const metadata = await getOrFetchPackageMetadata(depName);
|
|
725
|
+
if (metadata) {
|
|
726
|
+
const info = await _buildPackageInfo(packages, npmDeps, metadata, workspaceRoot, registryClient, logger);
|
|
727
|
+
return [depName, info];
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return [depName, _buildLocalPackageInfo(depName, npmDeps, workspaceRoot)];
|
|
731
|
+
}));
|
|
732
|
+
const packageInfoMap = new Map(packageInfoEntries);
|
|
733
|
+
const packagesToUpdate = new Map();
|
|
734
|
+
const migrationsToRun = [];
|
|
735
|
+
if (packages.size > 0) {
|
|
736
|
+
if (!(options.migrateOnly && options.from && options.packages)) {
|
|
737
|
+
const sublog = new core_1.logging.LevelCapLogger('validation', logger.createChild(''), 'warn');
|
|
738
|
+
_validateUpdatePackages(packageInfoMap, !!options.force, !!options.next, sublog);
|
|
739
|
+
for (const [name, info] of packageInfoMap.entries()) {
|
|
740
|
+
if (!info.target || !info.installed) {
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
packagesToUpdate.set(name, info.target.version);
|
|
744
|
+
if (info.target.updateMetadata.migrations) {
|
|
745
|
+
migrationsToRun.push({
|
|
746
|
+
package: name,
|
|
747
|
+
collection: info.target.updateMetadata.migrations,
|
|
748
|
+
from: info.installed.version,
|
|
749
|
+
to: info.target.version,
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
return {
|
|
756
|
+
packagesToUpdate,
|
|
757
|
+
migrationsToRun,
|
|
758
|
+
packageInfoMap,
|
|
759
|
+
registryClient,
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
async function printUpdateUsageMessage(infoMap, registryClient, logger, next = false) {
|
|
763
|
+
const packageGroups = new Map();
|
|
764
|
+
const mappedPackages = await Promise.all(Array.from(infoMap.entries(), async ([name, info]) => {
|
|
765
|
+
const distTags = info.npmPackageJson['dist-tags'] ?? {};
|
|
766
|
+
let tag = next ? (distTags['next'] ? 'next' : 'latest') : 'latest';
|
|
767
|
+
let version = distTags[tag] ?? info.installed.version;
|
|
768
|
+
const versions = info.npmPackageJson.versions ?? [];
|
|
769
|
+
const versionDiff = semver.diff(info.installed.version, version);
|
|
770
|
+
if (versionDiff !== 'patch' &&
|
|
771
|
+
versionDiff !== 'minor' &&
|
|
772
|
+
/^@(?:angular|nguniversal)\//.test(name)) {
|
|
773
|
+
const installedMajorVersion = semver.parse(info.installed.version)?.major;
|
|
774
|
+
const toInstallMajorVersion = semver.parse(version)?.major;
|
|
775
|
+
if (installedMajorVersion !== undefined &&
|
|
776
|
+
toInstallMajorVersion !== undefined &&
|
|
777
|
+
installedMajorVersion < toInstallMajorVersion - 1) {
|
|
778
|
+
const nextMajorVersion = `${installedMajorVersion + 1}.`;
|
|
779
|
+
const nextMajorVersions = versions
|
|
780
|
+
.filter((v) => v.startsWith(nextMajorVersion))
|
|
781
|
+
.sort((a, b) => (a > b ? -1 : 1));
|
|
782
|
+
if (nextMajorVersions.length) {
|
|
783
|
+
version = nextMajorVersions[0];
|
|
784
|
+
tag = '';
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
const target = info.target?.packageJson || (await registryClient.getManifest(name, version));
|
|
789
|
+
return {
|
|
790
|
+
name,
|
|
791
|
+
info,
|
|
792
|
+
version,
|
|
793
|
+
tag,
|
|
794
|
+
target,
|
|
795
|
+
};
|
|
796
|
+
}));
|
|
797
|
+
const packagesToUpdate = mappedPackages
|
|
798
|
+
.filter(({ info, version, target }) => target?.['ng-update'] && semver.compare(info.installed.version, version) < 0)
|
|
799
|
+
.map(({ name, info, version, tag, target }) => {
|
|
800
|
+
// Look for packageGroup.
|
|
801
|
+
const ngUpdate = target?.['ng-update'];
|
|
802
|
+
const packageGroup = ngUpdate?.['packageGroup'];
|
|
803
|
+
if (packageGroup) {
|
|
804
|
+
const packageGroupNames = Array.isArray(packageGroup)
|
|
805
|
+
? packageGroup
|
|
806
|
+
: Object.keys(packageGroup);
|
|
807
|
+
const packageGroupName = ngUpdate?.['packageGroupName'] ||
|
|
808
|
+
packageGroupNames.find((n) => infoMap.has(n));
|
|
809
|
+
if (packageGroupName) {
|
|
810
|
+
if (packageGroups.has(name)) {
|
|
811
|
+
return null;
|
|
812
|
+
}
|
|
813
|
+
for (const groupName of packageGroupNames) {
|
|
814
|
+
packageGroups.set(groupName, packageGroupName);
|
|
815
|
+
}
|
|
816
|
+
packageGroups.set(packageGroupName, packageGroupName);
|
|
817
|
+
name = packageGroupName;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
let command = `ng update ${name}`;
|
|
821
|
+
if (!tag) {
|
|
822
|
+
command += `@${semver.parse(version)?.major || version}`;
|
|
823
|
+
}
|
|
824
|
+
else if (tag == 'next') {
|
|
825
|
+
command += ' --next';
|
|
826
|
+
}
|
|
827
|
+
return [name, `${info.installed.version} -> ${version} `, command];
|
|
828
|
+
})
|
|
829
|
+
.filter((x) => x !== null)
|
|
830
|
+
.sort((a, b) => a[0].localeCompare(b[0]));
|
|
831
|
+
if (packagesToUpdate.length == 0) {
|
|
832
|
+
logger.info('We analyzed your package.json and everything seems to be in order. Good work!');
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
logger.info('We analyzed your package.json, there are some packages to update:\n');
|
|
836
|
+
// Find the largest name to know the padding needed.
|
|
837
|
+
let namePad = Math.max(...[...infoMap.keys()].map((x) => x.length)) + 2;
|
|
838
|
+
if (!Number.isFinite(namePad)) {
|
|
839
|
+
namePad = 30;
|
|
840
|
+
}
|
|
841
|
+
const pads = [namePad, 25, 0];
|
|
842
|
+
logger.info(' ' + ['Name', 'Version', 'Command to update'].map((x, i) => x.padEnd(pads[i])).join(''));
|
|
843
|
+
const totalWidth = pads.reduce((sum, width) => sum + width, 20);
|
|
844
|
+
logger.info(` ${'-'.repeat(totalWidth)}`);
|
|
845
|
+
packagesToUpdate.forEach((fields) => {
|
|
846
|
+
if (!fields) {
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
logger.info(' ' + fields.map((x, i) => x.padEnd(pads[i])).join(''));
|
|
850
|
+
});
|
|
851
|
+
logger.info(`\nThere might be additional packages which don't provide 'ng update' capabilities that are outdated.\n` +
|
|
852
|
+
`You can update the additional packages by running the update command of your package manager.`);
|
|
853
|
+
}
|
|
854
|
+
async function applyUpdatePlan(workspaceRoot, plan, logger) {
|
|
855
|
+
const packageJsonPath = path.join(workspaceRoot, 'package.json');
|
|
856
|
+
const packageJsonContent = await node_fs_1.promises.readFile(packageJsonPath, 'utf8');
|
|
857
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
858
|
+
const updateDependency = (deps, name, newVersion) => {
|
|
859
|
+
const oldVersion = deps[name];
|
|
860
|
+
const aliasPrefix = 'npm:';
|
|
861
|
+
// If the dependency uses an npm package alias (e.g., "npm:registry-name@version-range"),
|
|
862
|
+
// parse and reconstruct the alias with the new target version while preserving
|
|
863
|
+
// the original alias registry name and any version prefix character (like ^ or ~).
|
|
864
|
+
if (oldVersion.startsWith(aliasPrefix)) {
|
|
865
|
+
const specifier = oldVersion.slice(aliasPrefix.length);
|
|
866
|
+
const lastAtIndex = specifier.lastIndexOf('@');
|
|
867
|
+
if (lastAtIndex > 0) {
|
|
868
|
+
const registryName = specifier.slice(0, lastAtIndex);
|
|
869
|
+
const versionRange = specifier.slice(lastAtIndex + 1);
|
|
870
|
+
// Retain any semantic versioning operator prefix (e.g., ^ or ~) from the target version range.
|
|
871
|
+
const execResult = /^[\^~]/.exec(versionRange);
|
|
872
|
+
deps[name] =
|
|
873
|
+
`${aliasPrefix}${registryName}@${execResult ? execResult[0] : ''}${newVersion}`;
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
// If there's no `@` character defining a version specifier in the alias (e.g. "npm:packageName"),
|
|
877
|
+
// leave it as-is without attempting to inject a version suffix.
|
|
878
|
+
deps[name] = oldVersion;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
else {
|
|
882
|
+
// Standard dependency formatting, keeping any semantic versioning operator prefix (e.g., ^ or ~).
|
|
883
|
+
const execResult = /^[\^~]/.exec(oldVersion);
|
|
884
|
+
deps[name] = `${execResult ? execResult[0] : ''}${newVersion}`;
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
for (const [name, targetVersion] of plan.packagesToUpdate.entries()) {
|
|
888
|
+
logger.info(`Updating package.json with dependency ${name} to version ${targetVersion}...`);
|
|
889
|
+
if (packageJson.dependencies && packageJson.dependencies[name]) {
|
|
890
|
+
updateDependency(packageJson.dependencies, name, targetVersion);
|
|
891
|
+
if (packageJson.devDependencies) {
|
|
892
|
+
delete packageJson.devDependencies[name];
|
|
893
|
+
}
|
|
894
|
+
if (packageJson.peerDependencies) {
|
|
895
|
+
delete packageJson.peerDependencies[name];
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
else if (packageJson.devDependencies && packageJson.devDependencies[name]) {
|
|
899
|
+
updateDependency(packageJson.devDependencies, name, targetVersion);
|
|
900
|
+
if (packageJson.peerDependencies) {
|
|
901
|
+
delete packageJson.peerDependencies[name];
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
else if (packageJson.peerDependencies && packageJson.peerDependencies[name]) {
|
|
905
|
+
updateDependency(packageJson.peerDependencies, name, targetVersion);
|
|
906
|
+
}
|
|
907
|
+
else {
|
|
908
|
+
if (!packageJson.dependencies) {
|
|
909
|
+
packageJson.dependencies = {};
|
|
910
|
+
}
|
|
911
|
+
packageJson.dependencies[name] = `^${targetVersion}`;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
const eofMatches = packageJsonContent.match(/\r?\n$/);
|
|
915
|
+
const eof = eofMatches?.[0] ?? '';
|
|
916
|
+
const newContent = JSON.stringify(packageJson, null, 2) + eof;
|
|
917
|
+
await node_fs_1.promises.writeFile(packageJsonPath, newContent, 'utf8');
|
|
918
|
+
}
|
|
919
|
+
//# sourceMappingURL=update-resolver.js.map
|