mirror of
https://github.com/pnpm/pnpm.git
synced 2026-01-08 15:08:27 -05:00
feat: track transitive peer dependencies in the lockfile (#3186)
This commit is contained in:
5
.changeset/afraid-suns-kick.md
Normal file
5
.changeset/afraid-suns-kick.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/constants": major
|
||||
---
|
||||
|
||||
Bump lockfile version to 5.3.
|
||||
6
.changeset/dirty-files-doubt.md
Normal file
6
.changeset/dirty-files-doubt.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/lockfile-types": minor
|
||||
"@pnpm/resolve-dependencies": minor
|
||||
---
|
||||
|
||||
Add new transitivePeerDependencies field to lockfile.
|
||||
@@ -24,7 +24,7 @@
|
||||
"@commitlint/prompt-cli": "^11.0.0",
|
||||
"@pnpm/eslint-config": "workspace:*",
|
||||
"@pnpm/meta-updater": "^0.0.0",
|
||||
"@pnpm/registry-mock": "^2.3.0",
|
||||
"@pnpm/registry-mock": "^2.4.0",
|
||||
"@pnpm/tsconfig": "workspace:*",
|
||||
"@types/jest": "^26.0.19",
|
||||
"@types/node": "^14.14.22",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const WANTED_LOCKFILE = 'pnpm-lock.yaml'
|
||||
export const LOCKFILE_VERSION = 5.2
|
||||
export const LOCKFILE_VERSION = 5.3
|
||||
|
||||
export const ENGINE_NAME = `${process.platform}-${process.arch}-node-${process.version.split('.')[0]}`
|
||||
export const LAYOUT_VERSION = 5
|
||||
|
||||
@@ -78,6 +78,7 @@ export interface PackageSnapshot {
|
||||
optional: true
|
||||
}
|
||||
}
|
||||
transitivePeerDependencies?: string[]
|
||||
bundledDependencies?: string[]
|
||||
engines?: {
|
||||
node: string
|
||||
|
||||
@@ -430,7 +430,13 @@ function getDepsToResolve (
|
||||
const infoFromLockfile = getInfoFromLockfile(wantedLockfile, options.registries, reference, wantedDependency.alias)
|
||||
if (
|
||||
!proceedAll &&
|
||||
(infoFromLockfile?.dependencyLockfile?.peerDependencies != null || !infoFromLockfile)
|
||||
(
|
||||
!infoFromLockfile ||
|
||||
infoFromLockfile.dependencyLockfile != null && (
|
||||
infoFromLockfile.dependencyLockfile.peerDependencies != null ||
|
||||
infoFromLockfile.dependencyLockfile.transitivePeerDependencies?.length
|
||||
)
|
||||
)
|
||||
) {
|
||||
if (infoFromLockfile?.dependencyLockfile?.peerDependencies) {
|
||||
Object.keys(infoFromLockfile.dependencyLockfile.peerDependencies).forEach((peerName) => {
|
||||
|
||||
@@ -24,6 +24,7 @@ export interface GenericDependenciesGraphNode {
|
||||
children: {[alias: string]: string}
|
||||
depth: number
|
||||
peerDependencies?: Dependencies
|
||||
transitivePeerDependencies: Set<string>
|
||||
installable: boolean
|
||||
isBuilt?: boolean
|
||||
isPure: boolean
|
||||
@@ -253,11 +254,15 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
|
||||
if (!ctx.depGraph[depPath] || ctx.depGraph[depPath].depth > node.depth) {
|
||||
const dir = path.join(modules, resolvedPackage.name)
|
||||
|
||||
const unknownPeers = Object.keys(unknownResolvedPeersOfChildren)
|
||||
const transitivePeerDependencies = new Set<string>()
|
||||
const unknownPeers = [
|
||||
...Object.keys(unknownResolvedPeersOfChildren),
|
||||
...missingPeersOfChildren,
|
||||
]
|
||||
if (unknownPeers.length) {
|
||||
for (const unknownPeer of unknownPeers) {
|
||||
if (!peerDependencies[unknownPeer]) {
|
||||
peerDependencies[unknownPeer] = '*'
|
||||
transitivePeerDependencies.add(unknownPeer)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -275,6 +280,7 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
|
||||
isPure,
|
||||
modules,
|
||||
peerDependencies,
|
||||
transitivePeerDependencies,
|
||||
}
|
||||
}
|
||||
return { resolvedPeers: allResolvedPeers, missingPeers: allMissingPeers }
|
||||
|
||||
@@ -51,7 +51,7 @@ export default function (
|
||||
|
||||
function toLockfileDependency (
|
||||
pendingRequiresBuilds: string[],
|
||||
pkg: ResolvedPackage,
|
||||
pkg: ResolvedPackage & { transitivePeerDependencies: Set<string> },
|
||||
opts: {
|
||||
depPath: string
|
||||
registry: string
|
||||
@@ -113,6 +113,9 @@ function toLockfileDependency (
|
||||
if (!R.isEmpty(pkg.peerDependencies ?? {})) {
|
||||
result['peerDependencies'] = pkg.peerDependencies
|
||||
}
|
||||
if (pkg.transitivePeerDependencies.size) {
|
||||
result['transitivePeerDependencies'] = Array.from(pkg.transitivePeerDependencies)
|
||||
}
|
||||
if (pkg.peerDependenciesMeta) {
|
||||
const normalizedPeerDependenciesMeta = {}
|
||||
for (const peer of Object.keys(pkg.peerDependenciesMeta)) {
|
||||
|
||||
@@ -201,6 +201,7 @@ test('warning is not reported if the peer dependency can be required from a node
|
||||
|
||||
test('warning is reported when cannot resolve peer dependency for non-top-level dependency', async () => {
|
||||
prepareEmpty()
|
||||
await addDistTag({ package: 'abc-parent-with-ab', version: '1.0.0', distTag: 'latest' })
|
||||
|
||||
const reporter = sinon.spy()
|
||||
|
||||
@@ -250,6 +251,47 @@ test('top peer dependency is linked on subsequent install', async () => {
|
||||
expect(await exists(path.resolve('node_modules/.pnpm/abc-parent-with-ab@1.0.0_peer-c@1.0.0/node_modules/abc-parent-with-ab'))).toBeTruthy()
|
||||
})
|
||||
|
||||
test('top peer dependency is linked on subsequent install, through transitive peer', async () => {
|
||||
prepareEmpty()
|
||||
|
||||
const manifest = await addDependenciesToPackage({}, ['abc-grand-parent@1.0.0'], await testDefaults())
|
||||
|
||||
await addDependenciesToPackage(manifest, ['peer-c@1.0.0'], await testDefaults())
|
||||
|
||||
expect(await exists(path.resolve('node_modules/.pnpm/abc-grand-parent@1.0.0_peer-c@1.0.0/node_modules/abc-grand-parent'))).toBeTruthy()
|
||||
})
|
||||
|
||||
test('the list of transitive peer dependencies is kept up to date', async () => {
|
||||
const project = prepareEmpty()
|
||||
await addDistTag({ package: 'abc-parent-with-ab', version: '1.0.0', distTag: 'latest' })
|
||||
|
||||
const manifest = await addDependenciesToPackage({}, ['abc-grand-parent@1.0.0', 'peer-c@1.0.0'], await testDefaults())
|
||||
|
||||
await addDistTag({ package: 'abc-parent-with-ab', version: '1.1.0', distTag: 'latest' })
|
||||
|
||||
expect(await exists(path.resolve('node_modules/.pnpm/abc-grand-parent@1.0.0_peer-c@1.0.0/node_modules/abc-grand-parent'))).toBeTruthy()
|
||||
{
|
||||
const lockfile = await project.readLockfile()
|
||||
expect(lockfile.packages['/abc-grand-parent/1.0.0_peer-c@1.0.0'].transitivePeerDependencies).toStrictEqual(['peer-c'])
|
||||
}
|
||||
|
||||
await mutateModules([
|
||||
{
|
||||
buildIndex: 0,
|
||||
manifest,
|
||||
mutation: 'install',
|
||||
rootDir: process.cwd(),
|
||||
},
|
||||
], await testDefaults({ update: true, depth: Infinity }))
|
||||
|
||||
expect(await exists(path.resolve('node_modules/.pnpm/abc-grand-parent@1.0.0/node_modules/abc-grand-parent'))).toBeTruthy()
|
||||
|
||||
{
|
||||
const lockfile = await project.readLockfile()
|
||||
expect(lockfile.packages['/abc-grand-parent/1.0.0'].transitivePeerDependencies).toBeFalsy()
|
||||
}
|
||||
})
|
||||
|
||||
test('top peer dependency is linked on subsequent install. Reverse order', async () => {
|
||||
prepareEmpty()
|
||||
|
||||
@@ -268,6 +310,7 @@ async function okFile (filename: string) {
|
||||
|
||||
// This usecase was failing. See https://github.com/pnpm/supi/issues/15
|
||||
test('peer dependencies are linked when running one named installation', async () => {
|
||||
await addDistTag({ package: 'abc-parent-with-ab', version: '1.0.1', distTag: 'latest' })
|
||||
await addDistTag({ package: 'peer-a', version: '1.0.0', distTag: 'latest' })
|
||||
await addDistTag({ package: 'peer-c', version: '1.0.0', distTag: 'latest' })
|
||||
|
||||
|
||||
2
pnpm-lock.yaml
generated
2
pnpm-lock.yaml
generated
@@ -34,7 +34,7 @@ importers:
|
||||
'@commitlint/prompt-cli': ^11.0.0
|
||||
'@pnpm/eslint-config': workspace:*
|
||||
'@pnpm/meta-updater': ^0.0.0
|
||||
'@pnpm/registry-mock': ^2.3.0
|
||||
'@pnpm/registry-mock': ^2.4.0
|
||||
'@pnpm/tsconfig': workspace:*
|
||||
'@types/jest': ^26.0.19
|
||||
'@types/node': ^14.14.22
|
||||
|
||||
Reference in New Issue
Block a user