mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-30 04:52:04 -04:00
fix: resolve peer of peer from the deps of direct dependent package (#7606)
close #7444 Peer dependencies of peer dependencies are now resolved correctly. When peer dependencies have peer dependencies of their own, the peer dependencies are grouped with their own peer dependencies before being linked to their dependents. For instance, if `card` has `react` in peer dependencies and `react` has `typescript` in its peer dependencies, then the same version of `react` may be linked from different places if there are multiple versions of `typescript`. For instance: ``` project1/package.json { "dependencies": { "card": "1.0.0", "react": "16.8.0", "typescript": "7.0.0" } } project2/package.json { "dependencies": { "card": "1.0.0", "react": "16.8.0", "typescript": "8.0.0" } } node_modules .pnpm card@1.0.0(react@16.8.0(typescript@7.0.0)) node_modules card react --> ../../react@16.8.0(typescript@7.0.0)/node_modules/react react@16.8.0(typescript@7.0.0) node_modules react typescript --> ../../typescript@7.0.0/node_modules/typescript typescript@7.0.0 node_modules typescript card@1.0.0(react@16.8.0(typescript@8.0.0)) node_modules card react --> ../../react@16.8.0(typescript@8.0.0)/node_modules/react react@16.8.0(typescript@8.0.0) node_modules react typescript --> ../../typescript@8.0.0/node_modules/typescript typescript@8.0.0 node_modules typescript ``` In the above example, both projects have `card` in dependencies but the projects use different versions of `typescript`. Hence, even though the same version of `card` is used, `card` in `project1` will reference `react` from a directory where it is placed with `typescript@7.0.0` (because it resolves `typescript` from the dependencies of `project1`), while `card` in `project2` will reference `react` with `typescript@8.0.0`.
This commit is contained in:
56
.changeset/plenty-boats-trade.md
Normal file
56
.changeset/plenty-boats-trade.md
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
"@pnpm/resolve-dependencies": major
|
||||
"pnpm": major
|
||||
---
|
||||
|
||||
Peer dependencies of peer dependencies are now resolved correctly. When peer dependencies have peer dependencies of their own, the peer dependencies are grouped with their own peer dependencies before being linked to their dependents.
|
||||
|
||||
For instance, if `card` has `react` in peer dependencies and `react` has `typescript` in its peer dependencies, then the same version of `react` may be linked from different places if there are multiple versions of `typescript`. For instance:
|
||||
|
||||
```
|
||||
project1/package.json
|
||||
{
|
||||
"dependencies": {
|
||||
"card": "1.0.0",
|
||||
"react": "16.8.0",
|
||||
"typescript": "7.0.0"
|
||||
}
|
||||
}
|
||||
project2/package.json
|
||||
{
|
||||
"dependencies": {
|
||||
"card": "1.0.0",
|
||||
"react": "16.8.0",
|
||||
"typescript": "8.0.0"
|
||||
}
|
||||
}
|
||||
node_modules
|
||||
.pnpm
|
||||
card@1.0.0(react@16.8.0(typescript@7.0.0))
|
||||
node_modules
|
||||
card
|
||||
react --> ../../react@16.8.0(typescript@7.0.0)/node_modules/react
|
||||
react@16.8.0(typescript@7.0.0)
|
||||
node_modules
|
||||
react
|
||||
typescript --> ../../typescript@7.0.0/node_modules/typescript
|
||||
typescript@7.0.0
|
||||
node_modules
|
||||
typescript
|
||||
card@1.0.0(react@16.8.0(typescript@8.0.0))
|
||||
node_modules
|
||||
card
|
||||
react --> ../../react@16.8.0(typescript@8.0.0)/node_modules/react
|
||||
react@16.8.0(typescript@8.0.0)
|
||||
node_modules
|
||||
react
|
||||
typescript --> ../../typescript@8.0.0/node_modules/typescript
|
||||
typescript@8.0.0
|
||||
node_modules
|
||||
typescript
|
||||
```
|
||||
|
||||
In the above example, both projects have `card` in dependencies but the projects use different versions of `typescript`. Hence, even though the same version of `card` is used, `card` in `project1` will reference `react` from a directory where it is placed with `typescript@7.0.0` (because it resolves `typescript` from the dependencies of `project1`), while `card` in `project2` will reference `react` with `typescript@8.0.0`.
|
||||
|
||||
Related issue: [#7444](https://github.com/pnpm/pnpm/issues/7444).
|
||||
Related PR: [#7606](https://github.com/pnpm/pnpm/pull/7606).
|
||||
5
.changeset/tidy-grapes-float.md
Normal file
5
.changeset/tidy-grapes-float.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/dependency-path": minor
|
||||
---
|
||||
|
||||
createPeersDirSuffix may accept dep path.
|
||||
@@ -44,7 +44,7 @@
|
||||
"@pnpm/constants": "workspace:*",
|
||||
"@pnpm/lockfile-types": "workspace:*",
|
||||
"@pnpm/modules-yaml": "workspace:*",
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@pnpm/types": "workspace:*",
|
||||
"is-windows": "^1.0.2",
|
||||
"isexe": "2.0.0",
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
"test": "pnpm pretest && pnpm run compile && jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@pnpm/store.cafs": "workspace:*",
|
||||
"path-exists": "^4.0.0"
|
||||
},
|
||||
|
||||
15
deps/graph-sequencer/test/index.ts
vendored
15
deps/graph-sequencer/test/index.ts
vendored
@@ -349,3 +349,18 @@ test('graph with many nodes. Sequencing a subgraph', () => {
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
// TODO: fix this test
|
||||
test.skip('graph with big cycle', () => {
|
||||
expect(graphSequencer(new Map([
|
||||
['a', ['b']],
|
||||
['b', ['a', 'c']],
|
||||
['c', ['a', 'b']],
|
||||
]))).toStrictEqual(
|
||||
{
|
||||
safe: false,
|
||||
chunks: [['a', 'b', 'c']],
|
||||
cycles: [['a', 'b', 'c']],
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"@pnpm/filter-workspace-packages": "workspace:*",
|
||||
"@pnpm/plugin-commands-rebuild": "workspace:*",
|
||||
"@pnpm/prepare": "workspace:*",
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@pnpm/test-fixtures": "workspace:*",
|
||||
"@pnpm/test-ipc-server": "workspace:*",
|
||||
"@types/ramda": "0.28.20",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"@pnpm/filter-workspace-packages": "workspace:*",
|
||||
"@pnpm/plugin-commands-script-runners": "workspace:*",
|
||||
"@pnpm/prepare": "workspace:*",
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@pnpm/test-ipc-server": "workspace:*",
|
||||
"@types/is-windows": "^1.0.2",
|
||||
"@types/ramda": "0.28.20",
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"@commitlint/prompt-cli": "^17.8.1",
|
||||
"@pnpm/eslint-config": "workspace:*",
|
||||
"@pnpm/meta-updater": "1.0.0",
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@pnpm/tsconfig": "workspace:*",
|
||||
"@pnpm/worker": "workspace:*",
|
||||
"@types/jest": "^29.5.8",
|
||||
|
||||
@@ -118,7 +118,19 @@ function depPathToFilenameUnescaped (depPath: string) {
|
||||
return depPath.replace(':', '+')
|
||||
}
|
||||
|
||||
export function createPeersDirSuffix (peers: Array<{ name: string, version: string }>): string {
|
||||
const folderName = peers.map(({ name, version }) => `${name}@${version}`).sort().join(')(')
|
||||
return `(${folderName})`
|
||||
export type PeerId = { name: string, version: string } | string
|
||||
|
||||
export function createPeersDirSuffix (peerIds: PeerId[]): string {
|
||||
const dirName = peerIds.map(
|
||||
(peerId) => {
|
||||
if (typeof peerId !== 'string') {
|
||||
return `${peerId.name}@${peerId.version}`
|
||||
}
|
||||
if (peerId.startsWith('/')) {
|
||||
return peerId.substring(1)
|
||||
}
|
||||
return peerId
|
||||
}
|
||||
).sort().join(')(')
|
||||
return `(${dirName})`
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"@pnpm/filter-workspace-packages": "workspace:*",
|
||||
"@pnpm/plugin-commands-patching": "workspace:*",
|
||||
"@pnpm/prepare": "workspace:*",
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@pnpm/test-fixtures": "workspace:*",
|
||||
"@types/normalize-path": "^3.0.2",
|
||||
"@types/ramda": "0.28.20",
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
"@pnpm/lockfile-types": "workspace:*",
|
||||
"@pnpm/package-store": "workspace:*",
|
||||
"@pnpm/prepare": "workspace:*",
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@pnpm/store-path": "workspace:*",
|
||||
"@pnpm/store.cafs": "workspace:*",
|
||||
"@pnpm/test-fixtures": "workspace:*",
|
||||
|
||||
@@ -143,7 +143,7 @@ test('don\'t install the same missing peer dependency twice', async () => {
|
||||
const lockfile = await project.readLockfile()
|
||||
expect(Object.keys(lockfile.packages).sort()).toStrictEqual([
|
||||
'/@pnpm/y@1.0.0',
|
||||
`/@pnpm.e2e/has-has-y-peer-peer@1.0.0${createPeersDirSuffix([{ name: '@pnpm/y', version: '1.0.0' }, { name: '@pnpm.e2e/has-y-peer', version: '1.0.0' }])}`,
|
||||
'/@pnpm.e2e/has-has-y-peer-peer@1.0.0(@pnpm.e2e/has-y-peer@1.0.0(@pnpm/y@1.0.0))(@pnpm/y@1.0.0)',
|
||||
'/@pnpm.e2e/has-y-peer@1.0.0(@pnpm/y@1.0.0)',
|
||||
].sort())
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import { type RootLog } from '@pnpm/core-loggers'
|
||||
import { depPathToFilename } from '@pnpm/dependency-path'
|
||||
import { prepareEmpty } from '@pnpm/prepare'
|
||||
import {
|
||||
addDependenciesToPackage,
|
||||
@@ -133,14 +134,14 @@ test('a subdependency is from a github repo with different name', async () => {
|
||||
|
||||
const lockfile = await project.readLockfile()
|
||||
expect(lockfile.packages['/@pnpm.e2e/has-aliased-git-dependency@1.0.0'].dependencies).toStrictEqual({
|
||||
'@pnpm.e2e/has-say-hi-peer': '1.0.0(hi@1.0.0)',
|
||||
'@pnpm.e2e/has-say-hi-peer': '1.0.0(github.com/zkochan/hi/4cdebec76b7b9d1f6e219e06c42d92a6b8ea60cd)',
|
||||
'say-hi': 'github.com/zkochan/hi/4cdebec76b7b9d1f6e219e06c42d92a6b8ea60cd',
|
||||
})
|
||||
|
||||
await project.isExecutable('@pnpm.e2e/has-aliased-git-dependency/node_modules/.bin/hi')
|
||||
await project.isExecutable('@pnpm.e2e/has-aliased-git-dependency/node_modules/.bin/szia')
|
||||
|
||||
expect(await exists(path.resolve('node_modules/.pnpm/@pnpm.e2e+has-say-hi-peer@1.0.0_hi@1.0.0/node_modules/@pnpm.e2e/has-say-hi-peer'))).toBeTruthy()
|
||||
expect(await exists(path.resolve(`node_modules/.pnpm/${depPathToFilename('@pnpm.e2e/has-say-hi-peer@1.0.0(github.com/zkochan/hi/4cdebec76b7b9d1f6e219e06c42d92a6b8ea60cd)')}/node_modules/@pnpm.e2e/has-say-hi-peer`))).toBeTruthy()
|
||||
})
|
||||
|
||||
test('from a git repo', async () => {
|
||||
|
||||
@@ -986,10 +986,9 @@ test('peer dependency is resolved from parent package via its alias', async () =
|
||||
}, await testDefaults())
|
||||
|
||||
const lockfile = await readYamlFile<Lockfile>(WANTED_LOCKFILE)
|
||||
const suffix = createPeersDirSuffix([{ name: '@pnpm.e2e/tango-tango', version: '1.0.0' }])
|
||||
expect(Object.keys(lockfile.packages ?? {})).toStrictEqual([
|
||||
`/@pnpm.e2e/has-tango-as-peer-dep@1.0.0${suffix}`,
|
||||
`/@pnpm.e2e/tango-tango@1.0.0${suffix}`,
|
||||
'/@pnpm.e2e/has-tango-as-peer-dep@1.0.0(@pnpm.e2e/tango-tango@1.0.0(@pnpm.e2e/tango-tango@1.0.0))',
|
||||
'/@pnpm.e2e/tango-tango@1.0.0(@pnpm.e2e/tango-tango@1.0.0)',
|
||||
])
|
||||
})
|
||||
|
||||
@@ -1466,8 +1465,7 @@ test('in a subdependency, when there are several aliased dependencies of the sam
|
||||
expect(lockfile.packages['/@pnpm.e2e/abc@1.0.0(@pnpm.e2e/peer-c@2.0.0)']).toBeTruthy()
|
||||
})
|
||||
|
||||
// TODO: fix this test
|
||||
test.skip('peer having peer is resolved correctly', async () => {
|
||||
test('peer having peer is resolved correctly', async () => {
|
||||
const manifest1 = {
|
||||
name: 'project-1',
|
||||
|
||||
@@ -1530,5 +1528,249 @@ test.skip('peer having peer is resolved correctly', async () => {
|
||||
const lockfile = await readYamlFile<any>(path.resolve(WANTED_LOCKFILE)) // eslint-disable-line
|
||||
|
||||
expect(lockfile.importers['project-1'].dependencies?.['@pnpm.e2e/has-has-y-peer-only-as-peer']['version']).not.toEqual(lockfile.importers['project-2'].dependencies?.['@pnpm.e2e/has-has-y-peer-only-as-peer']['version'])
|
||||
expect(lockfile.packages['/@pnpm.e2e/has-has-y-peer-only-as-peer@1.0.0(@pnpm.e2e/has-y-peer@1.0.0)(@pnpm/y@1.0.0)'].transitivePeerDependencies).toEqual(['@pnpm/y'])
|
||||
expect(lockfile.packages['/@pnpm.e2e/has-has-y-peer-only-as-peer@1.0.0(@pnpm.e2e/has-y-peer@1.0.0(@pnpm/y@1.0.0))'].dependencies['@pnpm.e2e/has-y-peer']).toEqual('1.0.0(@pnpm/y@1.0.0)')
|
||||
})
|
||||
|
||||
test('peer having peer is resolved correctly. The peer is also in the dependencies of the dependent package', async () => {
|
||||
const manifest1 = {
|
||||
name: 'project-1',
|
||||
|
||||
dependencies: {
|
||||
'@pnpm.e2e/has-has-y-peer-only-as-peer-and-y': '1.0.0',
|
||||
'@pnpm.e2e/has-y-peer': '1.0.0',
|
||||
'@pnpm/y': '2.0.0',
|
||||
},
|
||||
}
|
||||
const manifest2 = {
|
||||
name: 'project-2',
|
||||
|
||||
dependencies: {
|
||||
'@pnpm.e2e/has-has-y-peer-only-as-peer-and-y': '1.0.0',
|
||||
'@pnpm.e2e/has-y-peer': '1.0.0',
|
||||
'@pnpm/y': '1.0.0',
|
||||
},
|
||||
}
|
||||
preparePackages([
|
||||
{
|
||||
location: 'project-1',
|
||||
package: manifest1,
|
||||
},
|
||||
{
|
||||
location: 'project-2',
|
||||
package: manifest2,
|
||||
},
|
||||
])
|
||||
|
||||
const importers: MutatedProject[] = [
|
||||
{
|
||||
mutation: 'install',
|
||||
rootDir: path.resolve('project-1'),
|
||||
},
|
||||
{
|
||||
mutation: 'install',
|
||||
rootDir: path.resolve('project-2'),
|
||||
},
|
||||
]
|
||||
const allProjects = [
|
||||
{
|
||||
buildIndex: 0,
|
||||
manifest: manifest1,
|
||||
rootDir: path.resolve('project-1'),
|
||||
},
|
||||
{
|
||||
buildIndex: 0,
|
||||
manifest: manifest2,
|
||||
rootDir: path.resolve('project-2'),
|
||||
},
|
||||
]
|
||||
await mutateModules(importers, await testDefaults({
|
||||
allProjects,
|
||||
autoInstallPeers: false,
|
||||
dedupePeerDependents: false,
|
||||
lockfileOnly: true,
|
||||
strictPeerDependencies: false,
|
||||
}))
|
||||
|
||||
const lockfile = await readYamlFile<any>(path.resolve(WANTED_LOCKFILE)) // eslint-disable-line
|
||||
|
||||
expect(lockfile.importers['project-1'].dependencies?.['@pnpm.e2e/has-has-y-peer-only-as-peer-and-y']['version']).toEqual('1.0.0(@pnpm.e2e/has-y-peer@1.0.0(@pnpm/y@2.0.0))')
|
||||
expect(lockfile.importers['project-2'].dependencies?.['@pnpm.e2e/has-has-y-peer-only-as-peer-and-y']['version']).toEqual('1.0.0(@pnpm.e2e/has-y-peer@1.0.0(@pnpm/y@1.0.0))')
|
||||
|
||||
expect(lockfile.packages['/@pnpm.e2e/has-has-y-peer-only-as-peer-and-y@1.0.0(@pnpm.e2e/has-y-peer@1.0.0(@pnpm/y@1.0.0))'].dependencies['@pnpm/y']).toEqual('1.0.0')
|
||||
expect(lockfile.packages['/@pnpm.e2e/has-has-y-peer-only-as-peer-and-y@1.0.0(@pnpm.e2e/has-y-peer@1.0.0(@pnpm/y@2.0.0))'].dependencies['@pnpm/y']).toEqual('1.0.0')
|
||||
|
||||
expect(lockfile.packages['/@pnpm.e2e/has-has-y-peer-only-as-peer-and-y@1.0.0(@pnpm.e2e/has-y-peer@1.0.0(@pnpm/y@1.0.0))'].dependencies['@pnpm.e2e/has-y-peer']).toEqual('1.0.0(@pnpm/y@1.0.0)')
|
||||
expect(lockfile.packages['/@pnpm.e2e/has-has-y-peer-only-as-peer-and-y@1.0.0(@pnpm.e2e/has-y-peer@1.0.0(@pnpm/y@2.0.0))'].dependencies['@pnpm.e2e/has-y-peer']).toEqual('1.0.0(@pnpm/y@2.0.0)')
|
||||
})
|
||||
|
||||
test('peer having peer is resolved correctly. The peer is also in the dependencies of the dependent package. Test #2', async () => {
|
||||
const manifest1 = {
|
||||
name: 'project-1',
|
||||
|
||||
dependencies: {
|
||||
'@pnpm.e2e/has-has-y-peer-only-as-peer-and-y': '2.0.0',
|
||||
'@pnpm.e2e/has-y-peer': '1.0.0',
|
||||
'@pnpm/y': '1.0.0',
|
||||
},
|
||||
}
|
||||
const manifest2 = {
|
||||
name: 'project-2',
|
||||
|
||||
dependencies: {
|
||||
'@pnpm.e2e/has-has-y-peer-only-as-peer-and-y': '1.0.0',
|
||||
'@pnpm.e2e/has-y-peer': '1.0.0',
|
||||
'@pnpm/y': '2.0.0',
|
||||
},
|
||||
}
|
||||
preparePackages([
|
||||
{
|
||||
location: 'project-1',
|
||||
package: manifest1,
|
||||
},
|
||||
{
|
||||
location: 'project-2',
|
||||
package: manifest2,
|
||||
},
|
||||
])
|
||||
|
||||
const importers: MutatedProject[] = [
|
||||
{
|
||||
mutation: 'install',
|
||||
rootDir: path.resolve('project-1'),
|
||||
},
|
||||
{
|
||||
mutation: 'install',
|
||||
rootDir: path.resolve('project-2'),
|
||||
},
|
||||
]
|
||||
const allProjects = [
|
||||
{
|
||||
buildIndex: 0,
|
||||
manifest: manifest1,
|
||||
rootDir: path.resolve('project-1'),
|
||||
},
|
||||
{
|
||||
buildIndex: 0,
|
||||
manifest: manifest2,
|
||||
rootDir: path.resolve('project-2'),
|
||||
},
|
||||
]
|
||||
await mutateModules(importers, await testDefaults({
|
||||
allProjects,
|
||||
autoInstallPeers: false,
|
||||
dedupePeerDependents: false,
|
||||
lockfileOnly: true,
|
||||
strictPeerDependencies: false,
|
||||
}))
|
||||
|
||||
const lockfile = await readYamlFile<any>(path.resolve(WANTED_LOCKFILE)) // eslint-disable-line
|
||||
|
||||
expect(lockfile.packages['/@pnpm.e2e/has-has-y-peer-only-as-peer-and-y@1.0.0(@pnpm.e2e/has-y-peer@1.0.0(@pnpm/y@2.0.0))'].dependencies['@pnpm.e2e/has-y-peer']).toEqual('1.0.0(@pnpm/y@2.0.0)')
|
||||
expect(lockfile.packages['/@pnpm.e2e/has-has-y-peer-only-as-peer-and-y@2.0.0(@pnpm.e2e/has-y-peer@1.0.0(@pnpm/y@1.0.0))'].dependencies['@pnpm.e2e/has-y-peer']).toEqual('1.0.0(@pnpm/y@1.0.0)')
|
||||
})
|
||||
|
||||
test('resolve peer of peer from the dependencies of the direct dependent package', async () => {
|
||||
const project = prepareEmpty()
|
||||
await addDependenciesToPackage({}, ['@pnpm.e2e/has-has-y-peer-only-as-peer-and-y@1.0.0', '@pnpm/y@2.0.0'], await testDefaults())
|
||||
|
||||
const lockfile = await project.readLockfile()
|
||||
|
||||
expect(lockfile.dependencies['@pnpm.e2e/has-has-y-peer-only-as-peer-and-y'].version).toBe('1.0.0(@pnpm.e2e/has-y-peer@1.0.0(@pnpm/y@2.0.0))')
|
||||
// Even though @pnpm/y@1.0.0 is in the dependencies of the direct dependent package, we resolve y from above.
|
||||
// It might make sense to print a warning in this case and suggest to make y a peer dependency in the dependent package too.
|
||||
expect(lockfile.packages['/@pnpm.e2e/has-has-y-peer-only-as-peer-and-y@1.0.0(@pnpm.e2e/has-y-peer@1.0.0(@pnpm/y@2.0.0))'].dependencies?.['@pnpm.e2e/has-y-peer']).toBe('1.0.0(@pnpm/y@2.0.0)')
|
||||
})
|
||||
|
||||
test('2 circular peers', async () => {
|
||||
const project = prepareEmpty()
|
||||
await addDependenciesToPackage({}, ['@pnpm.e2e/circular-peer-a@1.0.0', '@pnpm.e2e/circular-peer-b@1.0.0'], await testDefaults())
|
||||
|
||||
const lockfile = await project.readLockfile()
|
||||
|
||||
expect(lockfile.dependencies['@pnpm.e2e/circular-peer-a'].version).toBe('1.0.0(@pnpm.e2e/circular-peer-b@1.0.0)')
|
||||
expect(lockfile.dependencies['@pnpm.e2e/circular-peer-b'].version).toBe('1.0.0(@pnpm.e2e/circular-peer-a@1.0.0)')
|
||||
})
|
||||
|
||||
test('3 circular peers', async () => {
|
||||
const project = prepareEmpty()
|
||||
await addDependenciesToPackage({}, [
|
||||
'@pnpm.e2e/circular-peers-1-of-3@1.0.0',
|
||||
'@pnpm.e2e/circular-peers-2-of-3@1.0.0',
|
||||
'@pnpm.e2e/circular-peers-3-of-3@1.0.0',
|
||||
'@pnpm.e2e/peer-a@1.0.0',
|
||||
], await testDefaults())
|
||||
|
||||
const lockfile = await project.readLockfile()
|
||||
|
||||
expect(lockfile.dependencies['@pnpm.e2e/circular-peers-1-of-3'].version).toBe('1.0.0(@pnpm.e2e/circular-peers-2-of-3@1.0.0)(@pnpm.e2e/peer-a@1.0.0)')
|
||||
expect(lockfile.dependencies['@pnpm.e2e/circular-peers-2-of-3'].version).toBe('1.0.0(@pnpm.e2e/circular-peers-3-of-3@1.0.0)(@pnpm.e2e/peer-a@1.0.0)(@pnpm.e2e/peer-b@1.0.0)')
|
||||
expect(lockfile.dependencies['@pnpm.e2e/circular-peers-3-of-3'].version).toBe('1.0.0(@pnpm.e2e/circular-peers-1-of-3@1.0.0)')
|
||||
})
|
||||
|
||||
test('3 circular peers in workspace root', async () => {
|
||||
const projects = preparePackages([
|
||||
{
|
||||
location: '.',
|
||||
package: { name: 'root' },
|
||||
},
|
||||
{
|
||||
location: 'pkg',
|
||||
package: {},
|
||||
},
|
||||
])
|
||||
const allProjects: ProjectOptions[] = [
|
||||
{
|
||||
buildIndex: 0,
|
||||
manifest: {
|
||||
name: 'root',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'@pnpm.e2e/circular-peers-1-of-3': '1.0.0',
|
||||
'@pnpm.e2e/circular-peers-2-of-3': '1.0.0',
|
||||
'@pnpm.e2e/circular-peers-3-of-3': '1.0.0',
|
||||
'@pnpm.e2e/peer-a': '1.0.0',
|
||||
},
|
||||
},
|
||||
rootDir: process.cwd(),
|
||||
},
|
||||
{
|
||||
buildIndex: 0,
|
||||
manifest: {
|
||||
name: 'pkg',
|
||||
version: '1.0.0',
|
||||
|
||||
dependencies: {
|
||||
'@pnpm.e2e/circular-peers-1-of-3': '1.0.0',
|
||||
},
|
||||
},
|
||||
rootDir: path.resolve('pkg'),
|
||||
},
|
||||
]
|
||||
const reporter = jest.fn()
|
||||
await mutateModules([
|
||||
{
|
||||
mutation: 'install',
|
||||
rootDir: path.resolve('pkg'),
|
||||
},
|
||||
{
|
||||
mutation: 'install',
|
||||
rootDir: process.cwd(),
|
||||
},
|
||||
], await testDefaults({ allProjects, reporter, autoInstallPeers: false, resolvePeersFromWorkspaceRoot: true, strictPeerDependencies: false }))
|
||||
|
||||
const lockfile = await projects.root.readLockfile()
|
||||
expect(lockfile.importers.pkg?.dependencies?.['@pnpm.e2e/circular-peers-1-of-3'].version).toBe('1.0.0(@pnpm.e2e/circular-peers-2-of-3@1.0.0(@pnpm.e2e/circular-peers-3-of-3@1.0.0)(@pnpm.e2e/peer-a@1.0.0))(@pnpm.e2e/peer-a@1.0.0)')
|
||||
})
|
||||
|
||||
test('resolves complex circular deps', async () => {
|
||||
prepareEmpty()
|
||||
await addDependenciesToPackage({}, [
|
||||
'@pnpm.e2e/complex-circular-peers-a@1.0.0',
|
||||
'@pnpm.e2e/complex-circular-peers-b@1.0.0',
|
||||
'@pnpm.e2e/complex-circular-peers-c@1.0.0',
|
||||
], await testDefaults({
|
||||
autoInstallPeers: false,
|
||||
}))
|
||||
// it doesn't hang
|
||||
})
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"@pnpm/package-store": "workspace:*",
|
||||
"@pnpm/prepare": "workspace:*",
|
||||
"@pnpm/read-projects-context": "workspace:*",
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@pnpm/store-path": "workspace:*",
|
||||
"@pnpm/store.cafs": "workspace:*",
|
||||
"@pnpm/test-fixtures": "workspace:*",
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
"@pnpm/client": "workspace:*",
|
||||
"@pnpm/create-cafs-store": "workspace:*",
|
||||
"@pnpm/package-requester": "workspace:*",
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@pnpm/test-fixtures": "workspace:*",
|
||||
"@types/normalize-path": "^3.0.2",
|
||||
"@types/ramda": "0.28.20",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"@pnpm/modules-yaml": "workspace:*",
|
||||
"@pnpm/plugin-commands-installation": "workspace:*",
|
||||
"@pnpm/prepare": "workspace:*",
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@pnpm/test-fixtures": "workspace:*",
|
||||
"@pnpm/test-ipc-server": "workspace:*",
|
||||
"@types/proxyquire": "^1.3.31",
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
"@yarnpkg/core": "4.0.2",
|
||||
"filenamify": "^4.3.0",
|
||||
"get-npm-tarball-url": "^2.1.0",
|
||||
"graph-cycles": "1.2.1",
|
||||
"is-inner-link": "^4.0.0",
|
||||
"is-subdir": "^1.2.0",
|
||||
"normalize-path": "^3.0.0",
|
||||
|
||||
@@ -12,7 +12,7 @@ export function depPathToRef (
|
||||
|
||||
if (depPath[0] === '/' && opts.alias === opts.realName) {
|
||||
const ref = depPath.replace(`/${opts.realName}@`, '')
|
||||
if (!ref.includes('/') || !ref.replace(/(\([^)]+\))+$/, '').includes('/')) return ref
|
||||
return ref
|
||||
}
|
||||
return depPath
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ export async function resolveDependencies (
|
||||
dependenciesGraph,
|
||||
dependenciesByProjectId,
|
||||
peerDependencyIssuesByProjects,
|
||||
} = resolvePeers({
|
||||
} = await resolvePeers({
|
||||
dependenciesTree,
|
||||
dedupePeerDependents: opts.dedupePeerDependents,
|
||||
dedupeInjectedDeps: opts.dedupeInjectedDeps,
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import filenamify from 'filenamify'
|
||||
import { analyzeGraph, type Graph } from 'graph-cycles'
|
||||
import path from 'path'
|
||||
import pDefer from 'p-defer'
|
||||
import semver from 'semver'
|
||||
import { semverUtils } from '@yarnpkg/core'
|
||||
import type {
|
||||
PeerDependencyIssues,
|
||||
PeerDependencyIssuesByProjects,
|
||||
} from '@pnpm/types'
|
||||
import { depPathToFilename, createPeersDirSuffix } from '@pnpm/dependency-path'
|
||||
import { depPathToFilename, createPeersDirSuffix, type PeerId } from '@pnpm/dependency-path'
|
||||
import mapValues from 'ramda/src/map'
|
||||
import partition from 'ramda/src/partition'
|
||||
import pick from 'ramda/src/pick'
|
||||
@@ -59,7 +61,7 @@ export interface ProjectToResolve {
|
||||
|
||||
export type DependenciesByProjectId = Record<string, Record<string, string>>
|
||||
|
||||
export function resolvePeers<T extends PartialResolvedPackage> (
|
||||
export async function resolvePeers<T extends PartialResolvedPackage> (
|
||||
opts: {
|
||||
projects: ProjectToResolve[]
|
||||
dependenciesTree: DependenciesTree<T>
|
||||
@@ -70,30 +72,39 @@ export function resolvePeers<T extends PartialResolvedPackage> (
|
||||
dedupeInjectedDeps?: boolean
|
||||
resolvedImporters: ResolvedImporters
|
||||
}
|
||||
): {
|
||||
): Promise<{
|
||||
dependenciesGraph: GenericDependenciesGraph<T>
|
||||
dependenciesByProjectId: DependenciesByProjectId
|
||||
peerDependencyIssuesByProjects: PeerDependencyIssuesByProjects
|
||||
} {
|
||||
}> {
|
||||
const depGraph: GenericDependenciesGraph<T> = {}
|
||||
const pathsByNodeId = new Map<string, string>()
|
||||
const pathsByNodeIdPromises = new Map<string, pDefer.DeferredPromise<string>>()
|
||||
const depPathsByPkgId = new Map<string, Set<string>>()
|
||||
const _createPkgsByName = createPkgsByName.bind(null, opts.dependenciesTree)
|
||||
const rootPkgsByName = opts.resolvePeersFromWorkspaceRoot ? getRootPkgsByName(opts.dependenciesTree, opts.projects) : {}
|
||||
const peerDependencyIssuesByProjects: PeerDependencyIssuesByProjects = {}
|
||||
|
||||
const finishingList: FinishingResolutionPromise[] = []
|
||||
for (const { directNodeIdsByAlias, topParents, rootDir, id } of opts.projects) {
|
||||
const peerDependencyIssues: Pick<PeerDependencyIssues, 'bad' | 'missing'> = { bad: {}, missing: {} }
|
||||
const pkgsByName = {
|
||||
...rootPkgsByName,
|
||||
..._createPkgsByName({ directNodeIdsByAlias, topParents }),
|
||||
}
|
||||
for (const { nodeId } of Object.values(pkgsByName)) {
|
||||
if (nodeId && !pathsByNodeIdPromises.has(nodeId)) {
|
||||
pathsByNodeIdPromises.set(nodeId, pDefer())
|
||||
}
|
||||
}
|
||||
|
||||
resolvePeersOfChildren(directNodeIdsByAlias, pkgsByName, {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const { finishing } = await resolvePeersOfChildren(directNodeIdsByAlias, pkgsByName, {
|
||||
dependenciesTree: opts.dependenciesTree,
|
||||
depGraph,
|
||||
lockfileDir: opts.lockfileDir,
|
||||
pathsByNodeId,
|
||||
pathsByNodeIdPromises,
|
||||
depPathsByPkgId,
|
||||
peersCache: new Map(),
|
||||
peerDependencyIssues,
|
||||
@@ -101,6 +112,9 @@ export function resolvePeers<T extends PartialResolvedPackage> (
|
||||
rootDir,
|
||||
virtualStoreDir: opts.virtualStoreDir,
|
||||
})
|
||||
if (finishing) {
|
||||
finishingList.push(finishing)
|
||||
}
|
||||
if (Object.keys(peerDependencyIssues.bad).length > 0 || Object.keys(peerDependencyIssues.missing).length > 0) {
|
||||
peerDependencyIssuesByProjects[id] = {
|
||||
...peerDependencyIssues,
|
||||
@@ -108,6 +122,7 @@ export function resolvePeers<T extends PartialResolvedPackage> (
|
||||
}
|
||||
}
|
||||
}
|
||||
await Promise.all(finishingList)
|
||||
|
||||
Object.values(depGraph).forEach((node) => {
|
||||
node.children = mapValues((childNodeId) => pathsByNodeId.get(childNodeId) ?? childNodeId, node.children)
|
||||
@@ -261,7 +276,7 @@ function createPkgsByName<T extends PartialResolvedPackage> (
|
||||
}
|
||||
|
||||
interface PeersCacheItem {
|
||||
depPath: string
|
||||
depPath: pDefer.DeferredPromise<string>
|
||||
resolvedPeers: Map<string, string>
|
||||
missingPeers: Set<string>
|
||||
}
|
||||
@@ -275,10 +290,14 @@ interface PeersResolution {
|
||||
|
||||
interface ResolvePeersContext {
|
||||
pathsByNodeId: Map<string, string>
|
||||
pathsByNodeIdPromises: Map<string, pDefer.DeferredPromise<string>>
|
||||
depPathsByPkgId?: Map<string, Set<string>>
|
||||
}
|
||||
|
||||
function resolvePeersOfNode<T extends PartialResolvedPackage> (
|
||||
type CalculateDepPath = (cycles: string[][]) => Promise<void>
|
||||
type FinishingResolutionPromise = Promise<void>
|
||||
|
||||
async function resolvePeersOfNode<T extends PartialResolvedPackage> (
|
||||
nodeId: string,
|
||||
parentParentPkgs: ParentRefs,
|
||||
ctx: ResolvePeersContext & {
|
||||
@@ -291,7 +310,7 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
|
||||
rootDir: string
|
||||
lockfileDir: string
|
||||
}
|
||||
): PeersResolution {
|
||||
): Promise<PeersResolution & { finishing?: FinishingResolutionPromise, calculateDepPath?: CalculateDepPath }> {
|
||||
const node = ctx.dependenciesTree.get(nodeId)!
|
||||
if (node.depth === -1) return { resolvedPeers: new Map<string, string>(), missingPeers: new Set<string>() }
|
||||
const resolvedPackage = node.resolvedPackage as T
|
||||
@@ -301,6 +320,7 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
|
||||
Object.keys(resolvedPackage.peerDependencies).length === 0
|
||||
) {
|
||||
ctx.pathsByNodeId.set(nodeId, resolvedPackage.depPath)
|
||||
ctx.pathsByNodeIdPromises.get(nodeId)!.resolve(resolvedPackage.depPath)
|
||||
return { resolvedPeers: new Map<string, string>(), missingPeers: new Set<string>() }
|
||||
}
|
||||
if (typeof node.children === 'function') {
|
||||
@@ -326,7 +346,7 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
|
||||
if (parentPkgNodeId === cachedNodeId) continue
|
||||
if (
|
||||
ctx.pathsByNodeId.has(cachedNodeId) &&
|
||||
ctx.pathsByNodeId.get(cachedNodeId) === ctx.pathsByNodeId.get(parentPkgNodeId)
|
||||
ctx.pathsByNodeId.get(cachedNodeId) === ctx.pathsByNodeId.get(parentPkgNodeId)
|
||||
) continue
|
||||
if (!ctx.dependenciesTree.has(parentPkgNodeId) && parentPkgNodeId.startsWith('link:')) {
|
||||
return false
|
||||
@@ -342,10 +362,14 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
|
||||
return true
|
||||
})
|
||||
if (hit != null) {
|
||||
ctx.pathsByNodeId.set(nodeId, hit.depPath)
|
||||
ctx.depGraph[hit.depPath].depth = Math.min(ctx.depGraph[hit.depPath].depth, node.depth)
|
||||
return {
|
||||
missingPeers: hit.missingPeers,
|
||||
finishing: (async () => {
|
||||
const depPath = await hit.depPath.promise
|
||||
ctx.pathsByNodeId.set(nodeId, depPath)
|
||||
ctx.depGraph[depPath].depth = Math.min(ctx.depGraph[depPath].depth, node.depth)
|
||||
ctx.pathsByNodeIdPromises.get(nodeId)!.resolve(depPath)
|
||||
})(),
|
||||
resolvedPeers: hit.resolvedPeers,
|
||||
}
|
||||
}
|
||||
@@ -353,7 +377,8 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
|
||||
const {
|
||||
resolvedPeers: unknownResolvedPeersOfChildren,
|
||||
missingPeers: missingPeersOfChildren,
|
||||
} = resolvePeersOfChildren(children, parentPkgs, ctx)
|
||||
finishing,
|
||||
} = await resolvePeersOfChildren(children, parentPkgs, ctx)
|
||||
|
||||
const { resolvedPeers, missingPeers } = Object.keys(resolvedPackage.peerDependencies).length === 0
|
||||
? { resolvedPeers: new Map<string, string>(), missingPeers: new Set<string>() }
|
||||
@@ -382,35 +407,14 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
|
||||
allMissingPeers.add(peer)
|
||||
}
|
||||
|
||||
let depPath: string
|
||||
if (allResolvedPeers.size === 0) {
|
||||
depPath = resolvedPackage.depPath
|
||||
} else {
|
||||
const peersFolderSuffix = createPeersDirSuffix(
|
||||
[...allResolvedPeers.entries()]
|
||||
.map(([alias, nodeId]) => {
|
||||
if (nodeId.startsWith('link:')) {
|
||||
const linkedDir = nodeId.slice(5)
|
||||
return {
|
||||
name: alias,
|
||||
version: filenamify(linkedDir, { replacement: '+' }),
|
||||
}
|
||||
}
|
||||
const { name, version } = ctx.dependenciesTree.get(nodeId)!.resolvedPackage
|
||||
return { name, version }
|
||||
})
|
||||
)
|
||||
depPath = `${resolvedPackage.depPath}${peersFolderSuffix}`
|
||||
}
|
||||
const localLocation = path.join(ctx.virtualStoreDir, depPathToFilename(depPath))
|
||||
const modules = path.join(localLocation, 'node_modules')
|
||||
let cache: PeersCacheItem
|
||||
const isPure = allResolvedPeers.size === 0 && allMissingPeers.size === 0
|
||||
if (isPure) {
|
||||
ctx.purePkgs.add(resolvedPackage.depPath)
|
||||
} else {
|
||||
const cache = {
|
||||
cache = {
|
||||
missingPeers: allMissingPeers,
|
||||
depPath,
|
||||
depPath: pDefer(),
|
||||
resolvedPeers: allResolvedPeers,
|
||||
}
|
||||
if (ctx.peersCache.has(resolvedPackage.depPath)) {
|
||||
@@ -420,49 +424,117 @@ function resolvePeersOfNode<T extends PartialResolvedPackage> (
|
||||
}
|
||||
}
|
||||
|
||||
ctx.pathsByNodeId.set(nodeId, depPath)
|
||||
if (ctx.depPathsByPkgId != null) {
|
||||
if (!ctx.depPathsByPkgId.has(resolvedPackage.depPath)) {
|
||||
ctx.depPathsByPkgId.set(resolvedPackage.depPath, new Set())
|
||||
let calculateDepPathIfNeeded: CalculateDepPath | undefined
|
||||
if (allResolvedPeers.size === 0) {
|
||||
addDepPathToGraph(resolvedPackage.depPath)
|
||||
} else {
|
||||
const peerIds: PeerId[] = []
|
||||
const pendingPeerNodeIds: string[] = []
|
||||
for (const [alias, peerNodeId] of allResolvedPeers.entries()) {
|
||||
if (peerNodeId.startsWith('link:')) {
|
||||
const linkedDir = peerNodeId.slice(5)
|
||||
peerIds.push({
|
||||
name: alias,
|
||||
version: filenamify(linkedDir, { replacement: '+' }),
|
||||
})
|
||||
continue
|
||||
}
|
||||
const peerDepPath = ctx.pathsByNodeId.get(peerNodeId)
|
||||
if (peerDepPath) {
|
||||
peerIds.push(peerDepPath)
|
||||
continue
|
||||
}
|
||||
pendingPeerNodeIds.push(peerNodeId)
|
||||
}
|
||||
if (!ctx.depPathsByPkgId.get(resolvedPackage.depPath)!.has(depPath)) {
|
||||
ctx.depPathsByPkgId.get(resolvedPackage.depPath)!.add(depPath)
|
||||
if (pendingPeerNodeIds.length === 0) {
|
||||
const peersDirSuffix = createPeersDirSuffix(peerIds)
|
||||
addDepPathToGraph(`${resolvedPackage.depPath}${peersDirSuffix}`)
|
||||
} else {
|
||||
calculateDepPathIfNeeded = calculateDepPath.bind(null, peerIds, pendingPeerNodeIds)
|
||||
}
|
||||
}
|
||||
const peerDependencies = { ...resolvedPackage.peerDependencies }
|
||||
if (!ctx.depGraph[depPath] || ctx.depGraph[depPath].depth > node.depth) {
|
||||
const dir = path.join(modules, resolvedPackage.name)
|
||||
|
||||
const transitivePeerDependencies = new Set<string>()
|
||||
for (const unknownPeer of allResolvedPeers.keys()) {
|
||||
if (!peerDependencies[unknownPeer]) {
|
||||
transitivePeerDependencies.add(unknownPeer)
|
||||
return {
|
||||
resolvedPeers: allResolvedPeers,
|
||||
missingPeers: allMissingPeers,
|
||||
calculateDepPath: calculateDepPathIfNeeded,
|
||||
finishing,
|
||||
}
|
||||
|
||||
async function calculateDepPath (
|
||||
peerIds: PeerId[],
|
||||
pendingPeerNodeIds: string[],
|
||||
cycles: string[][]
|
||||
) {
|
||||
const cyclicPeerNodeIds = new Set()
|
||||
for (const cycle of cycles) {
|
||||
if (cycle.includes(nodeId)) {
|
||||
for (const peerNodeId of cycle) {
|
||||
cyclicPeerNodeIds.add(peerNodeId)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const unknownPeer of missingPeersOfChildren) {
|
||||
if (!peerDependencies[unknownPeer]) {
|
||||
transitivePeerDependencies.add(unknownPeer)
|
||||
}
|
||||
}
|
||||
ctx.depGraph[depPath] = {
|
||||
...(node.resolvedPackage as T),
|
||||
children: Object.assign(
|
||||
getPreviouslyResolvedChildren(nodeId, ctx.dependenciesTree),
|
||||
children,
|
||||
Object.fromEntries(resolvedPeers.entries())
|
||||
const peersDirSuffix = createPeersDirSuffix([
|
||||
...peerIds,
|
||||
...await Promise.all(pendingPeerNodeIds
|
||||
.map(async (peerNodeId) => {
|
||||
if (cyclicPeerNodeIds.has(peerNodeId)) {
|
||||
const { name, version } = (ctx.dependenciesTree.get(peerNodeId)!.resolvedPackage as T)
|
||||
return `${name}@${version}`
|
||||
}
|
||||
return ctx.pathsByNodeIdPromises.get(peerNodeId)!.promise
|
||||
})
|
||||
),
|
||||
depPath,
|
||||
depth: node.depth,
|
||||
dir,
|
||||
installable: node.installable,
|
||||
isPure,
|
||||
modules,
|
||||
peerDependencies,
|
||||
transitivePeerDependencies,
|
||||
resolvedPeerNames: new Set(allResolvedPeers.keys()),
|
||||
])
|
||||
addDepPathToGraph(`${resolvedPackage.depPath}${peersDirSuffix}`)
|
||||
}
|
||||
|
||||
function addDepPathToGraph (depPath: string) {
|
||||
cache?.depPath.resolve(depPath)
|
||||
ctx.pathsByNodeId.set(nodeId, depPath)
|
||||
ctx.pathsByNodeIdPromises.get(nodeId)!.resolve(depPath)
|
||||
if (ctx.depPathsByPkgId != null) {
|
||||
if (!ctx.depPathsByPkgId.has(resolvedPackage.depPath)) {
|
||||
ctx.depPathsByPkgId.set(resolvedPackage.depPath, new Set([depPath]))
|
||||
} else {
|
||||
ctx.depPathsByPkgId.get(resolvedPackage.depPath)!.add(depPath)
|
||||
}
|
||||
}
|
||||
const peerDependencies = { ...resolvedPackage.peerDependencies }
|
||||
if (!ctx.depGraph[depPath] || ctx.depGraph[depPath].depth > node.depth) {
|
||||
const modules = path.join(ctx.virtualStoreDir, depPathToFilename(depPath), 'node_modules')
|
||||
const dir = path.join(modules, resolvedPackage.name)
|
||||
|
||||
const transitivePeerDependencies = new Set<string>()
|
||||
for (const unknownPeer of allResolvedPeers.keys()) {
|
||||
if (!peerDependencies[unknownPeer]) {
|
||||
transitivePeerDependencies.add(unknownPeer)
|
||||
}
|
||||
}
|
||||
for (const unknownPeer of missingPeersOfChildren) {
|
||||
if (!peerDependencies[unknownPeer]) {
|
||||
transitivePeerDependencies.add(unknownPeer)
|
||||
}
|
||||
}
|
||||
ctx.depGraph[depPath] = {
|
||||
...(node.resolvedPackage as T),
|
||||
children: Object.assign(
|
||||
getPreviouslyResolvedChildren(nodeId, ctx.dependenciesTree),
|
||||
children,
|
||||
Object.fromEntries(resolvedPeers.entries())
|
||||
),
|
||||
depPath,
|
||||
depth: node.depth,
|
||||
dir,
|
||||
installable: node.installable,
|
||||
isPure,
|
||||
modules,
|
||||
peerDependencies,
|
||||
transitivePeerDependencies,
|
||||
resolvedPeerNames: new Set(allResolvedPeers.keys()),
|
||||
}
|
||||
}
|
||||
}
|
||||
return { resolvedPeers: allResolvedPeers, missingPeers: allMissingPeers }
|
||||
}
|
||||
|
||||
// When a package has itself in the subdependencies, so there's a cycle,
|
||||
@@ -495,7 +567,7 @@ function getPreviouslyResolvedChildren<T extends PartialResolvedPackage> (nodeId
|
||||
return allChildren
|
||||
}
|
||||
|
||||
function resolvePeersOfChildren<T extends PartialResolvedPackage> (
|
||||
async function resolvePeersOfChildren<T extends PartialResolvedPackage> (
|
||||
children: {
|
||||
[alias: string]: string
|
||||
},
|
||||
@@ -510,24 +582,53 @@ function resolvePeersOfChildren<T extends PartialResolvedPackage> (
|
||||
rootDir: string
|
||||
lockfileDir: string
|
||||
}
|
||||
): PeersResolution {
|
||||
): Promise<PeersResolution & { finishing: Promise<void> }> {
|
||||
const allResolvedPeers = new Map<string, string>()
|
||||
const allMissingPeers = new Set<string>()
|
||||
|
||||
// Partition children based on whether they're repeated in parentPkgs.
|
||||
// This impacts the efficiency of graph traversal and prevents potential out-of-memory errors.mes can even lead to out-of-memory exceptions.
|
||||
const [repeated, notRepeated] = partition(([alias]) => parentPkgs[alias] != null, Object.entries(children))
|
||||
const nodeIds = [...notRepeated, ...repeated]
|
||||
|
||||
for (const [,nodeId] of nodeIds) {
|
||||
if (!ctx.pathsByNodeIdPromises.has(nodeId)) {
|
||||
ctx.pathsByNodeIdPromises.set(nodeId, pDefer())
|
||||
}
|
||||
}
|
||||
|
||||
// Resolving non-repeated nodes before repeated nodes proved to be slightly faster.
|
||||
for (const [, childNodeId] of [...notRepeated, ...repeated]) {
|
||||
const { resolvedPeers, missingPeers } = resolvePeersOfNode(childNodeId, parentPkgs, ctx)
|
||||
for (const [k, v] of resolvedPeers) {
|
||||
allResolvedPeers.set(k, v)
|
||||
const calculateDepPaths: CalculateDepPath[] = []
|
||||
const graph = []
|
||||
const finishingList: FinishingResolutionPromise[] = []
|
||||
for (const [, childNodeId] of nodeIds) {
|
||||
const {
|
||||
resolvedPeers,
|
||||
missingPeers,
|
||||
calculateDepPath,
|
||||
finishing,
|
||||
} = await resolvePeersOfNode(childNodeId, parentPkgs, ctx) // eslint-disable-line no-await-in-loop
|
||||
if (finishing) {
|
||||
finishingList.push(finishing)
|
||||
}
|
||||
if (calculateDepPath) {
|
||||
calculateDepPaths.push(calculateDepPath)
|
||||
}
|
||||
const edges = []
|
||||
for (const [peerName, peerNodeId] of resolvedPeers) {
|
||||
allResolvedPeers.set(peerName, peerNodeId)
|
||||
edges.push(peerNodeId)
|
||||
}
|
||||
graph.push([childNodeId, edges])
|
||||
for (const missingPeer of missingPeers) {
|
||||
allMissingPeers.add(missingPeer)
|
||||
}
|
||||
}
|
||||
if (calculateDepPaths.length) {
|
||||
const { cycles } = analyzeGraph(graph as unknown as Graph)
|
||||
finishingList.push(...calculateDepPaths.map((calculateDepPath) => calculateDepPath(cycles)))
|
||||
}
|
||||
const finishing = Promise.all(finishingList).then(() => {})
|
||||
|
||||
const unknownResolvedPeersOfChildren = new Map<string, string>()
|
||||
for (const [alias, v] of allResolvedPeers) {
|
||||
@@ -536,7 +637,7 @@ function resolvePeersOfChildren<T extends PartialResolvedPackage> (
|
||||
}
|
||||
}
|
||||
|
||||
return { resolvedPeers: unknownResolvedPeersOfChildren, missingPeers: allMissingPeers }
|
||||
return { resolvedPeers: unknownResolvedPeersOfChildren, missingPeers: allMissingPeers, finishing }
|
||||
}
|
||||
|
||||
function _resolvePeers<T extends PartialResolvedPackage> (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { type PartialResolvedPackage, resolvePeers } from '../lib/resolvePeers'
|
||||
import { type DependenciesTreeNode } from '../lib/resolveDependencies'
|
||||
|
||||
test('packages are not deduplicated when versions do not match', () => {
|
||||
test('packages are not deduplicated when versions do not match', async () => {
|
||||
const fooPkg: PartialResolvedPackage = {
|
||||
name: 'foo',
|
||||
version: '1.0.0',
|
||||
@@ -31,7 +31,7 @@ test('packages are not deduplicated when versions do not match', () => {
|
||||
])
|
||||
)
|
||||
|
||||
const { dependenciesByProjectId } = resolvePeers({
|
||||
const { dependenciesByProjectId } = await resolvePeers({
|
||||
projects: [
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/// <reference path="../../../__typings__/index.d.ts" />
|
||||
import { type PeerDependencyIssuesByProjects } from '@pnpm/types'
|
||||
import { type PartialResolvedPackage, resolvePeers } from '../lib/resolvePeers'
|
||||
import { type DependenciesTreeNode, type PeerDependencies } from '../lib/resolveDependencies'
|
||||
|
||||
test('resolve peer dependencies of cyclic dependencies', () => {
|
||||
test('resolve peer dependencies of cyclic dependencies', async () => {
|
||||
const fooPkg = {
|
||||
name: 'foo',
|
||||
depPath: 'foo/1.0.0',
|
||||
@@ -23,7 +24,7 @@ test('resolve peer dependencies of cyclic dependencies', () => {
|
||||
},
|
||||
id: '',
|
||||
}
|
||||
const { dependenciesGraph } = resolvePeers({
|
||||
const { dependenciesGraph } = await resolvePeers({
|
||||
projects: [
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
@@ -103,16 +104,16 @@ test('resolve peer dependencies of cyclic dependencies', () => {
|
||||
lockfileDir: '',
|
||||
})
|
||||
expect(Object.keys(dependenciesGraph)).toStrictEqual([
|
||||
'foo/1.0.0(qar@1.0.0)(zoo@1.0.0)',
|
||||
'bar/1.0.0(foo@1.0.0)(zoo@1.0.0)',
|
||||
'zoo/1.0.0(qar@1.0.0)',
|
||||
'qar/1.0.0(bar@1.0.0)(foo@1.0.0)',
|
||||
'bar/1.0.0(foo@1.0.0)',
|
||||
'foo/1.0.0',
|
||||
'bar/1.0.0(foo/1.0.0)',
|
||||
'qar/1.0.0(bar/1.0.0(foo/1.0.0))(foo/1.0.0)',
|
||||
'zoo/1.0.0(qar/1.0.0(bar/1.0.0(foo/1.0.0))(foo/1.0.0))',
|
||||
'foo/1.0.0(qar/1.0.0(bar/1.0.0(foo/1.0.0))(foo/1.0.0))(zoo/1.0.0(qar/1.0.0(bar/1.0.0(foo/1.0.0))(foo/1.0.0)))',
|
||||
'bar/1.0.0(foo/1.0.0(qar/1.0.0(bar/1.0.0(foo/1.0.0))(foo/1.0.0))(zoo/1.0.0(qar/1.0.0(bar/1.0.0(foo/1.0.0))(foo/1.0.0))))(zoo/1.0.0(qar/1.0.0(bar/1.0.0(foo/1.0.0))(foo/1.0.0)))',
|
||||
])
|
||||
})
|
||||
|
||||
test('when a package is referenced twice in the dependencies graph and one of the times it cannot resolve its peers, still try to resolve it in the other occurrence', () => {
|
||||
test('when a package is referenced twice in the dependencies graph and one of the times it cannot resolve its peers, still try to resolve it in the other occurrence', async () => {
|
||||
const fooPkg = {
|
||||
name: 'foo',
|
||||
depPath: 'foo/1.0.0',
|
||||
@@ -136,7 +137,7 @@ test('when a package is referenced twice in the dependencies graph and one of th
|
||||
peerDependencies: {} as PeerDependencies,
|
||||
id: '',
|
||||
}
|
||||
const { dependenciesGraph } = resolvePeers({
|
||||
const { dependenciesGraph } = await resolvePeers({
|
||||
projects: [
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
@@ -203,182 +204,185 @@ test('when a package is referenced twice in the dependencies graph and one of th
|
||||
virtualStoreDir: '',
|
||||
lockfileDir: '',
|
||||
})
|
||||
expect(Object.keys(dependenciesGraph)).toStrictEqual([
|
||||
'foo/1.0.0',
|
||||
'zoo/1.0.0',
|
||||
'foo/1.0.0(qar@1.0.0)',
|
||||
'zoo/1.0.0(qar@1.0.0)',
|
||||
'qar/1.0.0',
|
||||
expect(Object.keys(dependenciesGraph).sort()).toStrictEqual([
|
||||
'bar/1.0.0',
|
||||
'foo/1.0.0',
|
||||
'foo/1.0.0(qar/1.0.0)',
|
||||
'qar/1.0.0',
|
||||
'zoo/1.0.0',
|
||||
'zoo/1.0.0(qar/1.0.0)',
|
||||
])
|
||||
})
|
||||
|
||||
describe('peer dependency issues', () => {
|
||||
const fooPkg = {
|
||||
name: 'foo',
|
||||
depPath: 'foo/1.0.0',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {
|
||||
peer: { version: '1' },
|
||||
},
|
||||
id: '',
|
||||
}
|
||||
const fooWithOptionalPeer = {
|
||||
name: 'foo',
|
||||
depPath: 'foo/2.0.0',
|
||||
version: '2.0.0',
|
||||
peerDependencies: {
|
||||
peer: { version: '1', optional: true },
|
||||
},
|
||||
id: '',
|
||||
}
|
||||
const barPkg = {
|
||||
name: 'bar',
|
||||
depPath: 'bar/1.0.0',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {
|
||||
peer: { version: '2' },
|
||||
},
|
||||
id: '',
|
||||
}
|
||||
const barWithOptionalPeer = {
|
||||
name: 'bar',
|
||||
depPath: 'bar/2.0.0',
|
||||
version: '2.0.0',
|
||||
peerDependencies: {
|
||||
peer: { version: '2', optional: true },
|
||||
},
|
||||
id: '',
|
||||
}
|
||||
const qarPkg = {
|
||||
name: 'qar',
|
||||
depPath: 'qar/1.0.0',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {
|
||||
peer: { version: '^2.2.0' },
|
||||
},
|
||||
id: '',
|
||||
}
|
||||
const { peerDependencyIssuesByProjects } = resolvePeers({
|
||||
projects: [
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
foo: '>project1>foo/1.0.0>',
|
||||
},
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project1',
|
||||
let peerDependencyIssuesByProjects: PeerDependencyIssuesByProjects
|
||||
beforeAll(async () => {
|
||||
const fooPkg = {
|
||||
name: 'foo',
|
||||
depPath: 'foo/1.0.0',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {
|
||||
peer: { version: '1' },
|
||||
},
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
bar: '>project2>bar/1.0.0>',
|
||||
},
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project2',
|
||||
id: '',
|
||||
}
|
||||
const fooWithOptionalPeer = {
|
||||
name: 'foo',
|
||||
depPath: 'foo/2.0.0',
|
||||
version: '2.0.0',
|
||||
peerDependencies: {
|
||||
peer: { version: '1', optional: true },
|
||||
},
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
foo: '>project3>foo/1.0.0>',
|
||||
bar: '>project3>bar/1.0.0>',
|
||||
},
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project3',
|
||||
id: '',
|
||||
}
|
||||
const barPkg = {
|
||||
name: 'bar',
|
||||
depPath: 'bar/1.0.0',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {
|
||||
peer: { version: '2' },
|
||||
},
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
bar: '>project4>bar/1.0.0>',
|
||||
qar: '>project4>qar/1.0.0>',
|
||||
},
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project4',
|
||||
id: '',
|
||||
}
|
||||
const barWithOptionalPeer = {
|
||||
name: 'bar',
|
||||
depPath: 'bar/2.0.0',
|
||||
version: '2.0.0',
|
||||
peerDependencies: {
|
||||
peer: { version: '2', optional: true },
|
||||
},
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
foo: '>project5>foo/1.0.0>',
|
||||
bar: '>project5>bar/2.0.0>',
|
||||
},
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project5',
|
||||
id: '',
|
||||
}
|
||||
const qarPkg = {
|
||||
name: 'qar',
|
||||
depPath: 'qar/1.0.0',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {
|
||||
peer: { version: '^2.2.0' },
|
||||
},
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
foo: '>project6>foo/2.0.0>',
|
||||
bar: '>project6>bar/2.0.0>',
|
||||
id: '',
|
||||
}
|
||||
peerDependencyIssuesByProjects = (await resolvePeers({
|
||||
projects: [
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
foo: '>project1>foo/1.0.0>',
|
||||
},
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project1',
|
||||
},
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project6',
|
||||
},
|
||||
],
|
||||
resolvedImporters: {},
|
||||
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([
|
||||
['>project1>foo/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: fooPkg,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project2>bar/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: barPkg,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project3>foo/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: fooPkg,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project3>bar/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: barPkg,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project4>bar/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: barPkg,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project4>qar/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: qarPkg,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project5>foo/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: fooPkg,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project5>bar/2.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: barWithOptionalPeer,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project6>foo/2.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: fooWithOptionalPeer,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project6>bar/2.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: barWithOptionalPeer,
|
||||
depth: 0,
|
||||
}],
|
||||
]),
|
||||
virtualStoreDir: '',
|
||||
lockfileDir: '',
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
bar: '>project2>bar/1.0.0>',
|
||||
},
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project2',
|
||||
},
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
foo: '>project3>foo/1.0.0>',
|
||||
bar: '>project3>bar/1.0.0>',
|
||||
},
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project3',
|
||||
},
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
bar: '>project4>bar/1.0.0>',
|
||||
qar: '>project4>qar/1.0.0>',
|
||||
},
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project4',
|
||||
},
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
foo: '>project5>foo/1.0.0>',
|
||||
bar: '>project5>bar/2.0.0>',
|
||||
},
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project5',
|
||||
},
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
foo: '>project6>foo/2.0.0>',
|
||||
bar: '>project6>bar/2.0.0>',
|
||||
},
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project6',
|
||||
},
|
||||
],
|
||||
resolvedImporters: {},
|
||||
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([
|
||||
['>project1>foo/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: fooPkg,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project2>bar/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: barPkg,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project3>foo/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: fooPkg,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project3>bar/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: barPkg,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project4>bar/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: barPkg,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project4>qar/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: qarPkg,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project5>foo/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: fooPkg,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project5>bar/2.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: barWithOptionalPeer,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project6>foo/2.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: fooWithOptionalPeer,
|
||||
depth: 0,
|
||||
}],
|
||||
['>project6>bar/2.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: barWithOptionalPeer,
|
||||
depth: 0,
|
||||
}],
|
||||
]),
|
||||
virtualStoreDir: '',
|
||||
lockfileDir: '',
|
||||
})).peerDependencyIssuesByProjects
|
||||
})
|
||||
it('should find peer dependency conflicts', () => {
|
||||
expect(peerDependencyIssuesByProjects['project3'].conflicts).toStrictEqual(['peer'])
|
||||
@@ -402,63 +406,66 @@ describe('peer dependency issues', () => {
|
||||
})
|
||||
|
||||
describe('unmet peer dependency issues', () => {
|
||||
const { peerDependencyIssuesByProjects } = resolvePeers({
|
||||
projects: [
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
foo: '>project1>foo/1.0.0>',
|
||||
peer1: '>project1>peer1/1.0.0-rc.0>',
|
||||
peer2: '>project1>peer2/1.1.0-rc.0>',
|
||||
},
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project1',
|
||||
},
|
||||
],
|
||||
resolvedImporters: {},
|
||||
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([
|
||||
['>project1>foo/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: {
|
||||
name: 'foo',
|
||||
version: '1.0.0',
|
||||
depPath: 'foo/1.0.0',
|
||||
peerDependencies: {
|
||||
peer1: { version: '*' },
|
||||
peer2: { version: '>=1' },
|
||||
let peerDependencyIssuesByProjects: PeerDependencyIssuesByProjects
|
||||
beforeAll(async () => {
|
||||
peerDependencyIssuesByProjects = (await resolvePeers({
|
||||
projects: [
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
foo: '>project1>foo/1.0.0>',
|
||||
peer1: '>project1>peer1/1.0.0-rc.0>',
|
||||
peer2: '>project1>peer2/1.1.0-rc.0>',
|
||||
},
|
||||
id: '',
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project1',
|
||||
},
|
||||
depth: 0,
|
||||
}],
|
||||
['>project1>peer1/1.0.0-rc.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: {
|
||||
name: 'peer1',
|
||||
version: '1.0.0-rc.0',
|
||||
depPath: 'peer/1.0.0-rc.0',
|
||||
peerDependencies: {},
|
||||
id: '',
|
||||
},
|
||||
depth: 0,
|
||||
}],
|
||||
['>project1>peer2/1.1.0-rc.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: {
|
||||
name: 'peer2',
|
||||
version: '1.1.0-rc.0',
|
||||
depPath: 'peer/1.1.0-rc.0',
|
||||
peerDependencies: {},
|
||||
id: '',
|
||||
},
|
||||
depth: 0,
|
||||
}],
|
||||
]),
|
||||
virtualStoreDir: '',
|
||||
lockfileDir: '',
|
||||
],
|
||||
resolvedImporters: {},
|
||||
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([
|
||||
['>project1>foo/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: {
|
||||
name: 'foo',
|
||||
version: '1.0.0',
|
||||
depPath: 'foo/1.0.0',
|
||||
peerDependencies: {
|
||||
peer1: { version: '*' },
|
||||
peer2: { version: '>=1' },
|
||||
},
|
||||
id: '',
|
||||
},
|
||||
depth: 0,
|
||||
}],
|
||||
['>project1>peer1/1.0.0-rc.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: {
|
||||
name: 'peer1',
|
||||
version: '1.0.0-rc.0',
|
||||
depPath: 'peer/1.0.0-rc.0',
|
||||
peerDependencies: {},
|
||||
id: '',
|
||||
},
|
||||
depth: 0,
|
||||
}],
|
||||
['>project1>peer2/1.1.0-rc.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: {
|
||||
name: 'peer2',
|
||||
version: '1.1.0-rc.0',
|
||||
depPath: 'peer/1.1.0-rc.0',
|
||||
peerDependencies: {},
|
||||
id: '',
|
||||
},
|
||||
depth: 0,
|
||||
}],
|
||||
]),
|
||||
virtualStoreDir: '',
|
||||
lockfileDir: '',
|
||||
})).peerDependencyIssuesByProjects
|
||||
})
|
||||
it('should not warn when the found package has prerelease version and the wanted range is *', () => {
|
||||
expect(peerDependencyIssuesByProjects).not.toHaveProperty(['project1', 'bad', 'peer1'])
|
||||
@@ -469,70 +476,73 @@ describe('unmet peer dependency issues', () => {
|
||||
})
|
||||
|
||||
describe('unmet peer dependency issue resolved from subdependency', () => {
|
||||
const { peerDependencyIssuesByProjects } = resolvePeers({
|
||||
projects: [
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
foo: '>project>foo/1.0.0>',
|
||||
},
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project',
|
||||
},
|
||||
],
|
||||
resolvedImporters: {},
|
||||
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([
|
||||
['>project>foo/1.0.0>', {
|
||||
children: {
|
||||
dep: '>project>foo/1.0.0>dep/1.0.0>',
|
||||
bar: '>project>foo/1.0.0>bar/1.0.0>',
|
||||
},
|
||||
installable: true,
|
||||
resolvedPackage: {
|
||||
name: 'foo',
|
||||
depPath: 'foo/1.0.0',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {},
|
||||
id: '',
|
||||
},
|
||||
depth: 0,
|
||||
}],
|
||||
['>project>foo/1.0.0>dep/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: {
|
||||
name: 'dep',
|
||||
depPath: 'dep/1.0.0',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {},
|
||||
id: '',
|
||||
},
|
||||
depth: 1,
|
||||
}],
|
||||
['>project>foo/1.0.0>bar/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: {
|
||||
name: 'bar',
|
||||
depPath: 'bar/1.0.0',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {
|
||||
dep: { version: '10' },
|
||||
let peerDependencyIssuesByProjects: PeerDependencyIssuesByProjects
|
||||
beforeAll(async () => {
|
||||
peerDependencyIssuesByProjects = (await resolvePeers({
|
||||
projects: [
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
foo: '>project>foo/1.0.0>',
|
||||
},
|
||||
id: '',
|
||||
topParents: [],
|
||||
rootDir: '',
|
||||
id: 'project',
|
||||
},
|
||||
depth: 1,
|
||||
}],
|
||||
]),
|
||||
virtualStoreDir: '',
|
||||
lockfileDir: '',
|
||||
],
|
||||
resolvedImporters: {},
|
||||
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([
|
||||
['>project>foo/1.0.0>', {
|
||||
children: {
|
||||
dep: '>project>foo/1.0.0>dep/1.0.0>',
|
||||
bar: '>project>foo/1.0.0>bar/1.0.0>',
|
||||
},
|
||||
installable: true,
|
||||
resolvedPackage: {
|
||||
name: 'foo',
|
||||
depPath: 'foo/1.0.0',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {},
|
||||
id: '',
|
||||
},
|
||||
depth: 0,
|
||||
}],
|
||||
['>project>foo/1.0.0>dep/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: {
|
||||
name: 'dep',
|
||||
depPath: 'dep/1.0.0',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {},
|
||||
id: '',
|
||||
},
|
||||
depth: 1,
|
||||
}],
|
||||
['>project>foo/1.0.0>bar/1.0.0>', {
|
||||
children: {},
|
||||
installable: true,
|
||||
resolvedPackage: {
|
||||
name: 'bar',
|
||||
depPath: 'bar/1.0.0',
|
||||
version: '1.0.0',
|
||||
peerDependencies: {
|
||||
dep: { version: '10' },
|
||||
},
|
||||
id: '',
|
||||
},
|
||||
depth: 1,
|
||||
}],
|
||||
]),
|
||||
virtualStoreDir: '',
|
||||
lockfileDir: '',
|
||||
})).peerDependencyIssuesByProjects
|
||||
})
|
||||
it('should return from where the bad peer dependency is resolved', () => {
|
||||
expect(peerDependencyIssuesByProjects.project.bad.dep[0].resolvedFrom).toStrictEqual([{ name: 'foo', version: '1.0.0' }])
|
||||
})
|
||||
})
|
||||
|
||||
test('resolve peer dependencies with npm aliases', () => {
|
||||
test('resolve peer dependencies with npm aliases', async () => {
|
||||
const fooPkg = {
|
||||
name: 'foo',
|
||||
depPath: 'foo/1.0.0',
|
||||
@@ -565,7 +575,7 @@ test('resolve peer dependencies with npm aliases', () => {
|
||||
peerDependencies: {},
|
||||
id: '',
|
||||
}
|
||||
const { dependenciesGraph } = resolvePeers({
|
||||
const { dependenciesGraph } = await resolvePeers({
|
||||
projects: [
|
||||
{
|
||||
directNodeIdsByAlias: {
|
||||
@@ -625,10 +635,10 @@ test('resolve peer dependencies with npm aliases', () => {
|
||||
virtualStoreDir: '',
|
||||
lockfileDir: '',
|
||||
})
|
||||
expect(Object.keys(dependenciesGraph)).toStrictEqual([
|
||||
expect(Object.keys(dependenciesGraph).sort()).toStrictEqual([
|
||||
'bar/1.0.0',
|
||||
'foo/1.0.0(bar@1.0.0)',
|
||||
'bar/2.0.0',
|
||||
'foo/2.0.0(bar@2.0.0)',
|
||||
'foo/1.0.0(bar/1.0.0)',
|
||||
'foo/2.0.0(bar/2.0.0)',
|
||||
])
|
||||
})
|
||||
|
||||
119
pnpm-lock.yaml
generated
119
pnpm-lock.yaml
generated
@@ -71,8 +71,8 @@ importers:
|
||||
specifier: 1.0.0
|
||||
version: 1.0.0
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@pnpm/tsconfig':
|
||||
specifier: workspace:*
|
||||
version: link:__utils__/tsconfig
|
||||
@@ -189,8 +189,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../pkg-manager/modules-yaml
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@pnpm/types':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/types
|
||||
@@ -226,8 +226,8 @@ importers:
|
||||
__utils__/assert-store:
|
||||
dependencies:
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@pnpm/store.cafs':
|
||||
specifier: workspace:*
|
||||
version: link:../../store/cafs
|
||||
@@ -1335,8 +1335,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@pnpm/test-fixtures':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-fixtures
|
||||
@@ -1456,8 +1456,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@pnpm/test-ipc-server':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-ipc-server
|
||||
@@ -2900,8 +2900,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@pnpm/test-fixtures':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-fixtures
|
||||
@@ -3160,8 +3160,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@pnpm/store-path':
|
||||
specifier: workspace:*
|
||||
version: link:../../store/store-path
|
||||
@@ -3442,8 +3442,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../read-projects-context
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@pnpm/store-path':
|
||||
specifier: workspace:*
|
||||
version: link:../../store/store-path
|
||||
@@ -3799,8 +3799,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@pnpm/test-fixtures':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-fixtures
|
||||
@@ -3992,8 +3992,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@pnpm/test-fixtures':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-fixtures
|
||||
@@ -4198,6 +4198,9 @@ importers:
|
||||
get-npm-tarball-url:
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.0
|
||||
graph-cycles:
|
||||
specifier: 1.2.1
|
||||
version: 1.2.1
|
||||
is-inner-link:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0
|
||||
@@ -4549,8 +4552,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../pkg-manifest/read-project-manifest
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@pnpm/run-npm':
|
||||
specifier: workspace:*
|
||||
version: link:../exec/run-npm
|
||||
@@ -4809,8 +4812,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
|
||||
releasing/plugin-commands-publishing:
|
||||
dependencies:
|
||||
@@ -4909,8 +4912,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@pnpm/test-ipc-server':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-ipc-server
|
||||
@@ -5472,8 +5475,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../pkg-manifest/read-package-json
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@pnpm/test-fixtures':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-fixtures
|
||||
@@ -5539,8 +5542,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@types/ramda':
|
||||
specifier: 0.28.20
|
||||
version: 0.28.20
|
||||
@@ -5633,8 +5636,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@pnpm/test-fixtures':
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/test-fixtures
|
||||
@@ -5965,8 +5968,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../../__utils__/prepare
|
||||
'@pnpm/registry-mock':
|
||||
specifier: 3.24.2
|
||||
version: 3.24.2(typanion@3.14.0)
|
||||
specifier: 3.25.0
|
||||
version: 3.25.0(typanion@3.14.0)
|
||||
'@types/archy':
|
||||
specifier: 0.0.33
|
||||
version: 0.0.33
|
||||
@@ -6538,7 +6541,7 @@ packages:
|
||||
'@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.3)
|
||||
'@babel/helper-skip-transparent-expression-wrappers': 7.22.5
|
||||
'@babel/helper-split-export-declaration': 7.22.6
|
||||
semver: 7.5.4
|
||||
semver: 7.6.0
|
||||
dev: true
|
||||
|
||||
/@babel/helper-environment-visitor@7.22.20:
|
||||
@@ -8733,8 +8736,8 @@ packages:
|
||||
strip-bom: 4.0.0
|
||||
dev: true
|
||||
|
||||
/@pnpm/registry-mock@3.24.2(typanion@3.14.0):
|
||||
resolution: {integrity: sha512-j756X+oEW71pYCHALwQ44MHxDXpL+/1uEVaghnI2phoCeiex9/E39mLP0sYp3rcO1a9Jqf+XaorKmAgtLT7Xbw==}
|
||||
/@pnpm/registry-mock@3.25.0(typanion@3.14.0):
|
||||
resolution: {integrity: sha512-rSKgbINc7T9s0hn8K/YHTJtMaUG2LWmXybzmiVNwDwrNx1SMHbLsZ+QafHz0wyktQhPQB2274BAd72jYLgFHFg==}
|
||||
engines: {node: '>=10.13'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
@@ -9004,6 +9007,10 @@ packages:
|
||||
'@babel/types': 7.23.3
|
||||
dev: true
|
||||
|
||||
/@types/bintrees@1.0.6:
|
||||
resolution: {integrity: sha512-pZWT4Bz+tWwxlDspSjdoIza4PE5lbGI4Xvs3FZV/2v5m5SDA8LwNpU8AXxlndmARO7OaQ1Vf3zFenOsNMzaRkQ==}
|
||||
dev: false
|
||||
|
||||
/@types/braces@3.0.4:
|
||||
resolution: {integrity: sha512-0WR3b8eaISjEW7RpZnclONaLFDf7buaowRHdqLp4vLj54AsSAYWfh3DRbfiYJY9XDxMgx1B4sE1Afw2PGpuHOA==}
|
||||
dev: true
|
||||
@@ -10274,6 +10281,10 @@ packages:
|
||||
write-file-atomic: 5.0.1
|
||||
dev: false
|
||||
|
||||
/bintrees@1.0.2:
|
||||
resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==}
|
||||
dev: false
|
||||
|
||||
/bl@4.1.0:
|
||||
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
|
||||
dependencies:
|
||||
@@ -12124,6 +12135,11 @@ packages:
|
||||
/fast-safe-stringify@2.1.1:
|
||||
resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==}
|
||||
|
||||
/fast-string-compare@1.0.0:
|
||||
resolution: {integrity: sha512-R0f7E2MvKy3LltU/eK7P9IYMc7kovnoaQIvWVvKx1LDDgHArVCrqgb4/6hlYJn6I9VYFn7mQoTKrsO3ELMVE8Q==}
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/fastest-levenshtein@1.0.16:
|
||||
resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==}
|
||||
engines: {node: '>= 4.9.1'}
|
||||
@@ -12785,6 +12801,15 @@ packages:
|
||||
resolution: {integrity: sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==}
|
||||
dev: true
|
||||
|
||||
/graph-cycles@1.2.1:
|
||||
resolution: {integrity: sha512-IJly1QJTKrACgy+HVQYELr9igxhCC6zGn78PbvEX9tj4AZBvv7ZNU45m9rOrhDgprrkLtJZYmOPyhY8exZLD/Q==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
fast-string-compare: 1.0.0
|
||||
rotated-array-set: 1.0.0
|
||||
short-tree: 1.0.0
|
||||
dev: false
|
||||
|
||||
/grapheme-splitter@1.0.4:
|
||||
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
|
||||
|
||||
@@ -13859,7 +13884,7 @@ packages:
|
||||
jest-util: 29.7.0
|
||||
natural-compare: 1.4.0
|
||||
pretty-format: 29.7.0
|
||||
semver: 7.5.4
|
||||
semver: 7.6.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
@@ -14337,7 +14362,7 @@ packages:
|
||||
resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
semver: 7.5.4
|
||||
semver: 7.6.0
|
||||
dev: true
|
||||
|
||||
/make-empty-dir@2.1.0:
|
||||
@@ -16272,6 +16297,11 @@ packages:
|
||||
path-temp: 2.0.0
|
||||
dev: false
|
||||
|
||||
/rotated-array-set@1.0.0:
|
||||
resolution: {integrity: sha512-MmMz9ERrZw7DVn8e6V7U/wkAWzEaULiPySEBXLWh4nA/F5gwSFx96bu+As4wyDpZ9bdhc+flB1CyTYmStwZo8A==}
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/run-async@2.4.1:
|
||||
resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
@@ -16479,6 +16509,14 @@ packages:
|
||||
rechoir: 0.6.2
|
||||
dev: true
|
||||
|
||||
/short-tree@1.0.0:
|
||||
resolution: {integrity: sha512-SPhGxbdypMMjYlmdVL/dzBUCT/5FboztmleoS4WPgvCI7DqZXv8xrLSTuJqzmmuAtCTkgxkIKzfZ+jfJR6ODZg==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
'@types/bintrees': 1.0.6
|
||||
bintrees: 1.0.2
|
||||
dev: false
|
||||
|
||||
/shx@0.3.4:
|
||||
resolution: {integrity: sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -18202,6 +18240,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
time:
|
||||
/@pnpm/registry-mock@3.24.2: '2024-02-06T23:50:07.901Z'
|
||||
/@pnpm/registry-mock@3.25.0: '2024-02-08T22:03:39.172Z'
|
||||
/fuse-native@2.2.6: '2020-06-03T19:26:36.838Z'
|
||||
/graph-cycles@1.2.1: '2021-04-11T09:47:00.295Z'
|
||||
/node-gyp@10.0.1: '2023-11-02T18:13:42.360Z'
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"@pnpm/prepare": "workspace:*",
|
||||
"@pnpm/read-package-json": "workspace:*",
|
||||
"@pnpm/read-project-manifest": "workspace:*",
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@pnpm/run-npm": "workspace:*",
|
||||
"@pnpm/tabtab": "^0.5.2",
|
||||
"@pnpm/test-fixtures": "workspace:*",
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"@pnpm/lockfile-types": "workspace:*",
|
||||
"@pnpm/plugin-commands-deploy": "workspace:*",
|
||||
"@pnpm/prepare": "workspace:*",
|
||||
"@pnpm/registry-mock": "3.24.2"
|
||||
"@pnpm/registry-mock": "3.25.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pnpm/cli-utils": "workspace:*",
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"@pnpm/filter-workspace-packages": "workspace:*",
|
||||
"@pnpm/plugin-commands-publishing": "workspace:*",
|
||||
"@pnpm/prepare": "workspace:*",
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@pnpm/test-ipc-server": "workspace:*",
|
||||
"@types/cross-spawn": "^6.0.5",
|
||||
"@types/is-windows": "^1.0.2",
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"@pnpm/plugin-commands-licenses": "workspace:*",
|
||||
"@pnpm/prepare": "workspace:*",
|
||||
"@pnpm/read-package-json": "workspace:*",
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@pnpm/test-fixtures": "workspace:*",
|
||||
"@types/ramda": "0.28.20",
|
||||
"@types/semver": "7.5.3",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"@pnpm/plugin-commands-installation": "workspace:*",
|
||||
"@pnpm/plugin-commands-listing": "workspace:*",
|
||||
"@pnpm/prepare": "workspace:*",
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@types/ramda": "0.28.20",
|
||||
"execa": "npm:safe-execa@0.1.2",
|
||||
"strip-ansi": "^6.0.1",
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"@pnpm/plugin-commands-installation": "workspace:*",
|
||||
"@pnpm/plugin-commands-outdated": "workspace:*",
|
||||
"@pnpm/prepare": "workspace:*",
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@pnpm/test-fixtures": "workspace:*",
|
||||
"@types/ramda": "0.28.20",
|
||||
"@types/wrap-ansi": "8.0.2",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"@pnpm/lockfile-file": "workspace:*",
|
||||
"@pnpm/plugin-commands-store": "workspace:*",
|
||||
"@pnpm/prepare": "workspace:*",
|
||||
"@pnpm/registry-mock": "3.24.2",
|
||||
"@pnpm/registry-mock": "3.25.0",
|
||||
"@types/archy": "0.0.33",
|
||||
"@types/ramda": "0.28.20",
|
||||
"@types/ssri": "^7.1.4",
|
||||
|
||||
Reference in New Issue
Block a user