refactor: create a separate type for NodeId (#8092)

This commit is contained in:
Zoltan Kochan
2024-05-17 11:23:52 +02:00
committed by GitHub
parent ef73c19f15
commit 45f4262f03
25 changed files with 210 additions and 185 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/types": minor
---
Add PkgResolutionId.

View File

@@ -1,11 +1,12 @@
import { type PackageSnapshot } from '@pnpm/lockfile-types' import { type PackageSnapshot } from '@pnpm/lockfile-types'
import * as dp from '@pnpm/dependency-path' import * as dp from '@pnpm/dependency-path'
import { type PkgResolutionId } from '@pnpm/types'
export interface NameVer { export interface NameVer {
name: string name: string
peersSuffix: string | undefined peersSuffix: string | undefined
version: string version: string
nonSemverVersion?: string nonSemverVersion?: PkgResolutionId
} }
export function nameVerFromPkgSnapshot ( export function nameVerFromPkgSnapshot (

View File

@@ -1,5 +1,5 @@
import { createBase32Hash } from '@pnpm/crypto.base32-hash' import { createBase32Hash } from '@pnpm/crypto.base32-hash'
import { type Registries } from '@pnpm/types' import { type PkgResolutionId, type Registries } from '@pnpm/types'
import semver from 'semver' import semver from 'semver'
export function isAbsolute (dependencyPath: string): boolean { export function isAbsolute (dependencyPath: string): boolean {
@@ -85,7 +85,7 @@ export interface DependencyPath {
name?: string name?: string
peersSuffix?: string peersSuffix?: string
version?: string version?: string
nonSemverVersion?: string nonSemverVersion?: PkgResolutionId
} }
export function parse (dependencyPath: string): DependencyPath { export function parse (dependencyPath: string): DependencyPath {
@@ -121,7 +121,7 @@ export function parse (dependencyPath: string): DependencyPath {
} }
return { return {
name, name,
nonSemverVersion: version, nonSemverVersion: version as PkgResolutionId,
peersSuffix, peersSuffix,
} }
} }

View File

@@ -31,3 +31,5 @@ export interface PatchFile {
path: string path: string
hash: string hash: string
} }
export type PkgResolutionId = string & { __brand: 'PkgResolutionId' }

View File

@@ -209,7 +209,7 @@ async function resolveAndFetch (
normalizedPref = resolveResult.normalizedPref normalizedPref = resolveResult.normalizedPref
} }
const id = pkgId as string const id = pkgId!
if (resolution.type === 'directory' && !id.startsWith('file:')) { if (resolution.type === 'directory' && !id.startsWith('file:')) {
if (manifest == null) { if (manifest == null) {

View File

@@ -15,7 +15,7 @@ import loadJsonFile from 'load-json-file'
import nock from 'nock' import nock from 'nock'
import normalize from 'normalize-path' import normalize from 'normalize-path'
import tempy from 'tempy' import tempy from 'tempy'
import { type PkgRequestFetchResult } from '@pnpm/store-controller-types' import { type PkgResolutionId, type PkgRequestFetchResult } from '@pnpm/store-controller-types'
const registry = `http://localhost:${REGISTRY_MOCK_PORT}` const registry = `http://localhost:${REGISTRY_MOCK_PORT}`
const f = fixtures(__dirname) const f = fixtures(__dirname)
@@ -128,7 +128,7 @@ test('request package but skip fetching, when resolution is already available',
const projectDir = tempy.directory() const projectDir = tempy.directory()
const pkgResponse = await requestPackage({ alias: 'is-positive', pref: '1.0.0' }, { const pkgResponse = await requestPackage({ alias: 'is-positive', pref: '1.0.0' }, {
currentPkg: { currentPkg: {
id: 'is-positive@1.0.0', id: 'is-positive@1.0.0' as PkgResolutionId,
resolution: { resolution: {
integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==', integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==',
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`, tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,
@@ -197,7 +197,7 @@ test('refetch local tarball if its integrity has changed', async () => {
const response = await requestPackage(wantedPackage, { const response = await requestPackage(wantedPackage, {
...requestPackageOpts, ...requestPackageOpts,
currentPkg: { currentPkg: {
id: pkgId, id: pkgId as PkgResolutionId,
resolution: { resolution: {
integrity: 'sha512-lqODmYcc/FKOGROEUByd5Sbugqhzgkv+Hij9PXH0sZVQsU2npTQ0x3L81GCtHilFKme8lhBtD31Vxg/AKYrAvg==', integrity: 'sha512-lqODmYcc/FKOGROEUByd5Sbugqhzgkv+Hij9PXH0sZVQsU2npTQ0x3L81GCtHilFKme8lhBtD31Vxg/AKYrAvg==',
tarball, tarball,
@@ -229,7 +229,7 @@ test('refetch local tarball if its integrity has changed', async () => {
const response = await requestPackage(wantedPackage, { const response = await requestPackage(wantedPackage, {
...requestPackageOpts, ...requestPackageOpts,
currentPkg: { currentPkg: {
id: pkgId, id: pkgId as PkgResolutionId,
resolution: { resolution: {
integrity: 'sha512-lqODmYcc/FKOGROEUByd5Sbugqhzgkv+Hij9PXH0sZVQsU2npTQ0x3L81GCtHilFKme8lhBtD31Vxg/AKYrAvg==', integrity: 'sha512-lqODmYcc/FKOGROEUByd5Sbugqhzgkv+Hij9PXH0sZVQsU2npTQ0x3L81GCtHilFKme8lhBtD31Vxg/AKYrAvg==',
tarball, tarball,
@@ -256,7 +256,7 @@ test('refetch local tarball if its integrity has changed', async () => {
const response = await requestPackage(wantedPackage, { const response = await requestPackage(wantedPackage, {
...requestPackageOpts, ...requestPackageOpts,
currentPkg: { currentPkg: {
id: pkgId, id: pkgId as PkgResolutionId,
resolution: { resolution: {
integrity: 'sha512-v3uhYkN+Eh3Nus4EZmegjQhrfpdPIH+2FjrkeBc6ueqZJWWRaLnSYIkD0An6m16D3v+6HCE18ox6t95eGxj5Pw==', integrity: 'sha512-v3uhYkN+Eh3Nus4EZmegjQhrfpdPIH+2FjrkeBc6ueqZJWWRaLnSYIkD0An6m16D3v+6HCE18ox6t95eGxj5Pw==',
tarball, tarball,
@@ -588,7 +588,7 @@ test('always return a package manifest in the response', async () => {
{ {
const pkgResponse = await requestPackage({ alias: 'is-positive', pref: '1.0.0' }, { const pkgResponse = await requestPackage({ alias: 'is-positive', pref: '1.0.0' }, {
currentPkg: { currentPkg: {
id: 'is-positive@1.0.0', id: 'is-positive@1.0.0' as PkgResolutionId,
resolution: { resolution: {
integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==', integrity: 'sha512-xxzPGZ4P2uN6rROUa5N9Z7zTX6ERuE0hs6GUOc/cKBLF2NqKc16UwqHMt3tFg4CO6EBTE5UecUasg+3jZx3Ckg==',
tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`, tarball: `http://localhost:${REGISTRY_MOCK_PORT}/is-positive/-/is-positive-1.0.0.tgz`,

View File

@@ -1,6 +0,0 @@
// The only reason package IDs are encoded is to avoid '>' signs.
// Otherwise, it would be impossible to split the node ID back to package IDs reliably.
// See issue https://github.com/pnpm/pnpm/issues/986
export function encodePkgId (pkgId: string): string {
return pkgId.replaceAll('%', '%25').replaceAll('>', '%3E')
}

View File

@@ -26,6 +26,7 @@ import zipWith from 'ramda/src/zipWith'
import isSubdir from 'is-subdir' import isSubdir from 'is-subdir'
import { getWantedDependencies, type WantedDependency } from './getWantedDependencies' import { getWantedDependencies, type WantedDependency } from './getWantedDependencies'
import { depPathToRef } from './depPathToRef' import { depPathToRef } from './depPathToRef'
import { type NodeId } from './nextNodeId'
import { createNodeIdForLinkedLocalPkg, type UpdateMatchingFunction } from './resolveDependencies' import { createNodeIdForLinkedLocalPkg, type UpdateMatchingFunction } from './resolveDependencies'
import { import {
type Importer, type Importer,
@@ -60,7 +61,7 @@ export {
interface ProjectToLink { interface ProjectToLink {
binsDir: string binsDir: string
directNodeIdsByAlias: { [alias: string]: string } directNodeIdsByAlias: { [alias: string]: NodeId }
id: string id: string
linkedDependencies: LinkedDependency[] linkedDependencies: LinkedDependency[]
manifest: ProjectManifest manifest: ProjectManifest

View File

@@ -1,5 +1,9 @@
let nodeIdCounter = 0 let nodeIdCounter = 0
export function nextNodeId (): string { type Brand<K, T> = K & { __brand: T }
return (++nodeIdCounter).toString()
export type NodeId = Brand<string, 'nodeId'>
export function nextNodeId (): NodeId {
return (++nodeIdCounter).toString() as NodeId
} }

View File

@@ -22,6 +22,7 @@ import {
type PreferredVersions, type PreferredVersions,
type Resolution, type Resolution,
type WorkspacePackages, type WorkspacePackages,
type PkgResolutionId,
} from '@pnpm/resolver-base' } from '@pnpm/resolver-base'
import { import {
type PkgRequestFetchResult, type PkgRequestFetchResult,
@@ -47,10 +48,9 @@ import pickBy from 'ramda/src/pickBy'
import omit from 'ramda/src/omit' import omit from 'ramda/src/omit'
import zipWith from 'ramda/src/zipWith' import zipWith from 'ramda/src/zipWith'
import semver from 'semver' import semver from 'semver'
import { encodePkgId } from './encodePkgId'
import { getNonDevWantedDependencies, type WantedDependency } from './getNonDevWantedDependencies' import { getNonDevWantedDependencies, type WantedDependency } from './getNonDevWantedDependencies'
import { safeIntersect } from './mergePeers' import { safeIntersect } from './mergePeers'
import { nextNodeId } from './nextNodeId' import { type NodeId, nextNodeId } from './nextNodeId'
import { parentIdsContainSequence } from './parentIdsContainSequence' import { parentIdsContainSequence } from './parentIdsContainSequence'
import { hoistPeers, getHoistableOptionalPeers } from './hoistPeers' import { hoistPeers, getHoistableOptionalPeers } from './hoistPeers'
import { wantedDepIsLocallyAvailable } from './wantedDepIsLocallyAvailable' import { wantedDepIsLocallyAvailable } from './wantedDepIsLocallyAvailable'
@@ -74,7 +74,7 @@ export function getPkgsInfoFromIds (
// child nodeId by child alias name in case of non-linked deps // child nodeId by child alias name in case of non-linked deps
export interface ChildrenMap { export interface ChildrenMap {
[alias: string]: string [alias: string]: NodeId
} }
export type DependenciesTreeNode<T> = { export type DependenciesTreeNode<T> = {
@@ -92,7 +92,7 @@ export type DependenciesTree<T> = Map<
// a node ID is the join of the package's keypath with a colon // a node ID is the join of the package's keypath with a colon
// E.g., a subdeps node ID which parent is `foo` will be // E.g., a subdeps node ID which parent is `foo` will be
// registry.npmjs.org/foo/1.0.0:registry.npmjs.org/bar/1.0.0 // registry.npmjs.org/foo/1.0.0:registry.npmjs.org/bar/1.0.0
string, NodeId,
DependenciesTreeNode<T> DependenciesTreeNode<T>
> >
@@ -113,7 +113,7 @@ export interface LinkedDependency {
export interface PendingNode { export interface PendingNode {
alias: string alias: string
nodeId: string nodeId: NodeId
resolvedPackage: ResolvedPackage resolvedPackage: ResolvedPackage
depth: number depth: number
installable: boolean installable: boolean
@@ -185,7 +185,7 @@ export type PkgAddress = {
depPath: string depPath: string
isNew: boolean isNew: boolean
isLinkedDependency?: false isLinkedDependency?: false
nodeId: string nodeId: NodeId
pkgId: string pkgId: string
normalizedPref?: string // is returned only for root dependencies normalizedPref?: string // is returned only for root dependencies
installable: boolean installable: boolean
@@ -795,8 +795,8 @@ async function resolveDependenciesOfDependency (
} }
} }
export function createNodeIdForLinkedLocalPkg (lockfileDir: string, pkgDir: string): string { export function createNodeIdForLinkedLocalPkg (lockfileDir: string, pkgDir: string): NodeId {
return `link:${normalizePath(path.relative(lockfileDir, pkgDir))}` return `link:${normalizePath(path.relative(lockfileDir, pkgDir))}` as NodeId
} }
function filterMissingPeers ( function filterMissingPeers (
@@ -895,9 +895,9 @@ async function resolveChildren (
})) }))
ctx.dependenciesTree.set(parentPkg.nodeId, { ctx.dependenciesTree.set(parentPkg.nodeId, {
children: pkgAddresses.reduce((chn, child) => { children: pkgAddresses.reduce((chn, child) => {
chn[child.alias] = (child as PkgAddress).nodeId ?? child.pkgId chn[child.alias] = (child as PkgAddress).nodeId ?? (child.pkgId as NodeId)
return chn return chn
}, {} as Record<string, string>), }, {} as Record<string, NodeId>),
depth: parentDepth, depth: parentDepth,
installable: parentPkg.installable, installable: parentPkg.installable,
resolvedPackage: ctx.resolvedPkgsById[parentPkg.pkgId], resolvedPackage: ctx.resolvedPkgsById[parentPkg.pkgId],
@@ -1007,7 +1007,7 @@ function referenceSatisfiesWantedSpec (
type InfoFromLockfile = { type InfoFromLockfile = {
depPath: string depPath: string
pkgId: string pkgId: PkgResolutionId
dependencyLockfile?: PackageSnapshot dependencyLockfile?: PackageSnapshot
name?: string name?: string
version?: string version?: string
@@ -1059,7 +1059,7 @@ function getInfoFromLockfile (
version, version,
dependencyLockfile, dependencyLockfile,
depPath, depPath,
pkgId: nonSemverVersion ?? `${name}@${version}`, pkgId: nonSemverVersion ?? (`${name}@${version}` as PkgResolutionId),
// resolution may not exist if lockfile is broken, and an unexpected error will be thrown // resolution may not exist if lockfile is broken, and an unexpected error will be thrown
// if resolution does not exist, return undefined so it can be autofixed later // if resolution does not exist, return undefined so it can be autofixed later
resolution: dependencyLockfile.resolution && pkgSnapshotToResolution(depPath, dependencyLockfile, registries), resolution: dependencyLockfile.resolution && pkgSnapshotToResolution(depPath, dependencyLockfile, registries),
@@ -1068,7 +1068,7 @@ function getInfoFromLockfile (
const parsed = dp.parse(depPath) const parsed = dp.parse(depPath)
return { return {
depPath, depPath,
pkgId: parsed.nonSemverVersion ?? (parsed.name && parsed.version ? `${parsed.name}@${parsed.version}` : depPath), // Does it make sense to set pkgId when we're not sure? pkgId: parsed.nonSemverVersion ?? (parsed.name && parsed.version ? `${parsed.name}@${parsed.version}` : depPath) as PkgResolutionId, // Does it make sense to set pkgId when we're not sure?
} }
} }
} }
@@ -1079,7 +1079,7 @@ interface ResolveDependencyOptions {
depPath?: string depPath?: string
name?: string name?: string
version?: string version?: string
pkgId?: string pkgId?: PkgResolutionId
resolution?: Resolution resolution?: Resolution
dependencyLockfile?: PackageSnapshot dependencyLockfile?: PackageSnapshot
} }
@@ -1207,8 +1207,6 @@ async function resolveDependency (
}, },
}) })
pkgResponse.body.id = encodePkgId(pkgResponse.body.id)
if (ctx.allPreferredVersions && pkgResponse.body.manifest?.version) { if (ctx.allPreferredVersions && pkgResponse.body.manifest?.version) {
if (!ctx.allPreferredVersions[pkgResponse.body.manifest.name]) { if (!ctx.allPreferredVersions[pkgResponse.body.manifest.name]) {
ctx.allPreferredVersions[pkgResponse.body.manifest.name] = {} ctx.allPreferredVersions[pkgResponse.body.manifest.name] = {}
@@ -1349,7 +1347,7 @@ async function resolveDependency (
} }
// In case of leaf dependencies (dependencies that have no prod deps or peer deps), // In case of leaf dependencies (dependencies that have no prod deps or peer deps),
// we only ever need to analyze one leaf dep in a graph, so the nodeId can be short and stateless. // we only ever need to analyze one leaf dep in a graph, so the nodeId can be short and stateless.
const nodeId = pkgIsLeaf(pkg) ? pkgResponse.body.id : nextNodeId() const nodeId = pkgIsLeaf(pkg) ? pkgResponse.body.id as unknown as NodeId : nextNodeId()
const parentIsInstallable = options.parentPkg.installable === undefined || options.parentPkg.installable const parentIsInstallable = options.parentPkg.installable === undefined || options.parentPkg.installable
const installable = parentIsInstallable && pkgResponse.body.isInstallable !== false const installable = parentIsInstallable && pkgResponse.body.isInstallable !== false

View File

@@ -11,7 +11,7 @@ import {
import partition from 'ramda/src/partition' import partition from 'ramda/src/partition'
import zipObj from 'ramda/src/zipObj' import zipObj from 'ramda/src/zipObj'
import { type WantedDependency } from './getNonDevWantedDependencies' import { type WantedDependency } from './getNonDevWantedDependencies'
import { nextNodeId } from './nextNodeId' import { type NodeId, nextNodeId } from './nextNodeId'
import { parentIdsContainSequence } from './parentIdsContainSequence' import { parentIdsContainSequence } from './parentIdsContainSequence'
import { import {
type ChildrenByParentId, type ChildrenByParentId,
@@ -34,7 +34,7 @@ export interface ResolvedImporters {
[id: string]: { [id: string]: {
directDependencies: ResolvedDirectDependency[] directDependencies: ResolvedDirectDependency[]
directNodeIdsByAlias: { directNodeIdsByAlias: {
[alias: string]: string [alias: string]: NodeId
} }
linkedDependencies: LinkedDependency[] linkedDependencies: LinkedDependency[]
} }
@@ -172,7 +172,7 @@ export async function resolveDependencyTree<T> (
currentDepth: 0, currentDepth: 0,
parentPkg: { parentPkg: {
installable: true, installable: true,
nodeId: `>${importer.id}>`, nodeId: `>${importer.id}>` as NodeId,
optional: false, optional: false,
pkgId: importer.id, pkgId: importer.id,
rootDir: importer.rootDir, rootDir: importer.rootDir,
@@ -241,7 +241,7 @@ export async function resolveDependencyTree<T> (
.reduce((acc, { alias, nodeId }) => { .reduce((acc, { alias, nodeId }) => {
acc[alias] = nodeId acc[alias] = nodeId
return acc return acc
}, {} as Record<string, string>), }, {} as Record<string, NodeId>),
linkedDependencies, linkedDependencies,
} }
} }
@@ -270,11 +270,11 @@ function buildTree (
children: Array<{ alias: string, id: string }>, children: Array<{ alias: string, id: string }>,
depth: number, depth: number,
installable: boolean installable: boolean
): Record<string, string> { ): Record<string, NodeId> {
const childrenNodeIds: Record<string, string> = {} const childrenNodeIds: Record<string, NodeId> = {}
for (const child of children) { for (const child of children) {
if (child.id.startsWith('link:')) { if (child.id.startsWith('link:')) {
childrenNodeIds[child.alias] = child.id childrenNodeIds[child.alias] = child.id as NodeId
continue continue
} }
if (parentIdsContainSequence(parentIds, parentId, child.id) || parentId === child.id) { if (parentIdsContainSequence(parentIds, parentId, child.id) || parentId === child.id) {

View File

@@ -13,6 +13,7 @@ import { depPathToFilename, createPeersDirSuffix, type PeerId } from '@pnpm/depe
import mapValues from 'ramda/src/map' import mapValues from 'ramda/src/map'
import partition from 'ramda/src/partition' import partition from 'ramda/src/partition'
import pick from 'ramda/src/pick' import pick from 'ramda/src/pick'
import { type NodeId } from './nextNodeId'
import { import {
type ChildrenMap, type ChildrenMap,
type PeerDependencies, type PeerDependencies,
@@ -52,7 +53,7 @@ export interface GenericDependenciesGraph<T extends PartialResolvedPackage> {
} }
export interface ProjectToResolve { export interface ProjectToResolve {
directNodeIdsByAlias: { [alias: string]: string } directNodeIdsByAlias: { [alias: string]: NodeId }
// only the top dependencies that were already installed // only the top dependencies that were already installed
// to avoid warnings about unresolved peer dependencies // to avoid warnings about unresolved peer dependencies
topParents: Array<{ name: string, version: string, alias?: string }> topParents: Array<{ name: string, version: string, alias?: string }>
@@ -259,7 +260,7 @@ function getRootPkgsByName<T extends PartialResolvedPackage> (dependenciesTree:
function createPkgsByName<T extends PartialResolvedPackage> ( function createPkgsByName<T extends PartialResolvedPackage> (
dependenciesTree: DependenciesTree<T>, dependenciesTree: DependenciesTree<T>,
{ directNodeIdsByAlias, topParents }: { { directNodeIdsByAlias, topParents }: {
directNodeIdsByAlias: { [alias: string]: string } directNodeIdsByAlias: { [alias: string]: NodeId }
topParents: Array<{ name: string, version: string, alias?: string, linkedDir?: string }> topParents: Array<{ name: string, version: string, alias?: string, linkedDir?: string }>
} }
): ParentRefs { ): ParentRefs {
@@ -280,7 +281,7 @@ function createPkgsByName<T extends PartialResolvedPackage> (
alias, alias,
depth: 0, depth: 0,
version, version,
nodeId: linkedDir, nodeId: linkedDir as NodeId,
parentNodeIds: [], parentNodeIds: [],
} }
_updateParentRefs(name, pkg) _updateParentRefs(name, pkg)
@@ -293,7 +294,7 @@ function createPkgsByName<T extends PartialResolvedPackage> (
interface PeersCacheItem { interface PeersCacheItem {
depPath: pDefer.DeferredPromise<string> depPath: pDefer.DeferredPromise<string>
resolvedPeers: Map<string, string> resolvedPeers: Map<string, NodeId>
missingPeers: Set<string> missingPeers: Set<string>
} }
@@ -301,7 +302,7 @@ type PeersCache = Map<string, PeersCacheItem[]>
interface PeersResolution { interface PeersResolution {
missingPeers: Set<string> missingPeers: Set<string>
resolvedPeers: Map<string, string> resolvedPeers: Map<string, NodeId>
} }
interface ResolvePeersContext { interface ResolvePeersContext {
@@ -323,12 +324,12 @@ interface ParentPkgInfo {
type ParentPkgsOfNode = Map<string, Record<string, ParentPkgInfo>> type ParentPkgsOfNode = Map<string, Record<string, ParentPkgInfo>>
async function resolvePeersOfNode<T extends PartialResolvedPackage> ( async function resolvePeersOfNode<T extends PartialResolvedPackage> (
nodeId: string, nodeId: NodeId,
parentParentPkgs: ParentRefs, parentParentPkgs: ParentRefs,
ctx: ResolvePeersContext & { ctx: ResolvePeersContext & {
allPeerDepNames: Set<string> allPeerDepNames: Set<string>
parentPkgsOfNode: ParentPkgsOfNode parentPkgsOfNode: ParentPkgsOfNode
parentNodeIds: string[] parentNodeIds: NodeId[]
parentDepPathsChain: string[] parentDepPathsChain: string[]
dependenciesTree: DependenciesTree<T> dependenciesTree: DependenciesTree<T>
depGraph: GenericDependenciesGraph<T> depGraph: GenericDependenciesGraph<T>
@@ -342,7 +343,7 @@ async function resolvePeersOfNode<T extends PartialResolvedPackage> (
} }
): Promise<PeersResolution & { finishing?: FinishingResolutionPromise, calculateDepPath?: CalculateDepPath }> { ): Promise<PeersResolution & { finishing?: FinishingResolutionPromise, calculateDepPath?: CalculateDepPath }> {
const node = ctx.dependenciesTree.get(nodeId)! const node = ctx.dependenciesTree.get(nodeId)!
if (node.depth === -1) return { resolvedPeers: new Map<string, string>(), missingPeers: new Set<string>() } if (node.depth === -1) return { resolvedPeers: new Map<string, NodeId>(), missingPeers: new Set<string>() }
const resolvedPackage = node.resolvedPackage as T const resolvedPackage = node.resolvedPackage as T
if ( if (
ctx.purePkgs.has(resolvedPackage.depPath) && ctx.purePkgs.has(resolvedPackage.depPath) &&
@@ -351,7 +352,7 @@ async function resolvePeersOfNode<T extends PartialResolvedPackage> (
) { ) {
ctx.pathsByNodeId.set(nodeId, resolvedPackage.depPath) ctx.pathsByNodeId.set(nodeId, resolvedPackage.depPath)
ctx.pathsByNodeIdPromises.get(nodeId)!.resolve(resolvedPackage.depPath) ctx.pathsByNodeIdPromises.get(nodeId)!.resolve(resolvedPackage.depPath)
return { resolvedPeers: new Map<string, string>(), missingPeers: new Set<string>() } return { resolvedPeers: new Map<string, NodeId>(), missingPeers: new Set<string>() }
} }
if (typeof node.children === 'function') { if (typeof node.children === 'function') {
node.children = node.children() node.children = node.children()
@@ -411,7 +412,7 @@ async function resolvePeersOfNode<T extends PartialResolvedPackage> (
}) })
const { resolvedPeers, missingPeers } = Object.keys(resolvedPackage.peerDependencies).length === 0 const { resolvedPeers, missingPeers } = Object.keys(resolvedPackage.peerDependencies).length === 0
? { resolvedPeers: new Map<string, string>(), missingPeers: new Set<string>() } ? { resolvedPeers: new Map<string, NodeId>(), missingPeers: new Set<string>() }
: _resolvePeers({ : _resolvePeers({
currentDepth: node.depth, currentDepth: node.depth,
dependenciesTree: ctx.dependenciesTree, dependenciesTree: ctx.dependenciesTree,
@@ -460,7 +461,7 @@ async function resolvePeersOfNode<T extends PartialResolvedPackage> (
addDepPathToGraph(resolvedPackage.depPath) addDepPathToGraph(resolvedPackage.depPath)
} else { } else {
const peerIds: PeerId[] = [] const peerIds: PeerId[] = []
const pendingPeerNodeIds: string[] = [] const pendingPeerNodeIds: NodeId[] = []
for (const [alias, peerNodeId] of allResolvedPeers.entries()) { for (const [alias, peerNodeId] of allResolvedPeers.entries()) {
if (peerNodeId.startsWith('link:')) { if (peerNodeId.startsWith('link:')) {
const linkedDir = peerNodeId.slice(5) const linkedDir = peerNodeId.slice(5)
@@ -494,7 +495,7 @@ async function resolvePeersOfNode<T extends PartialResolvedPackage> (
async function calculateDepPath ( async function calculateDepPath (
peerIds: PeerId[], peerIds: PeerId[],
pendingPeerNodeIds: string[], pendingPeerNodeIds: NodeId[],
cycles: string[][] cycles: string[][]
): Promise<void> { ): Promise<void> {
const cyclicPeerNodeIds = new Set() const cyclicPeerNodeIds = new Set()
@@ -657,7 +658,7 @@ function getPreviouslyResolvedChildren<T extends PartialResolvedPackage> (
parentDepPathsChain, parentDepPathsChain,
dependenciesTree, dependenciesTree,
}: { }: {
parentNodeIds: string[] parentNodeIds: NodeId[]
parentDepPathsChain: string[] parentDepPathsChain: string[]
dependenciesTree: DependenciesTree<T> dependenciesTree: DependenciesTree<T>
}, },
@@ -684,13 +685,13 @@ function getPreviouslyResolvedChildren<T extends PartialResolvedPackage> (
async function resolvePeersOfChildren<T extends PartialResolvedPackage> ( async function resolvePeersOfChildren<T extends PartialResolvedPackage> (
children: { children: {
[alias: string]: string [alias: string]: NodeId
}, },
parentPkgs: ParentRefs, parentPkgs: ParentRefs,
ctx: ResolvePeersContext & { ctx: ResolvePeersContext & {
allPeerDepNames: Set<string> allPeerDepNames: Set<string>
parentPkgsOfNode: ParentPkgsOfNode parentPkgsOfNode: ParentPkgsOfNode
parentNodeIds: string[] parentNodeIds: NodeId[]
parentDepPathsChain: string[] parentDepPathsChain: string[]
peerDependencyIssues: Pick<PeerDependencyIssues, 'bad' | 'missing'> peerDependencyIssues: Pick<PeerDependencyIssues, 'bad' | 'missing'>
peersCache: PeersCache peersCache: PeersCache
@@ -703,7 +704,7 @@ async function resolvePeersOfChildren<T extends PartialResolvedPackage> (
lockfileDir: string lockfileDir: string
} }
): Promise<PeersResolution & { finishing: Promise<void> }> { ): Promise<PeersResolution & { finishing: Promise<void> }> {
const allResolvedPeers = new Map<string, string>() const allResolvedPeers = new Map<string, NodeId>()
const allMissingPeers = new Set<string>() const allMissingPeers = new Set<string>()
// Partition children based on whether they're repeated in parentPkgs. // Partition children based on whether they're repeated in parentPkgs.
@@ -766,7 +767,7 @@ async function resolvePeersOfChildren<T extends PartialResolvedPackage> (
} }
const finishing = Promise.all(finishingList).then(() => {}) const finishing = Promise.all(finishingList).then(() => {})
const unknownResolvedPeersOfChildren = new Map<string, string>() const unknownResolvedPeersOfChildren = new Map<string, NodeId>()
for (const [alias, v] of allResolvedPeers) { for (const [alias, v] of allResolvedPeers) {
if (!children[alias]) { if (!children[alias]) {
unknownResolvedPeersOfChildren.set(alias, v) unknownResolvedPeersOfChildren.set(alias, v)
@@ -782,14 +783,14 @@ function _resolvePeers<T extends PartialResolvedPackage> (
lockfileDir: string lockfileDir: string
nodeId: string nodeId: string
parentPkgs: ParentRefs parentPkgs: ParentRefs
parentNodeIds: string[] parentNodeIds: NodeId[]
resolvedPackage: T resolvedPackage: T
dependenciesTree: DependenciesTree<T> dependenciesTree: DependenciesTree<T>
rootDir: string rootDir: string
peerDependencyIssues: Pick<PeerDependencyIssues, 'bad' | 'missing'> peerDependencyIssues: Pick<PeerDependencyIssues, 'bad' | 'missing'>
} }
): PeersResolution { ): PeersResolution {
const resolvedPeers = new Map<string, string>() const resolvedPeers = new Map<string, NodeId>()
const missingPeers = new Set<string>() const missingPeers = new Set<string>()
for (const [peerName, { version, optional }] of Object.entries(ctx.resolvedPackage.peerDependencies)) { for (const [peerName, { version, optional }] of Object.entries(ctx.resolvedPackage.peerDependencies)) {
const peerVersionRange = version.replace(/^workspace:/, '') const peerVersionRange = version.replace(/^workspace:/, '')
@@ -847,7 +848,7 @@ function getLocationFromParentNodeIds<T> (
parentNodeIds, parentNodeIds,
}: { }: {
dependenciesTree: DependenciesTree<T> dependenciesTree: DependenciesTree<T>
parentNodeIds: string[] parentNodeIds: NodeId[]
} }
): Location { ): Location {
const parents = parentNodeIds const parents = parentNodeIds
@@ -866,17 +867,17 @@ interface ParentRef {
version: string version: string
depth: number depth: number
// this is null only for already installed top dependencies // this is null only for already installed top dependencies
nodeId?: string nodeId?: NodeId
alias?: string alias?: string
occurrence: number occurrence: number
parentNodeIds: string[] parentNodeIds: NodeId[]
} }
interface ParentPkgNode<T> { interface ParentPkgNode<T> {
alias: string alias: string
nodeId: string nodeId: NodeId
node: DependenciesTreeNode<T> node: DependenciesTreeNode<T>
parentNodeIds: string[] parentNodeIds: NodeId[]
} }
function toPkgByName<T extends PartialResolvedPackage> (nodes: Array<ParentPkgNode<T>>): ParentRefs { function toPkgByName<T extends PartialResolvedPackage> (nodes: Array<ParentPkgNode<T>>): ParentRefs {

View File

@@ -1,5 +1,6 @@
import { type PartialResolvedPackage, resolvePeers } from '../lib/resolvePeers' import { type PartialResolvedPackage, resolvePeers } from '../lib/resolvePeers'
import { type DependenciesTreeNode } from '../lib/resolveDependencies' import { type DependenciesTreeNode } from '../lib/resolveDependencies'
import { type NodeId } from '../lib/nextNodeId'
test('packages are not deduplicated when versions do not match', async () => { test('packages are not deduplicated when versions do not match', async () => {
const fooPkg: PartialResolvedPackage = { const fooPkg: PartialResolvedPackage = {
@@ -36,8 +37,8 @@ test('packages are not deduplicated when versions do not match', async () => {
projects: [ projects: [
{ {
directNodeIdsByAlias: { directNodeIdsByAlias: {
foo: '>project1>foo/1.0.0>', foo: '>project1>foo/1.0.0>' as NodeId,
bar: '>project1>bar/1.0.0>', bar: '>project1>bar/1.0.0>' as NodeId,
}, },
topParents: [], topParents: [],
rootDir: '', rootDir: '',
@@ -45,9 +46,9 @@ test('packages are not deduplicated when versions do not match', async () => {
}, },
{ {
directNodeIdsByAlias: { directNodeIdsByAlias: {
foo: '>project2>foo/1.0.0>', foo: '>project2>foo/1.0.0>' as NodeId,
bar: '>project2>bar/1.0.0>', bar: '>project2>bar/1.0.0>' as NodeId,
baz: '>project2>baz/1.0.0>', baz: '>project2>baz/1.0.0>' as NodeId,
}, },
topParents: [], topParents: [],
rootDir: '', rootDir: '',
@@ -55,8 +56,8 @@ test('packages are not deduplicated when versions do not match', async () => {
}, },
{ {
directNodeIdsByAlias: { directNodeIdsByAlias: {
foo: '>project3>foo/1.0.0>', foo: '>project3>foo/1.0.0>' as NodeId,
bar: '>project3>bar/2.0.0>', bar: '>project3>bar/2.0.0>' as NodeId,
}, },
topParents: [], topParents: [],
rootDir: '', rootDir: '',
@@ -64,9 +65,9 @@ test('packages are not deduplicated when versions do not match', async () => {
}, },
{ {
directNodeIdsByAlias: { directNodeIdsByAlias: {
foo: '>project4>foo/1.0.0>', foo: '>project4>foo/1.0.0>' as NodeId,
bar: '>project4>bar/2.0.0>', bar: '>project4>bar/2.0.0>' as NodeId,
baz: '>project4>baz/2.0.0>', baz: '>project4>baz/2.0.0>' as NodeId,
}, },
topParents: [], topParents: [],
rootDir: '', rootDir: '',
@@ -74,20 +75,20 @@ test('packages are not deduplicated when versions do not match', async () => {
}, },
], ],
resolvedImporters: {}, resolvedImporters: {},
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>(([ dependenciesTree: new Map<NodeId, DependenciesTreeNode<PartialResolvedPackage>>(([
['>project1>foo/1.0.0>', fooPkg], ['>project1>foo/1.0.0>' as NodeId, fooPkg],
['>project1>bar/1.0.0>', peers.bar_1_0_0], ['>project1>bar/1.0.0>' as NodeId, peers.bar_1_0_0],
['>project2>foo/1.0.0>', fooPkg], ['>project2>foo/1.0.0>' as NodeId, fooPkg],
['>project2>bar/1.0.0>', peers.bar_1_0_0], ['>project2>bar/1.0.0>' as NodeId, peers.bar_1_0_0],
['>project2>baz/1.0.0>', peers.baz_1_0_0], ['>project2>baz/1.0.0>' as NodeId, peers.baz_1_0_0],
['>project3>foo/1.0.0>', fooPkg], ['>project3>foo/1.0.0>' as NodeId, fooPkg],
['>project3>bar/2.0.0>', peers.bar_2_0_0], ['>project3>bar/2.0.0>' as NodeId, peers.bar_2_0_0],
['>project4>foo/1.0.0>', fooPkg], ['>project4>foo/1.0.0>' as NodeId, fooPkg],
['>project4>bar/2.0.0>', peers.bar_2_0_0], ['>project4>bar/2.0.0>' as NodeId, peers.bar_2_0_0],
['>project4>baz/2.0.0>', peers.baz_2_0_0], ['>project4>baz/2.0.0>' as NodeId, peers.baz_2_0_0],
] satisfies Array<[string, PartialResolvedPackage]>).map(([path, resolvedPackage]) => [path, { ] satisfies Array<[string, PartialResolvedPackage]>).map(([path, resolvedPackage]) => [path, {
children: {}, children: {},

View File

@@ -2,6 +2,7 @@
import { type PeerDependencyIssuesByProjects } from '@pnpm/types' import { type PeerDependencyIssuesByProjects } from '@pnpm/types'
import { type PartialResolvedPackage, resolvePeers } from '../lib/resolvePeers' import { type PartialResolvedPackage, resolvePeers } from '../lib/resolvePeers'
import { type DependenciesTreeNode, type PeerDependencies } from '../lib/resolveDependencies' import { type DependenciesTreeNode, type PeerDependencies } from '../lib/resolveDependencies'
import { type NodeId } from '../lib/nextNodeId'
test('resolve peer dependencies of cyclic dependencies', async () => { test('resolve peer dependencies of cyclic dependencies', async () => {
const fooPkg = { const fooPkg = {
@@ -29,7 +30,7 @@ test('resolve peer dependencies of cyclic dependencies', async () => {
projects: [ projects: [
{ {
directNodeIdsByAlias: { directNodeIdsByAlias: {
foo: '>foo/1.0.0>', foo: '>foo/1.0.0>' as NodeId,
}, },
topParents: [], topParents: [],
rootDir: '', rootDir: '',
@@ -37,26 +38,26 @@ test('resolve peer dependencies of cyclic dependencies', async () => {
}, },
], ],
resolvedImporters: {}, resolvedImporters: {},
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([ dependenciesTree: new Map<NodeId, DependenciesTreeNode<PartialResolvedPackage>>([
['>foo/1.0.0>', { ['>foo/1.0.0>' as NodeId, {
children: { children: {
bar: '>foo/1.0.0>bar/1.0.0>', bar: '>foo/1.0.0>bar/1.0.0>' as NodeId,
}, },
installable: true, installable: true,
resolvedPackage: fooPkg, resolvedPackage: fooPkg,
depth: 0, depth: 0,
}], }],
['>foo/1.0.0>bar/1.0.0>', { ['>foo/1.0.0>bar/1.0.0>' as NodeId, {
children: { children: {
qar: '>foo/1.0.0>bar/1.0.0>qar/1.0.0>', qar: '>foo/1.0.0>bar/1.0.0>qar/1.0.0>' as NodeId,
}, },
installable: true, installable: true,
resolvedPackage: barPkg, resolvedPackage: barPkg,
depth: 1, depth: 1,
}], }],
['>foo/1.0.0>bar/1.0.0>qar/1.0.0>', { ['>foo/1.0.0>bar/1.0.0>qar/1.0.0>' as NodeId, {
children: { children: {
zoo: '>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>', zoo: '>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>' as NodeId,
}, },
installable: true, installable: true,
resolvedPackage: { resolvedPackage: {
@@ -71,10 +72,10 @@ test('resolve peer dependencies of cyclic dependencies', async () => {
}, },
depth: 2, depth: 2,
}], }],
['>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>', { ['>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>' as NodeId, {
children: { children: {
foo: '>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>foo/1.0.0>', foo: '>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>foo/1.0.0>' as NodeId,
bar: '>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>bar/1.0.0>', bar: '>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>bar/1.0.0>' as NodeId,
}, },
installable: true, installable: true,
resolvedPackage: { resolvedPackage: {
@@ -88,13 +89,13 @@ test('resolve peer dependencies of cyclic dependencies', async () => {
}, },
depth: 3, depth: 3,
}], }],
['>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>foo/1.0.0>', { ['>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>foo/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: fooPkg, resolvedPackage: fooPkg,
depth: 4, depth: 4,
}], }],
['>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>bar/1.0.0>', { ['>foo/1.0.0>bar/1.0.0>qar/1.0.0>zoo/1.0.0>bar/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: barPkg, resolvedPackage: barPkg,
@@ -144,8 +145,8 @@ test('when a package is referenced twice in the dependencies graph and one of th
projects: [ projects: [
{ {
directNodeIdsByAlias: { directNodeIdsByAlias: {
zoo: '>zoo/1.0.0>', zoo: '>zoo/1.0.0>' as NodeId,
bar: '>bar/1.0.0>', bar: '>bar/1.0.0>' as NodeId,
}, },
topParents: [], topParents: [],
rootDir: '', rootDir: '',
@@ -153,45 +154,45 @@ test('when a package is referenced twice in the dependencies graph and one of th
}, },
], ],
resolvedImporters: {}, resolvedImporters: {},
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([ dependenciesTree: new Map<NodeId, DependenciesTreeNode<PartialResolvedPackage>>([
['>zoo/1.0.0>', { ['>zoo/1.0.0>' as NodeId, {
children: { children: {
foo: '>zoo/1.0.0>foo/1.0.0>', foo: '>zoo/1.0.0>foo/1.0.0>' as NodeId,
}, },
installable: true, installable: true,
resolvedPackage: zooPkg, resolvedPackage: zooPkg,
depth: 0, depth: 0,
}], }],
['>zoo/1.0.0>foo/1.0.0>', { ['>zoo/1.0.0>foo/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: fooPkg, resolvedPackage: fooPkg,
depth: 1, depth: 1,
}], }],
['>bar/1.0.0>', { ['>bar/1.0.0>' as NodeId, {
children: { children: {
zoo: '>bar/1.0.0>zoo/1.0.0>', zoo: '>bar/1.0.0>zoo/1.0.0>' as NodeId,
qar: '>bar/1.0.0>qar/1.0.0>', qar: '>bar/1.0.0>qar/1.0.0>' as NodeId,
}, },
installable: true, installable: true,
resolvedPackage: barPkg, resolvedPackage: barPkg,
depth: 0, depth: 0,
}], }],
['>bar/1.0.0>zoo/1.0.0>', { ['>bar/1.0.0>zoo/1.0.0>' as NodeId, {
children: { children: {
foo: '>bar/1.0.0>zoo/1.0.0>foo/1.0.0>', foo: '>bar/1.0.0>zoo/1.0.0>foo/1.0.0>' as NodeId,
}, },
installable: true, installable: true,
resolvedPackage: zooPkg, resolvedPackage: zooPkg,
depth: 1, depth: 1,
}], }],
['>bar/1.0.0>zoo/1.0.0>foo/1.0.0>', { ['>bar/1.0.0>zoo/1.0.0>foo/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: fooPkg, resolvedPackage: fooPkg,
depth: 2, depth: 2,
}], }],
['>bar/1.0.0>qar/1.0.0>', { ['>bar/1.0.0>qar/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: { resolvedPackage: {
@@ -271,7 +272,7 @@ describe('peer dependency issues', () => {
projects: [ projects: [
{ {
directNodeIdsByAlias: { directNodeIdsByAlias: {
foo: '>project1>foo/1.0.0>', foo: '>project1>foo/1.0.0>' as NodeId,
}, },
topParents: [], topParents: [],
rootDir: '', rootDir: '',
@@ -279,7 +280,7 @@ describe('peer dependency issues', () => {
}, },
{ {
directNodeIdsByAlias: { directNodeIdsByAlias: {
bar: '>project2>bar/1.0.0>', bar: '>project2>bar/1.0.0>' as NodeId,
}, },
topParents: [], topParents: [],
rootDir: '', rootDir: '',
@@ -287,8 +288,8 @@ describe('peer dependency issues', () => {
}, },
{ {
directNodeIdsByAlias: { directNodeIdsByAlias: {
foo: '>project3>foo/1.0.0>', foo: '>project3>foo/1.0.0>' as NodeId,
bar: '>project3>bar/1.0.0>', bar: '>project3>bar/1.0.0>' as NodeId,
}, },
topParents: [], topParents: [],
rootDir: '', rootDir: '',
@@ -296,8 +297,8 @@ describe('peer dependency issues', () => {
}, },
{ {
directNodeIdsByAlias: { directNodeIdsByAlias: {
bar: '>project4>bar/1.0.0>', bar: '>project4>bar/1.0.0>' as NodeId,
qar: '>project4>qar/1.0.0>', qar: '>project4>qar/1.0.0>' as NodeId,
}, },
topParents: [], topParents: [],
rootDir: '', rootDir: '',
@@ -305,8 +306,8 @@ describe('peer dependency issues', () => {
}, },
{ {
directNodeIdsByAlias: { directNodeIdsByAlias: {
foo: '>project5>foo/1.0.0>', foo: '>project5>foo/1.0.0>' as NodeId,
bar: '>project5>bar/2.0.0>', bar: '>project5>bar/2.0.0>' as NodeId,
}, },
topParents: [], topParents: [],
rootDir: '', rootDir: '',
@@ -314,8 +315,8 @@ describe('peer dependency issues', () => {
}, },
{ {
directNodeIdsByAlias: { directNodeIdsByAlias: {
foo: '>project6>foo/2.0.0>', foo: '>project6>foo/2.0.0>' as NodeId,
bar: '>project6>bar/2.0.0>', bar: '>project6>bar/2.0.0>' as NodeId,
}, },
topParents: [], topParents: [],
rootDir: '', rootDir: '',
@@ -323,62 +324,62 @@ describe('peer dependency issues', () => {
}, },
], ],
resolvedImporters: {}, resolvedImporters: {},
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([ dependenciesTree: new Map<NodeId, DependenciesTreeNode<PartialResolvedPackage>>([
['>project1>foo/1.0.0>', { ['>project1>foo/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: fooPkg, resolvedPackage: fooPkg,
depth: 0, depth: 0,
}], }],
['>project2>bar/1.0.0>', { ['>project2>bar/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: barPkg, resolvedPackage: barPkg,
depth: 0, depth: 0,
}], }],
['>project3>foo/1.0.0>', { ['>project3>foo/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: fooPkg, resolvedPackage: fooPkg,
depth: 0, depth: 0,
}], }],
['>project3>bar/1.0.0>', { ['>project3>bar/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: barPkg, resolvedPackage: barPkg,
depth: 0, depth: 0,
}], }],
['>project4>bar/1.0.0>', { ['>project4>bar/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: barPkg, resolvedPackage: barPkg,
depth: 0, depth: 0,
}], }],
['>project4>qar/1.0.0>', { ['>project4>qar/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: qarPkg, resolvedPackage: qarPkg,
depth: 0, depth: 0,
}], }],
['>project5>foo/1.0.0>', { ['>project5>foo/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: fooPkg, resolvedPackage: fooPkg,
depth: 0, depth: 0,
}], }],
['>project5>bar/2.0.0>', { ['>project5>bar/2.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: barWithOptionalPeer, resolvedPackage: barWithOptionalPeer,
depth: 0, depth: 0,
}], }],
['>project6>foo/2.0.0>', { ['>project6>foo/2.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: fooWithOptionalPeer, resolvedPackage: fooWithOptionalPeer,
depth: 0, depth: 0,
}], }],
['>project6>bar/2.0.0>', { ['>project6>bar/2.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: barWithOptionalPeer, resolvedPackage: barWithOptionalPeer,
@@ -419,9 +420,9 @@ describe('unmet peer dependency issues', () => {
projects: [ projects: [
{ {
directNodeIdsByAlias: { directNodeIdsByAlias: {
foo: '>project1>foo/1.0.0>', foo: '>project1>foo/1.0.0>' as NodeId,
peer1: '>project1>peer1/1.0.0-rc.0>', peer1: '>project1>peer1/1.0.0-rc.0>' as NodeId,
peer2: '>project1>peer2/1.1.0-rc.0>', peer2: '>project1>peer2/1.1.0-rc.0>' as NodeId,
}, },
topParents: [], topParents: [],
rootDir: '', rootDir: '',
@@ -429,8 +430,8 @@ describe('unmet peer dependency issues', () => {
}, },
], ],
resolvedImporters: {}, resolvedImporters: {},
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([ dependenciesTree: new Map<NodeId, DependenciesTreeNode<PartialResolvedPackage>>([
['>project1>foo/1.0.0>', { ['>project1>foo/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: { resolvedPackage: {
@@ -445,7 +446,7 @@ describe('unmet peer dependency issues', () => {
}, },
depth: 0, depth: 0,
}], }],
['>project1>peer1/1.0.0-rc.0>', { ['>project1>peer1/1.0.0-rc.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: { resolvedPackage: {
@@ -457,7 +458,7 @@ describe('unmet peer dependency issues', () => {
}, },
depth: 0, depth: 0,
}], }],
['>project1>peer2/1.1.0-rc.0>', { ['>project1>peer2/1.1.0-rc.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: { resolvedPackage: {
@@ -491,7 +492,7 @@ describe('unmet peer dependency issue resolved from subdependency', () => {
projects: [ projects: [
{ {
directNodeIdsByAlias: { directNodeIdsByAlias: {
foo: '>project>foo/1.0.0>', foo: '>project>foo/1.0.0>' as NodeId,
}, },
topParents: [], topParents: [],
rootDir: '', rootDir: '',
@@ -499,11 +500,11 @@ describe('unmet peer dependency issue resolved from subdependency', () => {
}, },
], ],
resolvedImporters: {}, resolvedImporters: {},
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([ dependenciesTree: new Map<NodeId, DependenciesTreeNode<PartialResolvedPackage>>([
['>project>foo/1.0.0>', { ['>project>foo/1.0.0>' as NodeId, {
children: { children: {
dep: '>project>foo/1.0.0>dep/1.0.0>', dep: '>project>foo/1.0.0>dep/1.0.0>' as NodeId,
bar: '>project>foo/1.0.0>bar/1.0.0>', bar: '>project>foo/1.0.0>bar/1.0.0>' as NodeId,
}, },
installable: true, installable: true,
resolvedPackage: { resolvedPackage: {
@@ -515,7 +516,7 @@ describe('unmet peer dependency issue resolved from subdependency', () => {
}, },
depth: 0, depth: 0,
}], }],
['>project>foo/1.0.0>dep/1.0.0>', { ['>project>foo/1.0.0>dep/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: { resolvedPackage: {
@@ -527,7 +528,7 @@ describe('unmet peer dependency issue resolved from subdependency', () => {
}, },
depth: 1, depth: 1,
}], }],
['>project>foo/1.0.0>bar/1.0.0>', { ['>project>foo/1.0.0>bar/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: { resolvedPackage: {
@@ -590,10 +591,10 @@ test('resolve peer dependencies with npm aliases', async () => {
projects: [ projects: [
{ {
directNodeIdsByAlias: { directNodeIdsByAlias: {
foo: '>foo/1.0.0>', foo: '>foo/1.0.0>' as NodeId,
bar: '>bar/1.0.0>', bar: '>bar/1.0.0>' as NodeId,
'foo-next': '>foo/2.0.0>', 'foo-next': '>foo/2.0.0>' as NodeId,
'bar-next': '>bar/2.0.0>', 'bar-next': '>bar/2.0.0>' as NodeId,
}, },
topParents: [], topParents: [],
rootDir: '', rootDir: '',
@@ -601,42 +602,42 @@ test('resolve peer dependencies with npm aliases', async () => {
}, },
], ],
resolvedImporters: {}, resolvedImporters: {},
dependenciesTree: new Map<string, DependenciesTreeNode<PartialResolvedPackage>>([ dependenciesTree: new Map<NodeId, DependenciesTreeNode<PartialResolvedPackage>>([
['>foo/1.0.0>', { ['>foo/1.0.0>' as NodeId, {
children: { children: {
bar: '>foo/1.0.0>bar/1.0.0>', bar: '>foo/1.0.0>bar/1.0.0>' as NodeId,
}, },
installable: true, installable: true,
resolvedPackage: fooPkg, resolvedPackage: fooPkg,
depth: 0, depth: 0,
}], }],
['>foo/1.0.0>bar/1.0.0>', { ['>foo/1.0.0>bar/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: barPkg, resolvedPackage: barPkg,
depth: 1, depth: 1,
}], }],
['>foo/2.0.0>', { ['>foo/2.0.0>' as NodeId, {
children: { children: {
bar: '>foo/2.0.0>bar/2.0.0>', bar: '>foo/2.0.0>bar/2.0.0>' as NodeId,
}, },
installable: true, installable: true,
resolvedPackage: fooAliasPkg, resolvedPackage: fooAliasPkg,
depth: 0, depth: 0,
}], }],
['>foo/2.0.0>bar/2.0.0>', { ['>foo/2.0.0>bar/2.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: barAliasPkg, resolvedPackage: barAliasPkg,
depth: 1, depth: 1,
}], }],
['>bar/1.0.0>', { ['>bar/1.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: barPkg, resolvedPackage: barPkg,
depth: 0, depth: 0,
}], }],
['>bar/2.0.0>', { ['>bar/2.0.0>' as NodeId, {
children: {}, children: {},
installable: true, installable: true,
resolvedPackage: barAliasPkg, resolvedPackage: barAliasPkg,

3
pnpm-lock.yaml generated
View File

@@ -5519,6 +5519,9 @@ importers:
'@pnpm/outdated': '@pnpm/outdated':
specifier: workspace:* specifier: workspace:*
version: 'link:' version: 'link:'
'@pnpm/resolver-base':
specifier: workspace:*
version: link:../../resolving/resolver-base
'@types/ramda': '@types/ramda':
specifier: 0.29.12 specifier: 0.29.12
version: 0.29.12 version: 0.29.12

View File

@@ -1,8 +1,10 @@
export function createGitHostedPkgId ({ repo, commit, path }: { repo: string, commit: string, path?: string }): string { import { type PkgResolutionId } from '@pnpm/resolver-base'
export function createGitHostedPkgId ({ repo, commit, path }: { repo: string, commit: string, path?: string }): PkgResolutionId {
let id = `${repo.includes('://') ? '' : 'https://'}${repo}#${commit}` let id = `${repo.includes('://') ? '' : 'https://'}${repo}#${commit}`
if (!id.startsWith('git+')) id = `git+${id}` if (!id.startsWith('git+')) id = `git+${id}`
if (path) { if (path) {
id += `&path:${path}` id += `&path:${path}`
} }
return id return id as PkgResolutionId
} }

View File

@@ -1,4 +1,4 @@
import { type TarballResolution, type GitResolution, type ResolveResult } from '@pnpm/resolver-base' import { type TarballResolution, type GitResolution, type ResolveResult, type PkgResolutionId } from '@pnpm/resolver-base'
import git from 'graceful-git' import git from 'graceful-git'
import semver from 'semver' import semver from 'semver'
import { parsePref, type HostedPackageSpec } from './parsePref' import { parsePref, type HostedPackageSpec } from './parsePref'
@@ -50,11 +50,11 @@ export function createGitResolver (
resolution.path = parsedSpec.path resolution.path = parsedSpec.path
} }
let id: string let id: PkgResolutionId
if ('tarball' in resolution) { if ('tarball' in resolution) {
id = resolution.tarball id = resolution.tarball as PkgResolutionId
if (resolution.path) { if (resolution.path) {
id += `#path:${resolution.path}` id = `${id}#path:${resolution.path}` as PkgResolutionId
} }
} else { } else {
id = createGitHostedPkgId(resolution) id = createGitHostedPkgId(resolution)

View File

@@ -2,6 +2,7 @@ import os from 'os'
import path from 'path' import path from 'path'
import { PnpmError } from '@pnpm/error' import { PnpmError } from '@pnpm/error'
import normalize from 'normalize-path' import normalize from 'normalize-path'
import { type PkgResolutionId } from '@pnpm/resolver-base'
// @ts-expect-error // @ts-expect-error
const isWindows = process.platform === 'win32' || global['FAKE_WINDOWS'] const isWindows = process.platform === 'win32' || global['FAKE_WINDOWS']
@@ -12,7 +13,7 @@ const isAbsolutePath = /^[/]|^[A-Za-z]:/
export interface LocalPackageSpec { export interface LocalPackageSpec {
dependencyPath: string dependencyPath: string
fetchSpec: string fetchSpec: string
id: string id: PkgResolutionId
type: 'directory' | 'file' type: 'directory' | 'file'
normalizedPref: string normalizedPref: string
} }
@@ -90,9 +91,11 @@ function fromLocal (
const dependencyPath = injected const dependencyPath = injected
? normalize(path.relative(lockfileDir, fetchSpec)) ? normalize(path.relative(lockfileDir, fetchSpec))
: normalize(path.resolve(fetchSpec)) : normalize(path.resolve(fetchSpec))
const id = !injected && (type === 'directory' || projectDir === lockfileDir) const id = (
? `${protocol}${normalize(path.relative(projectDir, fetchSpec))}` !injected && (type === 'directory' || projectDir === lockfileDir)
: `${protocol}${normalize(path.relative(lockfileDir, fetchSpec))}` ? `${protocol}${normalize(path.relative(projectDir, fetchSpec))}`
: `${protocol}${normalize(path.relative(lockfileDir, fetchSpec))}`
) as PkgResolutionId
return { return {
dependencyPath, dependencyPath,

View File

@@ -7,6 +7,7 @@ import {
} from '@pnpm/fetching-types' } from '@pnpm/fetching-types'
import { resolveWorkspaceRange } from '@pnpm/resolve-workspace-range' import { resolveWorkspaceRange } from '@pnpm/resolve-workspace-range'
import { import {
type PkgResolutionId,
type PreferredVersions, type PreferredVersions,
type ResolveResult, type ResolveResult,
type WantedDependency, type WantedDependency,
@@ -222,7 +223,7 @@ async function resolveNpm (
} }
} }
const id = `${pickedPackage.name}@${pickedPackage.version}` const id = `${pickedPackage.name}@${pickedPackage.version}` as PkgResolutionId
const resolution = { const resolution = {
integrity: getIntegrity(pickedPackage.dist), integrity: getIntegrity(pickedPackage.dist),
tarball: pickedPackage.dist.tarball, tarball: pickedPackage.dist.tarball,
@@ -336,15 +337,15 @@ function resolveFromLocalPackage (
lockfileDir?: string lockfileDir?: string
} }
): ResolveResult { ): ResolveResult {
let id!: string let id!: PkgResolutionId
let directory!: string let directory!: string
const localPackageDir = resolveLocalPackageDir(localPackage) const localPackageDir = resolveLocalPackageDir(localPackage)
if (opts.hardLinkLocalPackages) { if (opts.hardLinkLocalPackages) {
directory = normalize(path.relative(opts.lockfileDir!, localPackageDir)) directory = normalize(path.relative(opts.lockfileDir!, localPackageDir))
id = `file:${directory}` id = `file:${directory}` as PkgResolutionId
} else { } else {
directory = localPackageDir directory = localPackageDir
id = `link:${normalize(path.relative(opts.projectDir, localPackageDir))}` id = `link:${normalize(path.relative(opts.projectDir, localPackageDir))}` as PkgResolutionId
} }
return { return {
id, id,

View File

@@ -1,4 +1,6 @@
import { type DependencyManifest } from '@pnpm/types' import { type DependencyManifest, type PkgResolutionId } from '@pnpm/types'
export { type PkgResolutionId }
/** /**
* tarball hosted remotely * tarball hosted remotely
@@ -32,7 +34,7 @@ export type Resolution =
({ type: string } & object) ({ type: string } & object)
export interface ResolveResult { export interface ResolveResult {
id: string id: PkgResolutionId
latest?: string latest?: string
publishedAt?: string publishedAt?: string
manifest?: DependencyManifest manifest?: DependencyManifest

View File

@@ -1,4 +1,4 @@
import { type ResolveResult } from '@pnpm/resolver-base' import { type PkgResolutionId, type ResolveResult } from '@pnpm/resolver-base'
export async function resolveFromTarball ( export async function resolveFromTarball (
wantedDependency: { pref: string } wantedDependency: { pref: string }
@@ -10,7 +10,7 @@ export async function resolveFromTarball (
if (isRepository(wantedDependency.pref)) return null if (isRepository(wantedDependency.pref)) return null
return { return {
id: wantedDependency.pref, id: wantedDependency.pref as PkgResolutionId,
normalizedPref: wantedDependency.pref, normalizedPref: wantedDependency.pref,
resolution: { resolution: {
tarball: wantedDependency.pref, tarball: wantedDependency.pref,

View File

@@ -52,6 +52,7 @@
"devDependencies": { "devDependencies": {
"@pnpm/outdated": "workspace:*", "@pnpm/outdated": "workspace:*",
"@types/ramda": "0.29.12", "@types/ramda": "0.29.12",
"@pnpm/resolver-base": "workspace:*",
"@types/semver": "7.5.3" "@types/semver": "7.5.3"
}, },
"funding": "https://opencollective.com/pnpm", "funding": "https://opencollective.com/pnpm",

View File

@@ -1,4 +1,5 @@
import { type ResolveFunction } from '@pnpm/client' import { type ResolveFunction } from '@pnpm/client'
import { type PkgResolutionId } from '@pnpm/resolver-base'
import { getManifest } from '../lib/createManifestGetter' import { getManifest } from '../lib/createManifestGetter'
test('getManifest()', async () => { test('getManifest()', async () => {
@@ -15,7 +16,7 @@ test('getManifest()', async () => {
const resolve: ResolveFunction = async function (wantedPackage, opts) { const resolve: ResolveFunction = async function (wantedPackage, opts) {
expect(opts.registry).toEqual('https://registry.npmjs.org/') expect(opts.registry).toEqual('https://registry.npmjs.org/')
return { return {
id: 'foo/1.0.0', id: 'foo/1.0.0' as PkgResolutionId,
latest: '1.0.0', latest: '1.0.0',
manifest: { manifest: {
name: 'foo', name: 'foo',
@@ -36,7 +37,7 @@ test('getManifest()', async () => {
const resolve2: ResolveFunction = async function (wantedPackage, opts) { const resolve2: ResolveFunction = async function (wantedPackage, opts) {
expect(opts.registry).toEqual('https://pnpm.io/') expect(opts.registry).toEqual('https://pnpm.io/')
return { return {
id: 'foo/2.0.0', id: 'foo/2.0.0' as PkgResolutionId,
latest: '2.0.0', latest: '2.0.0',
manifest: { manifest: {
name: 'foo', name: 'foo',

View File

@@ -47,6 +47,9 @@
}, },
{ {
"path": "../../resolving/npm-resolver" "path": "../../resolving/npm-resolver"
},
{
"path": "../../resolving/resolver-base"
} }
], ],
"composite": true "composite": true

View File

@@ -1,4 +1,5 @@
import { import {
type PkgResolutionId,
type DirectoryResolution, type DirectoryResolution,
type PreferredVersions, type PreferredVersions,
type Resolution, type Resolution,
@@ -110,7 +111,7 @@ export type RequestPackageFunction = (
export interface RequestPackageOptions { export interface RequestPackageOptions {
alwaysTryWorkspacePackages?: boolean alwaysTryWorkspacePackages?: boolean
currentPkg?: { currentPkg?: {
id?: string id?: PkgResolutionId
resolution?: Resolution resolution?: Resolution
} }
/** /**
@@ -147,7 +148,7 @@ export interface PackageResponse {
isInstallable?: boolean isInstallable?: boolean
resolution: Resolution resolution: Resolution
manifest?: PackageManifest manifest?: PackageManifest
id: string id: PkgResolutionId
normalizedPref?: string normalizedPref?: string
updated: boolean updated: boolean
publishedAt?: string publishedAt?: string