@pnpm/installing.deps-installer 1101.9.0 → 1102.1.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/install/extendInstallOptions.d.ts +35 -15
- package/lib/install/extendInstallOptions.js +14 -0
- package/lib/install/index.d.ts +18 -0
- package/lib/install/index.js +281 -58
- package/lib/install/link.d.ts +1 -1
- package/lib/install/link.js +109 -24
- package/lib/install/recordLockfileVerified.js +2 -2
- package/lib/install/verifyLockfileResolutions.d.ts +10 -6
- package/lib/install/verifyLockfileResolutions.js +60 -10
- package/package.json +68 -65
package/lib/install/link.js
CHANGED
|
@@ -2,13 +2,16 @@ import { promises as fs } from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { progressLogger, stageLogger, statsLogger, } from '@pnpm/core-loggers';
|
|
4
4
|
import { calcDepState, findRuntimeNodeVersion } from '@pnpm/deps.graph-hasher';
|
|
5
|
+
import { readModulesDir } from '@pnpm/fs.read-modules-dir';
|
|
5
6
|
import { symlinkDependency } from '@pnpm/fs.symlink-dependency';
|
|
7
|
+
import { isValidDependencyAlias, } from '@pnpm/installing.deps-resolver';
|
|
6
8
|
import { linkDirectDeps } from '@pnpm/installing.linking.direct-dep-linker';
|
|
7
9
|
import { hoist } from '@pnpm/installing.linking.hoist';
|
|
8
10
|
import { prune } from '@pnpm/installing.linking.modules-cleaner';
|
|
9
11
|
import { filterLockfileByImporters, } from '@pnpm/lockfile.filtering';
|
|
10
12
|
import { logger } from '@pnpm/logger';
|
|
11
13
|
import { symlinkAllModules } from '@pnpm/worker';
|
|
14
|
+
import { rimraf } from '@zkochan/rimraf';
|
|
12
15
|
import pLimit from 'p-limit';
|
|
13
16
|
import { pathExists } from 'path-exists';
|
|
14
17
|
import { difference, equals, isEmpty, pick, pickBy, props } from 'ramda';
|
|
@@ -250,20 +253,47 @@ async function linkNewPackages(currentLockfile, wantedLockfile, depGraph, opts)
|
|
|
250
253
|
});
|
|
251
254
|
const existingWithUpdatedDeps = [];
|
|
252
255
|
if (!opts.force && (currentLockfile.packages != null) && (wantedLockfile.packages != null)) {
|
|
256
|
+
const currentPackages = currentLockfile.packages;
|
|
257
|
+
const wantedPackages = wantedLockfile.packages;
|
|
253
258
|
// add subdependencies that have been updated
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
!isEmpty(
|
|
259
|
-
!isEmpty(wantedLockfile.packages[depPath].optionalDependencies ?? {}))) {
|
|
259
|
+
await Promise.all(wantedRelDepPaths.map((depPath) => limitModulesDirReads(async () => {
|
|
260
|
+
if (currentPackages[depPath] &&
|
|
261
|
+
(!equals(currentPackages[depPath].dependencies, wantedPackages[depPath].dependencies) ||
|
|
262
|
+
!isEmpty(currentPackages[depPath].optionalDependencies ?? {}) ||
|
|
263
|
+
!isEmpty(wantedPackages[depPath].optionalDependencies ?? {}))) {
|
|
260
264
|
// TODO: come up with a test that triggers the usecase of depGraph[depPath] undefined
|
|
261
265
|
// see related issue: https://github.com/pnpm/pnpm/issues/870
|
|
262
266
|
if (depGraph[depPath] && !newDepPathsSet.has(depPath)) {
|
|
263
|
-
|
|
267
|
+
const { actualChildrenChanged, removedAliases: actualRemovedAliases } = await getActualChildrenDiff(depGraph[depPath], depGraph, opts.lockfileDir, opts.optional);
|
|
268
|
+
if (actualChildrenChanged) {
|
|
269
|
+
existingWithUpdatedDeps.push({
|
|
270
|
+
children: depGraph[depPath].children,
|
|
271
|
+
modules: depGraph[depPath].modules,
|
|
272
|
+
name: depGraph[depPath].name,
|
|
273
|
+
optionalDependencies: depGraph[depPath].optionalDependencies,
|
|
274
|
+
removedAliases: actualRemovedAliases,
|
|
275
|
+
});
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const { changedChildren, removedAliases } = getChangedChildren({
|
|
279
|
+
currentDependencies: currentPackages[depPath].dependencies,
|
|
280
|
+
currentOptionalDependencies: currentPackages[depPath].optionalDependencies,
|
|
281
|
+
wantedDependencies: wantedPackages[depPath].dependencies,
|
|
282
|
+
wantedOptionalDependencies: wantedPackages[depPath].optionalDependencies,
|
|
283
|
+
allChildren: depGraph[depPath].children,
|
|
284
|
+
});
|
|
285
|
+
if (!isEmpty(changedChildren) || removedAliases.length > 0) {
|
|
286
|
+
existingWithUpdatedDeps.push({
|
|
287
|
+
children: changedChildren,
|
|
288
|
+
modules: depGraph[depPath].modules,
|
|
289
|
+
name: depGraph[depPath].name,
|
|
290
|
+
optionalDependencies: depGraph[depPath].optionalDependencies,
|
|
291
|
+
removedAliases,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
264
294
|
}
|
|
265
295
|
}
|
|
266
|
-
}
|
|
296
|
+
})));
|
|
267
297
|
}
|
|
268
298
|
if (!newDepPathsSet.size && (existingWithUpdatedDeps.length === 0))
|
|
269
299
|
return { newDepPaths: [], added };
|
|
@@ -317,6 +347,7 @@ async function selectNewFromWantedDeps(wantedRelDepPaths, currentLockfile, depGr
|
|
|
317
347
|
return newDeps;
|
|
318
348
|
}
|
|
319
349
|
const limitLinking = pLimit(16);
|
|
350
|
+
const limitModulesDirReads = pLimit(16);
|
|
320
351
|
async function linkAllPkgs(storeController, depNodes, opts) {
|
|
321
352
|
// Resolved `engines.runtime` Node version (when present) so the
|
|
322
353
|
// side-effects-cache key prefix tracks the script-runner Node
|
|
@@ -365,29 +396,83 @@ async function linkAllPkgs(storeController, depNodes, opts) {
|
|
|
365
396
|
}));
|
|
366
397
|
}
|
|
367
398
|
async function linkAllModules(depNodes, depGraph, opts) {
|
|
399
|
+
await Promise.all(depNodes.flatMap((depNode) => (depNode.removedAliases ?? []).map(async (alias) => limitModulesDirReads(async () => removeObsoleteChild(depNode.modules, alias)))));
|
|
368
400
|
await symlinkAllModules({
|
|
369
401
|
deps: depNodes.map((depNode) => {
|
|
370
|
-
const children = opts.optional
|
|
371
|
-
? depNode.children
|
|
372
|
-
: pickBy((_, childAlias) => !depNode.optionalDependencies.has(childAlias), depNode.children);
|
|
373
|
-
const childrenPaths = {};
|
|
374
|
-
for (const [alias, childDepPath] of Object.entries(children ?? {})) {
|
|
375
|
-
if (childDepPath.startsWith('link:')) {
|
|
376
|
-
childrenPaths[alias] = path.resolve(opts.lockfileDir, childDepPath.slice(5));
|
|
377
|
-
}
|
|
378
|
-
else {
|
|
379
|
-
const pkg = depGraph[childDepPath];
|
|
380
|
-
if (!pkg || !pkg.installable && pkg.optional || alias === depNode.name)
|
|
381
|
-
continue;
|
|
382
|
-
childrenPaths[alias] = pkg.dir;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
402
|
return {
|
|
386
|
-
children:
|
|
403
|
+
children: getChildrenPaths(depNode, depGraph, opts.lockfileDir, opts.optional),
|
|
387
404
|
modules: depNode.modules,
|
|
388
405
|
name: depNode.name,
|
|
389
406
|
};
|
|
390
407
|
}),
|
|
391
408
|
});
|
|
392
409
|
}
|
|
410
|
+
function getChangedChildren(opts) {
|
|
411
|
+
const { currentOptionalDependencies, wantedOptionalDependencies, allChildren } = opts;
|
|
412
|
+
// Use null-prototype maps so a child literally named `constructor`,
|
|
413
|
+
// `toString`, `__proto__`, etc. is treated as a normal alias instead
|
|
414
|
+
// of colliding with an inherited `Object.prototype` key during the
|
|
415
|
+
// `in`/index lookups below.
|
|
416
|
+
const currentChildren = Object.assign(Object.create(null), opts.currentDependencies, currentOptionalDependencies);
|
|
417
|
+
const wantedChildren = Object.assign(Object.create(null), opts.wantedDependencies, wantedOptionalDependencies);
|
|
418
|
+
const changedChildren = {};
|
|
419
|
+
const removedAliases = [];
|
|
420
|
+
for (const [alias, wantedChildDepPath] of Object.entries(wantedChildren)) {
|
|
421
|
+
const optionalityChanged = hasOwn(wantedOptionalDependencies, alias) !== hasOwn(currentOptionalDependencies, alias);
|
|
422
|
+
if (currentChildren[alias] !== wantedChildDepPath || optionalityChanged) {
|
|
423
|
+
const resolvedChildDepPath = hasOwn(allChildren, alias) ? allChildren[alias] : undefined;
|
|
424
|
+
if (resolvedChildDepPath != null) {
|
|
425
|
+
changedChildren[alias] = resolvedChildDepPath;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
for (const alias of Object.keys(currentChildren)) {
|
|
430
|
+
if (!hasOwn(wantedChildren, alias)) {
|
|
431
|
+
removedAliases.push(alias);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return { changedChildren, removedAliases };
|
|
435
|
+
}
|
|
436
|
+
function hasOwn(obj, key) {
|
|
437
|
+
return obj != null && Object.hasOwn(obj, key);
|
|
438
|
+
}
|
|
439
|
+
async function getActualChildrenDiff(depNode, depGraph, lockfileDir, optional) {
|
|
440
|
+
if (depNode.optionalDependencies.size === 0) {
|
|
441
|
+
return { actualChildrenChanged: false, removedAliases: [] };
|
|
442
|
+
}
|
|
443
|
+
const currentAliases = new Set((await readModulesDir(depNode.modules) ?? []).filter((alias) => alias !== depNode.name));
|
|
444
|
+
const nextAliases = new Set(Object.keys(getChildrenPaths(depNode, depGraph, lockfileDir, optional)));
|
|
445
|
+
const removedAliases = Array.from(currentAliases).filter((alias) => !nextAliases.has(alias));
|
|
446
|
+
const actualChildrenChanged = removedAliases.length > 0 ||
|
|
447
|
+
Array.from(nextAliases).some((alias) => !currentAliases.has(alias));
|
|
448
|
+
return { actualChildrenChanged, removedAliases };
|
|
449
|
+
}
|
|
450
|
+
async function removeObsoleteChild(modulesDir, alias) {
|
|
451
|
+
// Guard against an alias that would escape the modules directory (e.g. `../../x`).
|
|
452
|
+
if (!isValidDependencyAlias(alias))
|
|
453
|
+
return;
|
|
454
|
+
await rimraf(path.join(modulesDir, alias));
|
|
455
|
+
if (alias[0] === '@') {
|
|
456
|
+
await fs.rmdir(path.join(modulesDir, alias.split('/')[0])).catch(() => { });
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
function getChildrenPaths(depNode, depGraph, lockfileDir, optional) {
|
|
460
|
+
const children = optional
|
|
461
|
+
? depNode.children
|
|
462
|
+
: pickBy((_, childAlias) => !depNode.optionalDependencies.has(childAlias), depNode.children);
|
|
463
|
+
const childrenPaths = {};
|
|
464
|
+
for (const [alias, childDepPath] of Object.entries(children ?? {})) {
|
|
465
|
+
if (alias === depNode.name)
|
|
466
|
+
continue;
|
|
467
|
+
if (childDepPath.startsWith('link:')) {
|
|
468
|
+
childrenPaths[alias] = path.resolve(lockfileDir, childDepPath.slice(5));
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
const pkg = depGraph[childDepPath];
|
|
472
|
+
if (!pkg || !pkg.installable && pkg.optional)
|
|
473
|
+
continue;
|
|
474
|
+
childrenPaths[alias] = pkg.dir;
|
|
475
|
+
}
|
|
476
|
+
return childrenPaths;
|
|
477
|
+
}
|
|
393
478
|
//# sourceMappingURL=link.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { hashObject } from '@pnpm/crypto.object-hasher';
|
|
2
|
-
import {
|
|
2
|
+
import { withOfflineCheckCacheIdentities } from './verifyLockfileResolutions.js';
|
|
3
3
|
import { recordVerification } from './verifyLockfileResolutionsCache.js';
|
|
4
4
|
/**
|
|
5
5
|
* Records the post-resolution lockfile as verified so the next install
|
|
@@ -18,7 +18,7 @@ export function recordLockfileVerified(opts) {
|
|
|
18
18
|
return;
|
|
19
19
|
recordVerification(opts.cacheDir, {
|
|
20
20
|
lockfilePath: opts.lockfilePath,
|
|
21
|
-
verifiers:
|
|
21
|
+
verifiers: withOfflineCheckCacheIdentities(opts.resolutionVerifiers),
|
|
22
22
|
hashLockfile: () => hashObject(opts.lockfile),
|
|
23
23
|
});
|
|
24
24
|
}
|
|
@@ -3,15 +3,19 @@ import type { ResolutionPolicyViolation, ResolutionVerifier } from '@pnpm/resolv
|
|
|
3
3
|
import { type VerifierCacheIdentity } from './verifyLockfileResolutionsCache.js';
|
|
4
4
|
export type { ResolutionPolicyViolation };
|
|
5
5
|
export declare const RESOLUTION_SHAPE_MISMATCH_VIOLATION_CODE = "RESOLUTION_SHAPE_MISMATCH";
|
|
6
|
+
export declare const INVALID_DEPENDENCY_ALIAS_CODE = "INVALID_DEPENDENCY_NAME";
|
|
6
7
|
/**
|
|
7
8
|
* Every verifier list that flows into the verification cache must carry
|
|
8
|
-
* the
|
|
9
|
-
* existed cannot stat-fast-path around
|
|
10
|
-
*
|
|
11
|
-
* the
|
|
12
|
-
*
|
|
9
|
+
* the always-on offline structural checks' identities, so a record
|
|
10
|
+
* written before one of those rules existed cannot stat-fast-path around
|
|
11
|
+
* it — its missing flag fails `canTrustPastCheck`, forcing a
|
|
12
|
+
* re-verification that runs the new check. Used by the gate itself and by
|
|
13
|
+
* {@link recordLockfileVerified}, whose freshly-resolved lockfile
|
|
14
|
+
* satisfies these invariants by construction (the resolver validates
|
|
15
|
+
* aliases at manifest-read time and derives every resolution key from the
|
|
16
|
+
* resolution it just produced).
|
|
13
17
|
*/
|
|
14
|
-
export declare function
|
|
18
|
+
export declare function withOfflineCheckCacheIdentities(verifiers: readonly VerifierCacheIdentity[]): VerifierCacheIdentity[];
|
|
15
19
|
export interface VerifyLockfileResolutionsOptions {
|
|
16
20
|
concurrency?: number;
|
|
17
21
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { lockfileVerificationLogger } from '@pnpm/core-loggers';
|
|
2
2
|
import { hashObject } from '@pnpm/crypto.object-hasher';
|
|
3
3
|
import { PnpmError } from '@pnpm/error';
|
|
4
|
+
import { isValidDependencyAlias } from '@pnpm/installing.deps-resolver';
|
|
4
5
|
import { isGitHostedTarballUrl, nameVerFromPkgSnapshot } from '@pnpm/lockfile.utils';
|
|
5
6
|
import pLimit from 'p-limit';
|
|
6
7
|
import { recordVerification, tryLockfileVerificationCache, } from './verifyLockfileResolutionsCache.js';
|
|
@@ -13,20 +14,29 @@ const MAX_VIOLATIONS_TO_PRINT = 20;
|
|
|
13
14
|
// verification pass doesn't push past what the rest of the install respects.
|
|
14
15
|
const DEFAULT_CONCURRENCY = 64;
|
|
15
16
|
export const RESOLUTION_SHAPE_MISMATCH_VIOLATION_CODE = 'RESOLUTION_SHAPE_MISMATCH';
|
|
17
|
+
// Same code the sink-level guards (`safeJoinModulesDir`) throw.
|
|
18
|
+
export const INVALID_DEPENDENCY_ALIAS_CODE = 'INVALID_DEPENDENCY_NAME';
|
|
16
19
|
const RESOLUTION_SHAPE_CACHE_IDENTITY = {
|
|
17
20
|
policy: { resolutionShapeCheck: true },
|
|
18
21
|
canTrustPastCheck: (cached) => cached.resolutionShapeCheck === true,
|
|
19
22
|
};
|
|
23
|
+
const DEPENDENCY_ALIAS_CACHE_IDENTITY = {
|
|
24
|
+
policy: { dependencyAliasCheck: true },
|
|
25
|
+
canTrustPastCheck: (cached) => cached.dependencyAliasCheck === true,
|
|
26
|
+
};
|
|
20
27
|
/**
|
|
21
28
|
* Every verifier list that flows into the verification cache must carry
|
|
22
|
-
* the
|
|
23
|
-
* existed cannot stat-fast-path around
|
|
24
|
-
*
|
|
25
|
-
* the
|
|
26
|
-
*
|
|
29
|
+
* the always-on offline structural checks' identities, so a record
|
|
30
|
+
* written before one of those rules existed cannot stat-fast-path around
|
|
31
|
+
* it — its missing flag fails `canTrustPastCheck`, forcing a
|
|
32
|
+
* re-verification that runs the new check. Used by the gate itself and by
|
|
33
|
+
* {@link recordLockfileVerified}, whose freshly-resolved lockfile
|
|
34
|
+
* satisfies these invariants by construction (the resolver validates
|
|
35
|
+
* aliases at manifest-read time and derives every resolution key from the
|
|
36
|
+
* resolution it just produced).
|
|
27
37
|
*/
|
|
28
|
-
export function
|
|
29
|
-
return [...verifiers, RESOLUTION_SHAPE_CACHE_IDENTITY];
|
|
38
|
+
export function withOfflineCheckCacheIdentities(verifiers) {
|
|
39
|
+
return [...verifiers, RESOLUTION_SHAPE_CACHE_IDENTITY, DEPENDENCY_ALIAS_CACHE_IDENTITY];
|
|
30
40
|
}
|
|
31
41
|
/**
|
|
32
42
|
* Policy-neutral pass that asks every resolver-supplied
|
|
@@ -65,7 +75,7 @@ export async function verifyLockfileResolutions(lockfile, verifiers, options) {
|
|
|
65
75
|
const cache = options?.cacheDir && options?.lockfilePath
|
|
66
76
|
? { cacheDir: options.cacheDir, lockfilePath: options.lockfilePath }
|
|
67
77
|
: undefined;
|
|
68
|
-
const cacheVerifiers =
|
|
78
|
+
const cacheVerifiers = withOfflineCheckCacheIdentities(verifiers);
|
|
69
79
|
let cachePrecomputed;
|
|
70
80
|
// hashObject streams and is key-order-stable, unlike JSON.stringify.
|
|
71
81
|
let cachedHash;
|
|
@@ -104,7 +114,10 @@ export async function verifyLockfileResolutions(lockfile, verifiers, options) {
|
|
|
104
114
|
// A degenerate lockfile where every snapshot fails the
|
|
105
115
|
// name/version extraction (so candidates is empty) skips emission
|
|
106
116
|
// entirely — no work, no noise.
|
|
107
|
-
const { candidates, shapeViolations } = collectCandidates(lockfile);
|
|
117
|
+
const { candidates, shapeViolations, invalidAliases } = collectCandidates(lockfile);
|
|
118
|
+
if (invalidAliases.length > 0) {
|
|
119
|
+
throw buildInvalidAliasError(invalidAliases);
|
|
120
|
+
}
|
|
108
121
|
if (shapeViolations.length > 0) {
|
|
109
122
|
throw buildVerificationError(shapeViolations);
|
|
110
123
|
}
|
|
@@ -158,6 +171,19 @@ export async function verifyLockfileResolutions(lockfile, verifiers, options) {
|
|
|
158
171
|
});
|
|
159
172
|
}
|
|
160
173
|
}
|
|
174
|
+
function buildInvalidAliasError(aliases) {
|
|
175
|
+
const sorted = [...aliases].sort();
|
|
176
|
+
const visible = sorted.slice(0, MAX_VIOLATIONS_TO_PRINT);
|
|
177
|
+
const omitted = sorted.length - visible.length;
|
|
178
|
+
const breakdown = visible.map((alias) => ` ${JSON.stringify(alias)}`).join('\n');
|
|
179
|
+
const details = omitted > 0 ? `${breakdown}\n …and ${omitted} more` : breakdown;
|
|
180
|
+
const plural = aliases.length === 1 ? 'alias' : 'aliases';
|
|
181
|
+
return new PnpmError(INVALID_DEPENDENCY_ALIAS_CODE, `The lockfile contains ${aliases.length} dependency ${plural} that are not valid package names:\n${details}`, {
|
|
182
|
+
hint: 'A dependency alias becomes a directory under node_modules, so it must be a valid npm package name — a single `name` or `@scope/name` with no leading `.` or `_`, and not a reserved name such as `node_modules`. ' +
|
|
183
|
+
'An alias containing path-traversal segments or a reserved name such as `.bin` or `.pnpm` could make an install write outside the intended directory or overwrite pnpm-owned layout. ' +
|
|
184
|
+
'This usually means the lockfile was tampered with — inspect recent changes to pnpm-lock.yaml before trusting it.',
|
|
185
|
+
});
|
|
186
|
+
}
|
|
161
187
|
function buildVerificationError(violations) {
|
|
162
188
|
// Stable order so the error output is deterministic.
|
|
163
189
|
violations.sort((a, b) => `${a.name}@${a.version}`.localeCompare(`${b.name}@${b.version}`));
|
|
@@ -266,7 +292,17 @@ function isRegistryShapedResolution(resolution) {
|
|
|
266
292
|
function collectCandidates(lockfile) {
|
|
267
293
|
const candidates = new Map();
|
|
268
294
|
const shapeViolations = [];
|
|
295
|
+
// The importer alias maps are the one source not reached by the
|
|
296
|
+
// package loop below, so they're scanned here.
|
|
297
|
+
const invalidAliases = new Set();
|
|
298
|
+
for (const importer of Object.values(lockfile.importers ?? {})) {
|
|
299
|
+
pushInvalidAliases(importer.dependencies, invalidAliases);
|
|
300
|
+
pushInvalidAliases(importer.devDependencies, invalidAliases);
|
|
301
|
+
pushInvalidAliases(importer.optionalDependencies, invalidAliases);
|
|
302
|
+
}
|
|
269
303
|
for (const [depPath, snapshot] of Object.entries(lockfile.packages ?? {})) {
|
|
304
|
+
pushInvalidAliases(snapshot.dependencies, invalidAliases);
|
|
305
|
+
pushInvalidAliases(snapshot.optionalDependencies, invalidAliases);
|
|
270
306
|
const { name, version, nonSemverVersion } = nameVerFromPkgSnapshot(depPath, snapshot);
|
|
271
307
|
if (!name || !version)
|
|
272
308
|
continue;
|
|
@@ -292,7 +328,21 @@ function collectCandidates(lockfile) {
|
|
|
292
328
|
resolution: snapshot.resolution,
|
|
293
329
|
});
|
|
294
330
|
}
|
|
295
|
-
return { candidates, shapeViolations };
|
|
331
|
+
return { candidates, shapeViolations, invalidAliases: Array.from(invalidAliases) };
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Add every key of `deps` that is not a valid {@link isValidDependencyAlias}
|
|
335
|
+
* to `invalid`. Only pass maps whose keys become `node_modules/<alias>`
|
|
336
|
+
* directories — not `overrides` (`foo>bar` selectors) or
|
|
337
|
+
* `patchedDependencies` (`name@version` keys).
|
|
338
|
+
*/
|
|
339
|
+
function pushInvalidAliases(deps, invalid) {
|
|
340
|
+
if (deps == null)
|
|
341
|
+
return;
|
|
342
|
+
for (const alias of Object.keys(deps)) {
|
|
343
|
+
if (!isValidDependencyAlias(alias))
|
|
344
|
+
invalid.add(alias);
|
|
345
|
+
}
|
|
296
346
|
}
|
|
297
347
|
async function iterateLockfileViolations(candidates, verifiers, concurrency) {
|
|
298
348
|
const violations = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pnpm/installing.deps-installer",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1102.1.0",
|
|
4
4
|
"description": "Fast, disk space efficient installation engine",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pnpm",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@inquirer/prompts": "^8.4.3",
|
|
54
54
|
"@pnpm/npm-package-arg": "^2.0.0",
|
|
55
|
-
"@pnpm/util.lex-comparator": "^
|
|
55
|
+
"@pnpm/util.lex-comparator": "^4.0.1",
|
|
56
56
|
"@zkochan/rimraf": "^4.0.0",
|
|
57
57
|
"is-inner-link": "^5.0.0",
|
|
58
58
|
"is-subdir": "^2.0.0",
|
|
@@ -64,104 +64,105 @@
|
|
|
64
64
|
"path-exists": "^5.0.0",
|
|
65
65
|
"ramda": "npm:@pnpm/ramda@0.28.1",
|
|
66
66
|
"run-groups": "^5.0.0",
|
|
67
|
-
"semver": "^7.8.
|
|
68
|
-
"@pnpm/bins.linker": "1100.0.
|
|
69
|
-
"@pnpm/building.after-install": "
|
|
70
|
-
"@pnpm/building.during-install": "
|
|
71
|
-
"@pnpm/building.policy": "1100.0.
|
|
72
|
-
"@pnpm/catalogs.
|
|
73
|
-
"@pnpm/catalogs.types": "1100.0.0",
|
|
67
|
+
"semver": "^7.8.4",
|
|
68
|
+
"@pnpm/bins.linker": "1100.0.15",
|
|
69
|
+
"@pnpm/building.after-install": "1102.0.1",
|
|
70
|
+
"@pnpm/building.during-install": "1102.0.1",
|
|
71
|
+
"@pnpm/building.policy": "1100.0.10",
|
|
72
|
+
"@pnpm/catalogs.config": "1100.0.1",
|
|
74
73
|
"@pnpm/catalogs.protocol-parser": "1100.0.0",
|
|
74
|
+
"@pnpm/catalogs.resolver": "1100.0.0",
|
|
75
|
+
"@pnpm/config.matcher": "1100.0.1",
|
|
76
|
+
"@pnpm/config.normalize-registries": "1100.0.8",
|
|
75
77
|
"@pnpm/config.parse-overrides": "1100.0.1",
|
|
78
|
+
"@pnpm/core-loggers": "1100.2.1",
|
|
76
79
|
"@pnpm/constants": "1100.0.0",
|
|
77
|
-
"@pnpm/
|
|
78
|
-
"@pnpm/
|
|
79
|
-
"@pnpm/
|
|
80
|
-
"@pnpm/
|
|
81
|
-
"@pnpm/crypto.hash": "1100.0.1",
|
|
80
|
+
"@pnpm/bins.remover": "1100.0.10",
|
|
81
|
+
"@pnpm/deps.graph-hasher": "1100.2.5",
|
|
82
|
+
"@pnpm/catalogs.types": "1100.0.0",
|
|
83
|
+
"@pnpm/crypto.object-hasher": "1100.0.0",
|
|
82
84
|
"@pnpm/deps.graph-sequencer": "1100.0.0",
|
|
85
|
+
"@pnpm/deps.path": "1100.0.8",
|
|
83
86
|
"@pnpm/error": "1100.0.0",
|
|
84
|
-
"@pnpm/
|
|
85
|
-
"@pnpm/exec.lifecycle": "1100.0.17",
|
|
86
|
-
"@pnpm/fs.symlink-dependency": "1100.0.9",
|
|
87
|
-
"@pnpm/crypto.object-hasher": "1100.0.0",
|
|
88
|
-
"@pnpm/hooks.read-package-hook": "1100.0.7",
|
|
89
|
-
"@pnpm/hooks.types": "1100.0.11",
|
|
90
|
-
"@pnpm/installing.deps-resolver": "1100.2.2",
|
|
91
|
-
"@pnpm/bins.remover": "1100.0.9",
|
|
92
|
-
"@pnpm/installing.context": "1100.0.17",
|
|
93
|
-
"@pnpm/installing.linking.hoist": "1100.0.13",
|
|
94
|
-
"@pnpm/installing.deps-restorer": "1101.1.11",
|
|
95
|
-
"@pnpm/installing.linking.modules-cleaner": "1100.1.7",
|
|
96
|
-
"@pnpm/installing.linking.direct-dep-linker": "1100.0.9",
|
|
97
|
-
"@pnpm/installing.modules-yaml": "1100.0.8",
|
|
98
|
-
"@pnpm/lockfile.filtering": "1100.1.6",
|
|
99
|
-
"@pnpm/lockfile.fs": "1100.1.4",
|
|
100
|
-
"@pnpm/installing.package-requester": "1101.1.0",
|
|
101
|
-
"@pnpm/lockfile.preferred-versions": "1100.0.15",
|
|
87
|
+
"@pnpm/exec.lifecycle": "1100.1.0",
|
|
102
88
|
"@pnpm/fs.read-modules-dir": "1100.0.1",
|
|
103
|
-
"@pnpm/
|
|
104
|
-
"@pnpm/
|
|
105
|
-
"@pnpm/
|
|
106
|
-
"@pnpm/
|
|
107
|
-
"@pnpm/
|
|
108
|
-
"@pnpm/
|
|
109
|
-
"@pnpm/
|
|
110
|
-
"@pnpm/
|
|
111
|
-
"@pnpm/
|
|
112
|
-
"@pnpm/
|
|
113
|
-
"@pnpm/
|
|
114
|
-
"@pnpm/
|
|
115
|
-
"@pnpm/
|
|
116
|
-
"@pnpm/
|
|
89
|
+
"@pnpm/fs.symlink-dependency": "1100.0.10",
|
|
90
|
+
"@pnpm/hooks.types": "1100.0.12",
|
|
91
|
+
"@pnpm/installing.context": "1100.0.19",
|
|
92
|
+
"@pnpm/installing.deps-resolver": "1100.2.4",
|
|
93
|
+
"@pnpm/crypto.hash": "1100.0.1",
|
|
94
|
+
"@pnpm/installing.deps-restorer": "1102.1.0",
|
|
95
|
+
"@pnpm/installing.linking.hoist": "1100.0.15",
|
|
96
|
+
"@pnpm/installing.linking.modules-cleaner": "1100.1.8",
|
|
97
|
+
"@pnpm/installing.package-requester": "1102.0.0",
|
|
98
|
+
"@pnpm/installing.linking.direct-dep-linker": "1100.0.10",
|
|
99
|
+
"@pnpm/installing.modules-yaml": "1100.0.9",
|
|
100
|
+
"@pnpm/lockfile.filtering": "1100.1.7",
|
|
101
|
+
"@pnpm/lockfile.preferred-versions": "1100.0.16",
|
|
102
|
+
"@pnpm/lockfile.fs": "1100.1.6",
|
|
103
|
+
"@pnpm/lockfile.pruner": "1100.0.11",
|
|
104
|
+
"@pnpm/lockfile.to-pnp": "1100.1.0",
|
|
105
|
+
"@pnpm/lockfile.verification": "1100.0.19",
|
|
106
|
+
"@pnpm/lockfile.settings-checker": "1100.0.19",
|
|
107
|
+
"@pnpm/lockfile.utils": "1100.0.13",
|
|
108
|
+
"@pnpm/network.auth-header": "1101.1.2",
|
|
109
|
+
"@pnpm/patching.config": "1100.0.8",
|
|
110
|
+
"@pnpm/pkg-manifest.utils": "1100.2.5",
|
|
111
|
+
"@pnpm/pnpr.client": "1.2.2",
|
|
112
|
+
"@pnpm/resolving.resolver-base": "1100.4.2",
|
|
113
|
+
"@pnpm/store.controller-types": "1100.1.5",
|
|
117
114
|
"@pnpm/resolving.parse-wanted-dependency": "1100.0.1",
|
|
118
|
-
"@pnpm/
|
|
115
|
+
"@pnpm/types": "1101.3.2",
|
|
116
|
+
"@pnpm/workspace.project-manifest-reader": "1100.0.13",
|
|
117
|
+
"@pnpm/hooks.read-package-hook": "1100.0.8",
|
|
118
|
+
"@pnpm/lockfile.walker": "1100.0.11",
|
|
119
|
+
"@pnpm/store.index": "1100.2.0"
|
|
119
120
|
},
|
|
120
121
|
"peerDependencies": {
|
|
121
|
-
"@pnpm/logger": "^
|
|
122
|
-
"@pnpm/worker": "^1100.1
|
|
122
|
+
"@pnpm/logger": "^1100.0.0",
|
|
123
|
+
"@pnpm/worker": "^1100.2.1"
|
|
123
124
|
},
|
|
124
125
|
"devDependencies": {
|
|
125
|
-
"@jest/globals": "30.
|
|
126
|
+
"@jest/globals": "30.4.1",
|
|
126
127
|
"@types/fs-extra": "^11.0.4",
|
|
127
128
|
"@types/is-windows": "^1.0.2",
|
|
128
129
|
"@types/normalize-path": "^3.0.2",
|
|
129
130
|
"@types/ramda": "0.31.1",
|
|
130
131
|
"@types/semver": "7.7.1",
|
|
131
|
-
"@yarnpkg/core": "4.
|
|
132
|
+
"@yarnpkg/core": "4.8.0",
|
|
132
133
|
"ci-info": "^4.4.0",
|
|
133
134
|
"deep-require-cwd": "1.0.0",
|
|
134
135
|
"execa": "npm:safe-execa@0.3.0",
|
|
135
136
|
"exists-link": "2.0.0",
|
|
136
137
|
"is-windows": "^1.0.2",
|
|
137
|
-
"nock": "13.3.4",
|
|
138
138
|
"path-name": "^1.0.0",
|
|
139
139
|
"read-yaml-file": "^3.0.0",
|
|
140
140
|
"resolve-link-target": "^3.0.0",
|
|
141
141
|
"symlink-dir": "^10.0.1",
|
|
142
142
|
"write-json-file": "^7.0.0",
|
|
143
143
|
"write-yaml-file": "^6.0.0",
|
|
144
|
-
"@pnpm/assert-project": "1100.0.
|
|
145
|
-
"@pnpm/
|
|
146
|
-
"@pnpm/
|
|
144
|
+
"@pnpm/assert-project": "1100.0.16",
|
|
145
|
+
"@pnpm/installing.deps-installer": "1102.1.0",
|
|
146
|
+
"@pnpm/assert-store": "1100.0.16",
|
|
147
|
+
"@pnpm/lockfile.types": "1100.0.11",
|
|
147
148
|
"@pnpm/logger": "1100.0.0",
|
|
148
|
-
"@pnpm/
|
|
149
|
-
"@pnpm/
|
|
150
|
-
"@pnpm/
|
|
151
|
-
"@pnpm/pkg-manifest.reader": "1100.0.7",
|
|
152
|
-
"@pnpm/store.cafs": "1100.1.9",
|
|
149
|
+
"@pnpm/prepare": "1100.0.16",
|
|
150
|
+
"@pnpm/store.cafs": "1100.1.10",
|
|
151
|
+
"@pnpm/pkg-manifest.reader": "1100.0.8",
|
|
153
152
|
"@pnpm/store.path": "1100.0.1",
|
|
154
|
-
"@pnpm/prepare": "1100.0.15",
|
|
155
153
|
"@pnpm/test-ipc-server": "1100.0.0",
|
|
156
154
|
"@pnpm/test-fixtures": "1100.0.0",
|
|
157
|
-
"@pnpm/
|
|
158
|
-
"@pnpm/testing.
|
|
159
|
-
"@pnpm/
|
|
155
|
+
"@pnpm/network.git-utils": "1100.0.2",
|
|
156
|
+
"@pnpm/testing.mock-agent": "1101.0.3",
|
|
157
|
+
"@pnpm/resolving.registry.types": "1100.1.3",
|
|
158
|
+
"@pnpm/testing.temp-store": "1100.1.10",
|
|
159
|
+
"@pnpm/testing.registry-mock": "1100.0.6"
|
|
160
160
|
},
|
|
161
161
|
"engines": {
|
|
162
162
|
"node": ">=22.13"
|
|
163
163
|
},
|
|
164
164
|
"jest": {
|
|
165
|
+
"collectCoverage": false,
|
|
165
166
|
"preset": "@pnpm/jest-config/with-registry"
|
|
166
167
|
},
|
|
167
168
|
"scripts": {
|
|
@@ -170,6 +171,8 @@
|
|
|
170
171
|
"test-with-preview": "preview && pnpm run test:e2e",
|
|
171
172
|
"test": "pn compile && pn .test",
|
|
172
173
|
"compile": "tsgo --build && pn lint --fix",
|
|
173
|
-
".test": "
|
|
174
|
+
".test": "pn .test:heavy && pn .test:rest",
|
|
175
|
+
".test:heavy": "cross-env PNPM_REGISTRY_MOCK_PORT=7769 NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169\" jest test/install/deepRecursive.ts",
|
|
176
|
+
".test:rest": "cross-env PNPM_REGISTRY_MOCK_PORT=7769 NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules --disable-warning=ExperimentalWarning --disable-warning=DEP0169\" jest \"^(?!.*deepRecursive)\""
|
|
174
177
|
}
|
|
175
178
|
}
|