feat: add a new option to the hoister to ignore some deps (#5784)

This commit is contained in:
Zoltan Kochan
2022-12-13 00:15:26 +02:00
committed by GitHub
parent 234e9ebaec
commit 450e0b1d10
6 changed files with 47 additions and 2 deletions

View File

@@ -0,0 +1,7 @@
---
"@pnpm/core": minor
"@pnpm/headless": minor
"@pnpm/real-hoist": minor
---
A new option added for avoiding hoisting some dependencies to the root of `node_modules`: `externalDependencies`. This option is a set of dependency names that were added to `node_modules` by another tool. pnpm doesn't have information about these dependencies but they shouldn't be overwritten by hoisted dependencies.

View File

@@ -28,6 +28,7 @@ export interface StrictInstallOptions {
extraBinPaths: string[]
extraEnv: Record<string, string>
hoistingLimits?: HoistingLimits
externalDependencies?: Set<string>
useLockfile: boolean
saveLockfile: boolean
useGitBranchLockfile: boolean

View File

@@ -224,3 +224,20 @@ test('hoistingLimits should prevent packages to be hoisted', async () => {
expect(fs.existsSync('node_modules/ms')).toBeFalsy()
expect(fs.existsSync('node_modules/send/node_modules/ms')).toBeTruthy()
})
test('externalDependencies should prevent package from being hoisted to the root', async () => {
prepareEmpty()
const externalDependencies = new Set(['ms'])
await install({
dependencies: {
send: '0.17.2',
},
}, await testDefaults({
nodeLinker: 'hoisted',
externalDependencies,
}))
expect(fs.existsSync('node_modules/ms')).toBeFalsy()
expect(fs.existsSync('node_modules/send/node_modules/ms')).toBeTruthy()
})

View File

@@ -109,6 +109,7 @@ export interface HeadlessOptions {
extraNodePaths?: string[]
preferSymlinkedExecutables?: boolean
hoistingLimits?: HoistingLimits
externalDependencies?: Set<string>
ignoreDepScripts: boolean
ignoreScripts: boolean
ignorePackageManifest?: boolean

View File

@@ -29,6 +29,7 @@ export interface LockfileToHoistedDepGraphOptions {
engineStrict: boolean
force: boolean
hoistingLimits?: HoistingLimits
externalDependencies?: Set<string>
importerIds: string[]
include: IncludedDependencies
lockfileDir: string
@@ -64,7 +65,7 @@ async function _lockfileToHoistedDepGraph (
lockfile: Lockfile,
opts: LockfileToHoistedDepGraphOptions
): Promise<Omit<LockfileToDepGraphResult, 'prevGraph'>> {
const tree = hoist(lockfile, { hoistingLimits: opts.hoistingLimits })
const tree = hoist(lockfile, { hoistingLimits: opts.hoistingLimits, externalDependencies: opts.externalDependencies })
const graph: DependenciesGraph = {}
const modulesDir = path.join(opts.lockfileDir, 'node_modules')
const fetchDepsOpts = {

View File

@@ -14,6 +14,9 @@ export function hoist (
lockfile: Lockfile,
opts?: {
hoistingLimits?: HoistingLimits
// This option was added for Bit CLI in order to prevent pnpm from overwriting dependencies linked by Bit.
// However, in the future it might be useful to use it in pnpm for skipping any dependencies added by external tools.
externalDependencies?: Set<string>
}
): HoisterResult {
const nodes = new Map<string, HoisterTree>()
@@ -27,6 +30,13 @@ export function hoist (
...lockfile.importers['.']?.dependencies,
...lockfile.importers['.']?.devDependencies,
...lockfile.importers['.']?.optionalDependencies,
...(Array.from(opts?.externalDependencies ?? [])).reduce((acc, dep) => {
// It doesn't matter what version spec is used here.
// This dependency will be removed from the tree anyway.
// It is only needed to prevent the hoister from hoisting deps with this name to the root of node_modules.
acc[dep] = 'link:'
return acc
}, {}),
}),
}
for (const [importerId, importer] of Object.entries(lockfile.importers)) {
@@ -46,7 +56,15 @@ export function hoist (
node.dependencies.add(importerNode)
}
return _hoist(node, opts)
const hoisterResult = _hoist(node, opts)
if (opts?.externalDependencies) {
for (const hoistedDep of hoisterResult.dependencies.values()) {
if (opts.externalDependencies.has(hoistedDep.name)) {
hoisterResult.dependencies.delete(hoistedDep)
}
}
}
return hoisterResult
}
function toTree (nodes: Map<string, HoisterTree>, lockfile: Lockfile, deps: Record<string, string>): Set<HoisterTree> {