feat: track transitive peer dependencies in the lockfile (#3186)

This commit is contained in:
Zoltan Kochan
2021-02-25 21:24:22 +02:00
committed by GitHub
parent 7657c1603c
commit 6871d74b2b
10 changed files with 77 additions and 7 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/constants": major
---
Bump lockfile version to 5.3.

View File

@@ -0,0 +1,6 @@
---
"@pnpm/lockfile-types": minor
"@pnpm/resolve-dependencies": minor
---
Add new transitivePeerDependencies field to lockfile.

View File

@@ -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",

View File

@@ -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

View File

@@ -78,6 +78,7 @@ export interface PackageSnapshot {
optional: true
}
}
transitivePeerDependencies?: string[]
bundledDependencies?: string[]
engines?: {
node: string

View File

@@ -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) => {

View File

@@ -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 }

View File

@@ -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)) {

View File

@@ -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
View File

@@ -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