feat: improve dep path of local deps

This commit is contained in:
Zoltan Kochan
2022-03-19 17:18:38 +02:00
parent a36b6026bb
commit faf830b8f6
22 changed files with 46 additions and 55 deletions

View File

@@ -0,0 +1,6 @@
---
"dependency-path": major
"pnpm": major
---
Use a nicer path for saving local dependencies in the virtual store.

View File

@@ -250,7 +250,6 @@ export default async function linkPackages (
newHoistedDependencies = await hoist({
lockfile: hoistLockfile,
importerIds: projectIds,
lockfileDir: opts.lockfileDir,
privateHoistedModulesDir: opts.hoistedModulesDir,
privateHoistPattern: opts.hoistPattern ?? [],
publicHoistedModulesDir: opts.rootModulesDir,

View File

@@ -1149,7 +1149,7 @@ test('local tarball dependency with peer dependency', async () => {
], await testDefaults({ reporter }))
const integrityLocalPkgDirs = (await fs.readdir('node_modules/.pnpm'))
.filter((dir) => dir.startsWith('local+'))
.filter((dir) => dir.startsWith('file+'))
expect(integrityLocalPkgDirs.length).toBe(1)
@@ -1166,7 +1166,7 @@ test('local tarball dependency with peer dependency', async () => {
{
const updatedLocalPkgDirs = (await fs.readdir('node_modules/.pnpm'))
.filter((dir) => dir.startsWith('local+'))
.filter((dir) => dir.startsWith('file+'))
expect(updatedLocalPkgDirs).toStrictEqual(integrityLocalPkgDirs)
}
})

View File

@@ -395,7 +395,7 @@ function getPkgInfo (
isPeer: Boolean(opts.peers?.has(opts.alias)),
isSkipped,
name,
path: depPath ? path.join(opts.modulesDir, '.pnpm', depPathToFilename(depPath, opts.lockfileDir)) : path.join(opts.modulesDir, '..', opts.ref.substr(5)),
path: depPath ? path.join(opts.modulesDir, '.pnpm', depPathToFilename(depPath)) : path.join(opts.modulesDir, '..', opts.ref.substr(5)),
version,
}
if (resolved) {

View File

@@ -33,7 +33,6 @@
"dependencies": {
"@pnpm/types": "workspace:7.10.0",
"encode-registry": "^3.0.0",
"normalize-path": "^3.0.0",
"semver": "^7.3.4"
},
"devDependencies": {

View File

@@ -1,8 +1,6 @@
import crypto from 'crypto'
import path from 'path'
import { Registries } from '@pnpm/types'
import encodeRegistry from 'encode-registry'
import normalize from 'normalize-path'
import semver from 'semver'
export function isAbsolute (dependencyPath: string) {
@@ -130,15 +128,15 @@ export function parse (dependencyPath: string) {
}
}
export function depPathToFilename (depPath: string, lockfileDir: string) {
const filename = depPathToFilenameUnescaped(depPath, lockfileDir).replace(/\//g, '+')
if (filename.length > 120 || filename !== filename.toLowerCase() && !filename.startsWith('local+')) {
export function depPathToFilename (depPath: string) {
const filename = depPathToFilenameUnescaped(depPath).replace(/\//g, '+')
if (filename.length > 120 || filename !== filename.toLowerCase() && !filename.startsWith('file+')) {
return `${filename.substring(0, 50)}_${crypto.createHash('md5').update(filename).digest('hex')}`
}
return filename
}
function depPathToFilenameUnescaped (depPath: string, lockfileDir: string) {
function depPathToFilenameUnescaped (depPath: string) {
if (depPath.indexOf('file:') !== 0) {
if (depPath.startsWith('/')) {
depPath = depPath.substring(1)
@@ -146,7 +144,5 @@ function depPathToFilenameUnescaped (depPath: string, lockfileDir: string) {
const index = depPath.lastIndexOf('/')
return `${depPath.substring(0, index)}@${depPath.substr(index + 1)}`
}
const absolutePath = normalize(path.join(lockfileDir, depPath.slice(5)))
return `local+${absolutePath.replace(':', '+')}`
return depPath.replace(':', '+')
}

View File

@@ -128,16 +128,16 @@ test('resolve()', () => {
})
test('depPathToFilename()', () => {
expect(depPathToFilename('/foo/1.0.0', process.cwd())).toBe('foo@1.0.0')
expect(depPathToFilename('/@foo/bar/1.0.0', process.cwd())).toBe('@foo+bar@1.0.0')
expect(depPathToFilename('github.com/something/foo/0000', process.cwd())).toBe('github.com+something+foo@0000')
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')).toBe('github.com+something+foo@0000')
const filename = depPathToFilename('file:./test/foo-1.0.0.tgz_foo@2.0.0', process.cwd())
expect(filename).toMatch(/^local\+.*\+foo-1\.0\.0\.tgz_foo@2\.0\.0$/)
const filename = depPathToFilename('file:test/foo-1.0.0.tgz_foo@2.0.0')
expect(filename).toBe('file+test+foo-1.0.0.tgz_foo@2.0.0')
expect(filename).not.toContain(':')
expect(depPathToFilename('abcd/'.repeat(200), process.cwd())).toBe('abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+_27524303f1ddd808db67f175ff83606e')
expect(depPathToFilename('/JSONSteam/1.0.0', process.cwd())).toBe('JSONSteam@1.0.0_4b2567ab922fbdf01171f59fab8f6fef')
expect(depPathToFilename('abcd/'.repeat(200))).toBe('abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+_27524303f1ddd808db67f175ff83606e')
expect(depPathToFilename('/JSONSteam/1.0.0')).toBe('JSONSteam@1.0.0_4b2567ab922fbdf01171f59fab8f6fef')
})
test('tryGetPackageId', () => {

View File

@@ -343,7 +343,6 @@ export default async (opts: HeadlessOptions) => {
newHoistedDependencies = await hoist({
lockfile: hoistLockfile,
importerIds,
lockfileDir,
privateHoistedModulesDir: hoistedModulesDir,
privateHoistPattern: opts.hoistPattern ?? [],
publicHoistedModulesDir,
@@ -429,7 +428,6 @@ export default async (opts: HeadlessOptions) => {
}
const projectsToBeBuilt = extendProjectsWithTargetDirs(opts.projects, wantedLockfile, {
lockfileDir: opts.lockfileDir,
pkgLocationByDepPath,
virtualStoreDir,
})

View File

@@ -99,7 +99,7 @@ export default async function lockfileToDepGraph (
const pkgSnapshot = lockfile.packages![depPath]
// 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, opts.lockfileDir), 'node_modules')
const modules = path.join(opts.virtualStoreDir, dp.depPathToFilename(depPath), 'node_modules')
const packageId = packageIdFromSnapshot(depPath, pkgSnapshot, opts.registries)
const pkg = {
@@ -244,7 +244,7 @@ async 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, ctx.lockfileDir), 'node_modules', pkgName)
children[alias] = path.join(ctx.virtualStoreDir, dp.depPathToFilename(childRelDepPath), 'node_modules', pkgName)
} else if (allDeps[alias].indexOf('file:') === 0) {
children[alias] = path.resolve(ctx.lockfileDir, allDeps[alias].substr(5))
} else if (!ctx.skipped.has(childRelDepPath) && ((peerDeps == null) || !peerDeps.has(alias))) {

View File

@@ -17,7 +17,6 @@ const hoistLogger = logger('hoist')
export default async function hoistByLockfile (
opts: {
lockfile: Lockfile
lockfileDir: string
importerIds?: string[]
privateHoistPattern: string[]
privateHoistedModulesDir: string
@@ -55,7 +54,6 @@ export default async function hoistByLockfile (
await symlinkHoistedDependencies(hoistedDependencies, {
lockfile: opts.lockfile,
lockfileDir: opts.lockfileDir,
privateHoistedModulesDir: opts.privateHoistedModulesDir,
publicHoistedModulesDir: opts.publicHoistedModulesDir,
virtualStoreDir: opts.virtualStoreDir,
@@ -185,7 +183,6 @@ async function symlinkHoistedDependencies (
hoistedDependencies: HoistedDependencies,
opts: {
lockfile: Lockfile
lockfileDir: string
privateHoistedModulesDir: string
publicHoistedModulesDir: string
virtualStoreDir: string
@@ -201,7 +198,7 @@ async function symlinkHoistedDependencies (
return
}
const pkgName = nameVerFromPkgSnapshot(depPath, pkgSnapshot).name
const modules = path.join(opts.virtualStoreDir, dp.depPathToFilename(depPath, opts.lockfileDir), 'node_modules')
const modules = path.join(opts.virtualStoreDir, dp.depPathToFilename(depPath), 'node_modules')
const depLocation = path.join(modules, pkgName)
await Promise.all(Object.entries(pkgAliases).map(async ([pkgAlias, hoistType]) => {
const targetDir = hoistType === 'public'

View File

@@ -114,7 +114,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, opts.lockfileDir),
depPathToFilename(relDepPath),
'node_modules',
name
)))

View File

@@ -7,14 +7,13 @@ export default function extendProjectsWithTargetDirs<T> (
projects: Array<T & { id: string }>,
lockfile: Lockfile,
ctx: {
lockfileDir: string
virtualStoreDir: string
pkgLocationByDepPath?: Record<string, string>
}
): Array<T & { id: string, stages: string[], targetDirs: string[] }> {
const getLocalLocation = ctx.pkgLocationByDepPath != null
? (depPath: string) => ctx.pkgLocationByDepPath![depPath]
: (depPath: string, pkgName: string) => path.join(ctx.virtualStoreDir, depPathToFilename(depPath, ctx.lockfileDir), 'node_modules', pkgName)
: (depPath: string, pkgName: string) => path.join(ctx.virtualStoreDir, depPathToFilename(depPath), 'node_modules', pkgName)
const projectsById: Record<string, T & { id: string, targetDirs: string[], stages?: string[] }> =
fromPairs(projects.map((project) => [project.id, { ...project, targetDirs: [] as string[] }]))
Object.entries(lockfile.packages ?? {})

View File

@@ -152,13 +152,13 @@ export default async function prune (
const _tryRemovePkg = tryRemovePkg.bind(null, opts.lockfileDir, opts.virtualStoreDir)
await Promise.all(
orphanDepPaths
.map((orphanDepPath) => depPathToFilename(orphanDepPath, opts.lockfileDir))
.map((orphanDepPath) => depPathToFilename(orphanDepPath))
.map(async (orphanDepPath) => _tryRemovePkg(orphanDepPath))
)
const neededPkgs: Set<string> = new Set()
for (const depPath of Object.keys(opts.wantedLockfile.packages ?? {})) {
if (opts.skipped.has(depPath)) continue
neededPkgs.add(depPathToFilename(depPath, opts.lockfileDir))
neededPkgs.add(depPathToFilename(depPath))
}
const availablePkgs = await readVirtualStoreDir(opts.virtualStoreDir, opts.lockfileDir)
await Promise.all(

View File

@@ -23,13 +23,13 @@ const STAT_DEFAULT = {
export default async function createFuseHandlers (lockfileDir: string, cafsDir: string) {
const lockfile = await readWantedLockfile(lockfileDir, { ignoreIncompatible: true })
if (lockfile == null) throw new Error('Cannot generate a .pnp.cjs without a lockfile')
return createFuseHandlersFromLockfile(lockfile, lockfileDir, cafsDir)
return createFuseHandlersFromLockfile(lockfile, cafsDir)
}
/* eslint-disable node/no-callback-literal */
export function createFuseHandlersFromLockfile (lockfile: Lockfile, lockfileDir: string, cafsDir: string) {
export function createFuseHandlersFromLockfile (lockfile: Lockfile, cafsDir: string) {
const pkgSnapshotCache = new Map<string, { name: string, version: string, pkgSnapshot: PackageSnapshot, index: PackageFilesIndex }>()
const virtualNodeModules = makeVirtualNodeModules(lockfile, lockfileDir)
const virtualNodeModules = makeVirtualNodeModules(lockfile)
return {
open (p: string, flags: string | number, cb: (exitCode: number, fd?: number) => void) {
const dirEnt = getDirEnt(p)

View File

@@ -16,18 +16,18 @@ type DirEntry = {
entries: Record<string, DirEntry>
}
export default function (lockfile: Lockfile, lockfileDir: string): DirEntry {
export default function (lockfile: Lockfile): DirEntry {
const entries: Record<string, DirEntry> = {
'.pnpm': {
entryType: 'directory',
entries: createVirtualStoreDir(lockfile, lockfileDir),
entries: createVirtualStoreDir(lockfile),
},
}
for (const depType of DEPENDENCIES_FIELDS) {
for (const [depName, ref] of Object.entries(lockfile.importers['.'][depType] ?? {})) {
const symlink: DirEntry = {
entryType: 'symlink',
target: `./.pnpm/${dp.depPathToFilename(dp.refToRelative(ref, depName)!, lockfileDir)}/node_modules/${depName}`,
target: `./.pnpm/${dp.depPathToFilename(dp.refToRelative(ref, depName)!)}/node_modules/${depName}`,
}
addDirEntry(entries, depName, symlink)
}
@@ -38,12 +38,12 @@ export default function (lockfile: Lockfile, lockfileDir: string): DirEntry {
}
}
function createVirtualStoreDir (lockfile: Lockfile, lockfileDir: string) {
function createVirtualStoreDir (lockfile: Lockfile) {
const rootDir = {} as 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, lockfileDir)
const currentPath = dp.depPathToFilename(depPath)
const pkgDir: DirEntry = {
entryType: 'index',
depPath,
@@ -52,7 +52,7 @@ function createVirtualStoreDir (lockfile: Lockfile, lockfileDir: string) {
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)!, lockfileDir)}/node_modules/${depName}`)),
target: normalize(path.relative(`${currentPath}/node_modules/`, `${dp.depPathToFilename(dp.refToRelative(ref, depName)!)}/node_modules/${depName}`)),
}
addDirEntry(pkgNodeModules, depName, symlink)
}

View File

@@ -4,6 +4,5 @@ import makeVirtualNodeModules from '../src/makeVirtualNodeModules'
test('makeVirtualNodeModules', async () => {
const lockfile = await readWantedLockfile(path.join(__dirname, '__fixtures__/simple'), { ignoreIncompatible: true })
const cafsDir = path.join(__dirname, '__fixtures__/simple/store/v3/files')
expect(makeVirtualNodeModules(lockfile!, cafsDir)).toMatchSnapshot()
expect(makeVirtualNodeModules(lockfile!)).toMatchSnapshot()
})

View File

@@ -311,7 +311,7 @@ function fetchToStore (
if (!opts.pkg.name) {
opts.fetchRawManifest = true
}
const targetRelative = depPathToFilename(opts.pkg.id, opts.lockfileDir)
const targetRelative = depPathToFilename(opts.pkg.id)
const target = path.join(ctx.storeDir, targetRelative)
if (!ctx.fetchingLocker.has(opts.pkg.id)) {

View File

@@ -269,9 +269,9 @@ async function _rebuild (
async () => {
const pkgSnapshot = pkgSnapshots[depPath]
const pkgInfo = nameVerFromPkgSnapshot(depPath, pkgSnapshot)
const pkgRoot = path.join(ctx.virtualStoreDir, dp.depPathToFilename(depPath, opts.lockfileDir), 'node_modules', pkgInfo.name)
const pkgRoot = path.join(ctx.virtualStoreDir, dp.depPathToFilename(depPath), 'node_modules', pkgInfo.name)
try {
const modules = path.join(ctx.virtualStoreDir, dp.depPathToFilename(depPath, opts.lockfileDir), 'node_modules')
const modules = path.join(ctx.virtualStoreDir, dp.depPathToFilename(depPath), 'node_modules')
const binPath = path.join(pkgRoot, 'node_modules', '.bin')
await linkBins(modules, binPath, { warn })
await runPostinstallHooks({
@@ -316,7 +316,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, opts.lockfileDir), 'node_modules')
const modules = path.join(ctx.virtualStoreDir, dp.depPathToFilename(depPath), 'node_modules')
const binPath = path.join(modules, pkgInfo.name, 'node_modules', '.bin')
return linkBins(modules, binPath, { warn })
}))

View File

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

View File

@@ -585,7 +585,7 @@ async function resolveDependency (
await exists(
path.join(
ctx.virtualStoreDir,
dp.depPathToFilename(currentPkg.depPath, ctx.prefix),
dp.depPathToFilename(currentPkg.depPath),
'node_modules',
currentPkg.name!,
'package.json'

View File

@@ -259,7 +259,7 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
.map(({ name, version }) => ({ name, version })))
depPath = `${resolvedPackage.depPath}${peersFolderSuffix}`
}
const localLocation = path.join(ctx.virtualStoreDir, depPathToFilename(depPath, ctx.lockfileDir))
const localLocation = path.join(ctx.virtualStoreDir, depPathToFilename(depPath))
const modules = path.join(localLocation, 'node_modules')
const isPure = isEmpty(allResolvedPeers) && allMissingPeers.length === 0
if (isPure) {

2
pnpm-lock.yaml generated
View File

@@ -684,12 +684,10 @@ importers:
'@types/semver': ^7.3.4
dependency-path: workspace:8.0.11
encode-registry: ^3.0.0
normalize-path: ^3.0.0
semver: ^7.3.4
dependencies:
'@pnpm/types': link:../types
encode-registry: 3.0.0
normalize-path: 3.0.0
semver: 7.3.5
devDependencies:
'@types/semver': 7.3.9