mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-23 23:29:17 -05:00
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:
8
.changeset/soft-ghosts-kick.md
Normal file
8
.changeset/soft-ghosts-kick.md
Normal 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).
|
||||
@@ -184,6 +184,7 @@ export interface Config {
|
||||
gitBranchLockfile?: boolean
|
||||
globalDir?: string
|
||||
lockfile?: boolean
|
||||
dedupeInjectedDeps?: boolean
|
||||
}
|
||||
|
||||
export interface ConfigWithDeprecatedSettings extends Config {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -123,6 +123,7 @@ export interface StrictInstallOptions {
|
||||
allProjects: ProjectOptions[]
|
||||
resolveSymlinksInInjectedDirs: boolean
|
||||
dedupeDirectDeps: boolean
|
||||
dedupeInjectedDeps: boolean
|
||||
dedupePeerDependents: boolean
|
||||
extendNodePath: boolean
|
||||
excludeLinksFromLockfile: boolean
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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`)
|
||||
}
|
||||
})
|
||||
|
||||
96
pkg-manager/resolve-dependencies/src/dedupeInjectedDeps.ts
Normal file
96
pkg-manager/resolve-dependencies/src/dedupeInjectedDeps.ts
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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[]> = {}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user