mirror of
https://github.com/pnpm/pnpm.git
synced 2026-01-31 18:22:38 -05:00
fix: symlinked hoisting should not override files in the root of node_modules (#5663)
This commit is contained in:
6
.changeset/weak-pets-share.md
Normal file
6
.changeset/weak-pets-share.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/hoist": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
Hoisting with symlinks should not override external symlinks and directories in the root of node_modules.
|
||||
@@ -61,7 +61,7 @@
|
||||
"@types/is-windows": "^1.0.0",
|
||||
"@types/ramda": "0.28.15",
|
||||
"@types/which": "^2.0.1",
|
||||
"symlink-dir": "^5.0.1"
|
||||
"symlink-dir": "^5.1.0"
|
||||
},
|
||||
"funding": "https://opencollective.com/pnpm",
|
||||
"exports": {
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"dependencies": {
|
||||
"@pnpm/core-loggers": "workspace:*",
|
||||
"@pnpm/types": "workspace:*",
|
||||
"symlink-dir": "^5.0.1"
|
||||
"symlink-dir": "^5.1.0"
|
||||
},
|
||||
"funding": "https://opencollective.com/pnpm",
|
||||
"exports": {
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
"read-yaml-file": "^2.1.0",
|
||||
"resolve-link-target": "^2.0.0",
|
||||
"sinon": "^14.0.2",
|
||||
"symlink-dir": "^5.0.1",
|
||||
"symlink-dir": "^5.1.0",
|
||||
"write-json-file": "^4.3.0",
|
||||
"write-yaml-file": "^4.2.0"
|
||||
},
|
||||
|
||||
@@ -13,6 +13,7 @@ import rimraf from '@zkochan/rimraf'
|
||||
import resolveLinkTarget from 'resolve-link-target'
|
||||
import { WANTED_LOCKFILE } from '@pnpm/constants'
|
||||
import { addDistTag } from '@pnpm/registry-mock'
|
||||
import symlinkDir from 'symlink-dir'
|
||||
import writeYamlFile from 'write-yaml-file'
|
||||
import { testDefaults } from '../utils'
|
||||
|
||||
@@ -50,6 +51,24 @@ test('should hoist dependencies to the root of node_modules when publicHoistPatt
|
||||
await project.isExecutable('.bin/mime')
|
||||
})
|
||||
|
||||
test('public hoist should not override directories that are already in the root of node_modules', async () => {
|
||||
const project = prepareEmpty()
|
||||
fs.mkdirSync('node_modules/debug', { recursive: true })
|
||||
fs.writeFileSync('node_modules/debug/pnpm-test.txt', '')
|
||||
fs.mkdirSync('cookie')
|
||||
fs.writeFileSync('cookie/pnpm-test.txt', '')
|
||||
await symlinkDir('cookie', 'node_modules/cookie')
|
||||
|
||||
await addDependenciesToPackage({},
|
||||
['express@4.18.2'],
|
||||
await testDefaults({ fastUnpack: false, publicHoistPattern: '*' }))
|
||||
|
||||
await project.has('express')
|
||||
await project.has('debug/pnpm-test.txt')
|
||||
await project.has('cookie/pnpm-test.txt')
|
||||
await project.has('mime')
|
||||
})
|
||||
|
||||
test('should hoist some dependencies to the root of node_modules when publicHoistPattern is used and others to the virtual store directory', async () => {
|
||||
const project = prepareEmpty()
|
||||
|
||||
|
||||
@@ -40,16 +40,19 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@pnpm/constants": "workspace:*",
|
||||
"@pnpm/core-loggers": "workspace:*",
|
||||
"@pnpm/link-bins": "workspace:*",
|
||||
"@pnpm/lockfile-types": "workspace:*",
|
||||
"@pnpm/lockfile-utils": "workspace:*",
|
||||
"@pnpm/lockfile-walker": "workspace:*",
|
||||
"@pnpm/matcher": "workspace:*",
|
||||
"@pnpm/symlink-dependency": "workspace:*",
|
||||
"@pnpm/types": "workspace:*",
|
||||
"@pnpm/util.lex-comparator": "1.0.0",
|
||||
"dependency-path": "workspace:*",
|
||||
"ramda": "npm:@pnpm/ramda@0.28.1"
|
||||
"is-subdir": "^1.2.0",
|
||||
"ramda": "npm:@pnpm/ramda@0.28.1",
|
||||
"resolve-link-target": "^2.0.0",
|
||||
"symlink-dir": "^5.1.0"
|
||||
},
|
||||
"funding": "https://opencollective.com/pnpm",
|
||||
"exports": {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { linkLogger } from '@pnpm/core-loggers'
|
||||
import { WANTED_LOCKFILE } from '@pnpm/constants'
|
||||
import { linkBins, WarnFunction } from '@pnpm/link-bins'
|
||||
import {
|
||||
@@ -8,11 +10,13 @@ import {
|
||||
import { lockfileWalker, LockfileWalkerStep } from '@pnpm/lockfile-walker'
|
||||
import { logger } from '@pnpm/logger'
|
||||
import { createMatcher } from '@pnpm/matcher'
|
||||
import { symlinkDependency } from '@pnpm/symlink-dependency'
|
||||
import { HoistedDependencies } from '@pnpm/types'
|
||||
import { lexCompare } from '@pnpm/util.lex-comparator'
|
||||
import * as dp from 'dependency-path'
|
||||
import isSubdir from 'is-subdir'
|
||||
import mapObjIndexed from 'ramda/src/mapObjIndexed'
|
||||
import resolveLinkTarget from 'resolve-link-target'
|
||||
import symlinkDir from 'symlink-dir'
|
||||
|
||||
const hoistLogger = logger('hoist')
|
||||
|
||||
@@ -202,6 +206,7 @@ async function symlinkHoistedDependencies (
|
||||
virtualStoreDir: string
|
||||
}
|
||||
) {
|
||||
const symlink = symlinkHoistedDependency.bind(null, opts)
|
||||
await Promise.all(
|
||||
Object.entries(hoistedDependencies)
|
||||
.map(async ([depPath, pkgAliases]) => {
|
||||
@@ -218,8 +223,44 @@ async function symlinkHoistedDependencies (
|
||||
const targetDir = hoistType === 'public'
|
||||
? opts.publicHoistedModulesDir
|
||||
: opts.privateHoistedModulesDir
|
||||
await symlinkDependency(depLocation, targetDir, pkgAlias)
|
||||
const dest = path.join(targetDir, pkgAlias)
|
||||
return symlink(depLocation, dest)
|
||||
}))
|
||||
}
|
||||
))
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
async function symlinkHoistedDependency (
|
||||
opts: { virtualStoreDir: string },
|
||||
depLocation: string,
|
||||
dest: string
|
||||
) {
|
||||
try {
|
||||
await symlinkDir(depLocation, dest, { overwrite: false })
|
||||
linkLogger.debug({ target: dest, link: depLocation })
|
||||
return
|
||||
} catch (err: any) { // eslint-disable-line
|
||||
if (err.code !== 'EEXIST' && err.code !== 'EISDIR') throw err
|
||||
}
|
||||
let existingSymlink!: string
|
||||
try {
|
||||
existingSymlink = await resolveLinkTarget(dest)
|
||||
} catch (err) {
|
||||
hoistLogger.debug({
|
||||
skipped: dest,
|
||||
reason: 'a directory is present at the target location',
|
||||
})
|
||||
return
|
||||
}
|
||||
if (!isSubdir(opts.virtualStoreDir, existingSymlink)) {
|
||||
hoistLogger.debug({
|
||||
skipped: dest,
|
||||
existingSymlink,
|
||||
reason: 'an external symlink is present at the target location',
|
||||
})
|
||||
return
|
||||
}
|
||||
await fs.promises.unlink(dest)
|
||||
await symlinkDir(depLocation, dest)
|
||||
linkLogger.debug({ target: dest, link: depLocation })
|
||||
}
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
{
|
||||
"path": "../../config/matcher"
|
||||
},
|
||||
{
|
||||
"path": "../../fs/symlink-dependency"
|
||||
},
|
||||
{
|
||||
"path": "../../lockfile/lockfile-types"
|
||||
},
|
||||
@@ -27,6 +24,9 @@
|
||||
{
|
||||
"path": "../../packages/constants"
|
||||
},
|
||||
{
|
||||
"path": "../../packages/core-loggers"
|
||||
},
|
||||
{
|
||||
"path": "../../packages/dependency-path"
|
||||
},
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
"normalize-path": "^3.0.0",
|
||||
"p-settle": "^4.1.1",
|
||||
"ramda": "npm:@pnpm/ramda@0.28.1",
|
||||
"symlink-dir": "^5.0.1"
|
||||
"symlink-dir": "^5.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pnpm/link-bins": "workspace:*",
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
"proxyquire": "^2.1.3",
|
||||
"read-yaml-file": "^2.1.0",
|
||||
"sinon": "^14.0.2",
|
||||
"symlink-dir": "^5.0.1",
|
||||
"symlink-dir": "^5.1.0",
|
||||
"tempy": "^1.0.1",
|
||||
"write-json-file": "^4.3.0",
|
||||
"write-pkg": "4.0.0",
|
||||
|
||||
50
pnpm-lock.yaml
generated
50
pnpm-lock.yaml
generated
@@ -607,8 +607,8 @@ importers:
|
||||
specifier: ^2.0.1
|
||||
version: 2.0.1
|
||||
symlink-dir:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0
|
||||
|
||||
config/matcher:
|
||||
dependencies:
|
||||
@@ -1466,8 +1466,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/types
|
||||
symlink-dir:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0
|
||||
devDependencies:
|
||||
'@pnpm/prepare':
|
||||
specifier: workspace:*
|
||||
@@ -2703,8 +2703,8 @@ importers:
|
||||
specifier: ^14.0.2
|
||||
version: 14.0.2
|
||||
symlink-dir:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0
|
||||
write-json-file:
|
||||
specifier: ^4.3.0
|
||||
version: 4.3.0
|
||||
@@ -2920,6 +2920,9 @@ importers:
|
||||
'@pnpm/constants':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/constants
|
||||
'@pnpm/core-loggers':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/core-loggers
|
||||
'@pnpm/link-bins':
|
||||
specifier: workspace:*
|
||||
version: link:../link-bins
|
||||
@@ -2938,9 +2941,6 @@ importers:
|
||||
'@pnpm/matcher':
|
||||
specifier: workspace:*
|
||||
version: link:../../config/matcher
|
||||
'@pnpm/symlink-dependency':
|
||||
specifier: workspace:*
|
||||
version: link:../../fs/symlink-dependency
|
||||
'@pnpm/types':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/types
|
||||
@@ -2950,9 +2950,18 @@ importers:
|
||||
dependency-path:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/dependency-path
|
||||
is-subdir:
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.0
|
||||
ramda:
|
||||
specifier: npm:@pnpm/ramda@0.28.1
|
||||
version: /@pnpm/ramda/0.28.1
|
||||
resolve-link-target:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
symlink-dir:
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0
|
||||
devDependencies:
|
||||
'@pnpm/hoist':
|
||||
specifier: workspace:*
|
||||
@@ -3012,8 +3021,8 @@ importers:
|
||||
specifier: npm:@pnpm/ramda@0.28.1
|
||||
version: /@pnpm/ramda/0.28.1
|
||||
symlink-dir:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0
|
||||
devDependencies:
|
||||
'@pnpm/link-bins':
|
||||
specifier: workspace:*
|
||||
@@ -3453,8 +3462,8 @@ importers:
|
||||
specifier: ^14.0.2
|
||||
version: 14.0.2
|
||||
symlink-dir:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0
|
||||
tempy:
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1
|
||||
@@ -4060,8 +4069,8 @@ importers:
|
||||
specifier: ^6.0.1
|
||||
version: 6.0.1
|
||||
symlink-dir:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0
|
||||
tempy:
|
||||
specifier: ^1.0.1
|
||||
version: 1.0.1
|
||||
@@ -16115,6 +16124,15 @@ packages:
|
||||
dependencies:
|
||||
better-path-resolve: 1.0.0
|
||||
rename-overwrite: 4.0.3
|
||||
dev: true
|
||||
|
||||
/symlink-dir/5.1.0:
|
||||
resolution: {integrity: sha512-nrwSDbmMYGwc+wu6gzmTdQIV9GzItMG/0LX4c9Co0bhtx2KK1WUKjNIyhjhAdIKaA5eIwVhB2tqFsn8ooUsRKA==}
|
||||
engines: {node: '>=12.10'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
better-path-resolve: 1.0.0
|
||||
rename-overwrite: 4.0.3
|
||||
|
||||
/syncpack/8.3.9:
|
||||
resolution: {integrity: sha512-oDaEvgZAoU40WXX4okQ7DiCfVEcO2antl+jV+Av+FbS4Hyh8VoJ8F6HGo2G8eT9H+AyBBM1iDUdbtR1MQRimoQ==}
|
||||
@@ -17529,7 +17547,7 @@ time:
|
||||
/string.prototype.replaceall/1.0.6: '2021-10-05T01:00:00.092Z'
|
||||
/strip-ansi/6.0.1: '2021-09-23T16:34:41.798Z'
|
||||
/strip-bom/4.0.0: '2019-04-28T04:40:47.887Z'
|
||||
/symlink-dir/5.0.1: '2021-05-03T15:54:10.299Z'
|
||||
/symlink-dir/5.1.0: '2022-11-20T16:48:15.918Z'
|
||||
/syncpack/8.3.9: '2022-10-28T16:58:42.312Z'
|
||||
/tar-stream/2.2.0: '2020-12-29T10:22:57.508Z'
|
||||
/tar/6.1.12: '2022-11-01T16:33:12.294Z'
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
"semver": "^7.3.8",
|
||||
"split-cmd": "^1.0.1",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"symlink-dir": "^5.0.1",
|
||||
"symlink-dir": "^5.1.0",
|
||||
"tempy": "^1.0.1",
|
||||
"tree-kill": "^1.2.2",
|
||||
"which": "^2.0.2",
|
||||
|
||||
Reference in New Issue
Block a user