mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-23 23:29:17 -05:00
16
.changeset/fluffy-birds-carry.md
Normal file
16
.changeset/fluffy-birds-carry.md
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-patching": minor
|
||||
"@pnpm/resolve-dependencies": minor
|
||||
"@pnpm/plugin-commands-audit": minor
|
||||
"@pnpm/plugin-commands-rebuild": minor
|
||||
"@pnpm/plugin-commands-store": minor
|
||||
"@pnpm/dependency-path": minor
|
||||
"@pnpm/lockfile-types": minor
|
||||
"@pnpm/get-context": minor
|
||||
"@pnpm/lockfile-file": minor
|
||||
"@pnpm/core": minor
|
||||
"@pnpm/config": minor
|
||||
"pnpm": minor
|
||||
---
|
||||
|
||||
**Semi-breaking.** Dependency key names in the lockfile are shortened if they are longer than 1000 characters. We don't expect this change to affect many users. This change is required to fix some edge cases in which installation fails with an out-of-memory error or "Invalid string length (RangeError: Invalid string length)" error. The max allowed length of the dependency key can be controlled with the `peers-suffix-max-length` setting [#8177](https://github.com/pnpm/pnpm/pull/8177).
|
||||
@@ -194,6 +194,7 @@ export interface Config {
|
||||
packageManagerStrict?: boolean
|
||||
packageManagerStrictVersion?: boolean
|
||||
virtualStoreDirMaxLength: number
|
||||
peersSuffixMaxLength?: number
|
||||
}
|
||||
|
||||
export interface ConfigWithDeprecatedSettings extends Config {
|
||||
|
||||
@@ -141,6 +141,7 @@ export const types = Object.assign({
|
||||
'verify-store-integrity': Boolean,
|
||||
'virtual-store-dir': String,
|
||||
'virtual-store-dir-max-length': Number,
|
||||
'peers-suffix-max-length': Number,
|
||||
'workspace-concurrency': Number,
|
||||
'workspace-packages': [String, Array],
|
||||
'workspace-root': Boolean,
|
||||
@@ -275,6 +276,7 @@ export async function getConfig (
|
||||
'embed-readme': false,
|
||||
'registry-supports-time-field': false,
|
||||
'virtual-store-dir-max-length': 120,
|
||||
'peers-suffix-max-length': 1000,
|
||||
}
|
||||
|
||||
const { config: npmConfig, warnings, failedToLoadBuiltInConfig } = loadNpmConf(cliOptions, rcOptionsTypes, defaultOptions)
|
||||
|
||||
@@ -48,6 +48,7 @@ export type StrictRebuildOptions = {
|
||||
neverBuiltDependencies?: string[]
|
||||
onlyBuiltDependencies?: string[]
|
||||
virtualStoreDirMaxLength: number
|
||||
peersSuffixMaxLength: number
|
||||
} & Pick<Config, 'sslConfigs'>
|
||||
|
||||
export type RebuildOptions = Partial<StrictRebuildOptions> &
|
||||
|
||||
@@ -62,6 +62,9 @@ export function convertToLockfileFile (lockfile: Lockfile, opts: NormalizeLockfi
|
||||
lockfileVersion: LOCKFILE_VERSION,
|
||||
importers: mapValues(lockfile.importers, convertProjectSnapshotToInlineSpecifiersFormat),
|
||||
}
|
||||
if (newLockfile.settings?.peersSuffixMaxLength === 1000) {
|
||||
newLockfile.settings = omit(['peersSuffixMaxLength'], newLockfile.settings)
|
||||
}
|
||||
return normalizeLockfile(newLockfile, opts)
|
||||
}
|
||||
|
||||
|
||||
@@ -138,6 +138,7 @@ export function createLockfileObject (
|
||||
lockfileVersion: string
|
||||
autoInstallPeers: boolean
|
||||
excludeLinksFromLockfile: boolean
|
||||
peersSuffixMaxLength: number
|
||||
}
|
||||
): Lockfile {
|
||||
const importers = importerIds.reduce((acc, importerId) => {
|
||||
@@ -153,6 +154,7 @@ export function createLockfileObject (
|
||||
settings: {
|
||||
autoInstallPeers: opts.autoInstallPeers,
|
||||
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
|
||||
peersSuffixMaxLength: opts.peersSuffixMaxLength,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ export * from './lockfileFileTypes'
|
||||
export interface LockfileSettings {
|
||||
autoInstallPeers?: boolean
|
||||
excludeLinksFromLockfile?: boolean
|
||||
peersSuffixMaxLength?: number
|
||||
}
|
||||
|
||||
export interface Lockfile {
|
||||
|
||||
@@ -61,6 +61,7 @@ export const DEFAULT_OPTS = {
|
||||
useStoreServer: false,
|
||||
workspaceConcurrency: 4,
|
||||
virtualStoreDirMaxLength: 120,
|
||||
peersSuffixMaxLength: 1000,
|
||||
}
|
||||
|
||||
describe('plugin-commands-audit', () => {
|
||||
|
||||
@@ -191,8 +191,8 @@ function depPathToFilenameUnescaped (depPath: string): string {
|
||||
|
||||
export type PeerId = { name: string, version: string } | string
|
||||
|
||||
export function createPeersDirSuffix (peerIds: PeerId[]): string {
|
||||
const dirName = peerIds.map(
|
||||
export function createPeersDirSuffix (peerIds: PeerId[], maxLength: number = 1000): string {
|
||||
let dirName = peerIds.map(
|
||||
(peerId) => {
|
||||
if (typeof peerId !== 'string') {
|
||||
return `${peerId.name}@${peerId.version}`
|
||||
@@ -203,5 +203,8 @@ export function createPeersDirSuffix (peerIds: PeerId[]): string {
|
||||
return peerId
|
||||
}
|
||||
).sort().join(')(')
|
||||
if (dirName.length > maxLength) {
|
||||
dirName = createBase32Hash(dirName)
|
||||
}
|
||||
return `(${dirName})`
|
||||
}
|
||||
|
||||
@@ -54,4 +54,5 @@ export const DEFAULT_OPTS = {
|
||||
libc: ['current'],
|
||||
},
|
||||
virtualStoreDirMaxLength: 120,
|
||||
peersSuffixMaxLength: 1000,
|
||||
}
|
||||
|
||||
@@ -21,10 +21,11 @@ export type ListMissingPeersOptions = Partial<GetContextOptions>
|
||||
| 'storeController'
|
||||
| 'useGitBranchLockfile'
|
||||
| 'workspacePackages'
|
||||
| 'peersSuffixMaxLength'
|
||||
>
|
||||
& Partial<Pick<InstallOptions, 'supportedArchitectures'>>
|
||||
& Pick<GetContextOptions, 'autoInstallPeers' | 'excludeLinksFromLockfile' | 'storeDir'>
|
||||
& Required<Pick<InstallOptions, 'virtualStoreDirMaxLength'>>
|
||||
& Required<Pick<InstallOptions, 'virtualStoreDirMaxLength' | 'peersSuffixMaxLength'>>
|
||||
|
||||
export async function getPeerDependencyIssues (
|
||||
projects: ProjectOptions[],
|
||||
@@ -91,6 +92,7 @@ export async function getPeerDependencyIssues (
|
||||
wantedLockfile: ctx.wantedLockfile,
|
||||
workspacePackages: opts.workspacePackages ?? {},
|
||||
supportedArchitectures: opts.supportedArchitectures,
|
||||
peersSuffixMaxLength: opts.peersSuffixMaxLength,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -145,6 +145,7 @@ export interface StrictInstallOptions {
|
||||
supportedArchitectures?: SupportedArchitectures
|
||||
hoistWorkspacePackages?: boolean
|
||||
virtualStoreDirMaxLength: number
|
||||
peersSuffixMaxLength: number
|
||||
}
|
||||
|
||||
export type InstallOptions =
|
||||
@@ -239,6 +240,7 @@ const defaults = (opts: InstallOptions): StrictInstallOptions => {
|
||||
disallowWorkspaceCycles: false,
|
||||
excludeLinksFromLockfile: false,
|
||||
virtualStoreDirMaxLength: 120,
|
||||
peersSuffixMaxLength: 1000,
|
||||
} as StrictInstallOptions
|
||||
}
|
||||
|
||||
|
||||
@@ -341,6 +341,7 @@ export async function mutateModules (
|
||||
const outdatedLockfileSettingName = getOutdatedLockfileSetting(ctx.wantedLockfile, {
|
||||
autoInstallPeers: opts.autoInstallPeers,
|
||||
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
|
||||
peersSuffixMaxLength: opts.peersSuffixMaxLength,
|
||||
overrides: opts.overrides,
|
||||
ignoredOptionalDependencies: opts.ignoredOptionalDependencies?.sort(),
|
||||
packageExtensionsChecksum,
|
||||
@@ -361,6 +362,7 @@ export async function mutateModules (
|
||||
ctx.wantedLockfile.settings = {
|
||||
autoInstallPeers: opts.autoInstallPeers,
|
||||
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
|
||||
peersSuffixMaxLength: opts.peersSuffixMaxLength,
|
||||
}
|
||||
ctx.wantedLockfile.overrides = opts.overrides
|
||||
ctx.wantedLockfile.packageExtensionsChecksum = packageExtensionsChecksum
|
||||
@@ -371,6 +373,7 @@ export async function mutateModules (
|
||||
ctx.wantedLockfile.settings = {
|
||||
autoInstallPeers: opts.autoInstallPeers,
|
||||
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
|
||||
peersSuffixMaxLength: opts.peersSuffixMaxLength,
|
||||
}
|
||||
}
|
||||
if (
|
||||
@@ -724,27 +727,28 @@ type ChangedField =
|
||||
| 'ignoredOptionalDependencies'
|
||||
| 'settings.autoInstallPeers'
|
||||
| 'settings.excludeLinksFromLockfile'
|
||||
| 'settings.peersSuffixMaxLength'
|
||||
| 'pnpmfileChecksum'
|
||||
|
||||
function getOutdatedLockfileSetting (
|
||||
lockfile: Lockfile,
|
||||
{
|
||||
onlyBuiltDependencies,
|
||||
overrides,
|
||||
packageExtensionsChecksum,
|
||||
ignoredOptionalDependencies,
|
||||
patchedDependencies,
|
||||
autoInstallPeers,
|
||||
excludeLinksFromLockfile,
|
||||
peersSuffixMaxLength,
|
||||
pnpmfileChecksum,
|
||||
}: {
|
||||
onlyBuiltDependencies?: string[]
|
||||
overrides?: Record<string, string>
|
||||
packageExtensionsChecksum?: string
|
||||
patchedDependencies?: Record<string, PatchFile>
|
||||
ignoredOptionalDependencies?: string[]
|
||||
autoInstallPeers?: boolean
|
||||
excludeLinksFromLockfile?: boolean
|
||||
peersSuffixMaxLength?: number
|
||||
pnpmfileChecksum?: string
|
||||
}
|
||||
): ChangedField | null {
|
||||
@@ -766,6 +770,12 @@ function getOutdatedLockfileSetting (
|
||||
if (lockfile.settings?.excludeLinksFromLockfile != null && lockfile.settings.excludeLinksFromLockfile !== excludeLinksFromLockfile) {
|
||||
return 'settings.excludeLinksFromLockfile'
|
||||
}
|
||||
if (
|
||||
lockfile.settings?.peersSuffixMaxLength != null && lockfile.settings.peersSuffixMaxLength !== peersSuffixMaxLength ||
|
||||
lockfile.settings?.peersSuffixMaxLength == null && peersSuffixMaxLength !== 1000
|
||||
) {
|
||||
return 'settings.peersSuffixMaxLength'
|
||||
}
|
||||
if (lockfile.pnpmfileChecksum !== pnpmfileChecksum) {
|
||||
return 'pnpmfileChecksum'
|
||||
}
|
||||
@@ -1059,6 +1069,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
lockfileIncludeTarballUrl: opts.lockfileIncludeTarballUrl,
|
||||
resolvePeersFromWorkspaceRoot: opts.resolvePeersFromWorkspaceRoot,
|
||||
supportedArchitectures: opts.supportedArchitectures,
|
||||
peersSuffixMaxLength: opts.peersSuffixMaxLength,
|
||||
}
|
||||
)
|
||||
if (!opts.include.optionalDependencies || !opts.include.devDependencies || !opts.include.dependencies) {
|
||||
|
||||
@@ -35,6 +35,7 @@ interface StrictLinkOptions {
|
||||
useGitBranchLockfile: boolean
|
||||
mergeGitBranchLockfiles: boolean
|
||||
virtualStoreDirMaxLength: number
|
||||
peersSuffixMaxLength: number
|
||||
}
|
||||
|
||||
export type LinkOptions =
|
||||
|
||||
@@ -1566,3 +1566,13 @@ test('installation should work with packages that have () in the scope name', as
|
||||
const manifest = await addDependenciesToPackage({}, ['@(-.-)/env@0.3.1'], opts)
|
||||
await install(manifest, opts)
|
||||
})
|
||||
|
||||
test('setting a custom peersSuffixMaxLength', async () => {
|
||||
const project = prepareEmpty()
|
||||
|
||||
await addDependenciesToPackage({}, ['@pnpm.e2e/abc@1.0.0'], testDefaults({ peersSuffixMaxLength: 10 }))
|
||||
|
||||
const lockfile = project.readLockfile()
|
||||
expect(lockfile.settings.peersSuffixMaxLength).toBe(10)
|
||||
expect(lockfile.importers['.']?.dependencies?.['@pnpm.e2e/abc']?.version?.length).toBe(33)
|
||||
})
|
||||
|
||||
@@ -77,6 +77,7 @@ interface HookOptions {
|
||||
export interface GetContextOptions {
|
||||
autoInstallPeers: boolean
|
||||
excludeLinksFromLockfile: boolean
|
||||
peersSuffixMaxLength: number
|
||||
allProjects: Array<ProjectOptions & HookOptions>
|
||||
confirmModulesPurge?: boolean
|
||||
force: boolean
|
||||
@@ -189,6 +190,7 @@ export async function getContext (
|
||||
...await readLockfiles({
|
||||
autoInstallPeers: opts.autoInstallPeers,
|
||||
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
|
||||
peersSuffixMaxLength: opts.peersSuffixMaxLength,
|
||||
force: opts.force,
|
||||
frozenLockfile: opts.frozenLockfile === true,
|
||||
lockfileDir: opts.lockfileDir,
|
||||
@@ -423,6 +425,7 @@ export async function getContextForSingleImporter (
|
||||
opts: {
|
||||
autoInstallPeers: boolean
|
||||
excludeLinksFromLockfile: boolean
|
||||
peersSuffixMaxLength: number
|
||||
force: boolean
|
||||
forceNewModules?: boolean
|
||||
confirmModulesPurge?: boolean
|
||||
@@ -540,6 +543,7 @@ export async function getContextForSingleImporter (
|
||||
...await readLockfiles({
|
||||
autoInstallPeers: opts.autoInstallPeers,
|
||||
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
|
||||
peersSuffixMaxLength: opts.peersSuffixMaxLength,
|
||||
force: opts.force,
|
||||
frozenLockfile: false,
|
||||
lockfileDir: opts.lockfileDir,
|
||||
|
||||
@@ -30,6 +30,7 @@ export async function readLockfiles (
|
||||
opts: {
|
||||
autoInstallPeers: boolean
|
||||
excludeLinksFromLockfile: boolean
|
||||
peersSuffixMaxLength: number
|
||||
force: boolean
|
||||
frozenLockfile: boolean
|
||||
projects: Array<{
|
||||
@@ -111,6 +112,7 @@ export async function readLockfiles (
|
||||
autoInstallPeers: opts.autoInstallPeers,
|
||||
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
|
||||
lockfileVersion: wantedLockfileVersion,
|
||||
peersSuffixMaxLength: opts.peersSuffixMaxLength,
|
||||
}
|
||||
const importerIds = opts.projects.map((importer) => importer.id)
|
||||
const currentLockfile = files[1] ?? createLockfileObject(importerIds, sopts)
|
||||
|
||||
@@ -21,6 +21,7 @@ const DEFAULT_OPTIONS: GetContextOptions = {
|
||||
},
|
||||
storeDir: path.join(__dirname, 'store'),
|
||||
virtualStoreDirMaxLength: 120,
|
||||
peersSuffixMaxLength: 1000,
|
||||
}
|
||||
|
||||
test('getContext - extendNodePath false', async () => {
|
||||
|
||||
@@ -206,6 +206,7 @@ export async function resolveDependencies (
|
||||
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
|
||||
resolvePeersFromWorkspaceRoot: Boolean(opts.resolvePeersFromWorkspaceRoot),
|
||||
resolvedImporters,
|
||||
peersSuffixMaxLength: opts.peersSuffixMaxLength,
|
||||
})
|
||||
|
||||
const linkedDependenciesByProjectId: Record<string, LinkedDependency[]> = {}
|
||||
|
||||
@@ -102,6 +102,7 @@ export interface ResolveDependenciesOptions {
|
||||
workspacePackages: WorkspacePackages
|
||||
supportedArchitectures?: SupportedArchitectures
|
||||
updateToLatest?: boolean
|
||||
peersSuffixMaxLength: number
|
||||
}
|
||||
|
||||
export interface ResolveDependencyTreeResult {
|
||||
|
||||
@@ -88,6 +88,7 @@ export async function resolvePeers<T extends PartialResolvedPackage> (
|
||||
dedupePeerDependents?: boolean
|
||||
dedupeInjectedDeps?: boolean
|
||||
resolvedImporters: ResolvedImporters
|
||||
peersSuffixMaxLength: number
|
||||
}
|
||||
): Promise<{
|
||||
dependenciesGraph: GenericDependenciesGraphWithResolvedChildren<T>
|
||||
@@ -130,6 +131,7 @@ export async function resolvePeers<T extends PartialResolvedPackage> (
|
||||
peersCache: new Map(),
|
||||
peerDependencyIssues,
|
||||
purePkgs: new Set(),
|
||||
peersSuffixMaxLength: opts.peersSuffixMaxLength,
|
||||
rootDir,
|
||||
virtualStoreDir: opts.virtualStoreDir,
|
||||
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
|
||||
@@ -370,6 +372,7 @@ async function resolvePeersOfNode<T extends PartialResolvedPackage> (
|
||||
purePkgs: Set<PkgIdWithPatchHash> // pure packages are those that don't rely on externally resolved peers
|
||||
rootDir: string
|
||||
lockfileDir: string
|
||||
peersSuffixMaxLength: number
|
||||
}
|
||||
): Promise<PeersResolution & { finishing?: FinishingResolutionPromise, calculateDepPath?: CalculateDepPath }> {
|
||||
const node = ctx.dependenciesTree.get(nodeId)!
|
||||
@@ -509,7 +512,7 @@ async function resolvePeersOfNode<T extends PartialResolvedPackage> (
|
||||
pendingPeerNodeIds.push(peerNodeId)
|
||||
}
|
||||
if (pendingPeerNodeIds.length === 0) {
|
||||
const peersDirSuffix = createPeersDirSuffix(peerIds)
|
||||
const peersDirSuffix = createPeersDirSuffix(peerIds, ctx.peersSuffixMaxLength)
|
||||
addDepPathToGraph(`${resolvedPackage.pkgIdWithPatchHash}${peersDirSuffix}` as DepPath)
|
||||
} else {
|
||||
calculateDepPathIfNeeded = calculateDepPath.bind(null, peerIds, pendingPeerNodeIds)
|
||||
@@ -547,7 +550,7 @@ async function resolvePeersOfNode<T extends PartialResolvedPackage> (
|
||||
return ctx.pathsByNodeIdPromises.get(peerNodeId)!.promise
|
||||
})
|
||||
),
|
||||
])
|
||||
], ctx.peersSuffixMaxLength)
|
||||
addDepPathToGraph(`${resolvedPackage.pkgIdWithPatchHash}${peersDirSuffix}` as DepPath)
|
||||
}
|
||||
|
||||
@@ -732,6 +735,7 @@ async function resolvePeersOfChildren<T extends PartialResolvedPackage> (
|
||||
dependenciesTree: DependenciesTree<T>
|
||||
rootDir: string
|
||||
lockfileDir: string
|
||||
peersSuffixMaxLength: number
|
||||
}
|
||||
): Promise<PeersResolution & { finishing: Promise<void> }> {
|
||||
const allResolvedPeers = new Map<string, NodeId>()
|
||||
|
||||
@@ -101,6 +101,7 @@ test('packages are not deduplicated when versions do not match', async () => {
|
||||
virtualStoreDir: '',
|
||||
virtualStoreDirMaxLength: 120,
|
||||
lockfileDir: '',
|
||||
peersSuffixMaxLength: 1000,
|
||||
})
|
||||
|
||||
expect(dependenciesByProjectId.project1.get('foo')).toEqual(dependenciesByProjectId.project2.get('foo'))
|
||||
|
||||
@@ -105,6 +105,7 @@ test('resolve peer dependencies of cyclic dependencies', async () => {
|
||||
virtualStoreDir: '',
|
||||
lockfileDir: '',
|
||||
virtualStoreDirMaxLength: 120,
|
||||
peersSuffixMaxLength: 1000,
|
||||
})
|
||||
expect(Object.keys(dependenciesGraph)).toStrictEqual([
|
||||
'foo/1.0.0',
|
||||
@@ -208,6 +209,7 @@ test('when a package is referenced twice in the dependencies graph and one of th
|
||||
virtualStoreDir: '',
|
||||
virtualStoreDirMaxLength: 120,
|
||||
lockfileDir: '',
|
||||
peersSuffixMaxLength: 1000,
|
||||
})
|
||||
expect(Object.keys(dependenciesGraph).sort()).toStrictEqual([
|
||||
'bar/1.0.0',
|
||||
@@ -389,6 +391,7 @@ describe('peer dependency issues', () => {
|
||||
virtualStoreDir: '',
|
||||
virtualStoreDirMaxLength: 120,
|
||||
lockfileDir: '',
|
||||
peersSuffixMaxLength: 1000,
|
||||
})).peerDependencyIssuesByProjects
|
||||
})
|
||||
it('should find peer dependency conflicts', () => {
|
||||
@@ -474,6 +477,7 @@ describe('unmet peer dependency issues', () => {
|
||||
virtualStoreDir: '',
|
||||
virtualStoreDirMaxLength: 120,
|
||||
lockfileDir: '',
|
||||
peersSuffixMaxLength: 1000,
|
||||
})).peerDependencyIssuesByProjects
|
||||
})
|
||||
it('should not warn when the found package has prerelease version and the wanted range is *', () => {
|
||||
@@ -546,6 +550,7 @@ describe('unmet peer dependency issue resolved from subdependency', () => {
|
||||
virtualStoreDir: '',
|
||||
virtualStoreDirMaxLength: 120,
|
||||
lockfileDir: '',
|
||||
peersSuffixMaxLength: 1000,
|
||||
})).peerDependencyIssuesByProjects
|
||||
})
|
||||
it('should return from where the bad peer dependency is resolved', () => {
|
||||
@@ -647,6 +652,7 @@ test('resolve peer dependencies with npm aliases', async () => {
|
||||
virtualStoreDir: '',
|
||||
virtualStoreDirMaxLength: 120,
|
||||
lockfileDir: '',
|
||||
peersSuffixMaxLength: 1000,
|
||||
})
|
||||
expect(Object.keys(dependenciesGraph).sort()).toStrictEqual([
|
||||
'bar/1.0.0',
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface StrictStoreStatusOptions {
|
||||
optional: boolean
|
||||
binsDir: string
|
||||
virtualStoreDirMaxLength: number
|
||||
peersSuffixMaxLength: number
|
||||
}
|
||||
|
||||
export type StoreStatusOptions = Partial<StrictStoreStatusOptions> &
|
||||
|
||||
Reference in New Issue
Block a user