mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-30 04:52:04 -04:00
feat: use a shorter hash for dep path (#4552)
This commit is contained in:
5
.changeset/beige-ravens-speak.md
Normal file
5
.changeset/beige-ravens-speak.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"dependency-path": minor
|
||||
---
|
||||
|
||||
Export new function: createPeersFolderSuffix().
|
||||
7
.changeset/large-items-work.md
Normal file
7
.changeset/large-items-work.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@pnpm/core": major
|
||||
"@pnpm/resolve-dependencies": major
|
||||
"pnpm": major
|
||||
---
|
||||
|
||||
Use a base32 hash instead of a hex to encode too long dependency paths.
|
||||
@@ -1164,7 +1164,7 @@ test('resolve a subdependency from the workspace and use it as a peer', async ()
|
||||
'/abc-grand-parent-with-c/1.0.0',
|
||||
'/abc-parent-with-ab/1.0.0',
|
||||
'/abc-parent-with-ab/1.0.0_peer-c@1.0.1',
|
||||
'/abc/1.0.0_2ff3f699e79762943311edf90e6e1302',
|
||||
'/abc/1.0.0_f7z7ngphs5rjimyr5x4q43qtai',
|
||||
'/abc/1.0.0_peer-a@peer-a+peer-b@1.0.0',
|
||||
'/dep-of-pkg-with-1-dep/100.0.0',
|
||||
'/is-positive/1.0.0',
|
||||
|
||||
@@ -438,14 +438,14 @@ test('peer dependencies are linked when running one named installation', async (
|
||||
|
||||
const pkgVariationsDir = path.resolve('node_modules/.pnpm/abc@1.0.0')
|
||||
|
||||
const pkgVariation1 = path.join(pkgVariationsDir + '_165e1e08a3f7e7f77ddb572ad0e55660/node_modules')
|
||||
const pkgVariation1 = path.join(pkgVariationsDir + '_6ea473aweg4rki46lsbci3nehq/node_modules')
|
||||
await okFile(path.join(pkgVariation1, 'abc'))
|
||||
await okFile(path.join(pkgVariation1, 'peer-a'))
|
||||
await okFile(path.join(pkgVariation1, 'peer-b'))
|
||||
await okFile(path.join(pkgVariation1, 'peer-c'))
|
||||
await okFile(path.join(pkgVariation1, 'dep-of-pkg-with-1-dep'))
|
||||
|
||||
const pkgVariation2 = path.join(pkgVariationsDir + '_f101cfec1621b915239e5c82246da43c/node_modules')
|
||||
const pkgVariation2 = path.join(pkgVariationsDir + '_czpb4cfd67t7o7o3k4vnbzkwma/node_modules')
|
||||
await okFile(path.join(pkgVariation2, 'abc'))
|
||||
await okFile(path.join(pkgVariation2, 'peer-a'))
|
||||
await okFile(path.join(pkgVariation2, 'peer-b'))
|
||||
@@ -461,6 +461,7 @@ test('peer dependencies are linked when running one named installation', async (
|
||||
})
|
||||
|
||||
test('peer dependencies are linked when running two separate named installations', async () => {
|
||||
await addDistTag({ package: 'abc-parent-with-ab', version: '1.0.0', distTag: 'latest' })
|
||||
await addDistTag({ package: 'peer-a', version: '1.0.0', distTag: 'latest' })
|
||||
await addDistTag({ package: 'peer-c', version: '1.0.0', distTag: 'latest' })
|
||||
prepareEmpty()
|
||||
@@ -470,14 +471,14 @@ test('peer dependencies are linked when running two separate named installations
|
||||
|
||||
const pkgVariationsDir = path.resolve('node_modules/.pnpm/abc@1.0.0')
|
||||
|
||||
const pkgVariation1 = path.join(pkgVariationsDir + '_165e1e08a3f7e7f77ddb572ad0e55660/node_modules')
|
||||
const pkgVariation1 = path.join(pkgVariationsDir + '_6ea473aweg4rki46lsbci3nehq/node_modules')
|
||||
await okFile(path.join(pkgVariation1, 'abc'))
|
||||
await okFile(path.join(pkgVariation1, 'peer-a'))
|
||||
await okFile(path.join(pkgVariation1, 'peer-b'))
|
||||
await okFile(path.join(pkgVariation1, 'peer-c'))
|
||||
await okFile(path.join(pkgVariation1, 'dep-of-pkg-with-1-dep'))
|
||||
|
||||
const pkgVariation2 = path.join(pkgVariationsDir + '_165e1e08a3f7e7f77ddb572ad0e55660/node_modules')
|
||||
const pkgVariation2 = path.join(pkgVariationsDir + '_6ea473aweg4rki46lsbci3nehq/node_modules')
|
||||
await okFile(path.join(pkgVariation2, 'abc'))
|
||||
await okFile(path.join(pkgVariation2, 'peer-a'))
|
||||
await okFile(path.join(pkgVariation2, 'peer-b'))
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"dependencies": {
|
||||
"@pnpm/types": "workspace:8.0.0",
|
||||
"encode-registry": "^3.0.0",
|
||||
"rfc4648": "^1.5.1",
|
||||
"semver": "^7.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import crypto from 'crypto'
|
||||
import { Registries } from '@pnpm/types'
|
||||
import encodeRegistry from 'encode-registry'
|
||||
import { base32 } from 'rfc4648'
|
||||
import semver from 'semver'
|
||||
|
||||
export function isAbsolute (dependencyPath: string) {
|
||||
@@ -132,7 +133,7 @@ export function parse (dependencyPath: string) {
|
||||
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.substring(0, 50)}_${createBase32Hash(filename)}`
|
||||
}
|
||||
return filename
|
||||
}
|
||||
@@ -147,3 +148,22 @@ function depPathToFilenameUnescaped (depPath: string) {
|
||||
}
|
||||
return depPath.replace(':', '+')
|
||||
}
|
||||
|
||||
export function createPeersFolderSuffix (peers: Array<{name: string, version: string}>): string {
|
||||
const folderName = peers.map(({ name, version }) => `${name.replace('/', '+')}@${version}`).sort().join('+')
|
||||
|
||||
// We don't want the folder name to get too long.
|
||||
// Otherwise, an ENAMETOOLONG error might happen.
|
||||
// see: https://github.com/pnpm/pnpm/issues/977
|
||||
//
|
||||
// A bigger limit might be fine but the base32 encoded md5 hash will be 26 symbols,
|
||||
// so for consistency's sake, we go with 26.
|
||||
if (folderName.length > 26) {
|
||||
return `_${createBase32Hash(folderName)}`
|
||||
}
|
||||
return `_${folderName}`
|
||||
}
|
||||
|
||||
function createBase32Hash (str: string): string {
|
||||
return base32.stringify(crypto.createHash('md5').update(str).digest()).replace(/(=+)$/, '').toLowerCase()
|
||||
}
|
||||
|
||||
@@ -136,8 +136,8 @@ test('depPathToFilename()', () => {
|
||||
expect(filename).toBe('file+test+foo-1.0.0.tgz_foo@2.0.0')
|
||||
expect(filename).not.toContain(':')
|
||||
|
||||
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')
|
||||
expect(depPathToFilename('abcd/'.repeat(200))).toBe('abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+abcd+_e5jega7r3xmarw3h6f277a3any')
|
||||
expect(depPathToFilename('/JSONSteam/1.0.0')).toBe('JSONSteam@1.0.0_jmswpk4sf667aelr6wp2xd3p54')
|
||||
})
|
||||
|
||||
test('tryGetPackageId', () => {
|
||||
|
||||
@@ -612,5 +612,5 @@ test('readPackage hook is used during removal inside a workspace', async () => {
|
||||
|
||||
process.chdir('..')
|
||||
const lockfile = await readYamlFile<Lockfile>('pnpm-lock.yaml')
|
||||
expect(lockfile.packages!['/abc/1.0.0_is-negative@1.0.0+peer-a@1.0.0'].peerDependencies!['is-negative']).toBe('1.0.0')
|
||||
expect(lockfile.packages!['/abc/1.0.0_vt2fli7reel7pfbmpdhs3d7fya'].peerDependencies!['is-negative']).toBe('1.0.0')
|
||||
})
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import crypto from 'crypto'
|
||||
import filenamify from 'filenamify'
|
||||
import path from 'path'
|
||||
import { satisfiesWithPrereleases } from '@yarnpkg/core/lib/semverUtils'
|
||||
@@ -7,7 +6,7 @@ import {
|
||||
PeerDependencyIssues,
|
||||
PeerDependencyIssuesByProjects,
|
||||
} from '@pnpm/types'
|
||||
import { depPathToFilename } from 'dependency-path'
|
||||
import { depPathToFilename, createPeersFolderSuffix } from 'dependency-path'
|
||||
import { KeyValuePair } from 'ramda'
|
||||
import fromPairs from 'ramda/src/fromPairs'
|
||||
import isEmpty from 'ramda/src/isEmpty'
|
||||
@@ -511,18 +510,3 @@ function toPkgByName<T extends PartialResolvedPackage> (nodes: Array<{alias: str
|
||||
}
|
||||
return pkgsByName
|
||||
}
|
||||
|
||||
function createPeersFolderSuffix (peers: Array<{name: string, version: string}>) {
|
||||
const folderName = peers.map(({ name, version }) => `${name.replace('/', '+')}@${version}`).sort().join('+')
|
||||
|
||||
// We don't want the folder name to get too long.
|
||||
// Otherwise, an ENAMETOOLONG error might happen.
|
||||
// see: https://github.com/pnpm/pnpm/issues/977
|
||||
//
|
||||
// A bigger limit might be fine but the md5 hash will be 32 symbols,
|
||||
// so for consistency's sake, we go with 32.
|
||||
if (folderName.length > 32) {
|
||||
return `_${crypto.createHash('md5').update(folderName).digest('hex')}`
|
||||
}
|
||||
return `_${folderName}`
|
||||
}
|
||||
|
||||
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
@@ -684,10 +684,12 @@ importers:
|
||||
'@types/semver': ^7.3.4
|
||||
dependency-path: workspace:9.0.0
|
||||
encode-registry: ^3.0.0
|
||||
rfc4648: ^1.5.1
|
||||
semver: ^7.3.4
|
||||
dependencies:
|
||||
'@pnpm/types': link:../types
|
||||
encode-registry: 3.0.0
|
||||
rfc4648: 1.5.1
|
||||
semver: 7.3.6
|
||||
devDependencies:
|
||||
'@types/semver': 7.3.9
|
||||
@@ -13203,6 +13205,10 @@ packages:
|
||||
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
|
||||
/rfc4648/1.5.1:
|
||||
resolution: {integrity: sha512-60e/YWs2/D3MV1ErdjhJHcmlgnyLUiG4X/14dgsfm9/zmCWLN16xI6YqJYSCd/OANM7bUNzJqPY5B8/02S9Ibw==}
|
||||
dev: false
|
||||
|
||||
/right-pad/1.0.1:
|
||||
resolution: {integrity: sha1-jKCMLLtbVedNr6lr9/0aJ9VoyNA=}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
Reference in New Issue
Block a user