@pnpm/resolving.npm-resolver 1004.4.1

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/index.js ADDED
@@ -0,0 +1,547 @@
1
+ import path from 'node:path';
2
+ import { pickRegistryForPackage } from '@pnpm/config.pick-registry-for-package';
3
+ import { PnpmError } from '@pnpm/error';
4
+ import { storeIndexKey } from '@pnpm/store.index';
5
+ import { readPkgFromCafs, } from '@pnpm/worker';
6
+ import { resolveWorkspaceRange } from '@pnpm/workspace.range-resolver';
7
+ import { LRUCache } from 'lru-cache';
8
+ import normalize from 'normalize-path';
9
+ import pMemoize from 'p-memoize';
10
+ import { clone } from 'ramda';
11
+ import semver from 'semver';
12
+ import ssri from 'ssri';
13
+ import versionSelectorType from 'version-selector-type';
14
+ import { fetchMetadataFromFromRegistry, RegistryResponseError } from './fetch.js';
15
+ import { normalizeRegistryUrl } from './normalizeRegistryUrl.js';
16
+ import { parseBareSpecifier, parseJsrSpecifierToRegistryPackageSpec, } from './parseBareSpecifier.js';
17
+ import { pickPackage, } from './pickPackage.js';
18
+ import { pickVersionByVersionRange } from './pickPackageFromMeta.js';
19
+ import { failIfTrustDowngraded } from './trustChecks.js';
20
+ import { whichVersionIsPinned } from './whichVersionIsPinned.js';
21
+ import { workspacePrefToNpm } from './workspacePrefToNpm.js';
22
+ export class NoMatchingVersionError extends PnpmError {
23
+ packageMeta;
24
+ immatureVersion;
25
+ constructor(opts) {
26
+ const dep = opts.wantedDependency.alias
27
+ ? `${opts.wantedDependency.alias}@${opts.wantedDependency.bareSpecifier ?? ''}`
28
+ : opts.wantedDependency.bareSpecifier;
29
+ let errorMessage;
30
+ if (opts.publishedBy && opts.immatureVersion && opts.packageMeta.time) {
31
+ const time = new Date(opts.packageMeta.time[opts.immatureVersion]);
32
+ const releaseAgeText = formatTimeAgo(time);
33
+ const pkgName = opts.wantedDependency.alias ?? opts.packageMeta.name;
34
+ errorMessage = `Version ${opts.immatureVersion} (released ${releaseAgeText}) of ${pkgName} does not meet the minimumReleaseAge constraint`;
35
+ }
36
+ else {
37
+ errorMessage = `No matching version found for ${dep} while fetching it from ${opts.registry}`;
38
+ }
39
+ super(opts.publishedBy ? 'NO_MATURE_MATCHING_VERSION' : 'NO_MATCHING_VERSION', errorMessage);
40
+ this.packageMeta = opts.packageMeta;
41
+ this.immatureVersion = opts.immatureVersion;
42
+ }
43
+ }
44
+ function formatTimeAgo(date) {
45
+ const now = Date.now();
46
+ const diffMs = now - date.getTime();
47
+ // Handle clock skew (future dates) and very recent releases (< 1 minute)
48
+ if (diffMs < 60 * 1000) {
49
+ return 'just now';
50
+ }
51
+ const diffMinutes = Math.floor(diffMs / (60 * 1000));
52
+ const diffHours = Math.floor(diffMs / (60 * 60 * 1000));
53
+ const diffDays = Math.floor(diffMs / (24 * 60 * 60 * 1000));
54
+ if (diffHours >= 48) {
55
+ return `${diffDays} day${diffDays === 1 ? '' : 's'} ago`;
56
+ }
57
+ if (diffMinutes >= 90) {
58
+ return `${diffHours} hour${diffHours === 1 ? '' : 's'} ago`;
59
+ }
60
+ return `${diffMinutes} minute${diffMinutes === 1 ? '' : 's'} ago`;
61
+ }
62
+ export { parseBareSpecifier, RegistryResponseError, workspacePrefToNpm, };
63
+ export { whichVersionIsPinned } from './whichVersionIsPinned.js';
64
+ export function createNpmResolver(fetchFromRegistry, getAuthHeader, opts) {
65
+ if (typeof opts.cacheDir !== 'string') {
66
+ throw new TypeError('`opts.cacheDir` is required and needs to be a string');
67
+ }
68
+ const fetchOpts = {
69
+ fetch: fetchFromRegistry,
70
+ retry: opts.retry ?? {},
71
+ timeout: opts.timeout ?? 60000,
72
+ fetchWarnTimeoutMs: opts.fetchWarnTimeoutMs ?? 10 * 1000, // 10 sec
73
+ };
74
+ const fetch = pMemoize(fetchMetadataFromFromRegistry.bind(null, fetchOpts), {
75
+ cacheKey: (...args) => JSON.stringify(args),
76
+ });
77
+ const metaCache = new LRUCache({
78
+ max: 10000,
79
+ ttl: 120 * 1000, // 2 minutes
80
+ });
81
+ // Create peek function if storeDir is provided
82
+ const storeDir = opts.storeDir;
83
+ const peekLockerForPeek = new Map();
84
+ let peekManifestFromStore;
85
+ if (storeDir) {
86
+ peekManifestFromStore = async (peekOpts) => {
87
+ const filesIndexFile = storeIndexKey(peekOpts.integrity, peekOpts.id);
88
+ const existingRequest = peekLockerForPeek.get(filesIndexFile);
89
+ if (existingRequest != null) {
90
+ return existingRequest;
91
+ }
92
+ const request = readPkgFromCafs({
93
+ storeDir,
94
+ verifyStoreIntegrity: false,
95
+ }, filesIndexFile, {
96
+ expectedPkg: { name: peekOpts.name, version: peekOpts.version },
97
+ }).then(({ bundledManifest }) => {
98
+ if (!bundledManifest)
99
+ return undefined;
100
+ return bundledManifest;
101
+ }).catch(() => undefined);
102
+ peekLockerForPeek.set(filesIndexFile, request);
103
+ return request;
104
+ };
105
+ }
106
+ const ctx = {
107
+ getAuthHeaderValueByURI: getAuthHeader,
108
+ pickPackage: pickPackage.bind(null, {
109
+ fetch,
110
+ fullMetadata: opts.fullMetadata,
111
+ filterMetadata: opts.filterMetadata,
112
+ metaCache,
113
+ offline: opts.offline,
114
+ preferOffline: opts.preferOffline,
115
+ cacheDir: opts.cacheDir,
116
+ strictPublishedByCheck: opts.strictPublishedByCheck,
117
+ }),
118
+ registries: opts.registries,
119
+ saveWorkspaceProtocol: opts.saveWorkspaceProtocol,
120
+ peekManifestFromStore,
121
+ };
122
+ return {
123
+ resolveFromNpm: resolveNpm.bind(null, ctx),
124
+ resolveFromJsr: resolveJsr.bind(null, ctx),
125
+ clearCache: () => {
126
+ metaCache.clear();
127
+ },
128
+ };
129
+ }
130
+ async function resolveNpm(ctx, wantedDependency, opts) {
131
+ const defaultTag = opts.defaultTag ?? 'latest';
132
+ const registry = wantedDependency.alias
133
+ ? pickRegistryForPackage(ctx.registries, wantedDependency.alias, wantedDependency.bareSpecifier)
134
+ : ctx.registries.default;
135
+ if (wantedDependency.bareSpecifier?.startsWith('workspace:')) {
136
+ if (wantedDependency.bareSpecifier.startsWith('workspace:.'))
137
+ return null;
138
+ const resolvedFromWorkspace = tryResolveFromWorkspace(wantedDependency, {
139
+ defaultTag,
140
+ lockfileDir: opts.lockfileDir,
141
+ projectDir: opts.projectDir,
142
+ registry,
143
+ workspacePackages: opts.workspacePackages,
144
+ injectWorkspacePackages: opts.injectWorkspacePackages,
145
+ update: Boolean(opts.update),
146
+ saveWorkspaceProtocol: ctx.saveWorkspaceProtocol !== false ? ctx.saveWorkspaceProtocol : true,
147
+ calcSpecifier: opts.calcSpecifier,
148
+ pinnedVersion: opts.pinnedVersion,
149
+ });
150
+ if (resolvedFromWorkspace != null) {
151
+ return resolvedFromWorkspace;
152
+ }
153
+ }
154
+ const workspacePackages = opts.alwaysTryWorkspacePackages !== false ? opts.workspacePackages : undefined;
155
+ const spec = wantedDependency.bareSpecifier
156
+ ? parseBareSpecifier(wantedDependency.bareSpecifier, wantedDependency.alias, defaultTag, registry)
157
+ : defaultTagForAlias(wantedDependency.alias, defaultTag);
158
+ if (spec == null)
159
+ return null;
160
+ // Fast path: if we have a current resolution with integrity, try to peek the manifest from the store.
161
+ // This avoids the expensive metadata fetch from the registry.
162
+ // We do this AFTER ensuring the spec is valid for this resolver to avoids hijacking other resolvers.
163
+ if (ctx.peekManifestFromStore && opts.currentPkg?.resolution && !opts.update) {
164
+ const currentResolution = opts.currentPkg.resolution;
165
+ // Only use this optimization for tarball resolutions with integrity (npm packages)
166
+ if ('tarball' in currentResolution && currentResolution.integrity) {
167
+ const manifest = await ctx.peekManifestFromStore({
168
+ id: opts.currentPkg.id,
169
+ integrity: currentResolution.integrity,
170
+ name: opts.currentPkg.name,
171
+ version: opts.currentPkg.version,
172
+ });
173
+ // Verify the manifest matches what we expect
174
+ if (manifest?.name && manifest?.version) {
175
+ const id = `${manifest.name}@${manifest.version}`;
176
+ // Only return if the ID matches what we have in currentPkg
177
+ if (id === opts.currentPkg.id) {
178
+ return {
179
+ id,
180
+ manifest,
181
+ resolution: currentResolution,
182
+ resolvedVia: 'npm-registry',
183
+ publishedAt: undefined, // Don't have this without metadata
184
+ };
185
+ }
186
+ }
187
+ }
188
+ }
189
+ const authHeaderValue = ctx.getAuthHeaderValueByURI(registry);
190
+ let pickResult;
191
+ try {
192
+ pickResult = await ctx.pickPackage(spec, {
193
+ pickLowestVersion: opts.pickLowestVersion,
194
+ publishedBy: opts.publishedBy,
195
+ publishedByExclude: opts.publishedByExclude,
196
+ authHeaderValue,
197
+ dryRun: opts.dryRun === true,
198
+ preferredVersionSelectors: opts.preferredVersions?.[spec.name],
199
+ registry,
200
+ updateToLatest: opts.update === 'latest',
201
+ optional: wantedDependency.optional,
202
+ });
203
+ }
204
+ catch (err) { // eslint-disable-line
205
+ if ((workspacePackages != null) && opts.projectDir) {
206
+ try {
207
+ return tryResolveFromWorkspacePackages(workspacePackages, spec, {
208
+ wantedDependency,
209
+ projectDir: opts.projectDir,
210
+ lockfileDir: opts.lockfileDir,
211
+ hardLinkLocalPackages: opts.injectWorkspacePackages === true || wantedDependency.injected,
212
+ update: false,
213
+ saveWorkspaceProtocol: ctx.saveWorkspaceProtocol,
214
+ calcSpecifier: opts.calcSpecifier,
215
+ pinnedVersion: opts.pinnedVersion,
216
+ });
217
+ }
218
+ catch {
219
+ // ignore
220
+ }
221
+ }
222
+ throw err;
223
+ }
224
+ const pickedPackage = pickResult.pickedPackage;
225
+ const meta = pickResult.meta;
226
+ if (pickedPackage == null) {
227
+ if ((workspacePackages != null) && opts.projectDir) {
228
+ try {
229
+ return tryResolveFromWorkspacePackages(workspacePackages, spec, {
230
+ wantedDependency,
231
+ projectDir: opts.projectDir,
232
+ lockfileDir: opts.lockfileDir,
233
+ hardLinkLocalPackages: opts.injectWorkspacePackages === true || wantedDependency.injected,
234
+ update: false,
235
+ saveWorkspaceProtocol: ctx.saveWorkspaceProtocol,
236
+ calcSpecifier: opts.calcSpecifier,
237
+ pinnedVersion: opts.pinnedVersion,
238
+ });
239
+ }
240
+ catch {
241
+ // ignore
242
+ }
243
+ }
244
+ if (opts.publishedBy) {
245
+ const immatureVersion = pickVersionByVersionRange({
246
+ meta,
247
+ versionRange: spec.fetchSpec,
248
+ preferredVersionSelectors: opts.preferredVersions?.[spec.name],
249
+ });
250
+ if (immatureVersion) {
251
+ throw new NoMatchingVersionError({
252
+ wantedDependency,
253
+ packageMeta: meta,
254
+ registry,
255
+ immatureVersion,
256
+ publishedBy: opts.publishedBy,
257
+ });
258
+ }
259
+ }
260
+ throw new NoMatchingVersionError({ wantedDependency, packageMeta: meta, registry });
261
+ }
262
+ else if (opts.trustPolicy === 'no-downgrade') {
263
+ failIfTrustDowngraded(meta, pickedPackage.version, opts);
264
+ }
265
+ const workspacePkgsMatchingName = workspacePackages?.get(pickedPackage.name);
266
+ if (workspacePkgsMatchingName && opts.projectDir) {
267
+ const matchedPkg = workspacePkgsMatchingName.get(pickedPackage.version);
268
+ if (matchedPkg) {
269
+ return {
270
+ ...resolveFromLocalPackage(matchedPkg, spec, {
271
+ wantedDependency,
272
+ projectDir: opts.projectDir,
273
+ lockfileDir: opts.lockfileDir,
274
+ hardLinkLocalPackages: opts.injectWorkspacePackages === true || wantedDependency.injected,
275
+ saveWorkspaceProtocol: ctx.saveWorkspaceProtocol,
276
+ calcSpecifier: opts.calcSpecifier,
277
+ pinnedVersion: opts.pinnedVersion,
278
+ }),
279
+ latest: meta['dist-tags'].latest,
280
+ };
281
+ }
282
+ const localVersion = pickMatchingLocalVersionOrNull(workspacePkgsMatchingName, spec);
283
+ if (localVersion && (semver.gt(localVersion, pickedPackage.version) || opts.preferWorkspacePackages)) {
284
+ return {
285
+ ...resolveFromLocalPackage(workspacePkgsMatchingName.get(localVersion), spec, {
286
+ wantedDependency,
287
+ projectDir: opts.projectDir,
288
+ lockfileDir: opts.lockfileDir,
289
+ hardLinkLocalPackages: opts.injectWorkspacePackages === true || wantedDependency.injected,
290
+ saveWorkspaceProtocol: ctx.saveWorkspaceProtocol,
291
+ calcSpecifier: opts.calcSpecifier,
292
+ pinnedVersion: opts.pinnedVersion,
293
+ }),
294
+ latest: meta['dist-tags'].latest,
295
+ };
296
+ }
297
+ }
298
+ const id = `${pickedPackage.name}@${pickedPackage.version}`;
299
+ const resolution = {
300
+ integrity: getIntegrity(pickedPackage.dist),
301
+ tarball: normalizeRegistryUrl(pickedPackage.dist.tarball),
302
+ };
303
+ let normalizedBareSpecifier;
304
+ if (opts.calcSpecifier) {
305
+ normalizedBareSpecifier = spec.normalizedBareSpecifier ?? calcSpecifier({
306
+ wantedDependency,
307
+ spec,
308
+ version: pickedPackage.version,
309
+ defaultPinnedVersion: opts.pinnedVersion,
310
+ });
311
+ }
312
+ return {
313
+ id,
314
+ latest: meta['dist-tags'].latest,
315
+ manifest: pickedPackage,
316
+ resolution,
317
+ resolvedVia: 'npm-registry',
318
+ publishedAt: meta.time?.[pickedPackage.version],
319
+ normalizedBareSpecifier,
320
+ };
321
+ }
322
+ async function resolveJsr(ctx, wantedDependency, opts) {
323
+ if (!wantedDependency.bareSpecifier)
324
+ return null;
325
+ const defaultTag = opts.defaultTag ?? 'latest';
326
+ const registry = ctx.registries['@jsr']; // '@jsr' is always defined
327
+ const spec = parseJsrSpecifierToRegistryPackageSpec(wantedDependency.bareSpecifier, wantedDependency.alias, defaultTag);
328
+ if (spec == null)
329
+ return null;
330
+ const authHeaderValue = ctx.getAuthHeaderValueByURI(registry);
331
+ const { meta, pickedPackage } = await ctx.pickPackage(spec, {
332
+ pickLowestVersion: opts.pickLowestVersion,
333
+ publishedBy: opts.publishedBy,
334
+ authHeaderValue,
335
+ dryRun: opts.dryRun === true,
336
+ preferredVersionSelectors: opts.preferredVersions?.[spec.name],
337
+ registry,
338
+ updateToLatest: opts.update === 'latest',
339
+ });
340
+ if (pickedPackage == null) {
341
+ throw new NoMatchingVersionError({ wantedDependency, packageMeta: meta, registry });
342
+ }
343
+ const id = `${pickedPackage.name}@${pickedPackage.version}`;
344
+ const resolution = {
345
+ integrity: getIntegrity(pickedPackage.dist),
346
+ tarball: normalizeRegistryUrl(pickedPackage.dist.tarball),
347
+ };
348
+ return {
349
+ id,
350
+ latest: meta['dist-tags'].latest,
351
+ manifest: pickedPackage,
352
+ normalizedBareSpecifier: opts.calcSpecifier
353
+ ? calcJsrSpecifier({
354
+ wantedDependency,
355
+ spec,
356
+ version: pickedPackage.version,
357
+ defaultPinnedVersion: opts.pinnedVersion,
358
+ })
359
+ : undefined,
360
+ resolution,
361
+ resolvedVia: 'jsr-registry',
362
+ publishedAt: meta.time?.[pickedPackage.version],
363
+ alias: spec.jsrPkgName,
364
+ };
365
+ }
366
+ function calcJsrSpecifier({ wantedDependency, spec, version, defaultPinnedVersion, }) {
367
+ const range = calcRange(version, wantedDependency, defaultPinnedVersion);
368
+ if (!wantedDependency.alias || spec.jsrPkgName === wantedDependency.alias)
369
+ return `jsr:${range}`;
370
+ return `jsr:${spec.jsrPkgName}@${range}`;
371
+ }
372
+ function calcSpecifier({ wantedDependency, spec, version, defaultPinnedVersion, }) {
373
+ if (wantedDependency.prevSpecifier === wantedDependency.bareSpecifier && wantedDependency.prevSpecifier && versionSelectorType(wantedDependency.prevSpecifier)?.type === 'tag') {
374
+ return wantedDependency.prevSpecifier;
375
+ }
376
+ const range = calcRange(version, wantedDependency, defaultPinnedVersion);
377
+ if (!wantedDependency.alias || spec.name === wantedDependency.alias)
378
+ return range;
379
+ return `npm:${spec.name}@${range}`;
380
+ }
381
+ function calcRange(version, wantedDependency, defaultPinnedVersion) {
382
+ if (semver.parse(version)?.prerelease.length) {
383
+ return version;
384
+ }
385
+ const pinnedVersion = (wantedDependency.prevSpecifier ? whichVersionIsPinned(wantedDependency.prevSpecifier) : undefined) ??
386
+ (wantedDependency.bareSpecifier ? whichVersionIsPinned(wantedDependency.bareSpecifier) : undefined) ??
387
+ defaultPinnedVersion;
388
+ return createVersionSpec(version, pinnedVersion);
389
+ }
390
+ function tryResolveFromWorkspace(wantedDependency, opts) {
391
+ if (!wantedDependency.bareSpecifier?.startsWith('workspace:')) {
392
+ return null;
393
+ }
394
+ const bareSpecifier = workspacePrefToNpm(wantedDependency.bareSpecifier);
395
+ const spec = parseBareSpecifier(bareSpecifier, wantedDependency.alias, opts.defaultTag, opts.registry);
396
+ if (spec == null)
397
+ throw new Error(`Invalid workspace: spec (${wantedDependency.bareSpecifier})`);
398
+ if (opts.workspacePackages == null) {
399
+ throw new Error('Cannot resolve package from workspace because opts.workspacePackages is not defined');
400
+ }
401
+ if (!opts.projectDir) {
402
+ throw new Error('Cannot resolve package from workspace because opts.projectDir is not defined');
403
+ }
404
+ return tryResolveFromWorkspacePackages(opts.workspacePackages, spec, {
405
+ wantedDependency,
406
+ projectDir: opts.projectDir,
407
+ hardLinkLocalPackages: opts.injectWorkspacePackages === true || wantedDependency.injected,
408
+ lockfileDir: opts.lockfileDir,
409
+ update: opts.update,
410
+ saveWorkspaceProtocol: opts.saveWorkspaceProtocol,
411
+ calcSpecifier: opts.calcSpecifier,
412
+ pinnedVersion: opts.pinnedVersion,
413
+ });
414
+ }
415
+ function tryResolveFromWorkspacePackages(workspacePackages, spec, opts) {
416
+ const workspacePkgsMatchingName = workspacePackages.get(spec.name);
417
+ if (!workspacePkgsMatchingName) {
418
+ throw new PnpmError('WORKSPACE_PKG_NOT_FOUND', `In ${path.relative(process.cwd(), opts.projectDir)}: "${spec.name}@${opts.wantedDependency.bareSpecifier ?? ''}" is in the dependencies but no package named "${spec.name}" is present in the workspace`, {
419
+ hint: 'Packages found in the workspace: ' + Array.from(workspacePackages.keys()).join(', '),
420
+ });
421
+ }
422
+ const localVersion = pickMatchingLocalVersionOrNull(workspacePkgsMatchingName, opts.update ? { name: spec.name, fetchSpec: '*', type: 'range' } : spec);
423
+ if (!localVersion) {
424
+ const availableVersions = Array.from(workspacePkgsMatchingName.keys()).sort((a, b) => semver.rcompare(a, b));
425
+ throw new PnpmError('NO_MATCHING_VERSION_INSIDE_WORKSPACE', `In ${path.relative(process.cwd(), opts.projectDir)}: No matching version found for ${opts.wantedDependency.alias ?? ''}@${opts.wantedDependency.bareSpecifier ?? ''} inside the workspace` +
426
+ (availableVersions.length ? `. Available versions: ${availableVersions.join(', ')}` : ''), availableVersions.length
427
+ ? {
428
+ hint: `Available workspace versions for "${spec.name}": ${availableVersions.join(', ')}`,
429
+ }
430
+ : undefined);
431
+ }
432
+ return resolveFromLocalPackage(workspacePkgsMatchingName.get(localVersion), spec, opts);
433
+ }
434
+ function pickMatchingLocalVersionOrNull(versions, spec) {
435
+ switch (spec.type) {
436
+ case 'tag':
437
+ return semver.maxSatisfying(Array.from(versions.keys()), '*', {
438
+ includePrerelease: true,
439
+ });
440
+ case 'version':
441
+ return versions.has(spec.fetchSpec) ? spec.fetchSpec : null;
442
+ case 'range':
443
+ return resolveWorkspaceRange(spec.fetchSpec, Array.from(versions.keys()));
444
+ default:
445
+ return null;
446
+ }
447
+ }
448
+ function resolveFromLocalPackage(localPackage, spec, opts) {
449
+ let id;
450
+ let directory;
451
+ const localPackageDir = resolveLocalPackageDir(localPackage);
452
+ if (opts.hardLinkLocalPackages) {
453
+ directory = normalize(path.relative(opts.lockfileDir, localPackageDir));
454
+ id = `file:${directory}`;
455
+ }
456
+ else {
457
+ directory = localPackageDir;
458
+ id = `link:${normalize(path.relative(opts.projectDir, localPackageDir))}`;
459
+ }
460
+ let normalizedBareSpecifier;
461
+ if (opts.calcSpecifier) {
462
+ normalizedBareSpecifier = spec.normalizedBareSpecifier ?? calcSpecifierForWorkspaceDep({
463
+ wantedDependency: opts.wantedDependency,
464
+ spec,
465
+ saveWorkspaceProtocol: opts.saveWorkspaceProtocol,
466
+ version: localPackage.manifest.version,
467
+ defaultPinnedVersion: opts.pinnedVersion,
468
+ });
469
+ }
470
+ return {
471
+ id,
472
+ manifest: clone(localPackage.manifest),
473
+ resolution: {
474
+ directory,
475
+ type: 'directory',
476
+ },
477
+ resolvedVia: 'workspace',
478
+ normalizedBareSpecifier,
479
+ };
480
+ }
481
+ function calcSpecifierForWorkspaceDep({ wantedDependency, spec, saveWorkspaceProtocol, version, defaultPinnedVersion, }) {
482
+ if (!saveWorkspaceProtocol && !wantedDependency.bareSpecifier?.startsWith('workspace:')) {
483
+ return calcSpecifier({ wantedDependency, spec, version, defaultPinnedVersion });
484
+ }
485
+ const prefix = (!wantedDependency.alias || spec.name === wantedDependency.alias) ? 'workspace:' : `workspace:${spec.name}@`;
486
+ if (saveWorkspaceProtocol === 'rolling') {
487
+ const specifier = wantedDependency.prevSpecifier ?? wantedDependency.bareSpecifier;
488
+ if (specifier) {
489
+ if ([`${prefix}*`, `${prefix}^`, `${prefix}~`].includes(specifier))
490
+ return specifier;
491
+ const pinnedVersion = whichVersionIsPinned(specifier);
492
+ switch (pinnedVersion) {
493
+ case 'major': return `${prefix}^`;
494
+ case 'minor': return `${prefix}~`;
495
+ case 'patch':
496
+ case 'none': return `${prefix}*`;
497
+ }
498
+ }
499
+ return `${prefix}^`;
500
+ }
501
+ if (semver.parse(version)?.prerelease.length) {
502
+ return `${prefix}${version}`;
503
+ }
504
+ const pinnedVersion = (wantedDependency.prevSpecifier ? whichVersionIsPinned(wantedDependency.prevSpecifier) : undefined) ?? defaultPinnedVersion;
505
+ const range = createVersionSpec(version, pinnedVersion);
506
+ return `${prefix}${range}`;
507
+ }
508
+ function resolveLocalPackageDir(localPackage) {
509
+ if (localPackage.manifest.publishConfig?.directory == null ||
510
+ localPackage.manifest.publishConfig?.linkDirectory === false)
511
+ return localPackage.rootDir;
512
+ return path.join(localPackage.rootDir, localPackage.manifest.publishConfig.directory);
513
+ }
514
+ function defaultTagForAlias(alias, defaultTag) {
515
+ return {
516
+ fetchSpec: defaultTag,
517
+ name: alias,
518
+ type: 'tag',
519
+ };
520
+ }
521
+ function getIntegrity(dist) {
522
+ if (dist.integrity) {
523
+ return dist.integrity;
524
+ }
525
+ if (!dist.shasum) {
526
+ return undefined;
527
+ }
528
+ const integrity = ssri.fromHex(dist.shasum, 'sha1');
529
+ if (!integrity) {
530
+ throw new PnpmError('INVALID_TARBALL_INTEGRITY', `Tarball "${dist.tarball}" has invalid shasum specified in its metadata: ${dist.shasum}`);
531
+ }
532
+ return integrity.toString();
533
+ }
534
+ function createVersionSpec(version, pinnedVersion) {
535
+ switch (pinnedVersion ?? 'major') {
536
+ case 'none':
537
+ case 'major':
538
+ return `^${version}`;
539
+ case 'minor':
540
+ return `~${version}`;
541
+ case 'patch':
542
+ return version;
543
+ default:
544
+ throw new PnpmError('BAD_PINNED_VERSION', `Cannot pin '${pinnedVersion ?? 'undefined'}'`);
545
+ }
546
+ }
547
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Remove default ports (80 for HTTP, 443 for HTTPS) to ensure consistency
3
+ */
4
+ export declare function normalizeRegistryUrl(urlString: string): string;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Remove default ports (80 for HTTP, 443 for HTTPS) to ensure consistency
3
+ */
4
+ export function normalizeRegistryUrl(urlString) {
5
+ try {
6
+ return new URL(urlString).toString();
7
+ }
8
+ catch {
9
+ return urlString;
10
+ }
11
+ }
12
+ //# sourceMappingURL=normalizeRegistryUrl.js.map
@@ -0,0 +1,11 @@
1
+ export interface RegistryPackageSpec {
2
+ type: 'tag' | 'version' | 'range';
3
+ name: string;
4
+ fetchSpec: string;
5
+ normalizedBareSpecifier?: string;
6
+ }
7
+ export declare function parseBareSpecifier(bareSpecifier: string, alias: string | undefined, defaultTag: string, registry: string): RegistryPackageSpec | null;
8
+ export interface JsrRegistryPackageSpec extends RegistryPackageSpec {
9
+ jsrPkgName: string;
10
+ }
11
+ export declare function parseJsrSpecifierToRegistryPackageSpec(rawSpecifier: string, alias: string | undefined, defaultTag: string): JsrRegistryPackageSpec | null;
@@ -0,0 +1,55 @@
1
+ import { parseJsrSpecifier } from '@pnpm/resolving.jsr-specifier-parser';
2
+ import parseNpmTarballUrl from 'parse-npm-tarball-url';
3
+ import getVersionSelectorType from 'version-selector-type';
4
+ export function parseBareSpecifier(bareSpecifier, alias, defaultTag, registry) {
5
+ let name = alias;
6
+ if (bareSpecifier.startsWith('npm:')) {
7
+ bareSpecifier = bareSpecifier.slice(4);
8
+ const index = bareSpecifier.lastIndexOf('@');
9
+ if (index < 1) {
10
+ name = bareSpecifier;
11
+ bareSpecifier = defaultTag;
12
+ }
13
+ else {
14
+ name = bareSpecifier.slice(0, index);
15
+ bareSpecifier = bareSpecifier.slice(index + 1);
16
+ }
17
+ }
18
+ if (name) {
19
+ const selector = getVersionSelectorType(bareSpecifier);
20
+ if (selector != null) {
21
+ return {
22
+ fetchSpec: selector.normalized,
23
+ name,
24
+ type: selector.type,
25
+ };
26
+ }
27
+ }
28
+ if (bareSpecifier.startsWith(registry)) {
29
+ const pkg = parseNpmTarballUrl.default(bareSpecifier);
30
+ if (pkg != null) {
31
+ return {
32
+ fetchSpec: pkg.version,
33
+ name: pkg.name,
34
+ normalizedBareSpecifier: bareSpecifier,
35
+ type: 'version',
36
+ };
37
+ }
38
+ }
39
+ return null;
40
+ }
41
+ export function parseJsrSpecifierToRegistryPackageSpec(rawSpecifier, alias, defaultTag) {
42
+ const spec = parseJsrSpecifier(rawSpecifier, alias);
43
+ if (!spec?.npmPkgName)
44
+ return null;
45
+ const selector = getVersionSelectorType(spec.versionSelector ?? defaultTag);
46
+ if (selector == null)
47
+ return null;
48
+ return {
49
+ fetchSpec: selector.normalized,
50
+ name: spec.npmPkgName,
51
+ type: selector.type,
52
+ jsrPkgName: spec.jsrPkgName,
53
+ };
54
+ }
55
+ //# sourceMappingURL=parseBareSpecifier.js.map
@@ -0,0 +1,34 @@
1
+ import type { PackageInRegistry, PackageMeta } from '@pnpm/resolving.registry.types';
2
+ import type { FetchMetadataResult } from './fetch.js';
3
+ import type { RegistryPackageSpec } from './parseBareSpecifier.js';
4
+ import { type PickPackageFromMetaOptions } from './pickPackageFromMeta.js';
5
+ export interface PackageMetaCache {
6
+ get: (key: string) => PackageMeta | undefined;
7
+ set: (key: string, meta: PackageMeta) => void;
8
+ has: (key: string) => boolean;
9
+ }
10
+ export interface PickPackageOptions extends PickPackageFromMetaOptions {
11
+ authHeaderValue?: string;
12
+ pickLowestVersion?: boolean;
13
+ registry: string;
14
+ dryRun: boolean;
15
+ updateToLatest?: boolean;
16
+ optional?: boolean;
17
+ }
18
+ export declare function pickPackage(ctx: {
19
+ fetch: (pkgName: string, opts: {
20
+ registry: string;
21
+ authHeaderValue?: string;
22
+ fullMetadata?: boolean;
23
+ }) => Promise<FetchMetadataResult>;
24
+ fullMetadata?: boolean;
25
+ metaCache: PackageMetaCache;
26
+ cacheDir: string;
27
+ offline?: boolean;
28
+ preferOffline?: boolean;
29
+ filterMetadata?: boolean;
30
+ strictPublishedByCheck?: boolean;
31
+ }, spec: RegistryPackageSpec, opts: PickPackageOptions): Promise<{
32
+ meta: PackageMeta;
33
+ pickedPackage: PackageInRegistry | null;
34
+ }>;