fix(headless): .bin directory should be created in hoisted node_modules (#4191)

ref #4073
This commit is contained in:
Zoltan Kochan
2022-01-05 00:21:42 +02:00
committed by GitHub
parent 8257fac7fc
commit 0b5662fc5e
5 changed files with 59 additions and 10 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/headless": patch
---
The commands should be linked to `node_modules/.bin` directory when `nodeLinker=hoisted` is used.

View File

@@ -86,7 +86,7 @@ test('preserve subdeps on update', async () => {
expect(loadJsonFile<{ version: string }>('node_modules/foobarqar/node_modules/bar/package.json').version).toBe('100.0.0')
})
test('adding a new dependency to one of the the workspace projects', async () => {
test('adding a new dependency to one of the workspace projects', async () => {
prepareEmpty()
let [{ manifest }] = await mutateModules([
@@ -143,3 +143,20 @@ test('installing the same package with alias and no alias', async () => {
expect(loadJsonFile<{ version: string }>('node_modules/dep-of-pkg-with-1-dep/package.json').version).toBe('100.0.0')
expect(loadJsonFile<{ version: string }>('node_modules/dep/package.json').version).toBe('100.0.0')
})
test('run pre/postinstall scripts. bin files should be linked in a hoisted node_modules', async () => {
const project = prepareEmpty()
await addDependenciesToPackage({},
['pre-and-postinstall-scripts-example'],
await testDefaults({ fastUnpack: false, nodeLinker: 'hoisted', targetDependenciesField: 'devDependencies' })
)
expect(fs.existsSync('node_modules/pre-and-postinstall-scripts-example/generated-by-prepare.js')).toBeFalsy()
expect(fs.existsSync('node_modules/pre-and-postinstall-scripts-example/generated-by-preinstall.js')).toBeTruthy()
const generatedByPreinstall = project.requireModule('pre-and-postinstall-scripts-example/generated-by-preinstall')
expect(typeof generatedByPreinstall).toBe('function')
const generatedByPostinstall = project.requireModule('pre-and-postinstall-scripts-example/generated-by-postinstall')
expect(typeof generatedByPostinstall).toBe('function')
})

View File

@@ -284,6 +284,7 @@ export default async (opts: HeadlessOptions) => {
let newHoistedDependencies!: HoistedDependencies
if (opts.nodeLinker === 'hoisted' && hierarchy && prevGraph) {
await linkHoistedModules(opts.storeController, graph, prevGraph, hierarchy, {
extendNodePath: opts.extendNodePath,
force: opts.force,
lockfileDir: opts.lockfileDir,
targetEngine: opts.sideEffectsCacheRead && ENGINE_NAME || undefined,

View File

@@ -1,8 +1,11 @@
import path from 'path'
import {
progressLogger,
removalLogger,
statsLogger,
} from '@pnpm/core-loggers'
import linkBins from '@pnpm/link-bins'
import logger from '@pnpm/logger'
import {
PackageFilesResponse,
StoreController,
@@ -20,6 +23,7 @@ export default async function linkHoistedModules (
prevGraph: DependenciesGraph,
hierarchy: DepHierarchy,
opts: {
extendNodePath?: boolean
force: boolean
lockfileDir: string
targetEngine?: string
@@ -36,7 +40,19 @@ export default async function linkHoistedModules (
})
await Promise.all([
...dirsToRemove.map((dir) => tryRemoveDir(dir)),
linkAllPkgsInOrder(storeController, graph, prevGraph, hierarchy, opts),
...Object.entries(hierarchy)
.map(([parentDir, depsHierarchy]) => {
function warn (message: string) {
logger.info({
message,
prefix: parentDir,
})
}
return linkAllPkgsInOrder(storeController, graph, prevGraph, depsHierarchy, parentDir, {
...opts,
warn,
})
}),
])
}
@@ -60,10 +76,13 @@ async function linkAllPkgsInOrder (
graph: DependenciesGraph,
prevGraph: DependenciesGraph,
hierarchy: DepHierarchy,
parentDir: string,
opts: {
extendNodePath?: boolean
force: boolean
lockfileDir: string
targetEngine?: string
warn: (message: string) => void
}
) {
await Promise.all(
@@ -91,7 +110,14 @@ async function linkAllPkgsInOrder (
})
}
depNode.isBuilt = isBuilt
return linkAllPkgsInOrder(storeController, graph, prevGraph, deps, opts)
return linkAllPkgsInOrder(storeController, graph, prevGraph, deps, dir, opts)
})
)
const modulesDir = path.join(parentDir, 'node_modules')
const binsDir = path.join(modulesDir, '.bin')
await linkBins(modulesDir, binsDir, {
allowExoticManifests: true,
extendNodePath: opts.extendNodePath,
warn: opts.warn,
})
}

View File

@@ -66,21 +66,21 @@ async function _lockfileToHoistedDepGraph (
const tree = hoist(lockfile)
const graph: DependenciesGraph = {}
const modulesDir = path.join(opts.lockfileDir, 'node_modules')
let hierarchy = await fetchDeps(lockfile, opts, graph, modulesDir, tree.dependencies)
const hierarchy = {
[opts.lockfileDir]: await fetchDeps(lockfile, opts, graph, modulesDir, tree.dependencies),
}
const directDependenciesByImporterId: DirectDependenciesByImporterId = {
'.': directDepsMap(Object.keys(hierarchy), graph),
'.': directDepsMap(Object.keys(hierarchy[opts.lockfileDir]), graph),
}
const symlinkedDirectDependenciesByImporterId: DirectDependenciesByImporterId = { '.': {} }
for (const rootDep of Array.from(tree.dependencies)) {
const reference = Array.from(rootDep.references)[0]
if (reference.startsWith('workspace:')) {
const importerId = reference.replace('workspace:', '')
const modulesDir = path.join(opts.lockfileDir, importerId, 'node_modules')
const projectDir = path.join(opts.lockfileDir, importerId)
const modulesDir = path.join(projectDir, 'node_modules')
const nextHierarchy = (await fetchDeps(lockfile, opts, graph, modulesDir, rootDep.dependencies))
hierarchy = {
...hierarchy,
...nextHierarchy,
}
hierarchy[projectDir] = nextHierarchy
const importer = lockfile.importers[importerId]
const importerDir = path.join(opts.lockfileDir, importerId)