mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-10 19:28:05 -04:00
91 lines
2.8 KiB
TypeScript
91 lines
2.8 KiB
TypeScript
import path from 'path'
|
|
import { type Lockfile } from '@pnpm/lockfile-file'
|
|
import { DEPENDENCIES_FIELDS } from '@pnpm/types'
|
|
import { nameVerFromPkgSnapshot } from '@pnpm/lockfile-utils'
|
|
import * as dp from '@pnpm/dependency-path'
|
|
import normalize from 'normalize-path'
|
|
|
|
interface DirDirEntry {
|
|
entryType: 'directory'
|
|
entries: Record<string, DirEntry>
|
|
}
|
|
|
|
type DirEntry = {
|
|
entryType: 'index'
|
|
depPath: string
|
|
} | {
|
|
entryType: 'symlink'
|
|
target: string
|
|
} | DirDirEntry
|
|
|
|
export function makeVirtualNodeModules (lockfile: Lockfile): DirEntry {
|
|
const entries: Record<string, DirEntry> = {
|
|
'.pnpm': {
|
|
entryType: 'directory',
|
|
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)!)}/node_modules/${depName}`,
|
|
}
|
|
addDirEntry(entries, depName, symlink)
|
|
}
|
|
}
|
|
return {
|
|
entryType: 'directory',
|
|
entries,
|
|
}
|
|
}
|
|
|
|
function createVirtualStoreDir (lockfile: Lockfile): Record<string, DirEntry> {
|
|
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)
|
|
const pkgDir: DirEntry = {
|
|
entryType: 'index',
|
|
depPath,
|
|
}
|
|
addDirEntry(pkgNodeModules, name, pkgDir)
|
|
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}`)),
|
|
}
|
|
addDirEntry(pkgNodeModules, depName, symlink)
|
|
}
|
|
addDirEntry(rootDir, currentPath, {
|
|
entryType: 'directory',
|
|
entries: {
|
|
node_modules: {
|
|
entryType: 'directory',
|
|
entries: pkgNodeModules,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
return rootDir
|
|
}
|
|
|
|
function addDirEntry (target: Record<string, DirEntry>, subPath: string[] | string, newEntry: DirEntry): void {
|
|
const subPathArray = typeof subPath === 'string' ? subPath.split('/') : subPath
|
|
const p = subPathArray.shift()!
|
|
if (subPathArray.length > 0) {
|
|
if (!target[p]) {
|
|
target[p] = {
|
|
entryType: 'directory',
|
|
entries: {},
|
|
} as DirEntry
|
|
} else if (target[p].entryType !== 'directory') {
|
|
throw new Error()
|
|
}
|
|
addDirEntry((target[p] as DirDirEntry).entries, subPathArray, newEntry)
|
|
} else {
|
|
target[p] = newEntry
|
|
}
|
|
}
|