feat: configurable max length of directories inside node_modules/.pnpm (#7994)

close #7355
This commit is contained in:
Zoltan Kochan
2024-04-28 11:07:29 +02:00
committed by GitHub
parent b7ac82ea3c
commit 9719a42d0e
86 changed files with 404 additions and 82 deletions

View File

@@ -0,0 +1,37 @@
---
"@pnpm/dependency-path": major
"@pnpm/plugin-commands-installation": minor
"@pnpm/plugin-commands-publishing": minor
"@pnpm/plugin-commands-script-runners": minor
"@pnpm/plugin-commands-licenses": minor
"@pnpm/plugin-commands-outdated": minor
"@pnpm/plugin-commands-patching": minor
"@pnpm/read-projects-context": minor
"@pnpm/plugin-commands-listing": minor
"@pnpm/resolve-dependencies": minor
"@pnpm/plugin-commands-deploy": minor
"@pnpm/reviewing.dependencies-hierarchy": minor
"@pnpm/plugin-commands-audit": minor
"@pnpm/store-connection-manager": minor
"@pnpm/package-requester": minor
"@pnpm/plugin-commands-rebuild": minor
"@pnpm/modules-cleaner": minor
"@pnpm/plugin-commands-store": minor
"@pnpm/license-scanner": minor
"@pnpm/lockfile-to-pnp": minor
"@pnpm/modules-yaml": minor
"@pnpm/lockfile-utils": minor
"@pnpm/get-context": minor
"@pnpm/mount-modules": minor
"@pnpm/headless": minor
"@pnpm/package-store": minor
"@pnpm/deps.graph-builder": minor
"@pnpm/hoist": minor
"@pnpm/core": minor
"@pnpm/audit": minor
"@pnpm/list": minor
"@pnpm/config": minor
"@pnpm/server": minor
---
New setting called `virtual-store-dir-max-length` added for modifying the max allowed length of the directories inside `node_modules/.pnpm`. The default length is 120 characters [#7355](https://github.com/pnpm/pnpm/issues/7355).

View File

@@ -192,6 +192,7 @@ export interface Config {
dedupeInjectedDeps?: boolean
nodeOptions?: string
packageManagerStrict?: boolean
virtualStoreDirMaxLength: number
}
export interface ConfigWithDeprecatedSettings extends Config {

View File

@@ -140,6 +140,7 @@ export const types = Object.assign({
'use-stderr': Boolean,
'verify-store-integrity': Boolean,
'virtual-store-dir': String,
'virtual-store-dir-max-length': Number,
'workspace-concurrency': Number,
'workspace-packages': [String, Array],
'workspace-root': Boolean,
@@ -272,6 +273,7 @@ export async function getConfig (
'workspace-prefix': opts.workspaceDir,
'embed-readme': false,
'registry-supports-time-field': false,
'virtual-store-dir-max-length': 120,
}
const { config: npmConfig, warnings, failedToLoadBuiltInConfig } = loadNpmConf(cliOptions, rcOptionsTypes, defaultOptions)

View File

@@ -68,6 +68,7 @@ export interface LockfileToDepGraphOptions {
storeDir: string
virtualStoreDir: string
supportedArchitectures?: SupportedArchitectures
virtualStoreDirMaxLength: number
}
export interface DirectDependenciesByImporterId {
@@ -103,7 +104,7 @@ export async function lockfileToDepGraph (
if (opts.skipped.has(depPath)) return
// TODO: optimize. This info can be already returned by pkgSnapshotToResolution()
const { name: pkgName, version: pkgVersion } = nameVerFromPkgSnapshot(depPath, pkgSnapshot)
const modules = path.join(opts.virtualStoreDir, dp.depPathToFilename(depPath), 'node_modules')
const modules = path.join(opts.virtualStoreDir, dp.depPathToFilename(depPath, opts.virtualStoreDirMaxLength), 'node_modules')
const packageId = packageIdFromSnapshot(depPath, pkgSnapshot)
const pkg = {
@@ -208,6 +209,7 @@ export async function lockfileToDepGraph (
storeController: opts.storeController,
storeDir: opts.storeDir,
virtualStoreDir: opts.virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
}
for (const [dir, node] of Object.entries(graph)) {
const pkgSnapshot = pkgSnapshotByLocation[dir]
@@ -244,6 +246,7 @@ function getChildrenPaths (
lockfileDir: string
sideEffectsCacheRead: boolean
storeController: StoreController
virtualStoreDirMaxLength: number
},
allDeps: { [alias: string]: string },
peerDeps: Set<string> | null,
@@ -263,7 +266,7 @@ function getChildrenPaths (
} else if (childPkgSnapshot) {
if (ctx.skipped.has(childRelDepPath)) continue
const pkgName = nameVerFromPkgSnapshot(childRelDepPath, childPkgSnapshot).name
children[alias] = path.join(ctx.virtualStoreDir, dp.depPathToFilename(childRelDepPath), 'node_modules', pkgName)
children[alias] = path.join(ctx.virtualStoreDir, dp.depPathToFilename(childRelDepPath, ctx.virtualStoreDirMaxLength), 'node_modules', pkgName)
} else if (ref.indexOf('file:') === 0) {
children[alias] = path.resolve(ctx.lockfileDir, ref.slice(5))
} else if (!ctx.skipped.has(childRelDepPath) && ((peerDeps == null) || !peerDeps.has(alias))) {

View File

@@ -47,6 +47,7 @@ export type StrictRebuildOptions = {
deployAllFiles: boolean
neverBuiltDependencies?: string[]
onlyBuiltDependencies?: string[]
virtualStoreDirMaxLength: number
} & Pick<Config, 'sslConfigs'>
export type RebuildOptions = Partial<StrictRebuildOptions> &

View File

@@ -200,6 +200,7 @@ export async function rebuildProjects (
skipped: Array.from(ctx.skipped),
storeDir: ctx.storeDir,
virtualStoreDir: ctx.virtualStoreDir,
virtualStoreDirMaxLength: ctx.virtualStoreDirMaxLength,
})
}
@@ -295,7 +296,7 @@ async function _rebuild (
const pkgInfo = nameVerFromPkgSnapshot(depPath, pkgSnapshot)
const pkgRoots = opts.nodeLinker === 'hoisted'
? (ctx.modulesFile?.hoistedLocations?.[depPath] ?? []).map((hoistedLocation) => path.join(opts.lockfileDir, hoistedLocation))
: [path.join(ctx.virtualStoreDir, dp.depPathToFilename(depPath), 'node_modules', pkgInfo.name)]
: [path.join(ctx.virtualStoreDir, dp.depPathToFilename(depPath, opts.virtualStoreDirMaxLength), 'node_modules', pkgInfo.name)]
if (pkgRoots.length === 0) {
if (pkgSnapshot.optional) return
throw new PnpmError('MISSING_HOISTED_LOCATIONS', `${depPath} is not found in hoistedLocations inside node_modules/.modules.yaml`, {
@@ -306,7 +307,7 @@ async function _rebuild (
try {
const extraBinPaths = ctx.extraBinPaths
if (opts.nodeLinker !== 'hoisted') {
const modules = path.join(ctx.virtualStoreDir, dp.depPathToFilename(depPath), 'node_modules')
const modules = path.join(ctx.virtualStoreDir, dp.depPathToFilename(depPath, opts.virtualStoreDirMaxLength), 'node_modules')
const binPath = path.join(pkgRoot, 'node_modules', '.bin')
await linkBins(modules, binPath, { extraNodePaths: ctx.extraNodePaths, warn })
} else {
@@ -403,7 +404,7 @@ async function _rebuild (
.map(async (depPath) => limitLinking(async () => {
const pkgSnapshot = pkgSnapshots[depPath]
const pkgInfo = nameVerFromPkgSnapshot(depPath, pkgSnapshot)
const modules = path.join(ctx.virtualStoreDir, dp.depPathToFilename(depPath), 'node_modules')
const modules = path.join(ctx.virtualStoreDir, dp.depPathToFilename(depPath, opts.virtualStoreDirMaxLength), 'node_modules')
const binPath = path.join(modules, pkgInfo.name, 'node_modules', '.bin')
return linkBins(modules, binPath, { warn })
}))

View File

@@ -52,4 +52,5 @@ export const DEFAULT_OPTS = {
cpu: ['current'],
libc: ['current'],
},
virtualStoreDirMaxLength: 120,
}

View File

@@ -55,6 +55,7 @@ export const DEFAULT_OPTS = {
cpu: ['current'],
libc: ['current'],
},
virtualStoreDirMaxLength: 120,
}
export const DLX_DEFAULT_OPTS = {
@@ -91,4 +92,5 @@ export const DLX_DEFAULT_OPTS = {
cpu: ['current'],
libc: ['current'],
},
virtualStoreDirMaxLength: 120,
}

View File

@@ -23,6 +23,7 @@ export async function audit (
registry: string
retry?: RetryTimeoutOptions
timeout?: number
virtualStoreDirMaxLength: number
}
): Promise<AuditReport> {
const auditTree = await lockfileToAuditTree(lockfile, { include: opts.include, lockfileDir: opts.lockfileDir })
@@ -55,6 +56,7 @@ export async function audit (
lockfile,
lockfileDir: opts.lockfileDir,
include: opts.include,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
})
} catch (err: unknown) {
assert(util.types.isNativeError(err))
@@ -79,6 +81,7 @@ async function extendWithDependencyPaths (auditReport: AuditReport, opts: {
lockfile: Lockfile
lockfileDir: string
include?: { [dependenciesField in DependenciesField]: boolean }
virtualStoreDirMaxLength: number
}): Promise<AuditReport> {
const { advisories } = auditReport
if (!Object.keys(advisories).length) return auditReport
@@ -88,6 +91,7 @@ async function extendWithDependencyPaths (auditReport: AuditReport, opts: {
lockfileDir: opts.lockfileDir,
depth: Infinity,
include: opts.include,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
}
const _searchPackagePaths = searchPackagePaths.bind(null, searchOpts, projectDirs)
// eslint-disable-next-line @typescript-eslint/naming-convention
@@ -104,6 +108,7 @@ async function searchPackagePaths (
lockfileDir: string
depth: number
include?: { [dependenciesField in DependenciesField]: boolean }
virtualStoreDirMaxLength: number
},
projectDirs: string[],
pkg: string

View File

@@ -164,6 +164,7 @@ describe('audit', () => {
retry: {
retries: 0,
},
virtualStoreDirMaxLength: 120,
})
} catch (_err: any) { // eslint-disable-line
err = _err

View File

@@ -15,6 +15,7 @@ export async function writePnpFile (
importerNames: Record<string, string>
lockfileDir: string
virtualStoreDir: string
virtualStoreDirMaxLength: number
registries: Registries
}
): Promise<void> {
@@ -36,6 +37,7 @@ export function lockfileToPackageRegistry (
importerNames: { [importerId: string]: string }
lockfileDir: string
virtualStoreDir: string
virtualStoreDirMaxLength: number
registries: Registries
}
): PackageRegistry {
@@ -87,7 +89,7 @@ export function lockfileToPackageRegistry (
// Seems like this field should always contain a relative path
let packageLocation = normalizePath(path.relative(opts.lockfileDir, path.join(
opts.virtualStoreDir,
depPathToFilename(relDepPath),
depPathToFilename(relDepPath, opts.virtualStoreDirMaxLength),
'node_modules',
name
)))

View File

@@ -61,6 +61,7 @@ test('lockfileToPackageRegistry', () => {
default: 'https://registry.npmjs.org/',
},
virtualStoreDir: path.resolve('node_modules/.pnpm'),
virtualStoreDirMaxLength: 120,
})
const actual = Array.from(
@@ -212,6 +213,7 @@ test('lockfileToPackageRegistry packages that have peer deps', () => {
default: 'https://registry.npmjs.org/',
},
virtualStoreDir: path.resolve('node_modules/.pnpm'),
virtualStoreDirMaxLength: 120,
})
const actual = Array.from(

View File

@@ -12,11 +12,12 @@ export function extendProjectsWithTargetDirs<T> (
ctx: {
virtualStoreDir: string
pkgLocationsByDepPath?: Record<string, string[]>
virtualStoreDirMaxLength: number
}
): Array<T & { id: string, stages: string[], targetDirs: string[] }> {
const getLocalLocations: GetLocalLocations = ctx.pkgLocationsByDepPath != null
? (depPath: string) => ctx.pkgLocationsByDepPath![depPath]
: (depPath: string, pkgName: string) => [path.join(ctx.virtualStoreDir, depPathToFilename(depPath), 'node_modules', pkgName)]
: (depPath: string, pkgName: string) => [path.join(ctx.virtualStoreDir, depPathToFilename(depPath, ctx.virtualStoreDirMaxLength), 'node_modules', pkgName)]
const projectsById: Record<string, T & { id: string, targetDirs: string[], stages?: string[] }> =
Object.fromEntries(projects.map((project) => [project.id, { ...project, targetDirs: [] as string[] }]))
Object.entries(lockfile.packages ?? {})

View File

@@ -141,6 +141,7 @@ export async function handler (
| 'userConfig'
| 'rawConfig'
| 'rootProjectManifest'
| 'virtualStoreDirMaxLength'
>
): Promise<{ exitCode: number, output: string }> {
const lockfileDir = opts.lockfileDir ?? opts.dir
@@ -179,6 +180,7 @@ export async function handler (
retries: opts.fetchRetries,
},
timeout: opts.fetchTimeout,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
})
} catch (err: any) { // eslint-disable-line
if (opts.ignoreRegistryErrors) {

View File

@@ -29,6 +29,7 @@ test('overrides are added for vulnerable dependencies', async () => {
userConfig: {},
rawConfig,
registries,
virtualStoreDirMaxLength: 120,
})
expect(exitCode).toBe(0)
@@ -53,6 +54,7 @@ test('no overrides are added if no vulnerabilities are found', async () => {
userConfig: {},
rawConfig,
registries,
virtualStoreDirMaxLength: 120,
})
expect(exitCode).toBe(0)
@@ -88,6 +90,7 @@ test('CVEs found in the allow list are not added as overrides', async () => {
userConfig: {},
rawConfig,
registries,
virtualStoreDirMaxLength: 120,
})
expect(exitCode).toBe(0)
expect(output).toMatch(/Run "pnpm install"/)

View File

@@ -60,6 +60,7 @@ export const DEFAULT_OPTS = {
useRunningStoreServer: false,
useStoreServer: false,
workspaceConcurrency: 4,
virtualStoreDirMaxLength: 120,
}
describe('plugin-commands-audit', () => {
@@ -80,6 +81,7 @@ describe('plugin-commands-audit', () => {
userConfig: {},
rawConfig,
registries,
virtualStoreDirMaxLength: 120,
})
expect(exitCode).toBe(1)
expect(stripAnsi(output)).toMatchSnapshot()
@@ -97,6 +99,7 @@ describe('plugin-commands-audit', () => {
userConfig: {},
rawConfig,
registries,
virtualStoreDirMaxLength: 120,
})
expect(exitCode).toBe(1)
@@ -114,6 +117,7 @@ describe('plugin-commands-audit', () => {
userConfig: {},
rawConfig,
registries,
virtualStoreDirMaxLength: 120,
})
expect(exitCode).toBe(1)
@@ -130,6 +134,7 @@ describe('plugin-commands-audit', () => {
userConfig: {},
rawConfig,
registries,
virtualStoreDirMaxLength: 120,
})
expect(stripAnsi(output)).toBe('No known vulnerabilities found\n')
@@ -147,6 +152,7 @@ describe('plugin-commands-audit', () => {
userConfig: {},
rawConfig,
registries,
virtualStoreDirMaxLength: 120,
})
const json = JSON.parse(output)
@@ -166,6 +172,7 @@ describe('plugin-commands-audit', () => {
rawConfig,
dev: true,
registries,
virtualStoreDirMaxLength: 120,
})
expect(exitCode).toBe(0)
@@ -186,6 +193,7 @@ describe('plugin-commands-audit', () => {
userConfig: {},
rawConfig,
registries,
virtualStoreDirMaxLength: 120,
})
expect(exitCode).toBe(0)
@@ -207,6 +215,7 @@ describe('plugin-commands-audit', () => {
[`${registries.default.replace(/^https?:/, '')}:_authToken`]: '123',
},
registries,
virtualStoreDirMaxLength: 120,
})
expect(stripAnsi(output)).toBe('No known vulnerabilities found\n')
@@ -227,6 +236,7 @@ describe('plugin-commands-audit', () => {
userConfig: {},
rawConfig,
registries,
virtualStoreDirMaxLength: 120,
})).rejects.toThrow(AuditEndpointNotExistsError)
})
@@ -255,6 +265,7 @@ describe('plugin-commands-audit', () => {
},
},
},
virtualStoreDirMaxLength: 120,
})
expect(exitCode).toBe(1)
@@ -287,6 +298,7 @@ describe('plugin-commands-audit', () => {
},
},
},
virtualStoreDirMaxLength: 120,
})
expect(exitCode).toBe(1)

View File

@@ -29,7 +29,7 @@ export function makeVirtualNodeModules (lockfile: Lockfile): DirEntry {
for (const [depName, ref] of Object.entries(lockfile.importers['.'][depType] ?? {})) {
const symlink: DirEntry = {
entryType: 'symlink',
target: `./.pnpm/${dp.depPathToFilename(dp.refToRelative(ref, depName)!)}/node_modules/${depName}`,
target: `./.pnpm/${dp.depPathToFilename(dp.refToRelative(ref, depName)!, 120)}/node_modules/${depName}`,
}
addDirEntry(entries, depName, symlink)
}
@@ -45,7 +45,7 @@ function createVirtualStoreDir (lockfile: Lockfile): Record<string, DirEntry> {
for (const [depPath, pkgSnapshot] of Object.entries(lockfile.packages ?? {})) {
const { name } = nameVerFromPkgSnapshot(depPath, pkgSnapshot)
const pkgNodeModules = {} as Record<string, DirEntry>
const currentPath = dp.depPathToFilename(depPath)
const currentPath = dp.depPathToFilename(depPath, 120)
const pkgDir: DirEntry = {
entryType: 'index',
depPath,
@@ -54,7 +54,7 @@ function createVirtualStoreDir (lockfile: Lockfile): Record<string, DirEntry> {
for (const [depName, ref] of Object.entries({ ...pkgSnapshot.dependencies, ...pkgSnapshot.optionalDependencies })) {
const symlink: DirEntry = {
entryType: 'symlink',
target: normalize(path.relative(`${currentPath}/node_modules/`, `${dp.depPathToFilename(dp.refToRelative(ref, depName)!)}/node_modules/${depName}`)),
target: normalize(path.relative(`${currentPath}/node_modules/`, `${dp.depPathToFilename(dp.refToRelative(ref, depName)!, 120)}/node_modules/${depName}`)),
}
addDirEntry(pkgNodeModules, depName, symlink)
}

View File

@@ -128,17 +128,15 @@ export function parse (dependencyPath: string): DependencyPath {
return {}
}
const MAX_LENGTH_WITHOUT_HASH = 120 - 26 - 1
export function depPathToFilename (depPath: string): string {
export function depPathToFilename (depPath: string, maxLengthWithoutHash: number): string {
let filename = depPathToFilenameUnescaped(depPath).replace(/[\\/:*?"<>|]/g, '+')
if (filename.includes('(')) {
filename = filename
.replace(/\)$/, '')
.replace(/(\)\()|\(|\)/g, '_')
}
if (filename.length > 120 || filename !== filename.toLowerCase() && !filename.startsWith('file+')) {
return `${filename.substring(0, MAX_LENGTH_WITHOUT_HASH)}_${createBase32Hash(filename)}`
if (filename.length > maxLengthWithoutHash || filename !== filename.toLowerCase() && !filename.startsWith('file+')) {
return `${filename.substring(0, maxLengthWithoutHash - 27)}_${createBase32Hash(filename)}`
}
return filename
}

View File

@@ -71,19 +71,19 @@ test('refToRelative()', () => {
})
test('depPathToFilename()', () => {
expect(depPathToFilename('/foo@1.0.0')).toBe('foo@1.0.0')
expect(depPathToFilename('/@foo/bar@1.0.0')).toBe('@foo+bar@1.0.0')
expect(depPathToFilename('github.com/something/foo/0000?v=1')).toBe('github.com+something+foo+0000+v=1')
expect(depPathToFilename('\\//:*?"<>|')).toBe('++++++++++')
expect(depPathToFilename('/foo@1.0.0(react@16.0.0)(react-dom@16.0.0)')).toBe('foo@1.0.0_react@16.0.0_react-dom@16.0.0')
expect(depPathToFilename('/foo@1.0.0(react@16.0.0(react-dom@1.0.0))(react-dom@16.0.0)')).toBe('foo@1.0.0_react@16.0.0_react-dom@1.0.0__react-dom@16.0.0')
expect(depPathToFilename('/foo@1.0.0', 120)).toBe('foo@1.0.0')
expect(depPathToFilename('/@foo/bar@1.0.0', 120)).toBe('@foo+bar@1.0.0')
expect(depPathToFilename('github.com/something/foo/0000?v=1', 120)).toBe('github.com+something+foo+0000+v=1')
expect(depPathToFilename('\\//:*?"<>|', 120)).toBe('++++++++++')
expect(depPathToFilename('/foo@1.0.0(react@16.0.0)(react-dom@16.0.0)', 120)).toBe('foo@1.0.0_react@16.0.0_react-dom@16.0.0')
expect(depPathToFilename('/foo@1.0.0(react@16.0.0(react-dom@1.0.0))(react-dom@16.0.0)', 120)).toBe('foo@1.0.0_react@16.0.0_react-dom@1.0.0__react-dom@16.0.0')
const filename = depPathToFilename('file:test/foo-1.0.0.tgz_foo@2.0.0')
const filename = depPathToFilename('file:test/foo-1.0.0.tgz_foo@2.0.0', 120)
expect(filename).toBe('file+test+foo-1.0.0.tgz_foo@2.0.0')
expect(filename).not.toContain(':')
expect(depPathToFilename('abcd/'.repeat(200))).toBe('abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abc_jvx2blbax4cyhfgrgozfgpdv24') // cspell:disable-line
expect(depPathToFilename('/JSONSteam@1.0.0')).toBe('JSONSteam@1.0.0_jmswpk4sf667aelr6wp2xd3p54') // cspell:disable-line
expect(depPathToFilename('abcd/'.repeat(200), 120)).toBe('abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abc_jvx2blbax4cyhfgrgozfgpdv24') // cspell:disable-line
expect(depPathToFilename('/JSONSteam@1.0.0', 120)).toBe('JSONSteam@1.0.0_jmswpk4sf667aelr6wp2xd3p54') // cspell:disable-line
})
test('tryGetPackageId', () => {

View File

@@ -27,6 +27,7 @@ const basePatchOption = {
registries: { default: `http://localhost:${REGISTRY_MOCK_PORT}/` },
userConfig: {},
virtualStoreDir: 'node_modules/.pnpm',
virtualStoreDirMaxLength: 120,
}
describe('patch and commit', () => {

View File

@@ -53,4 +53,5 @@ export const DEFAULT_OPTS = {
cpu: ['current'],
libc: ['current'],
},
virtualStoreDirMaxLength: 120,
}

View File

@@ -24,6 +24,7 @@ export type ListMissingPeersOptions = Partial<GetContextOptions>
>
& Partial<Pick<InstallOptions, 'supportedArchitectures'>>
& Pick<GetContextOptions, 'autoInstallPeers' | 'excludeLinksFromLockfile' | 'storeDir'>
& Required<Pick<InstallOptions, 'virtualStoreDirMaxLength'>>
export async function getPeerDependencyIssues (
projects: ProjectOptions[],
@@ -86,6 +87,7 @@ export async function getPeerDependencyIssues (
storeController: opts.storeController,
tag: 'latest',
virtualStoreDir: ctx.virtualStoreDir,
virtualStoreDirMaxLength: ctx.virtualStoreDirMaxLength,
wantedLockfile: ctx.wantedLockfile,
workspacePackages: opts.workspacePackages ?? {},
supportedArchitectures: opts.supportedArchitectures,

View File

@@ -144,6 +144,7 @@ export interface StrictInstallOptions {
supportedArchitectures?: SupportedArchitectures
hoistWorkspacePackages?: boolean
virtualStoreDirMaxLength: number
}
export type InstallOptions =
@@ -237,6 +238,7 @@ const defaults = (opts: InstallOptions): StrictInstallOptions => {
ignoreWorkspaceCycles: false,
disallowWorkspaceCycles: false,
excludeLinksFromLockfile: false,
virtualStoreDirMaxLength: 120,
} as StrictInstallOptions
}

View File

@@ -1051,6 +1051,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
tag: opts.tag,
updateToLatest: opts.updateToLatest,
virtualStoreDir: ctx.virtualStoreDir,
virtualStoreDirMaxLength: ctx.virtualStoreDirMaxLength,
wantedLockfile: ctx.wantedLockfile,
workspacePackages: opts.workspacePackages,
patchedDependencies: opts.patchedDependencies,
@@ -1134,6 +1135,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
skipped: ctx.skipped,
storeController: opts.storeController,
virtualStoreDir: ctx.virtualStoreDir,
virtualStoreDirMaxLength: ctx.virtualStoreDirMaxLength,
wantedLockfile: newLockfile,
wantedToBeSkippedPackageIds,
hoistWorkspacePackages: opts.hoistWorkspacePackages,
@@ -1148,6 +1150,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
importerNames,
lockfileDir: ctx.lockfileDir,
virtualStoreDir: ctx.virtualStoreDir,
virtualStoreDirMaxLength: ctx.virtualStoreDirMaxLength,
registries: ctx.registries,
})
}
@@ -1276,7 +1279,10 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
}
}))
const projectsWithTargetDirs = extendProjectsWithTargetDirs(projects, newLockfile, ctx)
const projectsWithTargetDirs = extendProjectsWithTargetDirs(projects, newLockfile, {
virtualStoreDir: ctx.virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
})
await Promise.all([
opts.useLockfile && opts.saveLockfile
? writeLockfiles({
@@ -1315,6 +1321,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
skipped: Array.from(ctx.skipped),
storeDir: ctx.storeDir,
virtualStoreDir: ctx.virtualStoreDir,
virtualStoreDirMaxLength: ctx.virtualStoreDirMaxLength,
}, {
makeModulesDir: Object.keys(result.currentLockfile.packages ?? {}).length > 0,
})

View File

@@ -68,6 +68,7 @@ export interface LinkPackagesOptions {
skipped: Set<string>
storeController: StoreController
virtualStoreDir: string
virtualStoreDirMaxLength: number
wantedLockfile: Lockfile
wantedToBeSkippedPackageIds: Set<string>
hoistWorkspacePackages?: boolean
@@ -117,6 +118,7 @@ export async function linkPackages (projects: ImporterToUpdate[], depGraph: Depe
skipped: opts.skipped,
storeController: opts.storeController,
virtualStoreDir: opts.virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
wantedLockfile: opts.wantedLockfile,
})
@@ -224,6 +226,7 @@ export async function linkPackages (projects: ImporterToUpdate[], depGraph: Depe
publicHoistedModulesDir: opts.rootModulesDir,
publicHoistPattern: opts.publicHoistPattern ?? [],
virtualStoreDir: opts.virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
hoistedWorkspacePackages: opts.hoistWorkspacePackages
? projects.reduce((hoistedWorkspacePackages, project) => {
if (project.manifest.name && project.id !== '.') {

View File

@@ -140,7 +140,7 @@ test('a subdependency is from a github repo with different name', async () => {
project.isExecutable('@pnpm.e2e/has-aliased-git-dependency/node_modules/.bin/hi')
project.isExecutable('@pnpm.e2e/has-aliased-git-dependency/node_modules/.bin/szia')
expect(fs.existsSync(path.resolve(`node_modules/.pnpm/${depPathToFilename('@pnpm.e2e/has-say-hi-peer@1.0.0(hi@https://codeload.github.com/zkochan/hi/tar.gz/4cdebec76b7b9d1f6e219e06c42d92a6b8ea60cd)')}/node_modules/@pnpm.e2e/has-say-hi-peer`))).toBeTruthy()
expect(fs.existsSync(path.resolve(`node_modules/.pnpm/${depPathToFilename('@pnpm.e2e/has-say-hi-peer@1.0.0(hi@https://codeload.github.com/zkochan/hi/tar.gz/4cdebec76b7b9d1f6e219e06c42d92a6b8ea60cd)', 120)}/node_modules/@pnpm.e2e/has-say-hi-peer`))).toBeTruthy()
})
test('from a git repo', async () => {

View File

@@ -550,7 +550,7 @@ test('peer dependencies are linked when running one named installation', async (
const pkgVariation1 = path.join(
'node_modules/.pnpm',
depPathToFilename(`@pnpm.e2e/abc@1.0.0${createPeersDirSuffix([{ name: '@pnpm.e2e/peer-a', version: '1.0.0' }, { name: '@pnpm.e2e/peer-b', version: '1.0.0' }, { name: '@pnpm.e2e/peer-c', version: '1.0.0' }])}`),
depPathToFilename(`@pnpm.e2e/abc@1.0.0${createPeersDirSuffix([{ name: '@pnpm.e2e/peer-a', version: '1.0.0' }, { name: '@pnpm.e2e/peer-b', version: '1.0.0' }, { name: '@pnpm.e2e/peer-c', version: '1.0.0' }])}`, 120),
'node_modules'
)
await okFile(path.join(pkgVariation1, '@pnpm.e2e/abc'))
@@ -561,7 +561,7 @@ test('peer dependencies are linked when running one named installation', async (
const pkgVariation2 = path.join(
'node_modules/.pnpm',
depPathToFilename(`@pnpm.e2e/abc@1.0.0${createPeersDirSuffix([{ name: '@pnpm.e2e/peer-a', version: '1.0.0' }, { name: '@pnpm.e2e/peer-b', version: '1.0.0' }, { name: '@pnpm.e2e/peer-c', version: '1.0.0' }])}`),
depPathToFilename(`@pnpm.e2e/abc@1.0.0${createPeersDirSuffix([{ name: '@pnpm.e2e/peer-a', version: '1.0.0' }, { name: '@pnpm.e2e/peer-b', version: '1.0.0' }, { name: '@pnpm.e2e/peer-c', version: '1.0.0' }])}`, 120),
'node_modules'
)
await okFile(path.join(pkgVariation2, '@pnpm.e2e/abc'))
@@ -589,7 +589,7 @@ test('peer dependencies are linked when running two separate named installations
const pkgVariation1 = path.join(
'node_modules/.pnpm',
depPathToFilename(`@pnpm.e2e/abc@1.0.0${createPeersDirSuffix([{ name: '@pnpm.e2e/peer-a', version: '1.0.0' }, { name: '@pnpm.e2e/peer-b', version: '1.0.0' }, { name: '@pnpm.e2e/peer-c', version: '1.0.0' }])}`),
depPathToFilename(`@pnpm.e2e/abc@1.0.0${createPeersDirSuffix([{ name: '@pnpm.e2e/peer-a', version: '1.0.0' }, { name: '@pnpm.e2e/peer-b', version: '1.0.0' }, { name: '@pnpm.e2e/peer-c', version: '1.0.0' }])}`, 120),
'node_modules'
)
await okFile(path.join(pkgVariation1, '@pnpm.e2e/abc'))
@@ -600,7 +600,7 @@ test('peer dependencies are linked when running two separate named installations
const pkgVariation2 = path.join(
'node_modules/.pnpm',
depPathToFilename(`@pnpm.e2e/abc@1.0.0${createPeersDirSuffix([{ name: '@pnpm.e2e/peer-a', version: '1.0.0' }, { name: '@pnpm.e2e/peer-b', version: '1.0.0' }, { name: '@pnpm.e2e/peer-c', version: '2.0.0' }])}`),
depPathToFilename(`@pnpm.e2e/abc@1.0.0${createPeersDirSuffix([{ name: '@pnpm.e2e/peer-a', version: '1.0.0' }, { name: '@pnpm.e2e/peer-b', version: '1.0.0' }, { name: '@pnpm.e2e/peer-c', version: '2.0.0' }])}`, 120),
'node_modules'
)
await okFile(path.join(pkgVariation2, '@pnpm.e2e/abc'))
@@ -661,7 +661,7 @@ test('peer bins are linked', async () => {
await addDependenciesToPackage({}, ['@pnpm.e2e/for-testing-peers-having-bins'], testDefaults({ fastUnpack: false }))
const suffix = createPeersDirSuffix([{ name: '@pnpm.e2e/peer-with-bin', version: '1.0.0' }])
const pkgVariation = path.join('.pnpm', depPathToFilename(`@pnpm.e2e/pkg-with-peer-having-bin@1.0.0${suffix}`), 'node_modules')
const pkgVariation = path.join('.pnpm', depPathToFilename(`@pnpm.e2e/pkg-with-peer-having-bin@1.0.0${suffix}`, 120), 'node_modules')
project.isExecutable(path.join(pkgVariation, '@pnpm.e2e/pkg-with-peer-having-bin/node_modules/.bin', 'peer-with-bin'))

View File

@@ -0,0 +1,17 @@
import fs from 'fs'
import { addDependenciesToPackage } from '@pnpm/core'
import { prepareEmpty } from '@pnpm/prepare'
import { testDefaults } from '../utils'
test('setting a custom virtual store directory max length', async () => {
prepareEmpty()
await addDependenciesToPackage({}, ['@babel/helper-member-expression-to-functions@7.23.0'], testDefaults({
virtualStoreDirMaxLength: 50,
}))
const dirs = fs.readdirSync('node_modules/.pnpm')
for (const dir of dirs) {
expect(dir.length).toBeLessThanOrEqual(50)
}
})

View File

@@ -52,6 +52,7 @@ export interface PnpmContext {
publicHoistPattern: string[] | undefined
lockfileDir: string
virtualStoreDir: string
virtualStoreDirMaxLength: number
skipped: Set<string>
storeDir: string
wantedLockfile: Lockfile
@@ -92,6 +93,7 @@ export interface GetContextOptions {
useGitBranchLockfile?: boolean
mergeGitBranchLockfiles?: boolean
virtualStoreDir?: string
virtualStoreDirMaxLength: number
hoistPattern?: string[] | undefined
forceHoistPattern?: boolean
@@ -180,6 +182,7 @@ export async function getContext (
skipped: importersContext.skipped,
storeDir: opts.storeDir,
virtualStoreDir,
virtualStoreDirMaxLength: importersContext.virtualStoreDirMaxLength ?? opts.virtualStoreDirMaxLength,
...await readLockfiles({
autoInstallPeers: opts.autoInstallPeers,
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,

View File

@@ -20,6 +20,7 @@ const DEFAULT_OPTIONS: GetContextOptions = {
optionalDependencies: true,
},
storeDir: path.join(__dirname, 'store'),
virtualStoreDirMaxLength: 120,
}
test('getContext - extendNodePath false', async () => {

View File

@@ -129,6 +129,7 @@ export interface HeadlessOptions {
lockfileDir: string
modulesDir?: string
virtualStoreDir?: string
virtualStoreDirMaxLength: number
patchedDependencies?: Record<string, PatchFile>
scriptsPrependNodePath?: boolean | 'warn-only'
scriptShell?: string
@@ -248,6 +249,7 @@ export async function headlessInstall (opts: HeadlessOptions): Promise<Installat
skipped,
storeController: opts.storeController,
virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
wantedLockfile: filterLockfileByEngine(wantedLockfile, filterOpts).lockfile,
}
)
@@ -338,6 +340,7 @@ export async function headlessInstall (opts: HeadlessOptions): Promise<Installat
importerNames,
lockfileDir,
virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
registries: opts.registries,
})
}
@@ -424,6 +427,7 @@ export async function headlessInstall (opts: HeadlessOptions): Promise<Installat
publicHoistedModulesDir,
publicHoistPattern: opts.publicHoistPattern ?? [],
virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
hoistedWorkspacePackages: opts.hoistWorkspacePackages
? Object.values(opts.allProjects).reduce((hoistedWorkspacePackages, project) => {
if (project.manifest.name && project.id !== '.') {
@@ -531,6 +535,7 @@ export async function headlessInstall (opts: HeadlessOptions): Promise<Installat
const projectsToBeBuilt = extendProjectsWithTargetDirs(selectedProjects, wantedLockfile, {
pkgLocationsByDepPath,
virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
})
if (opts.enableModulesDir !== false) {
@@ -598,6 +603,7 @@ export async function headlessInstall (opts: HeadlessOptions): Promise<Installat
skipped: Array.from(skipped),
storeDir: opts.storeDir,
virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
}, {
makeModulesDir: Object.keys(filteredLockfile.packages ?? {}).length > 0,
})

View File

@@ -24,6 +24,7 @@ export interface HoistOpts extends GetHoistedDependenciesOpts {
extraNodePath?: string[]
preferSymlinkedExecutables?: boolean
virtualStoreDir: string
virtualStoreDirMaxLength: number
}
export async function hoist (opts: HoistOpts): Promise<HoistedDependencies> {
@@ -36,6 +37,7 @@ export async function hoist (opts: HoistOpts): Promise<HoistedDependencies> {
privateHoistedModulesDir: opts.privateHoistedModulesDir,
publicHoistedModulesDir: opts.publicHoistedModulesDir,
virtualStoreDir: opts.virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
hoistedWorkspacePackages: opts.hoistedWorkspacePackages,
})
@@ -244,6 +246,7 @@ async function symlinkHoistedDependencies (
privateHoistedModulesDir: string
publicHoistedModulesDir: string
virtualStoreDir: string
virtualStoreDirMaxLength: number
hoistedWorkspacePackages?: Record<string, HoistedWorkspaceProject>
}
): Promise<void> {
@@ -255,7 +258,7 @@ async function symlinkHoistedDependencies (
let depLocation!: string
if (pkgSnapshot) {
const pkgName = nameVerFromPkgSnapshot(hoistedDepId, pkgSnapshot).name
const modules = path.join(opts.virtualStoreDir, dp.depPathToFilename(hoistedDepId), 'node_modules')
const modules = path.join(opts.virtualStoreDir, dp.depPathToFilename(hoistedDepId, opts.virtualStoreDirMaxLength), 'node_modules')
depLocation = path.join(modules, pkgName as string)
} else {
if (!opts.lockfile.importers[hoistedDepId]) {

View File

@@ -49,6 +49,7 @@ export async function prune (
pruneVirtualStore?: boolean
skipped: Set<string>
virtualStoreDir: string
virtualStoreDirMaxLength: number
lockfileDir: string
storeController: StoreController
}
@@ -169,13 +170,13 @@ export async function prune (
const _tryRemovePkg = tryRemovePkg.bind(null, opts.lockfileDir, opts.virtualStoreDir)
await Promise.all(
orphanDepPaths
.map((orphanDepPath) => depPathToFilename(orphanDepPath))
.map((orphanDepPath) => depPathToFilename(orphanDepPath, opts.virtualStoreDirMaxLength))
.map(async (orphanDepPath) => _tryRemovePkg(orphanDepPath))
)
const neededPkgs = new Set<string>(['node_modules'])
for (const depPath of Object.keys(opts.wantedLockfile.packages ?? {})) {
if (opts.skipped.has(depPath)) continue
neededPkgs.add(depPathToFilename(depPath))
neededPkgs.add(depPathToFilename(depPath, opts.virtualStoreDirMaxLength))
}
const availablePkgs = await readVirtualStoreDir(opts.virtualStoreDir, opts.lockfileDir)
await Promise.all(

View File

@@ -29,6 +29,7 @@ export interface Modules {
skipped: string[]
storeDir: string
virtualStoreDir: string
virtualStoreDirMaxLength: number
injectedDeps?: Record<string, string[]>
hoistedLocations?: Record<string, string[]>
}
@@ -80,6 +81,9 @@ export async function readModulesManifest (modulesDir: string): Promise<Modules
if (!modules.prunedAt) {
modules.prunedAt = new Date().toUTCString()
}
if (!modules.virtualStoreDirMaxLength) {
modules.virtualStoreDirMaxLength = 120
}
return modules
}

View File

@@ -27,6 +27,7 @@ test('writeModulesManifest() and readModulesManifest()', async () => {
skipped: [],
storeDir: '/.pnpm-store',
virtualStoreDir: path.join(modulesDir, '.pnpm'),
virtualStoreDirMaxLength: 120,
}
await writeModulesManifest(modulesDir, modulesYaml)
expect(await readModulesManifest(modulesDir)).toEqual(modulesYaml)
@@ -83,6 +84,7 @@ test('readModulesManifest() should not create a node_modules directory if it doe
skipped: [],
storeDir: '/.pnpm-store',
virtualStoreDir: path.join(modulesDir, '.pnpm'),
virtualStoreDirMaxLength: 120,
}
await writeModulesManifest(modulesDir, modulesYaml)
expect(fs.existsSync(modulesDir)).toBeFalsy()
@@ -109,6 +111,7 @@ test('readModulesManifest() should create a node_modules directory if makeModule
skipped: [],
storeDir: '/.pnpm-store',
virtualStoreDir: path.join(modulesDir, '.pnpm'),
virtualStoreDirMaxLength: 120,
}
await writeModulesManifest(modulesDir, modulesYaml, { makeModulesDir: true })
expect(await readModulesManifest(modulesDir)).toEqual(modulesYaml)

View File

@@ -90,6 +90,7 @@ export function createPackageRequester (
networkConcurrency?: number
storeDir: string
verifyStoreIntegrity: boolean
virtualStoreDirMaxLength: number
}
): RequestPackageFunction & {
fetchPackageToStore: FetchPackageToStoreFunction
@@ -120,6 +121,7 @@ export function createPackageRequester (
concurrency: networkConcurrency,
}),
storeDir: opts.storeDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
})
const requestPackage = resolveAndFetch.bind(null, {
engineStrict: opts.engineStrict,
@@ -137,6 +139,7 @@ export function createPackageRequester (
getFilesIndexFilePath: getFilesIndexFilePath.bind(null, {
getFilePathInCafs,
storeDir: opts.storeDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
}),
requestPackage,
})
@@ -306,10 +309,11 @@ function getFilesIndexFilePath (
ctx: {
getFilePathInCafs: (integrity: string, fileType: FileType) => string
storeDir: string
virtualStoreDirMaxLength: number
},
opts: Pick<FetchPackageToStoreOptions, 'pkg' | 'ignoreScripts'>
) {
const targetRelative = depPathToFilename(opts.pkg.id)
const targetRelative = depPathToFilename(opts.pkg.id, ctx.virtualStoreDirMaxLength)
const target = path.join(ctx.storeDir, targetRelative)
const filesIndexFile = (opts.pkg.resolution as TarballResolution).integrity
? ctx.getFilePathInCafs((opts.pkg.resolution as TarballResolution).integrity!, 'index')
@@ -337,6 +341,7 @@ function fetchToStore (
concurrency: number
}
storeDir: string
virtualStoreDirMaxLength: number
},
opts: FetchPackageToStoreOptions
): {

View File

@@ -39,6 +39,7 @@ test('request package', async () => {
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
expect(typeof requestPackage).toBe('function')
@@ -80,6 +81,7 @@ test('request package but skip fetching', async () => {
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
expect(typeof requestPackage).toBe('function')
@@ -119,6 +121,7 @@ test('request package but skip fetching, when resolution is already available',
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
expect(typeof requestPackage).toBe('function')
@@ -188,6 +191,7 @@ test('refetch local tarball if its integrity has changed', async () => {
cafs,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const response = await requestPackage(wantedPackage, {
@@ -219,6 +223,7 @@ test('refetch local tarball if its integrity has changed', async () => {
cafs,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const response = await requestPackage(wantedPackage, {
@@ -245,6 +250,7 @@ test('refetch local tarball if its integrity has changed', async () => {
cafs,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const response = await requestPackage(wantedPackage, {
@@ -291,6 +297,7 @@ test('refetch local tarball if its integrity has changed. The requester does not
cafs,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const response = await requestPackage(wantedPackage, requestPackageOpts) as PackageResponse & {
@@ -313,6 +320,7 @@ test('refetch local tarball if its integrity has changed. The requester does not
cafs,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const response = await requestPackage(wantedPackage, requestPackageOpts) as PackageResponse & {
@@ -332,6 +340,7 @@ test('refetch local tarball if its integrity has changed. The requester does not
cafs,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const response = await requestPackage(wantedPackage, requestPackageOpts) as PackageResponse & {
@@ -354,6 +363,7 @@ test('fetchPackageToStore()', async () => {
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const pkgId = 'is-positive@1.0.0'
@@ -419,6 +429,7 @@ test('fetchPackageToStore() concurrency check', async () => {
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const pkgId = 'is-positive@1.0.0'
@@ -504,6 +515,7 @@ test('fetchPackageToStore() does not cache errors', async () => {
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const pkgId = 'is-positive@1.0.0'
@@ -555,6 +567,7 @@ test('always return a package manifest in the response', async () => {
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
expect(typeof requestPackage).toBe('function')
const projectDir = tempy.directory()
@@ -617,6 +630,7 @@ test('fetchPackageToStore() fetch raw manifest of cached package', async () => {
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const pkgId = 'is-positive@1.0.0'
@@ -671,6 +685,7 @@ test('refetch package to store if it has been modified', async () => {
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const fetchResult = packageRequester.fetchPackageToStore({
@@ -709,6 +724,7 @@ test('refetch package to store if it has been modified', async () => {
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const fetchResult = packageRequester.fetchPackageToStore({
@@ -732,7 +748,7 @@ test('refetch package to store if it has been modified', async () => {
expect(reporter).toHaveBeenCalledWith(expect.objectContaining({
level: 'warn',
message: `Refetching ${path.join(storeDir, depPathToFilename(pkgId))} to store. It was either modified or had no integrity checksums`,
message: `Refetching ${path.join(storeDir, depPathToFilename(pkgId, 120))} to store. It was either modified or had no integrity checksums`,
name: 'pnpm:package-requester',
prefix: lockfileDir,
}))
@@ -748,6 +764,7 @@ test('do not fetch an optional package that is not installable', async () => {
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
expect(typeof requestPackage).toBe('function')
@@ -785,6 +802,7 @@ test('fetch a git package without a package.json', async () => {
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
expect(typeof requestPackage).toBe('function')
const projectDir = tempy.directory()
@@ -818,6 +836,7 @@ test('throw exception if the package data in the store differs from the expected
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const projectDir = tempy.directory()
@@ -840,6 +859,7 @@ test('throw exception if the package data in the store differs from the expected
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const { fetching } = requestPackage.fetchPackageToStore({
force: false,
@@ -867,6 +887,7 @@ test('throw exception if the package data in the store differs from the expected
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const { fetching } = requestPackage.fetchPackageToStore({
force: false,
@@ -894,6 +915,7 @@ test('throw exception if the package data in the store differs from the expected
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const { fetching } = requestPackage.fetchPackageToStore({
force: false,
@@ -920,6 +942,7 @@ test('throw exception if the package data in the store differs from the expected
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const { fetching } = requestPackage.fetchPackageToStore({
force: false,
@@ -950,6 +973,7 @@ test("don't throw an error if the package was updated, so the expectedPkg has a
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const projectDir = tempy.directory()
@@ -969,6 +993,7 @@ test("don't throw an error if the package was updated, so the expectedPkg has a
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const projectDir = tempy.directory()
const pkgResponse = await requestPackage({ alias: 'is-positive', pref: '3.1.0' }, {
@@ -996,6 +1021,7 @@ test('the version in the bundled manifest should be normalized', async () => {
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const pkgResponse = await requestPackage({ alias: 'react-terminal', pref: '1.2.1' }, {
@@ -1024,6 +1050,7 @@ test('should skip store integrity check and resolve manifest if fetchRawManifest
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: false,
virtualStoreDirMaxLength: 120,
})
const projectDir = tempy.directory()
@@ -1047,6 +1074,7 @@ test('should skip store integrity check and resolve manifest if fetchRawManifest
networkConcurrency: 1,
storeDir,
verifyStoreIntegrity: false,
virtualStoreDirMaxLength: 120,
})
const fetchResult = requestPackage.fetchPackageToStore({

View File

@@ -37,6 +37,7 @@ const DEFAULT_OPTIONS = {
storeDir: path.join(tmp, 'store'),
userConfig: {},
workspaceConcurrency: 1,
virtualStoreDirMaxLength: 120,
}
test('installing with "workspace:" should work even if link-workspace-packages is off', async () => {

View File

@@ -32,6 +32,7 @@ const DEFAULT_OPTIONS = {
sort: true,
userConfig: {},
workspaceConcurrency: 1,
virtualStoreDirMaxLength: 120,
}
test('fetch dependencies', async () => {

View File

@@ -36,6 +36,7 @@ const DEFAULT_OPTIONS = {
storeDir: path.join(tmp, 'store'),
userConfig: {},
workspaceConcurrency: 1,
virtualStoreDirMaxLength: 120,
}
test('globally installed package is linked with active version of Node.js', async () => {

View File

@@ -40,6 +40,7 @@ const DEFAULT_OPTS = {
userConfig: {},
useRunningStoreServer: false,
useStoreServer: false,
virtualStoreDirMaxLength: 120,
}
test('import from package-lock.json', async () => {

View File

@@ -38,6 +38,7 @@ const DEFAULT_OPTS = {
userConfig: {},
useRunningStoreServer: false,
useStoreServer: false,
virtualStoreDirMaxLength: 120,
}
test('import from shared yarn.lock of monorepo', async () => {

View File

@@ -35,6 +35,7 @@ const DEFAULT_OPTIONS = {
storeDir: path.join(TMP, 'store'),
userConfig: {},
workspaceConcurrency: 1,
virtualStoreDirMaxLength: 120,
}
test('root dependency that has a peer is correctly updated after its version changes', async () => {

View File

@@ -36,6 +36,7 @@ const DEFAULT_OPTIONS = {
sort: true,
userConfig: {},
workspaceConcurrency: 1,
virtualStoreDirMaxLength: 120,
}
test('prune removes external link that is not in package.json', async () => {

View File

@@ -41,6 +41,7 @@ const DEFAULT_OPTIONS = {
sort: true,
userConfig: {},
workspaceConcurrency: 1,
virtualStoreDirMaxLength: 120,
}
test('interactively update', async () => {

View File

@@ -38,6 +38,7 @@ const DEFAULT_OPTIONS = {
sort: true,
userConfig: {},
workspaceConcurrency: 1,
virtualStoreDirMaxLength: 120,
}
test('interactive recursive should not error on git specifier override', async () => {

View File

@@ -49,4 +49,5 @@ export const DEFAULT_OPTS = {
useRunningStoreServer: false,
useStoreServer: false,
workspaceConcurrency: 4,
virtualStoreDirMaxLength: 120,
}

View File

@@ -31,6 +31,7 @@ export async function readProjectsContext<T> (
registries: Registries | null | undefined
rootModulesDir: string
skipped: Set<string>
virtualStoreDirMaxLength?: number
}> {
const relativeModulesDir = opts.modulesDir ?? 'node_modules'
const rootModulesDir = await realpathMissing(path.join(opts.lockfileDir, relativeModulesDir))
@@ -58,5 +59,6 @@ export async function readProjectsContext<T> (
registries: ((modules?.registries) != null) ? normalizeRegistries(modules.registries) : undefined,
rootModulesDir,
skipped: new Set(modules?.skipped ?? []),
virtualStoreDirMaxLength: modules?.virtualStoreDirMaxLength,
}
}

View File

@@ -197,6 +197,7 @@ export async function resolveDependencies (
lockfileDir: opts.lockfileDir,
projects: projectsToLink,
virtualStoreDir: opts.virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
resolvePeersFromWorkspaceRoot: Boolean(opts.resolvePeersFromWorkspaceRoot),
resolvedImporters,
})

View File

@@ -164,6 +164,7 @@ export interface ResolutionContext {
registries: Registries
resolutionMode?: 'highest' | 'time-based' | 'lowest-direct'
virtualStoreDir: string
virtualStoreDirMaxLength: number
workspacePackages?: WorkspacePackages
missingPeersOfChildrenByPkgId: Record<string, { parentImporterId: string, missingPeersOfChildren: MissingPeersOfChildren }>
hoistPeers?: boolean
@@ -1104,7 +1105,7 @@ async function resolveDependency (
await exists(
path.join(
ctx.virtualStoreDir,
dp.depPathToFilename(currentPkg.depPath),
dp.depPathToFilename(currentPkg.depPath, ctx.virtualStoreDirMaxLength),
'node_modules',
currentPkg.name,
'package.json'

View File

@@ -99,6 +99,7 @@ export interface ResolveDependenciesOptions {
storeController: StoreController
tag: string
virtualStoreDir: string
virtualStoreDirMaxLength: number
wantedLockfile: Lockfile
workspacePackages: WorkspacePackages
supportedArchitectures?: SupportedArchitectures
@@ -152,6 +153,7 @@ export async function resolveDependencyTree<T> (
skipped: wantedToBeSkippedPackageIds,
storeController: opts.storeController,
virtualStoreDir: opts.virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
wantedLockfile: opts.wantedLockfile,
appliedPatches: new Set<string>(),
updatedSet: new Set<string>(),

View File

@@ -69,6 +69,7 @@ export async function resolvePeers<T extends PartialResolvedPackage> (
projects: ProjectToResolve[]
dependenciesTree: DependenciesTree<T>
virtualStoreDir: string
virtualStoreDirMaxLength: number
lockfileDir: string
resolvePeersFromWorkspaceRoot?: boolean
dedupePeerDependents?: boolean
@@ -114,6 +115,7 @@ export async function resolvePeers<T extends PartialResolvedPackage> (
purePkgs: new Set(),
rootDir,
virtualStoreDir: opts.virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
})
if (finishing) {
finishingList.push(finishing)
@@ -312,6 +314,7 @@ async function resolvePeersOfNode<T extends PartialResolvedPackage> (
dependenciesTree: DependenciesTree<T>
depGraph: GenericDependenciesGraph<T>
virtualStoreDir: string
virtualStoreDirMaxLength: number
peerDependencyIssues: Pick<PeerDependencyIssues, 'bad' | 'missing'>
peersCache: PeersCache
purePkgs: Set<string> // pure packages are those that don't rely on externally resolved peers
@@ -510,7 +513,7 @@ async function resolvePeersOfNode<T extends PartialResolvedPackage> (
}
const peerDependencies = { ...resolvedPackage.peerDependencies }
if (!ctx.depGraph[depPath] || ctx.depGraph[depPath].depth > node.depth) {
const modules = path.join(ctx.virtualStoreDir, depPathToFilename(depPath), 'node_modules')
const modules = path.join(ctx.virtualStoreDir, depPathToFilename(depPath, ctx.virtualStoreDirMaxLength), 'node_modules')
const dir = path.join(modules, resolvedPackage.name)
const transitivePeerDependencies = new Set<string>()
@@ -584,6 +587,7 @@ async function resolvePeersOfChildren<T extends PartialResolvedPackage> (
peerDependencyIssues: Pick<PeerDependencyIssues, 'bad' | 'missing'>
peersCache: PeersCache
virtualStoreDir: string
virtualStoreDirMaxLength: number
purePkgs: Set<string>
depGraph: GenericDependenciesGraph<T>
dependenciesTree: DependenciesTree<T>

View File

@@ -96,6 +96,7 @@ test('packages are not deduplicated when versions do not match', async () => {
} as DependenciesTreeNode<PartialResolvedPackage>])),
dedupePeerDependents: true,
virtualStoreDir: '',
virtualStoreDirMaxLength: 120,
lockfileDir: '',
})

View File

@@ -102,6 +102,7 @@ test('resolve peer dependencies of cyclic dependencies', async () => {
]),
virtualStoreDir: '',
lockfileDir: '',
virtualStoreDirMaxLength: 120,
})
expect(Object.keys(dependenciesGraph)).toStrictEqual([
'foo/1.0.0',
@@ -202,6 +203,7 @@ test('when a package is referenced twice in the dependencies graph and one of th
}],
]),
virtualStoreDir: '',
virtualStoreDirMaxLength: 120,
lockfileDir: '',
})
expect(Object.keys(dependenciesGraph).sort()).toStrictEqual([
@@ -381,6 +383,7 @@ describe('peer dependency issues', () => {
}],
]),
virtualStoreDir: '',
virtualStoreDirMaxLength: 120,
lockfileDir: '',
})).peerDependencyIssuesByProjects
})
@@ -464,6 +467,7 @@ describe('unmet peer dependency issues', () => {
}],
]),
virtualStoreDir: '',
virtualStoreDirMaxLength: 120,
lockfileDir: '',
})).peerDependencyIssuesByProjects
})
@@ -534,6 +538,7 @@ describe('unmet peer dependency issue resolved from subdependency', () => {
}],
]),
virtualStoreDir: '',
virtualStoreDirMaxLength: 120,
lockfileDir: '',
})).peerDependencyIssuesByProjects
})
@@ -633,6 +638,7 @@ test('resolve peer dependencies with npm aliases', async () => {
}],
]),
virtualStoreDir: '',
virtualStoreDirMaxLength: 120,
lockfileDir: '',
})
expect(Object.keys(dependenciesGraph).sort()).toStrictEqual([

View File

@@ -49,4 +49,5 @@ export const DEFAULT_OPTS = {
useRunningStoreServer: false,
useStoreServer: false,
workspaceConcurrency: 4,
virtualStoreDirMaxLength: 120,
}

View File

@@ -45,4 +45,5 @@ export const DEFAULT_OPTS = {
useRunningStoreServer: false,
useStoreServer: false,
workspaceConcurrency: 4,
virtualStoreDirMaxLength: 120,
}

View File

@@ -40,6 +40,7 @@ export async function buildDependenciesHierarchy (
search?: SearchFunction
lockfileDir: string
modulesDir?: string
virtualStoreDirMaxLength: number
}
): Promise<{ [projectDir: string]: DependenciesHierarchy }> {
if (!maybeOpts?.lockfileDir) {
@@ -81,6 +82,7 @@ export async function buildDependenciesHierarchy (
skipped: new Set(modules?.skipped ?? []),
modulesDir: maybeOpts.modulesDir,
virtualStoreDir: modules?.virtualStoreDir,
virtualStoreDirMaxLength: modules?.virtualStoreDirMaxLength ?? maybeOpts.virtualStoreDirMaxLength,
}
; (
await Promise.all(projectPaths.map(async (projectPath) => {
@@ -109,6 +111,7 @@ async function dependenciesHierarchyForPackage (
lockfileDir: string
modulesDir?: string
virtualStoreDir?: string
virtualStoreDirMaxLength: number
}
): Promise<DependenciesHierarchy> {
const importerId = getLockfileImporterId(opts.lockfileDir, projectPath)
@@ -137,6 +140,7 @@ async function dependenciesHierarchyForPackage (
skipped: opts.skipped,
wantedPackages: wantedLockfile?.packages ?? {},
virtualStoreDir: opts.virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
})
const parentId: TreeNodeId = { type: 'importer', importerId }
const result: DependenciesHierarchy = {}
@@ -155,6 +159,7 @@ async function dependenciesHierarchyForPackage (
skipped: opts.skipped,
wantedPackages: wantedLockfile?.packages ?? {},
virtualStoreDir: opts.virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
})
let newEntry: PackageNode | null = null
const matchedSearched = opts.search?.(packageInfo)

View File

@@ -22,6 +22,7 @@ export interface GetPkgInfoOpts {
readonly skipped: Set<string>
readonly wantedPackages: PackageSnapshots
readonly virtualStoreDir?: string
readonly virtualStoreDirMaxLength: number
readonly depTypes: DepTypes
/**
@@ -79,7 +80,7 @@ export function getPkgInfo (opts: GetPkgInfoOpts): PackageInfo {
version = opts.ref
}
const fullPackagePath = depPath
? path.join(opts.virtualStoreDir ?? '.pnpm', depPathToFilename(depPath), 'node_modules', name)
? path.join(opts.virtualStoreDir ?? '.pnpm', depPathToFilename(depPath, opts.virtualStoreDirMaxLength), 'node_modules', name)
: path.join(opts.linkedPathBaseDir, opts.ref.slice(5))
if (version.startsWith('link:') && opts.rewriteLinkVersionDir) {

View File

@@ -23,6 +23,7 @@ interface GetTreeOpts {
currentPackages: PackageSnapshots
wantedPackages: PackageSnapshots
virtualStoreDir?: string
virtualStoreDirMaxLength: number
}
interface DependencyInfo {
@@ -136,6 +137,7 @@ function getTreeHelper (
skipped: opts.skipped,
wantedPackages: opts.wantedPackages,
virtualStoreDir: opts.virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
})
let circular: boolean
const matchedSearched = opts.search?.(packageInfo)

View File

@@ -99,7 +99,7 @@ describe('getTree', () => {
}
test('full test case to print when max depth is large', () => {
const result = normalizePackageNodeForTesting(getTree({ ...getTreeArgs, maxDepth: 9999 }, rootNodeId))
const result = normalizePackageNodeForTesting(getTree({ ...getTreeArgs, maxDepth: 9999, virtualStoreDirMaxLength: 120 }, rootNodeId))
expect(result).toEqual([
expect.objectContaining({
@@ -119,12 +119,12 @@ describe('getTree', () => {
})
test('no result when current depth exceeds max depth', () => {
const result = getTree({ ...getTreeArgs, maxDepth: 0 }, rootNodeId)
const result = getTree({ ...getTreeArgs, maxDepth: 0, virtualStoreDirMaxLength: 120 }, rootNodeId)
expect(result).toEqual([])
})
test('max depth of 1 to print flat dependencies', () => {
const result = getTree({ ...getTreeArgs, maxDepth: 1 }, rootNodeId)
const result = getTree({ ...getTreeArgs, maxDepth: 1, virtualStoreDirMaxLength: 120 }, rootNodeId)
expect(normalizePackageNodeForTesting(result)).toEqual([
expect.objectContaining({ alias: 'b1', dependencies: undefined }),
@@ -134,7 +134,7 @@ describe('getTree', () => {
})
test('max depth of 2 to print a1 -> b1 -> c1, but not d1', () => {
const result = getTree({ ...getTreeArgs, maxDepth: 2 }, rootNodeId)
const result = getTree({ ...getTreeArgs, maxDepth: 2, virtualStoreDirMaxLength: 120 }, rootNodeId)
expect(normalizePackageNodeForTesting(result)).toEqual([
expect.objectContaining({
@@ -197,6 +197,7 @@ describe('getTree', () => {
maxDepth: 3,
currentPackages,
wantedPackages: currentPackages,
virtualStoreDirMaxLength: 120,
}, rootNodeId)
expect(normalizePackageNodeForTesting(result)).toEqual([
@@ -255,6 +256,7 @@ describe('getTree', () => {
maxDepth: 3,
currentPackages,
wantedPackages: currentPackages,
virtualStoreDirMaxLength: 120,
}, rootNodeId)
expect(normalizePackageNodeForTesting(result)).toEqual([
@@ -306,6 +308,7 @@ describe('getTree', () => {
registries: {
default: 'mock-registry-for-testing.example',
},
virtualStoreDirMaxLength: 120,
}
// The fully visited cache can be used in this situation.

View File

@@ -19,7 +19,7 @@ const workspaceWithNestedWorkspaceDeps = f.find('workspace-with-nested-workspace
const customModulesDirFixture = f.find('custom-modules-dir')
test('one package depth 0', async () => {
const tree = await buildDependenciesHierarchy([generalFixture], { depth: 0, lockfileDir: generalFixture })
const tree = await buildDependenciesHierarchy([generalFixture], { depth: 0, lockfileDir: generalFixture, virtualStoreDirMaxLength: 120 })
const modulesDir = path.join(generalFixture, 'node_modules')
expect(tree).toStrictEqual({
@@ -80,7 +80,7 @@ test('one package depth 0', async () => {
})
test('one package depth 1', async () => {
const tree = await buildDependenciesHierarchy([generalFixture], { depth: 1, lockfileDir: generalFixture })
const tree = await buildDependenciesHierarchy([generalFixture], { depth: 1, lockfileDir: generalFixture, virtualStoreDirMaxLength: 120 })
const modulesDir = path.join(generalFixture, 'node_modules')
expect(tree).toStrictEqual({
@@ -179,6 +179,7 @@ test('only prod depth 0', async () => {
optionalDependencies: false,
},
lockfileDir: generalFixture,
virtualStoreDirMaxLength: 120,
}
)
const modulesDir = path.join(generalFixture, 'node_modules')
@@ -224,6 +225,7 @@ test('only dev depth 0', async () => {
optionalDependencies: false,
},
lockfileDir: generalFixture,
virtualStoreDirMaxLength: 120,
}
)
const modulesDir = path.join(generalFixture, 'node_modules')
@@ -252,6 +254,7 @@ test('hierarchy for no packages', async () => {
depth: 100,
lockfileDir: generalFixture,
search: () => false,
virtualStoreDirMaxLength: 120,
})
expect(tree).toStrictEqual({
@@ -270,6 +273,7 @@ test('filter 1 package with depth 0', async () => {
depth: 0,
lockfileDir: generalFixture,
search: ({ name }) => name === 'rimraf',
virtualStoreDirMaxLength: 120,
}
)
const modulesDir = path.join(generalFixture, 'node_modules')
@@ -297,7 +301,11 @@ test('filter 1 package with depth 0', async () => {
})
test('circular dependency', async () => {
const tree = await buildDependenciesHierarchy([circularFixture], { depth: 1000, lockfileDir: circularFixture })
const tree = await buildDependenciesHierarchy([circularFixture], {
depth: 1000,
lockfileDir: circularFixture,
virtualStoreDirMaxLength: 120,
})
const modulesDir = path.join(circularFixture, 'node_modules')
expect(tree).toStrictEqual({
@@ -329,7 +337,11 @@ function resolvePaths (modulesDir: string, node: PackageNode): PackageNode {
}
test('local package depth 0', async () => {
const tree = await buildDependenciesHierarchy([withFileDepFixture], { depth: 1, lockfileDir: withFileDepFixture })
const tree = await buildDependenciesHierarchy([withFileDepFixture], {
depth: 1,
lockfileDir: withFileDepFixture,
virtualStoreDirMaxLength: 120,
})
const modulesDir = path.join(withFileDepFixture, 'node_modules')
expect(tree).toStrictEqual({
@@ -363,7 +375,11 @@ test('local package depth 0', async () => {
})
test('on a package that has only links', async () => {
const tree = await buildDependenciesHierarchy([withLinksOnlyFixture], { depth: 1000, lockfileDir: withLinksOnlyFixture })
const tree = await buildDependenciesHierarchy([withLinksOnlyFixture], {
depth: 1000,
lockfileDir: withLinksOnlyFixture,
virtualStoreDirMaxLength: 120,
})
expect(tree).toStrictEqual({
[withLinksOnlyFixture]: {
@@ -388,7 +404,11 @@ test('on a package that has only links', async () => {
test('on a package with nested workspace links', async () => {
const tree = await buildDependenciesHierarchy(
[workspaceWithNestedWorkspaceDeps],
{ depth: 1000, lockfileDir: workspaceWithNestedWorkspaceDeps }
{
depth: 1000,
lockfileDir: workspaceWithNestedWorkspaceDeps,
virtualStoreDirMaxLength: 120,
}
)
expect(tree).toEqual({
@@ -426,7 +446,11 @@ test('on a package with nested workspace links', async () => {
test('unsaved dependencies are listed', async () => {
const modulesDir = path.join(withUnsavedDepsFixture, 'node_modules')
expect(await buildDependenciesHierarchy([withUnsavedDepsFixture], { depth: 0, lockfileDir: withUnsavedDepsFixture }))
expect(await buildDependenciesHierarchy([withUnsavedDepsFixture], {
depth: 0,
lockfileDir: withUnsavedDepsFixture,
virtualStoreDirMaxLength: 120,
}))
.toStrictEqual({
[withUnsavedDepsFixture]: {
dependencies: [
@@ -468,6 +492,7 @@ test('unsaved dependencies are listed and filtered', async () => {
depth: 0,
lockfileDir: withUnsavedDepsFixture,
search: ({ name }) => name === 'symlink-dir',
virtualStoreDirMaxLength: 120,
}
)
).toStrictEqual({
@@ -494,13 +519,21 @@ test('unsaved dependencies are listed and filtered', async () => {
// Covers https://github.com/pnpm/pnpm/issues/1549
test(`do not fail on importers that are not in current ${WANTED_LOCKFILE}`, async () => {
expect(await buildDependenciesHierarchy([fixtureMonorepo], { depth: 0, lockfileDir: fixtureMonorepo })).toStrictEqual({ [fixtureMonorepo]: {} })
expect(await buildDependenciesHierarchy([fixtureMonorepo], {
depth: 0,
lockfileDir: fixtureMonorepo,
virtualStoreDirMaxLength: 120,
})).toStrictEqual({ [fixtureMonorepo]: {} })
})
test('dependency with an alias', async () => {
const modulesDir = path.join(withAliasedDepFixture, 'node_modules')
expect(
await buildDependenciesHierarchy([withAliasedDepFixture], { depth: 0, lockfileDir: withAliasedDepFixture })
await buildDependenciesHierarchy([withAliasedDepFixture], {
depth: 0,
lockfileDir: withAliasedDepFixture,
virtualStoreDirMaxLength: 120,
})
).toStrictEqual({
[withAliasedDepFixture]: {
dependencies: [
@@ -523,7 +556,11 @@ test('dependency with an alias', async () => {
})
test('peer dependencies', async () => {
const hierarchy = await buildDependenciesHierarchy([withPeerFixture], { depth: 1, lockfileDir: withPeerFixture })
const hierarchy = await buildDependenciesHierarchy([withPeerFixture], {
depth: 1,
lockfileDir: withPeerFixture,
virtualStoreDirMaxLength: 120,
})
expect(hierarchy[withPeerFixture].dependencies![1].dependencies![0].name).toEqual('ajv')
expect(hierarchy[withPeerFixture].dependencies![1].dependencies![0].isPeer).toEqual(true)
})
@@ -533,7 +570,11 @@ test('dependency without a package.json', async () => {
const org = 'denolib'
const pkg = 'camelcase'
const commit = 'aeb6b15f9c9957c8fa56f9731e914c4d8a6d2f2b'
const tree = await buildDependenciesHierarchy([withNonPackageDepFixture], { depth: 0, lockfileDir: withNonPackageDepFixture })
const tree = await buildDependenciesHierarchy([withNonPackageDepFixture], {
depth: 0,
lockfileDir: withNonPackageDepFixture,
virtualStoreDirMaxLength: 120,
})
const resolved = `https://codeload.github.com/${org}/${pkg}/tar.gz/${commit}`
expect(tree).toStrictEqual({
[withNonPackageDepFixture]: {
@@ -545,7 +586,7 @@ test('dependency without a package.json', async () => {
isPeer: false,
isSkipped: false,
name: 'camelcase',
path: path.join(withNonPackageDepFixture, 'node_modules/.pnpm', `camelcase@${depPathToFilename(resolved)}`, 'node_modules/camelcase'),
path: path.join(withNonPackageDepFixture, 'node_modules/.pnpm', `camelcase@${depPathToFilename(resolved, 120)}`, 'node_modules/camelcase'),
resolved,
version: '0.0.0',
},
@@ -568,7 +609,12 @@ test('dependency without a package.json', async () => {
test('on custom modules-dir workspaces', async () => {
const tree = await buildDependenciesHierarchy(
[customModulesDirFixture, path.join(customModulesDirFixture, './packages/foo'), path.join(customModulesDirFixture, './packages/bar')],
{ depth: 1000, lockfileDir: customModulesDirFixture, modulesDir: 'fake_modules' }
{
depth: 1000,
lockfileDir: customModulesDirFixture,
modulesDir: 'fake_modules',
virtualStoreDirMaxLength: 120,
}
)
expect(tree).toEqual({
[customModulesDirFixture]: {

View File

@@ -220,6 +220,13 @@ export type ReadPackageIndexFileResult =
| { local: false, files: Record<string, PackageFileInfo> }
| { local: true, files: Record<string, string> }
export interface ReadPackageIndexFileOptions {
cafsDir: string
storeDir: string
lockfileDir: string
virtualStoreDirMaxLength: number
}
/**
* Returns the index of files included in
* the package identified by the integrity id
@@ -230,7 +237,7 @@ export type ReadPackageIndexFileResult =
export async function readPackageIndexFile (
packageResolution: Resolution,
id: string,
opts: { cafsDir: string, storeDir: string, lockfileDir: string }
opts: ReadPackageIndexFileOptions
): Promise<ReadPackageIndexFileResult> {
// If the package resolution is of type directory we need to do things
// differently and generate our own package index file
@@ -257,7 +264,7 @@ export async function readPackageIndexFile (
'index'
)
} else if (!packageResolution.type && packageResolution.tarball) {
const packageDirInStore = depPathToFilename(parse(id).nonSemverVersion ?? id)
const packageDirInStore = depPathToFilename(parse(id).nonSemverVersion ?? id, opts.virtualStoreDirMaxLength)
pkgIndexFilePath = path.join(
opts.storeDir,
packageDirInStore,
@@ -300,6 +307,7 @@ export interface PackageInfo {
export interface GetPackageInfoOptions {
storeDir: string
virtualStoreDir: string
virtualStoreDirMaxLength: number
dir: string
modulesDir: string
}
@@ -334,6 +342,7 @@ export async function getPkgInfo (
cafsDir,
storeDir: opts.storeDir,
lockfileDir: opts.dir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
}
)
@@ -377,7 +386,7 @@ export async function getPkgInfo (
// TODO: fix issue that path is only correct when using node-linked=isolated
const packageModulePath = path.join(
virtualStoreDir,
depPathToFilename(pkg.depPath),
depPathToFilename(pkg.depPath, opts.virtualStoreDirMaxLength),
modulesDir,
manifest.name
)

View File

@@ -73,6 +73,7 @@ export async function findDependencyLicenses (opts: {
manifest: ProjectManifest
storeDir: string
virtualStoreDir: string
virtualStoreDirMaxLength: number
modulesDir?: string
registries: Registries
wantedLockfile: Lockfile | null
@@ -92,6 +93,7 @@ export async function findDependencyLicenses (opts: {
modulesDir: opts.modulesDir,
storeDir: opts.storeDir,
virtualStoreDir: opts.virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
include: opts.include,
registries: opts.registries,
includedImporterIds: opts.includedImporterIds,

View File

@@ -34,6 +34,7 @@ LicenseNode,
export interface LicenseExtractOptions {
storeDir: string
virtualStoreDir: string
virtualStoreDirMaxLength: number
modulesDir?: string
dir: string
registries: Registries
@@ -80,6 +81,7 @@ export async function lockfileToLicenseNode (
{
storeDir: options.storeDir,
virtualStoreDir: options.virtualStoreDir,
virtualStoreDirMaxLength: options.virtualStoreDirMaxLength,
dir: options.dir,
modulesDir: options.modulesDir ?? 'node_modules',
}
@@ -140,6 +142,7 @@ export async function lockfileToLicenseNodeTree (
const importerDeps = await lockfileToLicenseNode(importerWalker.step, {
storeDir: opts.storeDir,
virtualStoreDir: opts.virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
modulesDir: opts.modulesDir,
dir: opts.dir,
registries: opts.registries,

View File

@@ -25,6 +25,7 @@ describe('licences', () => {
virtualStoreDir: 'virtual-store-dir',
modulesDir: 'modules-dir',
dir: 'workspace-dir',
virtualStoreDirMaxLength: 120,
}
)
).rejects.toThrow('Failed to find package index file for /bogus-package@1.0.0, please consider running \'pnpm install\'')

View File

@@ -71,6 +71,7 @@ describe('licences', () => {
registries: {} as Registries,
wantedLockfile: lockfile,
storeDir: '/opt/.pnpm',
virtualStoreDirMaxLength: 120,
})
expect(licensePackages).toEqual([
@@ -157,6 +158,7 @@ describe('licences', () => {
wantedLockfile: lockfile,
storeDir: '/opt/.pnpm',
includedImporterIds: ['packages/a'],
virtualStoreDirMaxLength: 120,
})
expect(licensePackages).toEqual([
@@ -232,6 +234,7 @@ describe('licences', () => {
registries: {} as Registries,
wantedLockfile: lockfile,
storeDir: '/opt/.pnpm',
virtualStoreDirMaxLength: 120,
})
expect(licensePackages).toEqual([

View File

@@ -64,6 +64,7 @@ export async function searchForPackages (
onlyProjects?: boolean
registries?: Registries
modulesDir?: string
virtualStoreDirMaxLength: number
}
): Promise<PackageDependencyHierarchy[]> {
const search = createPackagesSearcher(packages)
@@ -77,6 +78,7 @@ export async function searchForPackages (
registries: opts.registries,
search,
modulesDir: opts.modulesDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
}))
.map(async ([projectPath, buildDependenciesHierarchy]) => {
const entryPkg = await safeReadProjectManifestOnly(projectPath) ?? {}
@@ -104,6 +106,7 @@ export async function listForPackages (
reportAs?: 'parseable' | 'tree' | 'json'
registries?: Registries
modulesDir?: string
virtualStoreDirMaxLength: number
}
): Promise<string> {
const opts = { ...DEFAULTS, ...maybeOpts }
@@ -135,6 +138,7 @@ export async function list (
registries?: Registries
showExtraneous?: boolean
modulesDir?: string
virtualStoreDirMaxLength: number
}
): Promise<string> {
const opts = { ...DEFAULTS, ...maybeOpts }
@@ -153,6 +157,7 @@ export async function list (
onlyProjects: maybeOpts?.onlyProjects,
registries: opts.registries,
modulesDir: opts.modulesDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
})
)
.map(async ([projectPath, dependenciesHierarchy]) => {

View File

@@ -35,6 +35,7 @@ const fixtureWithAliasedDep = f.find('with-aliased-dep')
test('list all deps of a package that has an external lockfile', async () => {
expect(await list([fixtureWithExternalLockfile], {
lockfileDir: path.join(fixtureWithExternalLockfile, '..'),
virtualStoreDirMaxLength: 120,
})).toBe(`${LEGEND}
${boldHighlighted(`pkg@1.0.0 ${fixtureWithExternalLockfile}`)}
@@ -49,6 +50,7 @@ test('print legend only once', async () => {
path.join(workspaceWith2Pkgs, 'packages/foo'),
], {
lockfileDir: workspaceWith2Pkgs,
virtualStoreDirMaxLength: 120,
})).toBe(`${LEGEND}
${boldHighlighted(`bar@0.0.0 ${path.join(workspaceWith2Pkgs, 'packages/bar')}`)}
@@ -68,6 +70,7 @@ test('list in workspace with private package', async () => {
path.join(workspaceWithPrivatePkgs, 'packages/public'),
], {
lockfileDir: workspaceWithPrivatePkgs,
virtualStoreDirMaxLength: 120,
})).toBe(`${LEGEND}
${boldHighlighted(`private@1.0.0 ${path.join(workspaceWithPrivatePkgs, 'packages/private')} (PRIVATE)`)}
@@ -82,7 +85,7 @@ is-positive ${VERSION_CLR('1.0.0')}`)
})
test('list with default parameters', async () => {
expect(await list([fixture], { lockfileDir: fixture })).toBe(`${LEGEND}
expect(await list([fixture], { lockfileDir: fixture, virtualStoreDirMaxLength: 120 })).toBe(`${LEGEND}
${boldHighlighted(`fixture@1.0.0 ${fixture}`)}
@@ -97,7 +100,7 @@ ${OPTIONAL_DEP_CLR('is-negative')} ${VERSION_CLR('2.1.0')}`)
})
test('list with default parameters in pkg that has no name and version', async () => {
expect(await list([fixtureWithNoPkgNameAndNoVersion], { lockfileDir: fixtureWithNoPkgNameAndNoVersion })).toBe(`${LEGEND}
expect(await list([fixtureWithNoPkgNameAndNoVersion], { lockfileDir: fixtureWithNoPkgNameAndNoVersion, virtualStoreDirMaxLength: 120 })).toBe(`${LEGEND}
${boldHighlighted(fixtureWithNoPkgNameAndNoVersion)}
@@ -112,7 +115,7 @@ ${OPTIONAL_DEP_CLR('is-negative')} ${VERSION_CLR('2.1.0')}`)
})
test('list with default parameters in pkg that has no version', async () => {
expect(await list([fixtureWithNoPkgVersion], { lockfileDir: fixtureWithNoPkgVersion })).toBe(`${LEGEND}
expect(await list([fixtureWithNoPkgVersion], { lockfileDir: fixtureWithNoPkgVersion, virtualStoreDirMaxLength: 120 })).toBe(`${LEGEND}
${boldHighlighted(`fixture ${fixtureWithNoPkgVersion}`)}
@@ -131,6 +134,7 @@ test('list dev only', async () => {
await list([fixture], {
include: { dependencies: false, devDependencies: true, optionalDependencies: false },
lockfileDir: fixture,
virtualStoreDirMaxLength: 120,
})
).toBe(`${LEGEND}
@@ -146,6 +150,7 @@ test('list prod only', async () => {
await list([fixture], {
include: { dependencies: true, devDependencies: false, optionalDependencies: false },
lockfileDir: fixture,
virtualStoreDirMaxLength: 120,
})
).toBe(`${LEGEND}
@@ -162,6 +167,7 @@ test('list prod only with depth 2', async () => {
depth: 2,
include: { dependencies: true, devDependencies: false, optionalDependencies: false },
lockfileDir: fixture,
virtualStoreDirMaxLength: 120,
})
).toBe(`${LEGEND}
@@ -184,7 +190,7 @@ write-json-file ${VERSION_CLR('2.3.0')}
})
test('list with depth 1', async () => {
expect(await list([fixture], { depth: 1, lockfileDir: fixture })).toBe(`${LEGEND}
expect(await list([fixture], { depth: 1, lockfileDir: fixture, virtualStoreDirMaxLength: 120 })).toBe(`${LEGEND}
${boldHighlighted(`fixture@1.0.0 ${fixture}`)}
@@ -205,12 +211,12 @@ ${OPTIONAL_DEP_CLR('is-negative')} ${VERSION_CLR('2.1.0')}`)
})
test('list with depth -1', async () => {
expect(await list([fixture], { depth: -1, lockfileDir: fixture })).toBe(`${boldHighlighted(`fixture@1.0.0 ${fixture}`)}`)
expect(await list([fixture], { depth: -1, lockfileDir: fixture, virtualStoreDirMaxLength: 120 })).toBe(`${boldHighlighted(`fixture@1.0.0 ${fixture}`)}`)
})
test('list with depth 1 and selected packages', async () => {
expect(
await listForPackages(['make-dir', 'pify@2', 'sort-keys@2', 'is-negative'], [fixture], { depth: 1, lockfileDir: fixture })
await listForPackages(['make-dir', 'pify@2', 'sort-keys@2', 'is-negative'], [fixture], { depth: 1, lockfileDir: fixture, virtualStoreDirMaxLength: 120 })
).toBe(`${LEGEND}
${boldHighlighted(`fixture@1.0.0 ${fixture}`)}
@@ -226,7 +232,7 @@ ${highlighted(OPTIONAL_DEP_CLR('is-negative') + ' ' + VERSION_CLR('2.1.0'))}`
})
test('list in long format', async () => {
expect(await list([fixture], { long: true, lockfileDir: fixture })).toBe(`${LEGEND}
expect(await list([fixture], { long: true, lockfileDir: fixture, virtualStoreDirMaxLength: 0 })).toBe(`${LEGEND}
${boldHighlighted(`fixture@1.0.0 ${fixture}`)}
@@ -259,6 +265,7 @@ test('parseable list in workspace with private package', async () => {
], {
reportAs: 'parseable',
lockfileDir: workspaceWithPrivatePkgs,
virtualStoreDirMaxLength: 120,
})).toBe(`${path.join(workspaceWithPrivatePkgs, 'packages/private')}
${path.join(workspaceWithPrivatePkgs, 'node_modules/.pnpm/is-positive@1.0.0/node_modules/is-positive')}
${path.join(workspaceWithPrivatePkgs, 'packages/public')}`)
@@ -272,6 +279,7 @@ test('long parseable list in workspace with private package', async () => {
reportAs: 'parseable',
long: true,
lockfileDir: workspaceWithPrivatePkgs,
virtualStoreDirMaxLength: 120,
})).toBe(`${path.join(workspaceWithPrivatePkgs, 'packages/private')}:private@1.0.0:PRIVATE
${path.join(workspaceWithPrivatePkgs, 'node_modules/.pnpm/is-positive@1.0.0/node_modules/is-positive')}:is-positive@1.0.0
${path.join(workspaceWithPrivatePkgs, 'packages/public')}:public@1.0.0`)
@@ -284,6 +292,7 @@ test('JSON list in workspace with private package', async () => {
], {
reportAs: 'json',
lockfileDir: workspaceWithPrivatePkgs,
virtualStoreDirMaxLength: 120,
})).toBe(
JSON.stringify([
{
@@ -319,7 +328,7 @@ test('JSON list in workspace with private package', async () => {
})
test('parseable list with depth 1', async () => {
expect(await list([fixture], { reportAs: 'parseable', depth: 1, lockfileDir: fixture })).toBe(`${fixture}
expect(await list([fixture], { reportAs: 'parseable', depth: 1, lockfileDir: fixture, virtualStoreDirMaxLength: 120 })).toBe(`${fixture}
${path.join(fixture, 'node_modules/.pnpm/detect-indent@5.0.0/node_modules/detect-indent')}
${path.join(fixture, 'node_modules/.pnpm/graceful-fs@4.2.2/node_modules/graceful-fs')}
${path.join(fixture, 'node_modules/.pnpm/is-negative@2.1.0/node_modules/is-negative')}
@@ -332,7 +341,7 @@ ${path.join(fixture, 'node_modules/.pnpm/write-json-file@2.3.0/node_modules/writ
})
test('JSON list with depth 1', async () => {
expect(await list([fixture], { reportAs: 'json', depth: 1, lockfileDir: fixture })).toBe(JSON.stringify([{
expect(await list([fixture], { reportAs: 'json', depth: 1, lockfileDir: fixture, virtualStoreDirMaxLength: 120 })).toBe(JSON.stringify([{
name: 'fixture',
version: '1.0.0',
path: fixture,
@@ -414,7 +423,7 @@ test('JSON list with depth 1', async () => {
test('JSON list with aliased dep', async () => {
expect(
await list([fixtureWithAliasedDep], { reportAs: 'json', lockfileDir: fixtureWithAliasedDep })
await list([fixtureWithAliasedDep], { reportAs: 'json', lockfileDir: fixtureWithAliasedDep, virtualStoreDirMaxLength: 120 })
).toBe(
JSON.stringify([
{
@@ -434,7 +443,7 @@ test('JSON list with aliased dep', async () => {
], null, 2)
)
expect(
await list([fixtureWithAliasedDep], { lockfileDir: fixtureWithAliasedDep, long: true, reportAs: 'json' })
await list([fixtureWithAliasedDep], { lockfileDir: fixtureWithAliasedDep, long: true, reportAs: 'json', virtualStoreDirMaxLength: 120 })
).toBe(
JSON.stringify([{
name: 'with-aliased-dep',
@@ -471,6 +480,7 @@ test('parseable list with depth 1 and dev only', async () => {
include: { dependencies: false, devDependencies: true, optionalDependencies: false },
lockfileDir: fixture,
reportAs: 'parseable',
virtualStoreDirMaxLength: 120,
})
).toBe(`${fixture}
${path.join(fixture, 'node_modules/.pnpm/is-positive@3.1.0/node_modules/is-positive')}`
@@ -488,6 +498,7 @@ test('parseable list with depth 1 without unnecessary empty newlines', async ()
lockfileDir: workspaceWithDifferentDeps,
depth: 1,
reportAs: 'parseable',
virtualStoreDirMaxLength: 120,
}
)).toBe(`${path.join(workspaceWithDifferentDeps, 'packages/bar')}
${path.join(workspaceWithDifferentDeps, 'node_modules/.pnpm/is-positive@3.1.0/node_modules/is-positive')}`
@@ -495,7 +506,7 @@ ${path.join(workspaceWithDifferentDeps, 'node_modules/.pnpm/is-positive@3.1.0/no
})
test('long parseable list with depth 1', async () => {
expect(await list([fixture], { reportAs: 'parseable', depth: 1, lockfileDir: fixture, long: true })).toBe(`${fixture}:fixture@1.0.0
expect(await list([fixture], { reportAs: 'parseable', depth: 1, lockfileDir: fixture, long: true, virtualStoreDirMaxLength: 120 })).toBe(`${fixture}:fixture@1.0.0
${path.join(fixture, 'node_modules/.pnpm/detect-indent@5.0.0/node_modules/detect-indent')}:detect-indent@5.0.0
${path.join(fixture, 'node_modules/.pnpm/graceful-fs@4.2.2/node_modules/graceful-fs')}:graceful-fs@4.2.2
${path.join(fixture, 'node_modules/.pnpm/is-negative@2.1.0/node_modules/is-negative')}:is-negative@2.1.0
@@ -508,7 +519,7 @@ ${path.join(fixture, 'node_modules/.pnpm/write-json-file@2.3.0/node_modules/writ
})
test('long parseable list with depth 1 when package has no version', async () => {
expect(await list([fixtureWithNoPkgVersion], { reportAs: 'parseable', depth: 1, lockfileDir: fixtureWithNoPkgVersion, long: true })).toBe(`\
expect(await list([fixtureWithNoPkgVersion], { reportAs: 'parseable', depth: 1, lockfileDir: fixtureWithNoPkgVersion, long: true, virtualStoreDirMaxLength: 120 })).toBe(`\
${fixtureWithNoPkgVersion}:fixture
${path.join(fixtureWithNoPkgVersion, 'node_modules/.pnpm/detect-indent@5.0.0/node_modules/detect-indent')}:detect-indent@5.0.0
${path.join(fixtureWithNoPkgVersion, 'node_modules/.pnpm/graceful-fs@4.2.2/node_modules/graceful-fs')}:graceful-fs@4.2.2
@@ -525,7 +536,7 @@ test('long parseable list with depth 1 when package has no name and no version',
expect(
await list(
[fixtureWithNoPkgNameAndNoVersion],
{ reportAs: 'parseable', depth: 1, lockfileDir: fixtureWithNoPkgNameAndNoVersion, long: true }
{ reportAs: 'parseable', depth: 1, lockfileDir: fixtureWithNoPkgNameAndNoVersion, long: true, virtualStoreDirMaxLength: 120 }
)
).toBe(`${fixtureWithNoPkgNameAndNoVersion}
${path.join(fixtureWithNoPkgNameAndNoVersion, 'node_modules/.pnpm/detect-indent@5.0.0/node_modules/detect-indent')}:detect-indent@5.0.0
@@ -541,11 +552,11 @@ ${path.join(fixtureWithNoPkgNameAndNoVersion, 'node_modules/.pnpm/write-json-fil
})
test('print empty', async () => {
expect(await list([emptyFixture], { lockfileDir: emptyFixture })).toBe(`${LEGEND}\n\n${boldHighlighted(`empty@1.0.0 ${emptyFixture}`)}`)
expect(await list([emptyFixture], { lockfileDir: emptyFixture, virtualStoreDirMaxLength: 120 })).toBe(`${LEGEND}\n\n${boldHighlighted(`empty@1.0.0 ${emptyFixture}`)}`)
})
test("don't print empty", async () => {
expect(await list([emptyFixture], { alwaysPrintRootPackage: false, lockfileDir: emptyFixture })).toBe('')
expect(await list([emptyFixture], { alwaysPrintRootPackage: false, lockfileDir: emptyFixture, virtualStoreDirMaxLength: 120 })).toBe('')
})
test('unsaved dependencies are marked', async () => {
@@ -783,7 +794,7 @@ foo ${VERSION_CLR('1.0.0')}
test('peer dependencies are marked', async () => {
const fixture = f.find('with-peer')
const output = await list([fixture], { depth: 1, lockfileDir: fixture })
const output = await list([fixture], { depth: 1, lockfileDir: fixture, virtualStoreDirMaxLength: 120 })
expect(output).toBe(`${LEGEND}
${boldHighlighted(`with-peer@1.0.0 ${fixture}`)}
@@ -800,7 +811,7 @@ ajv-keywords ${VERSION_CLR('3.4.1')}
test('peer dependencies are marked when searching', async () => {
const fixture = f.find('with-peer')
const output = await listForPackages(['ajv'], [fixture], { depth: 1, lockfileDir: fixture })
const output = await listForPackages(['ajv'], [fixture], { depth: 1, lockfileDir: fixture, virtualStoreDirMaxLength: 120 })
expect(output).toBe(`${LEGEND}
${boldHighlighted(`with-peer@1.0.0 ${fixture}`)}
@@ -813,7 +824,7 @@ ajv-keywords ${VERSION_CLR('3.4.1')}
test('--only-projects shows only projects', async () => {
const fixture = f.find('workspace-with-nested-workspace-deps')
const output = await list([fixture], { depth: 999, lockfileDir: fixture, onlyProjects: true })
const output = await list([fixture], { depth: 999, lockfileDir: fixture, onlyProjects: true, virtualStoreDirMaxLength: 120 })
// The "workspace-with-nested-workspace-deps" test case has an external
// dependency under @scope/b, but that package should not be printed when

View File

@@ -28,6 +28,7 @@ Config,
| 'selectedProjectsGraph'
| 'rootProjectManifest'
| 'rootProjectManifestDir'
| 'virtualStoreDirMaxLength'
> &
Partial<Pick<Config, 'userConfig'>>
@@ -66,6 +67,7 @@ export async function licensesList (opts: LicensesCommandOptions): Promise<Licen
lockfileDir: opts.lockfileDir ?? opts.dir,
storeDir,
virtualStoreDir: opts.virtualStoreDir ?? '.',
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
modulesDir: opts.modulesDir,
registries: opts.registries,
wantedLockfile: lockfile,

View File

@@ -47,4 +47,5 @@ export const DEFAULT_OPTS = {
useStoreServer: false,
virtualStoreDir: 'node_modules/.pnpm',
workspaceConcurrency: 4,
virtualStoreDirMaxLength: 120,
}

View File

@@ -121,6 +121,7 @@ export type ListCommandOptions = Pick<Config,
| 'production'
| 'selectedProjectsGraph'
| 'modulesDir'
| 'virtualStoreDirMaxLength'
> & Partial<Pick<Config, 'cliOptions'>> & {
alwaysPrintRootPackage?: boolean
depth?: number
@@ -166,6 +167,7 @@ export async function render (
onlyProjects?: boolean
parseable?: boolean
modulesDir?: string
virtualStoreDirMaxLength: number
}
): Promise<string> {
const listOpts = {
@@ -178,6 +180,7 @@ export async function render (
reportAs: (opts.parseable ? 'parseable' : (opts.json ? 'json' : 'tree')) as ('parseable' | 'json' | 'tree'),
showExtraneous: false,
modulesDir: opts.modulesDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
}
return (params.length > 0)
? listForPackages(params, prefixes, listOpts)

View File

@@ -8,7 +8,7 @@ import { render } from './list'
export async function listRecursive (
pkgs: Project[],
params: string[],
opts: Pick<Config, 'lockfileDir'> & {
opts: Pick<Config, 'lockfileDir' | 'virtualStoreDirMaxLength'> & {
depth?: number
include: IncludedDependencies
long?: boolean

View File

@@ -28,6 +28,7 @@ test('listing packages', async () => {
dev: false,
dir: process.cwd(),
optional: false,
virtualStoreDirMaxLength: 120,
}, [])
expect(stripAnsi(output)).toBe(`Legend: production dependency, optional only, dev only
@@ -43,6 +44,7 @@ is-positive 1.0.0`)
dir: process.cwd(),
optional: false,
production: false,
virtualStoreDirMaxLength: 120,
}, [])
expect(stripAnsi(output)).toBe(`Legend: production dependency, optional only, dev only
@@ -56,6 +58,7 @@ is-negative 1.0.0`)
{
const output = await list.handler({
dir: process.cwd(),
virtualStoreDirMaxLength: 120,
}, [])
expect(stripAnsi(output)).toBe(`Legend: production dependency, optional only, dev only
@@ -92,6 +95,7 @@ test(`listing packages of a project that has an external ${WANTED_LOCKFILE}`, as
const output = await list.handler({
dir: process.cwd(),
lockfileDir: path.resolve('..'),
virtualStoreDirMaxLength: 120,
}, [])
expect(stripAnsi(output)).toBe(`Legend: production dependency, optional only, dev only
@@ -113,6 +117,7 @@ test.skip('list on a project with skipped optional dependencies', async () => {
const output = await list.handler({
depth: 10,
dir: process.cwd(),
virtualStoreDirMaxLength: 120,
}, [])
expect(stripAnsi(output)).toBe(`Legend: production dependency, optional only, dev only
@@ -129,6 +134,7 @@ pkg-with-optional 1.0.0
const output = await list.handler({
depth: 10,
dir: process.cwd(),
virtualStoreDirMaxLength: 120,
}, ['not-compatible-with-any-os'])
expect(stripAnsi(output)).toBe(`Legend: production dependency, optional only, dev only
@@ -143,6 +149,7 @@ pkg-with-optional 1.0.0
{
const output = await why.handler({
dir: process.cwd(),
virtualStoreDirMaxLength: 120,
}, ['not-compatible-with-any-os'])
expect(stripAnsi(output)).toBe(`Legend: production dependency, optional only, dev only
@@ -179,6 +186,7 @@ test('listing packages should not fail on package that has local file directory
dev: false,
dir: pkgDir,
optional: false,
virtualStoreDirMaxLength: 120,
}, [])
expect(stripAnsi(output)).toBe(`Legend: production dependency, optional only, dev only

View File

@@ -47,4 +47,5 @@ export const DEFAULT_OPTS = {
useRunningStoreServer: false,
useStoreServer: false,
workspaceConcurrency: 4,
virtualStoreDirMaxLength: 120,
}

View File

@@ -15,6 +15,7 @@ test('`pnpm why` should fail if no package name was provided', async () => {
try {
await why.handler({
dir: process.cwd(),
virtualStoreDirMaxLength: 120,
}, [])
} catch (_err: any) { // eslint-disable-line
err = _err
@@ -38,6 +39,7 @@ test('"why" should find non-direct dependency', async () => {
dev: false,
dir: process.cwd(),
optional: false,
virtualStoreDirMaxLength: 120,
}, ['@pnpm.e2e/dep-of-pkg-with-1-dep'])
expect(stripAnsi(output)).toBe(`Legend: production dependency, optional only, dev only

View File

@@ -51,4 +51,5 @@ export const DEFAULT_OPTS = {
useRunningStoreServer: false,
useStoreServer: false,
workspaceConcurrency: 4,
virtualStoreDirMaxLength: 120,
}

View File

@@ -27,6 +27,7 @@ export function createPackageStore (
networkConcurrency?: number
packageImportMethod?: 'auto' | 'hardlink' | 'copy' | 'clone' | 'clone-or-copy'
verifyStoreIntegrity: boolean
virtualStoreDirMaxLength: number
}
): StoreController {
const storeDir = initOpts.storeDir
@@ -46,6 +47,7 @@ export function createPackageStore (
networkConcurrency: initOpts.networkConcurrency,
storeDir: initOpts.storeDir,
verifyStoreIntegrity: initOpts.verifyStoreIntegrity,
virtualStoreDirMaxLength: initOpts.virtualStoreDirMaxLength,
})
return {

View File

@@ -21,6 +21,7 @@ describe('store.importPackage()', () => {
storeDir,
cacheDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const pkgId = 'registry.npmjs.org/is-positive/1.0.0'
const fetchResponse = (storeController.fetchPackage as FetchPackageToStoreFunction)({
@@ -59,6 +60,7 @@ describe('store.importPackage()', () => {
storeDir,
cacheDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
const pkgId = 'registry.npmjs.org/is-positive/1.0.0'
const fetchResponse = (storeController.fetchPackage as FetchPackageToStoreFunction)({

View File

@@ -20,10 +20,11 @@ export interface StrictStoreStatusOptions {
development: boolean
optional: boolean
binsDir: string
virtualStoreDirMaxLength: number
}
export type StoreStatusOptions = Partial<StrictStoreStatusOptions> &
Pick<StrictStoreStatusOptions, 'storeDir'>
Pick<StrictStoreStatusOptions, 'storeDir' | 'virtualStoreDirMaxLength'>
const defaults = async (opts: StoreStatusOptions): Promise<StrictStoreStatusOptions> => {
const dir = opts.dir ?? process.cwd()

View File

@@ -50,9 +50,9 @@ export async function storeStatus (maybeOpts: StoreStatusOptions): Promise<strin
const modified = await pFilter(pkgs, async ({ id, integrity, depPath, name }) => {
const pkgIndexFilePath = integrity
? getFilePathInCafs(cafsDir, integrity, 'index')
: path.join(storeDir, dp.depPathToFilename(id), 'integrity.json')
: path.join(storeDir, dp.depPathToFilename(id, maybeOpts.virtualStoreDirMaxLength), 'integrity.json')
const { files } = await loadJsonFile<PackageFilesIndex>(pkgIndexFilePath)
return (await dint.check(path.join(virtualStoreDir, dp.depPathToFilename(depPath), 'node_modules', name), files)) === false
return (await dint.check(path.join(virtualStoreDir, dp.depPathToFilename(depPath, maybeOpts.virtualStoreDirMaxLength), 'node_modules', name), files)) === false
}, { concurrency: 8 })
if ((reporter != null) && typeof reporter === 'function') {

View File

@@ -24,6 +24,7 @@ test('pnpm store add express@4.16.3', async () => {
storeDir,
userConfig: {},
dlxCacheMaxAge: 0,
virtualStoreDirMaxLength: 120,
}, ['add', 'express@4.16.3'])
const { cafsHas } = assertStore(path.join(storeDir, STORE_VERSION))
@@ -50,6 +51,7 @@ test('pnpm store add scoped package that uses not the standard registry', async
storeDir,
userConfig: {},
dlxCacheMaxAge: 0,
virtualStoreDirMaxLength: 120,
}, ['add', '@foo/no-deps@1.0.0'])
const { cafsHas } = assertStore(path.join(storeDir, STORE_VERSION))
@@ -79,6 +81,7 @@ test('should fail if some packages can not be added', async () => {
storeDir,
userConfig: {},
dlxCacheMaxAge: 0,
virtualStoreDirMaxLength: 120,
}, ['add', '@pnpm/this-does-not-exist'])
} catch (e: any) { // eslint-disable-line
thrown = true

View File

@@ -20,6 +20,7 @@ test('CLI prints the current store path', async () => {
storeDir: '/home/example/.pnpm-store',
userConfig: {},
dlxCacheMaxAge: 0,
virtualStoreDirMaxLength: 120,
}, ['path'])
const expectedStorePath = os.platform() === 'win32'

View File

@@ -49,6 +49,7 @@ test('remove unreferenced packages', async () => {
storeDir,
userConfig: {},
dlxCacheMaxAge: Infinity,
virtualStoreDirMaxLength: 120,
}, ['prune'])
expect(reporter).toHaveBeenCalledWith(
@@ -73,6 +74,7 @@ test('remove unreferenced packages', async () => {
storeDir,
userConfig: {},
dlxCacheMaxAge: Infinity,
virtualStoreDirMaxLength: 120,
}, ['prune'])
expect(reporter).not.toHaveBeenCalledWith(
@@ -109,6 +111,7 @@ test.skip('remove packages that are used by project that no longer exist', async
storeDir,
userConfig: {},
dlxCacheMaxAge: Infinity,
virtualStoreDirMaxLength: 120,
}, ['prune'])
expect(reporter).toHaveBeenCalledWith(
@@ -148,6 +151,7 @@ test('keep dependencies used by others', async () => {
storeDir,
userConfig: {},
dlxCacheMaxAge: Infinity,
virtualStoreDirMaxLength: 120,
}, ['prune'])
project.storeHasNot('camelcase-keys', '3.0.0')
@@ -173,6 +177,7 @@ test('keep dependency used by package', async () => {
storeDir,
userConfig: {},
dlxCacheMaxAge: Infinity,
virtualStoreDirMaxLength: 120,
}, ['prune'])
project.storeHas('is-positive', '3.1.0')
@@ -196,6 +201,7 @@ test('prune will skip scanning non-directory in storeDir', async () => {
storeDir,
userConfig: {},
dlxCacheMaxAge: Infinity,
virtualStoreDirMaxLength: 120,
}, ['prune'])
})
@@ -223,6 +229,7 @@ test('prune does not fail if the store contains an unexpected directory', async
storeDir,
userConfig: {},
dlxCacheMaxAge: Infinity,
virtualStoreDirMaxLength: 120,
}, ['prune'])
expect(reporter).toHaveBeenCalledWith(
@@ -261,6 +268,7 @@ test('prune removes alien files from the store if the --force flag is used', asy
userConfig: {},
force: true,
dlxCacheMaxAge: Infinity,
virtualStoreDirMaxLength: 120,
}, ['prune'])
expect(reporter).toHaveBeenCalledWith(
expect.objectContaining({
@@ -326,6 +334,7 @@ test('prune removes cache directories that outlives dlx-cache-max-age', async ()
storeDir,
userConfig: {},
dlxCacheMaxAge: 7,
virtualStoreDirMaxLength: 120,
}, ['prune'])
expect(

View File

@@ -41,6 +41,7 @@ test('CLI fails when store status finds modified packages', async () => {
storeDir,
userConfig: {},
dlxCacheMaxAge: 0,
virtualStoreDirMaxLength: 120,
}, ['status'])
} catch (_err: any) { // eslint-disable-line
err = _err
@@ -93,5 +94,6 @@ test('CLI does not fail when store status does not find modified packages', asyn
storeDir,
userConfig: {},
dlxCacheMaxAge: 0,
virtualStoreDirMaxLength: 120,
}, ['status'])
})

View File

@@ -30,6 +30,7 @@ async function createStoreController (storeDir?: string) {
cacheDir,
storeDir,
verifyStoreIntegrity: true,
virtualStoreDirMaxLength: 120,
})
}

View File

@@ -41,6 +41,7 @@ export type CreateNewStoreControllerOptions = CreateResolverOptions & Pick<Confi
| 'unsafePerm'
| 'userAgent'
| 'verifyStoreIntegrity'
| 'virtualStoreDirMaxLength'
> & {
cafsLocker?: CafsLocker
ignoreFile?: (filename: string) => boolean
@@ -105,6 +106,7 @@ export async function createNewStoreController (
verifyStoreIntegrity: typeof opts.verifyStoreIntegrity === 'boolean'
? opts.verifyStoreIntegrity
: true,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
}),
dir: opts.storeDir,
}