fix: preserve version and hasBin for variations packages (#10065)

close #10022
This commit is contained in:
Ryo Matsukawa
2025-10-13 17:00:56 +09:00
committed by GitHub
parent a8797c4e59
commit 9c65b96f2c
3 changed files with 173 additions and 4 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/resolve-dependencies": patch
pnpm: patch
---
Preserve version and hasBin for variations packages [#10022](https://github.com/pnpm/pnpm/issues/10022).

View File

@@ -60,6 +60,8 @@ import { wantedDepIsLocallyAvailable } from './wantedDepIsLocallyAvailable.js'
import { type CatalogLookupMetadata } from './resolveDependencyTree.js'
import { replaceVersionInBareSpecifier } from './replaceVersionInBareSpecifier.js'
export type { WantedDependency }
const dependencyResolvedLogger = logger('_dependency_resolved')
const omitDepsFields = omit(['dependencies', 'optionalDependencies', 'peerDependencies', 'peerDependenciesMeta'])
@@ -1422,7 +1424,7 @@ async function resolveDependency (
let prepare!: boolean
let hasBin!: boolean
let pkg: PackageManifest = getManifestFromResponse(pkgResponse, wantedDependency)
let pkg: PackageManifest = getManifestFromResponse(pkgResponse, wantedDependency, currentPkg)
if (!pkg.dependencies) {
pkg.dependencies = {}
}
@@ -1506,7 +1508,9 @@ async function resolveDependency (
) {
pkg.deprecated = currentPkg.dependencyLockfile.deprecated
}
hasBin = Boolean((pkg.bin && !(pkg.bin === '' || Object.keys(pkg.bin).length === 0)) ?? pkg.directories?.bin)
hasBin = (currentPkg.dependencyLockfile?.hasBin != null && !pkg.bin)
? currentPkg.dependencyLockfile.hasBin
: Boolean((pkg.bin && !(pkg.bin === '' || Object.keys(pkg.bin).length === 0)) ?? pkg.directories?.bin)
}
if (options.currentDepth === 0 && pkgResponse.body.latest && pkgResponse.body.latest !== pkg.version) {
ctx.outdatedDependencies[pkgResponse.body.id] = pkgResponse.body.latest
@@ -1649,11 +1653,19 @@ async function resolveDependency (
}
}
function getManifestFromResponse (
export function getManifestFromResponse (
pkgResponse: PackageResponse,
wantedDependency: WantedDependency
wantedDependency: WantedDependency,
currentPkg?: Partial<InfoFromLockfile>
): PackageManifest {
if (pkgResponse.body.manifest) return pkgResponse.body.manifest
if (currentPkg?.name && currentPkg?.version) {
return {
name: currentPkg.name,
version: currentPkg.version,
}
}
return {
name: wantedDependency.alias ? wantedDependency.alias : wantedDependency.bareSpecifier.split('/').pop()!,
version: '0.0.0',

View File

@@ -0,0 +1,151 @@
import { getManifestFromResponse, type WantedDependency } from '../lib/resolveDependencies.js'
import type { PackageResponse } from '@pnpm/store-controller-types'
test('getManifestFromResponse returns manifest from pkgResponse when available', () => {
const pkgResponse = {
body: {
manifest: {
name: 'foo',
version: '1.0.0',
},
},
} as PackageResponse
const wantedDependency = {
alias: 'foo',
bareSpecifier: 'foo',
dev: false,
optional: false,
} as WantedDependency
const result = getManifestFromResponse(pkgResponse, wantedDependency)
expect(result).toEqual({
name: 'foo',
version: '1.0.0',
})
})
test('getManifestFromResponse returns currentPkg info when manifest is undefined', () => {
const pkgResponse = {
body: {
manifest: undefined,
},
} as PackageResponse
const wantedDependency = {
alias: 'node',
bareSpecifier: 'runtime:^22.0.0',
dev: false,
optional: false,
} as WantedDependency
const currentPkg = {
name: 'node',
version: '22.20.0',
}
const result = getManifestFromResponse(pkgResponse, wantedDependency, currentPkg)
expect(result).toEqual({
name: 'node',
version: '22.20.0',
})
})
test('getManifestFromResponse returns default 0.0.0 when manifest and currentPkg are unavailable', () => {
const pkgResponse = {
body: {
manifest: undefined,
},
} as PackageResponse
const wantedDependency = {
alias: 'foo',
bareSpecifier: 'foo@^1.0.0',
dev: false,
optional: false,
} as WantedDependency
const result = getManifestFromResponse(pkgResponse, wantedDependency)
expect(result).toEqual({
name: 'foo',
version: '0.0.0',
})
})
test('getManifestFromResponse extracts name from bareSpecifier when no alias', () => {
const pkgResponse = {
body: {
manifest: undefined,
},
} as PackageResponse
const wantedDependency = {
bareSpecifier: '@scope/package@^1.0.0',
dev: false,
optional: false,
} as WantedDependency
const result = getManifestFromResponse(pkgResponse, wantedDependency)
expect(result).toEqual({
name: 'package@^1.0.0',
version: '0.0.0',
})
})
test('getManifestFromResponse does not use currentPkg when only name is available', () => {
const pkgResponse = {
body: {
manifest: undefined,
},
} as PackageResponse
const wantedDependency = {
alias: 'foo',
bareSpecifier: 'foo',
dev: false,
optional: false,
} as WantedDependency
const currentPkg = {
name: 'foo',
version: undefined,
}
const result = getManifestFromResponse(pkgResponse, wantedDependency, currentPkg)
expect(result).toEqual({
name: 'foo',
version: '0.0.0',
})
})
test('getManifestFromResponse does not use currentPkg when only version is available', () => {
const pkgResponse = {
body: {
manifest: undefined,
},
} as PackageResponse
const wantedDependency = {
alias: 'foo',
bareSpecifier: 'foo',
dev: false,
optional: false,
} as WantedDependency
const currentPkg = {
name: undefined,
version: '1.0.0',
}
const result = getManifestFromResponse(pkgResponse, wantedDependency, currentPkg)
expect(result).toEqual({
name: 'foo',
version: '0.0.0',
})
})