diff --git a/.changeset/flat-insects-act.md b/.changeset/flat-insects-act.md new file mode 100644 index 0000000000..4831d42558 --- /dev/null +++ b/.changeset/flat-insects-act.md @@ -0,0 +1,8 @@ +--- +"@pnpm/hoist": major +"@pnpm/headless": patch +"@pnpm/core": patch +"pnpm": patch +--- + +Fixed hoisting with `enableGlobalVirtualStore` set to `true` [#9648](https://github.com/pnpm/pnpm/pull/9648). diff --git a/.changeset/odd-mammals-battle.md b/.changeset/odd-mammals-battle.md new file mode 100644 index 0000000000..a94bb9bce8 --- /dev/null +++ b/.changeset/odd-mammals-battle.md @@ -0,0 +1,5 @@ +--- +"@pnpm/deps.graph-builder": minor +--- + +New option added: includeUnchangedDeps. diff --git a/deps/graph-builder/src/lockfileToDepGraph.ts b/deps/graph-builder/src/lockfileToDepGraph.ts index 04ec9ea310..3cb1ebe596 100644 --- a/deps/graph-builder/src/lockfileToDepGraph.ts +++ b/deps/graph-builder/src/lockfileToDepGraph.ts @@ -58,6 +58,7 @@ export interface LockfileToDepGraphOptions { force: boolean importerIds: ProjectId[] include: IncludedDependencies + includeUnchangedDeps?: boolean ignoreScripts: boolean lockfileDir: string nodeVersion: string @@ -190,9 +191,12 @@ async function buildGraphFromPackages ( locationByDepPath[depPath] = dir let dirExists: boolean | undefined - if (depIsPresent && + if ( + depIsPresent && isEmpty(currentPackages[depPath].optionalDependencies ?? {}) && - isEmpty(pkgSnapshot.optionalDependencies ?? {})) { + isEmpty(pkgSnapshot.optionalDependencies ?? {}) && + !opts.includeUnchangedDeps + ) { dirExists = await pathExists(dir) if (dirExists) return brokenModulesLogger.debug({ missing: dir }) diff --git a/pkg-manager/core/src/install/link.ts b/pkg-manager/core/src/install/link.ts index 084fc7b542..07fc78748c 100644 --- a/pkg-manager/core/src/install/link.ts +++ b/pkg-manager/core/src/install/link.ts @@ -35,7 +35,6 @@ import pathExists from 'path-exists' import equals from 'ramda/src/equals' import isEmpty from 'ramda/src/isEmpty' import difference from 'ramda/src/difference' -import omit from 'ramda/src/omit' import pick from 'ramda/src/pick' import pickBy from 'ramda/src/pickBy' import props from 'ramda/src/props' @@ -214,35 +213,33 @@ export async function linkPackages (projects: ImporterToUpdate[], depGraph: Depe if (opts.hoistPattern == null && opts.publicHoistPattern == null) { newHoistedDependencies = {} } else if (newDepPaths.length > 0 || removedDepPaths.size > 0) { - // It is important to keep the skipped packages in the lockfile which will be saved as the "current lockfile". - // pnpm is comparing the current lockfile to the wanted one and they should match. - // But for hoisting, we need a version of the lockfile w/o the skipped packages, so we're making a copy. - const hoistLockfile = { - ...currentLockfile, - packages: currentLockfile.packages != null ? omit(Array.from(opts.skipped), currentLockfile.packages) : {}, - } - newHoistedDependencies = await hoist({ - extraNodePath: opts.extraNodePaths, - lockfile: hoistLockfile, - importerIds: projectIds, - privateHoistedModulesDir: opts.hoistedModulesDir, - privateHoistPattern: opts.hoistPattern ?? [], - publicHoistedModulesDir: opts.rootModulesDir, - publicHoistPattern: opts.publicHoistPattern ?? [], - virtualStoreDir: opts.virtualStoreDir, - virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength, - hoistedWorkspacePackages: opts.hoistWorkspacePackages - ? projects.reduce((hoistedWorkspacePackages, project) => { - if (project.manifest.name && project.id !== '.') { - hoistedWorkspacePackages[project.id] = { - dir: project.rootDir, - name: project.manifest.name, + newHoistedDependencies = { + ...opts.hoistedDependencies, + ...await hoist({ + extraNodePath: opts.extraNodePaths, + graph: depGraph, + directDepsByImporterId: opts.dependenciesByProjectId, + importerIds: projectIds, + privateHoistedModulesDir: opts.hoistedModulesDir, + privateHoistPattern: opts.hoistPattern ?? [], + publicHoistedModulesDir: opts.rootModulesDir, + publicHoistPattern: opts.publicHoistPattern ?? [], + virtualStoreDir: opts.virtualStoreDir, + virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength, + hoistedWorkspacePackages: opts.hoistWorkspacePackages + ? projects.reduce((hoistedWorkspacePackages, project) => { + if (project.manifest.name && project.id !== '.') { + hoistedWorkspacePackages[project.id] = { + dir: project.rootDir, + name: project.manifest.name, + } } - } - return hoistedWorkspacePackages - }, {} as Record) - : undefined, - }) + return hoistedWorkspacePackages + }, {} as Record) + : undefined, + skipped: opts.skipped, + }), + } } else { newHoistedDependencies = opts.hoistedDependencies } diff --git a/pkg-manager/core/test/install/globalVirtualStore.ts b/pkg-manager/core/test/install/globalVirtualStore.ts index d5d1da2e93..ec02f666f8 100644 --- a/pkg-manager/core/test/install/globalVirtualStore.ts +++ b/pkg-manager/core/test/install/globalVirtualStore.ts @@ -16,11 +16,11 @@ test('using a global virtual store', async () => { await install(manifest, testDefaults({ enableGlobalVirtualStore: true, virtualStoreDir: globalVirtualStoreDir, - privateHoistPattern: '*', + hoistPattern: ['*'], })) { - expect(fs.existsSync(path.resolve('node_modules/.pnpm/node_modules/@pnpm.e2e/dep-of-pkg-with-1-dep'))) + expect(fs.existsSync(path.resolve('node_modules/.pnpm/node_modules/@pnpm.e2e/dep-of-pkg-with-1-dep/package.json'))).toBeTruthy() expect(fs.existsSync(path.resolve('node_modules/.pnpm/lock.yaml'))).toBeTruthy() const files = fs.readdirSync(path.join(globalVirtualStoreDir, '@pnpm.e2e/pkg-with-1-dep/100.0.0')) expect(files.length).toBe(1) @@ -34,10 +34,11 @@ test('using a global virtual store', async () => { enableGlobalVirtualStore: true, virtualStoreDir: globalVirtualStoreDir, frozenLockfile: true, + hoistPattern: ['*'], })) { - expect(fs.existsSync(path.resolve('node_modules/.pnpm/node_modules/@pnpm.e2e/dep-of-pkg-with-1-dep'))) + expect(fs.existsSync(path.resolve('node_modules/.pnpm/node_modules/@pnpm.e2e/dep-of-pkg-with-1-dep/package.json'))).toBeTruthy() expect(fs.existsSync(path.resolve('node_modules/.pnpm/lock.yaml'))).toBeTruthy() const files = fs.readdirSync(path.join(globalVirtualStoreDir, '@pnpm.e2e/pkg-with-1-dep/100.0.0')) expect(files.length).toBe(1) diff --git a/pkg-manager/core/test/install/hoist.ts b/pkg-manager/core/test/install/hoist.ts index 7b208ab920..2821649dec 100644 --- a/pkg-manager/core/test/install/hoist.ts +++ b/pkg-manager/core/test/install/hoist.ts @@ -512,6 +512,7 @@ test('hoist when updating in one of the workspace projects', async () => { const modulesManifest = rootModules.readModulesManifest() expect(modulesManifest?.hoistedDependencies).toStrictEqual({ '@pnpm.e2e/dep-of-pkg-with-1-dep@100.0.0': { '@pnpm.e2e/dep-of-pkg-with-1-dep': 'private' }, + '@pnpm.e2e/foo@100.0.0': { '@pnpm.e2e/foo': 'private' }, }) } }) diff --git a/pkg-manager/headless/src/index.ts b/pkg-manager/headless/src/index.ts index 5beb8b0c08..3fe12a3985 100644 --- a/pkg-manager/headless/src/index.ts +++ b/pkg-manager/headless/src/index.ts @@ -139,6 +139,8 @@ export interface HeadlessOptions { hoistedDependencies: HoistedDependencies hoistPattern?: string[] publicHoistPattern?: string[] + currentHoistPattern?: string[] + currentPublicHoistPattern?: string[] currentHoistedLocations?: Record lockfileDir: string modulesDir?: string @@ -331,6 +333,8 @@ export async function headlessInstall (opts: HeadlessOptions): Promise { - if (project.manifest.name && project.id !== '.') { - hoistedWorkspacePackages[project.id] = { - dir: project.rootDir, - name: project.manifest.name, + newHoistedDependencies = { + ...opts.hoistedDependencies, + ...await hoist({ + extraNodePath: opts.extraNodePaths, + graph, + directDepsByImporterId: Object.fromEntries(Object.entries(directDependenciesByImporterId).map(([projectId, deps]) => [ + projectId, + new Map(Object.entries(deps)), + ])), + importerIds, + preferSymlinkedExecutables: opts.preferSymlinkedExecutables, + privateHoistedModulesDir: hoistedModulesDir, + privateHoistPattern: opts.hoistPattern ?? [], + publicHoistedModulesDir, + publicHoistPattern: opts.publicHoistPattern ?? [], + virtualStoreDir, + virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength, + hoistedWorkspacePackages: opts.hoistWorkspacePackages + ? Object.values(opts.allProjects).reduce((hoistedWorkspacePackages, project) => { + if (project.manifest.name && project.id !== '.') { + hoistedWorkspacePackages[project.id] = { + dir: project.rootDir, + name: project.manifest.name, + } } - } - return hoistedWorkspacePackages - }, {} as Record) - : undefined, - }) + return hoistedWorkspacePackages + }, {} as Record) + : undefined, + skipped: opts.skipped, + }), + } } else { newHoistedDependencies = {} } diff --git a/pkg-manager/hoist/package.json b/pkg-manager/hoist/package.json index 6f1f3ddf6c..66b9c972c4 100644 --- a/pkg-manager/hoist/package.json +++ b/pkg-manager/hoist/package.json @@ -35,11 +35,7 @@ "dependencies": { "@pnpm/constants": "workspace:*", "@pnpm/core-loggers": "workspace:*", - "@pnpm/dependency-path": "workspace:*", "@pnpm/link-bins": "workspace:*", - "@pnpm/lockfile.types": "workspace:*", - "@pnpm/lockfile.utils": "workspace:*", - "@pnpm/lockfile.walker": "workspace:*", "@pnpm/matcher": "workspace:*", "@pnpm/types": "workspace:*", "@pnpm/util.lex-comparator": "catalog:", diff --git a/pkg-manager/hoist/src/index.ts b/pkg-manager/hoist/src/index.ts index efb0e8a6c4..d84e336cec 100644 --- a/pkg-manager/hoist/src/index.ts +++ b/pkg-manager/hoist/src/index.ts @@ -3,37 +3,46 @@ import path from 'path' import { linkLogger } from '@pnpm/core-loggers' import { WANTED_LOCKFILE } from '@pnpm/constants' import { linkBinsOfPkgsByAliases, type WarnFunction } from '@pnpm/link-bins' -import { - type LockfileObject, - nameVerFromPkgSnapshot, -} from '@pnpm/lockfile.utils' -import { lockfileWalker, type LockfileWalkerStep } from '@pnpm/lockfile.walker' import { logger } from '@pnpm/logger' import { createMatcher } from '@pnpm/matcher' -import { type DepPath, type HoistedDependencies, type ProjectId } from '@pnpm/types' +import { type DepPath, type HoistedDependencies, type ProjectId, type DependenciesField } from '@pnpm/types' import { lexCompare } from '@pnpm/util.lex-comparator' -import * as dp from '@pnpm/dependency-path' import isSubdir from 'is-subdir' -import mapObjIndexed from 'ramda/src/mapObjIndexed' import resolveLinkTarget from 'resolve-link-target' import symlinkDir from 'symlink-dir' +export interface DependenciesGraphNode { + dir: string + children: Record + optionalDependencies: Set + hasBin: boolean + name: string + depPath: DepPath +} + +export type DependenciesGraph = Record> + +export interface DirectDependenciesByImporterId { + [importerId: string]: Map +} + const hoistLogger = logger('hoist') -export interface HoistOpts extends GetHoistedDependenciesOpts { +export interface HoistOpts extends GetHoistedDependenciesOpts { extraNodePath?: string[] preferSymlinkedExecutables?: boolean virtualStoreDir: string virtualStoreDirMaxLength: number } -export async function hoist (opts: HoistOpts): Promise { +export async function hoist (opts: HoistOpts): Promise { const result = getHoistedDependencies(opts) - if (!result) return {} - const { hoistedDependencies, hoistedAliasesWithBins } = result + if (!result) return null + const { hoistedDependencies, hoistedAliasesWithBins, hoistedDependenciesByNodeId } = result - await symlinkHoistedDependencies(hoistedDependencies, { - lockfile: opts.lockfile, + await symlinkHoistedDependencies(hoistedDependenciesByNodeId, { + graph: opts.graph, + directDepsByImporterId: opts.directDepsByImporterId, privateHoistedModulesDir: opts.privateHoistedModulesDir, publicHoistedModulesDir: opts.publicHoistedModulesDir, virtualStoreDir: opts.virtualStoreDir, @@ -55,8 +64,10 @@ export async function hoist (opts: HoistOpts): Promise { return hoistedDependencies } -export interface GetHoistedDependenciesOpts { - lockfile: LockfileObject +export interface GetHoistedDependenciesOpts { + graph: DependenciesGraph + skipped: Set + directDepsByImporterId: DirectDependenciesByImporterId importerIds?: ProjectId[] privateHoistPattern: string[] privateHoistedModulesDir: string @@ -70,12 +81,11 @@ export interface HoistedWorkspaceProject { dir: string } -export function getHoistedDependencies (opts: GetHoistedDependenciesOpts): HoistGraphResult | null { - if (opts.lockfile.packages == null) return null - - const { directDeps, step } = lockfileWalker( - opts.lockfile, - opts.importerIds ?? Object.keys(opts.lockfile.importers) as ProjectId[] +export function getHoistedDependencies (opts: GetHoistedDependenciesOpts): HoistGraphResult | null { + if (Object.keys(opts.graph ?? {}).length === 0) return null + const { directDeps, step } = graphWalker( + opts.graph, + opts.directDepsByImporterId ) // We want to hoist all the workspace packages, not only those that are in the dependencies // of any other workspace packages. @@ -85,19 +95,19 @@ export function getHoistedDependencies (opts: GetHoistedDependenciesOpts): Hoist Object.entries(opts.hoistedWorkspacePackages ?? {}) .map(([id, { name }]) => [name, id as ProjectId]) ) - const deps: Dependency[] = [ + const deps: Array> = [ { children: { ...hoistedWorkspaceDeps, ...directDeps - .reduce((acc, { alias, depPath }) => { + .reduce((acc, { alias, nodeId }) => { if (!acc[alias]) { - acc[alias] = depPath + acc[alias] = nodeId } return acc - }, {} as Record), + }, {} as Record), }, - depPath: '', + nodeId: '' as T, depth: -1, }, ...getDependencies(0, step), @@ -105,9 +115,10 @@ export function getHoistedDependencies (opts: GetHoistedDependenciesOpts): Hoist const getAliasHoistType = createGetAliasHoistType(opts.publicHoistPattern, opts.privateHoistPattern) - return hoistGraph(deps, opts.lockfile.importers['.' as ProjectId]?.specifiers ?? {}, { + return hoistGraph(deps, opts.directDepsByImporterId['.' as ProjectId] ?? new Map(), { getAliasHoistType, - lockfile: opts.lockfile, + graph: opts.graph, + skipped: opts.skipped, }) } @@ -154,20 +165,16 @@ async function linkAllBins (modulesDir: string, opts: LinkAllBinsOptions): Promi } } -function getDependencies ( +function getDependencies ( depth: number, - step: LockfileWalkerStep -): Dependency[] { - const deps: Dependency[] = [] - const nextSteps: LockfileWalkerStep[] = [] - for (const { pkgSnapshot, depPath, next } of step.dependencies) { - const allDeps: Record = { - ...pkgSnapshot.dependencies, - ...pkgSnapshot.optionalDependencies, - } + step: GraphWalkerStep +): Array> { + const deps: Array> = [] + const nextSteps: Array> = [] + for (const { node, nodeId, next } of step.dependencies) { deps.push({ - children: mapObjIndexed(dp.refToRelative, allDeps) as Record, - depPath, + children: node.children, + nodeId, depth, }) @@ -182,42 +189,47 @@ function getDependencies ( return [ ...deps, - ...nextSteps.flatMap(getDependencies.bind(null, depth + 1)), + ...(nextSteps.flatMap(getDependencies.bind(null, depth + 1)) as Array>), ] } -export interface Dependency { - children: Record - depPath: string +export interface Dependency { + children: Record + nodeId: T depth: number } -interface HoistGraphResult { +interface HoistGraphResult { hoistedDependencies: HoistedDependencies + hoistedDependenciesByNodeId: HoistedDependenciesByNodeId hoistedAliasesWithBins: string[] } -function hoistGraph ( - depNodes: Dependency[], - currentSpecifiers: Record, +type HoistedDependenciesByNodeId = Map> + +function hoistGraph ( + depNodes: Array>, + currentSpecifiers: Map, opts: { getAliasHoistType: GetAliasHoistType - lockfile: LockfileObject + graph: DependenciesGraph + skipped: Set } -): HoistGraphResult { - const hoistedAliases = new Set(Object.keys(currentSpecifiers)) - const hoistedDependencies: HoistedDependencies = {} +): HoistGraphResult { + const hoistedAliases = new Set(currentSpecifiers.keys()) + const hoistedDependencies: HoistedDependencies = Object.create(null) + const hoistedDependenciesByNodeId: HoistedDependenciesByNodeId = new Map() const hoistedAliasesWithBins = new Set() depNodes // sort by depth and then alphabetically .sort((a, b) => { const depthDiff = a.depth - b.depth - return depthDiff === 0 ? lexCompare(a.depPath, b.depPath) : depthDiff + return depthDiff === 0 ? lexCompare(a.nodeId, b.nodeId) : depthDiff }) // build the alias map and the id map .forEach((depNode) => { - for (const [childAlias, childPath] of Object.entries(depNode.children)) { + for (const [childAlias, childNodeId] of Object.entries(depNode.children)) { const hoist = opts.getAliasHoistType(childAlias) if (!hoist) continue const childAliasNormalized = childAlias.toLowerCase() @@ -225,24 +237,37 @@ function hoistGraph ( if (hoistedAliases.has(childAliasNormalized)) { continue } - if (opts.lockfile.packages?.[childPath as DepPath]?.hasBin) { + if (!hoistedDependenciesByNodeId.has(childNodeId)) { + hoistedDependenciesByNodeId.set(childNodeId, {}) + } + hoistedDependenciesByNodeId.get(childNodeId)![childAlias] = hoist + const node = opts.graph[childNodeId as T] + if (node?.depPath == null || opts.skipped.has(node.depPath)) { + continue + } + if (node.hasBin) { hoistedAliasesWithBins.add(childAlias) } hoistedAliases.add(childAliasNormalized) - if (!hoistedDependencies[childPath]) { - hoistedDependencies[childPath] = {} + if (!hoistedDependencies[node.depPath]) { + hoistedDependencies[node.depPath] = {} } - hoistedDependencies[childPath][childAlias] = hoist + hoistedDependencies[node.depPath][childAlias] = hoist } }) - return { hoistedDependencies, hoistedAliasesWithBins: Array.from(hoistedAliasesWithBins) } + return { + hoistedDependencies, + hoistedDependenciesByNodeId, + hoistedAliasesWithBins: Array.from(hoistedAliasesWithBins), + } } -async function symlinkHoistedDependencies ( - hoistedDependencies: HoistedDependencies, +async function symlinkHoistedDependencies ( + hoistedDependenciesByNodeId: HoistedDependenciesByNodeId, opts: { - lockfile: LockfileObject + graph: DependenciesGraph + directDepsByImporterId: DirectDependenciesByImporterId privateHoistedModulesDir: string publicHoistedModulesDir: string virtualStoreDir: string @@ -251,32 +276,31 @@ async function symlinkHoistedDependencies ( } ): Promise { const symlink = symlinkHoistedDependency.bind(null, opts) - await Promise.all( - Object.entries(hoistedDependencies) - .map(async ([hoistedDepId, pkgAliases]) => { - const pkgSnapshot = opts.lockfile.packages![hoistedDepId as DepPath] - let depLocation!: string - if (pkgSnapshot) { - const pkgName = nameVerFromPkgSnapshot(hoistedDepId, pkgSnapshot).name - const modules = path.join(opts.virtualStoreDir, dp.depPathToFilename(hoistedDepId, opts.virtualStoreDirMaxLength), 'node_modules') - depLocation = path.join(modules, pkgName as string) - } else { - if (!opts.lockfile.importers[hoistedDepId as ProjectId]) { - // This dependency is probably a skipped optional dependency. - hoistLogger.debug({ hoistFailedFor: hoistedDepId }) - return - } - depLocation = opts.hoistedWorkspacePackages![hoistedDepId].dir + const promises: Array> = [] + for (const [hoistedDepNodeId, pkgAliases] of hoistedDependenciesByNodeId.entries()) { + promises.push((async () => { + const node = opts.graph[hoistedDepNodeId as T] + let depLocation!: string + if (node) { + depLocation = node.dir + } else { + if (!opts.directDepsByImporterId[hoistedDepNodeId as ProjectId]) { + // This dependency is probably a skipped optional dependency. + hoistLogger.debug({ hoistFailedFor: hoistedDepNodeId }) + return } - await Promise.all(Object.entries(pkgAliases).map(async ([pkgAlias, hoistType]) => { - const targetDir = hoistType === 'public' - ? opts.publicHoistedModulesDir - : opts.privateHoistedModulesDir - const dest = path.join(targetDir, pkgAlias) - return symlink(depLocation, dest) - })) - }) - ) + depLocation = opts.hoistedWorkspacePackages![hoistedDepNodeId].dir + } + await Promise.all(Object.entries(pkgAliases).map(async ([pkgAlias, hoistType]) => { + const targetDir = hoistType === 'public' + ? opts.publicHoistedModulesDir + : opts.privateHoistedModulesDir + const dest = path.join(targetDir, pkgAlias) + return symlink(depLocation, dest) + })) + })()) + } + await Promise.all(promises) } async function symlinkHoistedDependency ( @@ -313,3 +337,107 @@ async function symlinkHoistedDependency ( await symlinkDir(depLocation, dest) linkLogger.debug({ target: dest, link: depLocation }) } + +export function graphWalker ( + graph: DependenciesGraph, + directDepsByImporterId: DirectDependenciesByImporterId, + opts?: { + include?: { [dependenciesField in DependenciesField]: boolean } + skipped?: Set + } +): GraphWalker { + const startNodeIds = [] as T[] + const allDirectDeps = [] as Array<{ alias: string, nodeId: T }> + + for (const directDeps of Object.values(directDepsByImporterId)) { + for (const [alias, nodeId] of directDeps.entries()) { + const depNode = graph[nodeId] + if (depNode == null) continue + startNodeIds.push(nodeId) + allDirectDeps.push({ alias, nodeId }) + } + } + const visited = new Set() + return { + directDeps: allDirectDeps, + step: makeStep({ + includeOptionalDependencies: opts?.include?.optionalDependencies !== false, + graph, + visited, + skipped: opts?.skipped, + }, startNodeIds), + } +} + +function makeStep ( + ctx: { + includeOptionalDependencies: boolean + graph: DependenciesGraph + visited: Set + skipped?: Set + }, + nextNodeIds: T[] +): GraphWalkerStep { + const result: GraphWalkerStep = { + dependencies: [], + links: [], + missing: [], + } + const _next = collectChildNodeIds.bind(null, { + includeOptionalDependencies: ctx.includeOptionalDependencies, + }) + for (const nodeId of nextNodeIds) { + if (ctx.visited.has(nodeId)) continue + ctx.visited.add(nodeId) + const node = ctx.graph[nodeId] + if (node == null) { + if (nodeId.startsWith('link:')) { + result.links.push(nodeId) + continue + } + result.missing.push(nodeId) + continue + } + if (ctx.skipped?.has(node.depPath)) continue + result.dependencies.push({ + nodeId, + next: () => makeStep(ctx, _next(node) as T[]), + node, + }) + } + return result +} + +function collectChildNodeIds (opts: { includeOptionalDependencies: boolean }, nextPkg: DependenciesGraphNode): T[] { + if (opts.includeOptionalDependencies) { + return Object.values(nextPkg.children) + } else { + const nextNodeIds: T[] = [] + for (const [alias, nodeId] of Object.entries(nextPkg.children)) { + if (!nextPkg.optionalDependencies.has(alias)) { + nextNodeIds.push(nodeId) + } + } + return nextNodeIds + } +} + +export interface GraphWalker { + directDeps: Array<{ + alias: string + nodeId: T + }> + step: GraphWalkerStep +} + +export interface GraphWalkerStep { + dependencies: Array> + links: string[] + missing: string[] +} + +export interface GraphDependency { + nodeId: T + node: DependenciesGraphNode + next: () => GraphWalkerStep +} diff --git a/pkg-manager/hoist/tsconfig.json b/pkg-manager/hoist/tsconfig.json index 0cbc5c261c..4a30dee270 100644 --- a/pkg-manager/hoist/tsconfig.json +++ b/pkg-manager/hoist/tsconfig.json @@ -12,24 +12,12 @@ { "path": "../../config/matcher" }, - { - "path": "../../lockfile/types" - }, - { - "path": "../../lockfile/utils" - }, - { - "path": "../../lockfile/walker" - }, { "path": "../../packages/constants" }, { "path": "../../packages/core-loggers" }, - { - "path": "../../packages/dependency-path" - }, { "path": "../../packages/logger" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7cf367e4ed..6ca030b909 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,6 +33,9 @@ catalogs: '@pnpm/log.group': specifier: 3.0.1 version: 3.0.1 + '@pnpm/logger': + specifier: '>=1001.0.0 <1002.0.0' + version: 1001.0.0 '@pnpm/meta-updater': specifier: 2.0.6 version: 2.0.6 @@ -5025,21 +5028,9 @@ importers: '@pnpm/core-loggers': specifier: workspace:* version: link:../../packages/core-loggers - '@pnpm/dependency-path': - specifier: workspace:* - version: link:../../packages/dependency-path '@pnpm/link-bins': specifier: workspace:* version: link:../link-bins - '@pnpm/lockfile.types': - specifier: workspace:* - version: link:../../lockfile/types - '@pnpm/lockfile.utils': - specifier: workspace:* - version: link:../../lockfile/utils - '@pnpm/lockfile.walker': - specifier: workspace:* - version: link:../../lockfile/walker '@pnpm/matcher': specifier: workspace:* version: link:../../config/matcher