feat: dedupe injected dependencies (#7416)

A new setting added for symlinking [injected dependencies](https://pnpm.io/package_json#dependenciesmetainjected) from the workspace, if their dependencies use the same peer dependencies as the dependent package. The setting is called `dedupe-injected-deps`
This commit is contained in:
Zoltan Kochan
2023-12-14 16:13:22 +01:00
committed by GitHub
parent e79ab3847b
commit 672c559e49
13 changed files with 435 additions and 12 deletions

View File

@@ -0,0 +1,8 @@
---
"@pnpm/resolve-dependencies": minor
"@pnpm/core": minor
"@pnpm/config": minor
"pnpm": minor
---
A new setting added for symlinking [injected dependencies](https://pnpm.io/package_json#dependenciesmetainjected) from the workspace, if their dependencies use the same peer dependencies as the dependent package. The setting is called `dedupe-injected-deps` [#7416](https://github.com/pnpm/pnpm/pull/7416).

View File

@@ -184,6 +184,7 @@ export interface Config {
gitBranchLockfile?: boolean
globalDir?: string
lockfile?: boolean
dedupeInjectedDeps?: boolean
}
export interface ConfigWithDeprecatedSettings extends Config {

View File

@@ -52,6 +52,7 @@ export const types = Object.assign({
'deploy-all-files': Boolean,
'dedupe-peer-dependents': Boolean,
'dedupe-direct-deps': Boolean,
'dedupe-injected-deps': Boolean,
dev: [null, true],
dir: String,
'disallow-workspace-cycles': Boolean,
@@ -204,6 +205,7 @@ export async function getConfig (
'deploy-all-files': false,
'dedupe-peer-dependents': true,
'dedupe-direct-deps': false,
'dedupe-injected-deps': false,
'disallow-workspace-cycles': false,
'enable-modules-dir': true,
'exclude-links-from-lockfile': false,

View File

@@ -123,6 +123,7 @@ export interface StrictInstallOptions {
allProjects: ProjectOptions[]
resolveSymlinksInInjectedDirs: boolean
dedupeDirectDeps: boolean
dedupeInjectedDeps: boolean
dedupePeerDependents: boolean
extendNodePath: boolean
excludeLinksFromLockfile: boolean

View File

@@ -1010,6 +1010,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
currentLockfile: ctx.currentLockfile,
defaultUpdateDepth: opts.depth,
dedupeDirectDeps: opts.dedupeDirectDeps,
dedupeInjectedDeps: opts.dedupeInjectedDeps,
dedupePeerDependents: opts.dedupePeerDependents,
dryRun: opts.lockfileOnly,
engineStrict: opts.engineStrict,

View File

@@ -1780,3 +1780,264 @@ test('do not relink injected dependency on install when disableRelinkLocalDirDep
expect(newInode).toEqual(getInode())
})
test('injected local packages are deduped', async () => {
const project1Manifest = {
name: 'project-1',
version: '1.0.0',
dependencies: {
'is-negative': '1.0.0',
},
devDependencies: {
'@pnpm.e2e/dep-of-pkg-with-1-dep': '100.0.0',
},
peerDependencies: {
'is-positive': '>=1.0.0',
},
}
const project2Manifest = {
name: 'project-2',
version: '1.0.0',
dependencies: {
'project-1': 'workspace:1.0.0',
},
devDependencies: {
'is-positive': '1.0.0',
},
dependenciesMeta: {
'project-1': {
injected: true,
},
},
}
const project3Manifest = {
name: 'project-3',
version: '1.0.0',
dependencies: {
'project-2': 'workspace:1.0.0',
},
devDependencies: {
'is-positive': '2.0.0',
},
dependenciesMeta: {
'project-2': {
injected: true,
},
},
}
const project4Manifest = {
name: 'project-4',
version: '1.0.0',
dependencies: {
'project-2': 'workspace:1.0.0',
},
devDependencies: {
'is-positive': '1.0.0',
},
dependenciesMeta: {
'project-2': {
injected: true,
},
},
}
const project5Manifest = {
name: 'project-5',
version: '1.0.0',
dependencies: {
'project-4': 'workspace:1.0.0',
},
devDependencies: {
'is-positive': '1.0.0',
},
dependenciesMeta: {
'project-4': {
injected: true,
},
},
}
const projects = preparePackages([
{
location: 'project-1',
package: project1Manifest,
},
{
location: 'project-2',
package: project2Manifest,
},
{
location: 'project-3',
package: project3Manifest,
},
{
location: 'project-4',
package: project4Manifest,
},
{
location: 'project-5',
package: project5Manifest,
},
])
const importers: MutatedProject[] = [
{
mutation: 'install',
rootDir: path.resolve('project-1'),
},
{
mutation: 'install',
rootDir: path.resolve('project-2'),
},
{
mutation: 'install',
rootDir: path.resolve('project-3'),
},
{
mutation: 'install',
rootDir: path.resolve('project-4'),
},
{
mutation: 'install',
rootDir: path.resolve('project-5'),
},
]
const allProjects: ProjectOptions[] = [
{
buildIndex: 0,
manifest: project1Manifest,
rootDir: path.resolve('project-1'),
},
{
buildIndex: 0,
manifest: project2Manifest,
rootDir: path.resolve('project-2'),
},
{
buildIndex: 0,
manifest: project3Manifest,
rootDir: path.resolve('project-3'),
},
{
buildIndex: 0,
manifest: project4Manifest,
rootDir: path.resolve('project-4'),
},
{
buildIndex: 0,
manifest: project5Manifest,
rootDir: path.resolve('project-5'),
},
]
const workspacePackages = {
'project-1': {
'1.0.0': {
dir: path.resolve('project-1'),
manifest: project1Manifest,
},
},
'project-2': {
'1.0.0': {
dir: path.resolve('project-2'),
manifest: project2Manifest,
},
},
'project-3': {
'1.0.0': {
dir: path.resolve('project-3'),
manifest: project3Manifest,
},
},
'project-4': {
'1.0.0': {
dir: path.resolve('project-4'),
manifest: project4Manifest,
},
},
}
await mutateModules(importers, await testDefaults({
autoInstallPeers: true,
allProjects,
dedupeInjectedDeps: true,
workspacePackages,
}))
await projects['project-1'].has('is-negative')
await projects['project-1'].has('@pnpm.e2e/dep-of-pkg-with-1-dep')
await projects['project-1'].has('is-positive')
await projects['project-2'].has('is-positive')
await projects['project-2'].has('project-1')
await projects['project-3'].has('is-positive')
await projects['project-3'].has('project-2')
expect(fs.readdirSync('node_modules/.pnpm').length).toBe(7)
const rootModules = assertProject(process.cwd())
{
const lockfile = await rootModules.readLockfile()
expect(lockfile.importers['project-2'].dependenciesMeta).toEqual({
'project-1': {
injected: true,
},
})
expect(lockfile.packages['file:project-1(is-positive@1.0.0)']).toBeFalsy()
expect(lockfile.packages['file:project-2(is-positive@2.0.0)']).toEqual({
resolution: {
directory: 'project-2',
type: 'directory',
},
id: 'file:project-2',
name: 'project-2',
dependencies: {
'project-1': 'file:project-1(is-positive@2.0.0)',
},
transitivePeerDependencies: ['is-positive'],
dev: false,
})
const modulesState = await rootModules.readModulesManifest()
expect(modulesState?.injectedDeps?.['project-1'].length).toEqual(1)
expect(modulesState?.injectedDeps?.['project-1'][0]).toContain(`node_modules${path.sep}.pnpm`)
}
await rimraf('node_modules')
await rimraf('project-1/node_modules')
await rimraf('project-2/node_modules')
await rimraf('project-3/node_modules')
await mutateModules(importers, await testDefaults({
autoInstallPeers: true,
allProjects,
dedupeInjectedDeps: true,
frozenLockfile: true,
workspacePackages,
}))
await projects['project-1'].has('is-negative')
await projects['project-1'].has('@pnpm.e2e/dep-of-pkg-with-1-dep')
await projects['project-1'].has('is-positive')
await projects['project-2'].has('is-positive')
await projects['project-2'].has('project-1')
await projects['project-3'].has('is-positive')
await projects['project-3'].has('project-2')
expect(fs.readdirSync('node_modules/.pnpm').length).toBe(7)
// The injected project is updated when one of its dependencies needs to be updated
allProjects[0].manifest.dependencies!['is-negative'] = '2.0.0'
await mutateModules(importers, await testDefaults({ autoInstallPeers: true, allProjects, dedupeInjectedDeps: true, workspacePackages }))
{
const lockfile = await rootModules.readLockfile()
expect(lockfile.importers['project-2'].dependenciesMeta).toEqual({
'project-1': {
injected: true,
},
})
expect(lockfile.packages['file:project-1(is-positive@1.0.0)']).toBeFalsy()
const modulesState = await rootModules.readModulesManifest()
expect(modulesState?.injectedDeps?.['project-1'].length).toEqual(1)
expect(modulesState?.injectedDeps?.['project-1'][0]).toContain(`node_modules${path.sep}.pnpm`)
}
})

View File

@@ -0,0 +1,96 @@
import path from 'path'
import normalize from 'normalize-path'
import { type ResolvedImporters } from './resolveDependencyTree'
import { type LinkedDependency } from './resolveDependencies'
import {
type DependenciesByProjectId,
type GenericDependenciesGraph,
type PartialResolvedPackage,
type ProjectToResolve,
} from './resolvePeers'
export interface DedupeInjectedDepsOptions<T extends PartialResolvedPackage> {
depGraph: GenericDependenciesGraph<T>
dependenciesByProjectId: DependenciesByProjectId
lockfileDir: string
pathsByNodeId: Map<string, string>
projects: ProjectToResolve[]
resolvedImporters: ResolvedImporters
}
export function dedupeInjectedDeps<T extends PartialResolvedPackage> (
opts: DedupeInjectedDepsOptions<T>
) {
const injectedDepsByProjects = getInjectedDepsByProjects(opts)
const dedupeMap = getDedupeMap(injectedDepsByProjects, opts)
applyDedupeMap(dedupeMap, opts)
}
type InjectedDepsByProjects = Map<string, Map<string, { depPath: string, id: string }>>
function getInjectedDepsByProjects<T extends PartialResolvedPackage> (
opts: Pick<DedupeInjectedDepsOptions<T>, 'projects' | 'pathsByNodeId' | 'depGraph'>
): InjectedDepsByProjects {
const injectedDepsByProjects = new Map<string, Map<string, { depPath: string, id: string }>>()
for (const project of opts.projects) {
for (const [alias, nodeId] of Object.entries(project.directNodeIdsByAlias)) {
const depPath = opts.pathsByNodeId.get(nodeId)!
if (!depPath.startsWith('file:')) continue
const id = opts.depGraph[depPath].id.substring(5)
if (opts.projects.some((project) => project.id === id)) {
if (!injectedDepsByProjects.has(project.id)) injectedDepsByProjects.set(project.id, new Map())
injectedDepsByProjects.get(project.id)!.set(alias, { depPath, id })
}
}
}
return injectedDepsByProjects
}
type DedupeMap = Map<string, Map<string, string>>
function getDedupeMap<T extends PartialResolvedPackage> (
injectedDepsByProjects: InjectedDepsByProjects,
opts: Pick<DedupeInjectedDepsOptions<T>, 'depGraph' | 'dependenciesByProjectId'>
): DedupeMap {
const toDedupe = new Map<string, Map<string, string>>()
for (const [id, deps] of injectedDepsByProjects.entries()) {
const dedupedInjectedDeps = new Map<string, string>()
for (const [alias, dep] of deps.entries()) {
// Check for subgroup not equal.
// The injected project in the workspace may have dev deps
const isSubset = Object.entries(opts.depGraph[dep.depPath].children)
.every(([alias, depPath]) => opts.dependenciesByProjectId[dep.id][alias] === depPath)
if (isSubset) {
dedupedInjectedDeps.set(alias, dep.id)
}
}
toDedupe.set(id, dedupedInjectedDeps)
}
return toDedupe
}
function applyDedupeMap<T extends PartialResolvedPackage> (
dedupeMap: DedupeMap,
opts: Pick<DedupeInjectedDepsOptions<T>, 'dependenciesByProjectId' | 'resolvedImporters' | 'lockfileDir'>
) {
for (const [id, aliases] of dedupeMap.entries()) {
for (const [alias, dedupedProjectId] of aliases.entries()) {
delete opts.dependenciesByProjectId[id][alias]
const index = opts.resolvedImporters[id].directDependencies.findIndex((dep) => dep.alias === alias)
const prev = opts.resolvedImporters[id].directDependencies[index]
const depPath = `link:${normalize(path.relative(id, dedupedProjectId))}`
const linkedDep: LinkedDependency = {
...prev,
isLinkedDependency: true,
depPath,
pkgId: depPath,
resolution: {
type: 'directory',
directory: path.join(opts.lockfileDir, dedupedProjectId),
},
}
opts.resolvedImporters[id].directDependencies[index] = linkedDep
opts.resolvedImporters[id].linkedDependencies.push(linkedDep)
}
}
}

View File

@@ -96,6 +96,7 @@ export async function resolveDependencies (
defaultUpdateDepth: number
dedupePeerDependents?: boolean
dedupeDirectDeps?: boolean
dedupeInjectedDeps?: boolean
excludeLinksFromLockfile?: boolean
preserveWorkspaceProtocol: boolean
saveWorkspaceProtocol: 'rolling' | boolean
@@ -179,10 +180,12 @@ export async function resolveDependencies (
} = resolvePeers({
dependenciesTree,
dedupePeerDependents: opts.dedupePeerDependents,
dedupeInjectedDeps: opts.dedupeInjectedDeps,
lockfileDir: opts.lockfileDir,
projects: projectsToLink,
virtualStoreDir: opts.virtualStoreDir,
resolvePeersFromWorkspaceRoot: Boolean(opts.resolvePeersFromWorkspaceRoot),
resolvedImporters,
})
const linkedDependenciesByProjectId: Record<string, LinkedDependency[]> = {}

View File

@@ -1248,7 +1248,6 @@ async function resolveDependency (
pkg.deprecated = currentPkg.dependencyLockfile.deprecated
}
hasBin = Boolean((pkg.bin && !(pkg.bin === '' || Object.keys(pkg.bin).length === 0)) ?? pkg.directories?.bin)
/* eslint-enable @typescript-eslint/dot-notation */
}
if (options.currentDepth === 0 && pkgResponse.body.latest && pkgResponse.body.latest !== pkg.version) {
ctx.outdatedDependencies[pkgResponse.body.id] = pkgResponse.body.latest

View File

@@ -31,6 +31,16 @@ import {
export * from './nodeIdUtils'
export type { LinkedDependency, ResolvedPackage, DependenciesTree, DependenciesTreeNode } from './resolveDependencies'
export interface ResolvedImporters {
[id: string]: {
directDependencies: ResolvedDirectDependency[]
directNodeIdsByAlias: {
[alias: string]: string
}
linkedDependencies: LinkedDependency[]
}
}
export interface ResolvedDirectDependency {
alias: string
optional: boolean
@@ -180,15 +190,7 @@ export async function resolveDependencyTree<T> (
})
})
const resolvedImporters = {} as {
[id: string]: {
directDependencies: ResolvedDirectDependency[]
directNodeIdsByAlias: {
[alias: string]: string
}
linkedDependencies: LinkedDependency[]
}
}
const resolvedImporters: ResolvedImporters = {}
for (const { id, wantedDependencies } of importers) {
const directDeps = dedupeSameAliasDirectDeps(directDepsByImporterId[id], wantedDependencies)

View File

@@ -17,8 +17,10 @@ import {
type DependenciesTreeNode,
type ResolvedPackage,
} from './resolveDependencies'
import { type ResolvedImporters } from './resolveDependencyTree'
import { mergePeers } from './mergePeers'
import { createNodeId, splitNodeId } from './nodeIdUtils'
import { dedupeInjectedDeps } from './dedupeInjectedDeps'
export interface GenericDependenciesGraphNode {
// at this point the version is really needed only for logging
@@ -35,6 +37,7 @@ export interface GenericDependenciesGraphNode {
}
export type PartialResolvedPackage = Pick<ResolvedPackage,
| 'id'
| 'depPath'
| 'name'
| 'peerDependencies'
@@ -55,6 +58,8 @@ export interface ProjectToResolve {
id: string
}
export type DependenciesByProjectId = Record<string, Record<string, string>>
export function resolvePeers<T extends PartialResolvedPackage> (
opts: {
projects: ProjectToResolve[]
@@ -63,10 +68,12 @@ export function resolvePeers<T extends PartialResolvedPackage> (
lockfileDir: string
resolvePeersFromWorkspaceRoot?: boolean
dedupePeerDependents?: boolean
dedupeInjectedDeps?: boolean
resolvedImporters: ResolvedImporters
}
): {
dependenciesGraph: GenericDependenciesGraph<T>
dependenciesByProjectId: { [id: string]: { [alias: string]: string } }
dependenciesByProjectId: DependenciesByProjectId
peerDependencyIssuesByProjects: PeerDependencyIssuesByProjects
} {
const depGraph: GenericDependenciesGraph<T> = {}
@@ -107,10 +114,20 @@ export function resolvePeers<T extends PartialResolvedPackage> (
node.children = mapValues((childNodeId) => pathsByNodeId.get(childNodeId) ?? childNodeId, node.children)
})
const dependenciesByProjectId: { [id: string]: Record<string, string> } = {}
const dependenciesByProjectId: DependenciesByProjectId = {}
for (const { directNodeIdsByAlias, id } of opts.projects) {
dependenciesByProjectId[id] = mapValues((nodeId) => pathsByNodeId.get(nodeId)!, directNodeIdsByAlias)
}
if (opts.dedupeInjectedDeps) {
dedupeInjectedDeps({
dependenciesByProjectId,
projects: opts.projects,
depGraph,
pathsByNodeId,
lockfileDir: opts.lockfileDir,
resolvedImporters: opts.resolvedImporters,
})
}
if (opts.dedupePeerDependents) {
const duplicates = Array.from(depPathsByPkgId.values()).filter((item) => item.size > 1)
const allDepPathsMap = deduplicateAll(depGraph, duplicates)

View File

@@ -6,6 +6,7 @@ test('packages are not deduplicated when versions do not match', () => {
name: 'foo',
version: '1.0.0',
depPath: 'foo/1.0.0',
id: '',
peerDependencies: {
bar: '1.0.0 || 2.0.0',
baz: '1.0.0 || 2.0.0',
@@ -30,6 +31,7 @@ test('packages are not deduplicated when versions do not match', () => {
version,
depPath: `${name}/${version}`,
peerDependencies: {},
id: '',
} satisfies PartialResolvedPackage,
])
)
@@ -75,6 +77,7 @@ test('packages are not deduplicated when versions do not match', () => {
id: 'project4',
},
],
resolvedImporters: {},
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>(([
['>project1>foo/1.0.0>', fooPkg],
['>project1>bar/1.0.0>', peers.bar_1_0_0],

View File

@@ -11,6 +11,7 @@ test('resolve peer dependencies of cyclic dependencies', () => {
qar: '1.0.0',
zoo: '1.0.0',
},
id: '',
}
const barPkg = {
name: 'bar',
@@ -20,6 +21,7 @@ test('resolve peer dependencies of cyclic dependencies', () => {
foo: '1.0.0',
zoo: '1.0.0',
} as Record<string, string>,
id: '',
}
const { dependenciesGraph } = resolvePeers({
projects: [
@@ -32,6 +34,7 @@ test('resolve peer dependencies of cyclic dependencies', () => {
id: '',
},
],
resolvedImporters: {},
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([
['>foo/1.0.0>', {
children: {
@@ -62,6 +65,7 @@ test('resolve peer dependencies of cyclic dependencies', () => {
foo: '1.0.0',
bar: '1.0.0',
},
id: '',
},
depth: 2,
}],
@@ -78,6 +82,7 @@ test('resolve peer dependencies of cyclic dependencies', () => {
peerDependencies: {
qar: '1.0.0',
},
id: '',
},
depth: 3,
}],
@@ -115,18 +120,21 @@ test('when a package is referenced twice in the dependencies graph and one of th
peerDependencies: {
qar: '1.0.0',
},
id: '',
}
const barPkg = {
name: 'bar',
depPath: 'bar/1.0.0',
version: '1.0.0',
peerDependencies: {} as Record<string, string>,
id: '',
}
const zooPkg = {
name: 'zoo',
depPath: 'zoo/1.0.0',
version: '1.0.0',
peerDependencies: {} as Record<string, string>,
id: '',
}
const { dependenciesGraph } = resolvePeers({
projects: [
@@ -140,6 +148,7 @@ test('when a package is referenced twice in the dependencies graph and one of th
id: '',
},
],
resolvedImporters: {},
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([
['>zoo/1.0.0>', {
children: {
@@ -186,6 +195,7 @@ test('when a package is referenced twice in the dependencies graph and one of th
depPath: 'qar/1.0.0',
version: '1.0.0',
peerDependencies: {},
id: '',
},
depth: 1,
}],
@@ -211,6 +221,7 @@ describe('peer dependency issues', () => {
peerDependencies: {
peer: '1',
},
id: '',
}
const fooWithOptionalPeer = {
name: 'foo',
@@ -224,6 +235,7 @@ describe('peer dependency issues', () => {
optional: true,
},
},
id: '',
}
const barPkg = {
name: 'bar',
@@ -232,6 +244,7 @@ describe('peer dependency issues', () => {
peerDependencies: {
peer: '2',
},
id: '',
}
const barWithOptionalPeer = {
name: 'bar',
@@ -245,6 +258,7 @@ describe('peer dependency issues', () => {
optional: true,
},
},
id: '',
}
const qarPkg = {
name: 'qar',
@@ -253,6 +267,7 @@ describe('peer dependency issues', () => {
peerDependencies: {
peer: '^2.2.0',
},
id: '',
}
const { peerDependencyIssuesByProjects } = resolvePeers({
projects: [
@@ -309,6 +324,7 @@ describe('peer dependency issues', () => {
id: 'project6',
},
],
resolvedImporters: {},
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([
['>project1>foo/1.0.0>', {
children: {},
@@ -409,6 +425,7 @@ describe('unmet peer dependency issues', () => {
id: 'project1',
},
],
resolvedImporters: {},
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([
['>project1>foo/1.0.0>', {
children: {},
@@ -421,6 +438,7 @@ describe('unmet peer dependency issues', () => {
peer1: '*',
peer2: '>=1',
},
id: '',
},
depth: 0,
}],
@@ -432,6 +450,7 @@ describe('unmet peer dependency issues', () => {
version: '1.0.0-rc.0',
depPath: 'peer/1.0.0-rc.0',
peerDependencies: {},
id: '',
},
depth: 0,
}],
@@ -443,6 +462,7 @@ describe('unmet peer dependency issues', () => {
version: '1.1.0-rc.0',
depPath: 'peer/1.1.0-rc.0',
peerDependencies: {},
id: '',
},
depth: 0,
}],
@@ -470,6 +490,7 @@ describe('unmet peer dependency issue resolved from subdependency', () => {
id: 'project',
},
],
resolvedImporters: {},
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([
['>project>foo/1.0.0>', {
children: {
@@ -482,6 +503,7 @@ describe('unmet peer dependency issue resolved from subdependency', () => {
depPath: 'foo/1.0.0',
version: '1.0.0',
peerDependencies: {},
id: '',
},
depth: 0,
}],
@@ -493,6 +515,7 @@ describe('unmet peer dependency issue resolved from subdependency', () => {
depPath: 'dep/1.0.0',
version: '1.0.0',
peerDependencies: {},
id: '',
},
depth: 1,
}],
@@ -506,6 +529,7 @@ describe('unmet peer dependency issue resolved from subdependency', () => {
peerDependencies: {
dep: '10',
},
id: '',
},
depth: 1,
}],
@@ -526,6 +550,7 @@ test('resolve peer dependencies with npm aliases', () => {
peerDependencies: {
bar: '1.0.0',
},
id: '',
}
const fooAliasPkg = {
name: 'foo',
@@ -534,18 +559,21 @@ test('resolve peer dependencies with npm aliases', () => {
peerDependencies: {
bar: '2.0.0',
},
id: '',
}
const barPkg = {
name: 'bar',
depPath: 'bar/1.0.0',
version: '1.0.0',
peerDependencies: {},
id: '',
}
const barAliasPkg = {
name: 'bar',
depPath: 'bar/2.0.0',
version: '2.0.0',
peerDependencies: {},
id: '',
}
const { dependenciesGraph } = resolvePeers({
projects: [
@@ -561,6 +589,7 @@ test('resolve peer dependencies with npm aliases', () => {
id: '',
},
],
resolvedImporters: {},
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([
['>foo/1.0.0>', {
children: {