mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-28 02:53:15 -04:00
refactor: @pnpm/resolve-dependencies and @pnpm/core (#4085)
This commit is contained in:
5
.changeset/afraid-bikes-sip.md
Normal file
5
.changeset/afraid-bikes-sip.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/which-version-is-pinned": major
|
||||
---
|
||||
|
||||
Initial release.
|
||||
5
.changeset/hungry-seas-itch.md
Normal file
5
.changeset/hungry-seas-itch.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/resolve-dependencies": major
|
||||
---
|
||||
|
||||
Breaking changes to the API. New required options added: `defaultUpdateDepth` and `preferredVersions`.
|
||||
@@ -46,12 +46,12 @@
|
||||
"@pnpm/store-controller-types": "workspace:11.0.7",
|
||||
"@pnpm/symlink-dependency": "workspace:4.0.8",
|
||||
"@pnpm/types": "workspace:7.6.0",
|
||||
"@pnpm/which-version-is-pinned": "workspace:0.0.0",
|
||||
"@zkochan/npm-package-arg": "^2.0.1",
|
||||
"@zkochan/rimraf": "^2.1.1",
|
||||
"dependency-path": "workspace:8.0.6",
|
||||
"graph-sequencer": "2.0.0",
|
||||
"is-inner-link": "^4.0.0",
|
||||
"is-subdir": "^1.1.1",
|
||||
"load-json-file": "^6.2.0",
|
||||
"normalize-path": "^3.0.0",
|
||||
"p-every": "^2.0.0",
|
||||
@@ -62,7 +62,6 @@
|
||||
"ramda": "^0.27.1",
|
||||
"run-groups": "^3.0.1",
|
||||
"semver": "^7.3.4",
|
||||
"semver-utils": "^1.1.4",
|
||||
"version-selector-type": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { nameVerFromPkgSnapshot, PackageSnapshots } from '@pnpm/lockfile-utils'
|
||||
import { getAllDependenciesFromManifest } from '@pnpm/manifest-utils'
|
||||
import { PreferredVersions } from '@pnpm/resolver-base'
|
||||
import { Dependencies, DependencyManifest, ProjectManifest } from '@pnpm/types'
|
||||
import getVerSelType from 'version-selector-type'
|
||||
import { DependencyManifest } from '@pnpm/types'
|
||||
|
||||
export function getAllUniqueSpecs (manifests: DependencyManifest[]) {
|
||||
const allSpecs: Record<string, string> = {}
|
||||
@@ -22,36 +21,6 @@ export function getAllUniqueSpecs (manifests: DependencyManifest[]) {
|
||||
return allSpecs
|
||||
}
|
||||
|
||||
export default function getPreferredVersionsFromPackage (
|
||||
pkg: Pick<ProjectManifest, 'devDependencies' | 'dependencies' | 'optionalDependencies'>
|
||||
): PreferredVersions {
|
||||
return getVersionSpecsByRealNames(getAllDependenciesFromManifest(pkg))
|
||||
}
|
||||
|
||||
function getVersionSpecsByRealNames (deps: Dependencies) {
|
||||
return Object.keys(deps)
|
||||
.reduce((acc, depName) => {
|
||||
if (deps[depName].startsWith('npm:')) {
|
||||
const pref = deps[depName].substr(4)
|
||||
const index = pref.lastIndexOf('@')
|
||||
const spec = pref.substr(index + 1)
|
||||
const selector = getVerSelType(spec)
|
||||
if (selector != null) {
|
||||
const pkgName = pref.substr(0, index)
|
||||
acc[pkgName] = acc[pkgName] || {}
|
||||
acc[pkgName][selector.normalized] = selector.type
|
||||
}
|
||||
} else if (!deps[depName].includes(':')) { // we really care only about semver specs
|
||||
const selector = getVerSelType(deps[depName])
|
||||
if (selector != null) {
|
||||
acc[depName] = acc[depName] || {}
|
||||
acc[depName][selector.normalized] = selector.type
|
||||
}
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
||||
export function getPreferredVersionsFromLockfile (snapshots: PackageSnapshots): PreferredVersions {
|
||||
const preferredVersions: PreferredVersions = {}
|
||||
for (const [depPath, snapshot] of Object.entries(snapshots)) {
|
||||
|
||||
@@ -38,9 +38,12 @@ import resolveDependencies, {
|
||||
DependenciesGraph,
|
||||
DependenciesGraphNode,
|
||||
} from '@pnpm/resolve-dependencies'
|
||||
import getWantedDependencies, {
|
||||
PinnedVersion,
|
||||
WantedDependency,
|
||||
} from '@pnpm/resolve-dependencies/lib/getWantedDependencies'
|
||||
import {
|
||||
PreferredVersions,
|
||||
WorkspacePackages,
|
||||
} from '@pnpm/resolver-base'
|
||||
import {
|
||||
DependenciesField,
|
||||
@@ -61,7 +64,6 @@ import pipeWith from 'ramda/src/pipeWith'
|
||||
import props from 'ramda/src/props'
|
||||
import unnest from 'ramda/src/unnest'
|
||||
import parseWantedDependencies from '../parseWantedDependencies'
|
||||
import safeIsInnerLink from '../safeIsInnerLink'
|
||||
import removeDeps from '../uninstall/removeDeps'
|
||||
import allProjectsAreUpToDate from './allProjectsAreUpToDate'
|
||||
import createPackageExtender from './createPackageExtender'
|
||||
@@ -70,11 +72,7 @@ import extendOptions, {
|
||||
InstallOptions,
|
||||
StrictInstallOptions,
|
||||
} from './extendInstallOptions'
|
||||
import getPreferredVersionsFromPackage, { getPreferredVersionsFromLockfile, getAllUniqueSpecs } from './getPreferredVersions'
|
||||
import getWantedDependencies, {
|
||||
PinnedVersion,
|
||||
WantedDependency,
|
||||
} from './getWantedDependencies'
|
||||
import { getPreferredVersionsFromLockfile, getAllUniqueSpecs } from './getPreferredVersions'
|
||||
import linkPackages from './link'
|
||||
|
||||
const BROKEN_LOCKFILE_INTEGRITY_ERRORS = new Set([
|
||||
@@ -561,51 +559,6 @@ function pkgHasDependencies (manifest: ProjectManifest) {
|
||||
)
|
||||
}
|
||||
|
||||
async function partitionLinkedPackages (
|
||||
dependencies: WantedDependency[],
|
||||
opts: {
|
||||
projectDir: string
|
||||
lockfileOnly: boolean
|
||||
modulesDir: string
|
||||
storeDir: string
|
||||
virtualStoreDir: string
|
||||
workspacePackages?: WorkspacePackages
|
||||
}
|
||||
) {
|
||||
const nonLinkedDependencies: WantedDependency[] = []
|
||||
const linkedAliases = new Set<string>()
|
||||
for (const dependency of dependencies) {
|
||||
if (
|
||||
!dependency.alias ||
|
||||
opts.workspacePackages?.[dependency.alias] != null ||
|
||||
dependency.pref.startsWith('workspace:')
|
||||
) {
|
||||
nonLinkedDependencies.push(dependency)
|
||||
continue
|
||||
}
|
||||
const isInnerLink = await safeIsInnerLink(opts.modulesDir, dependency.alias, {
|
||||
hideAlienModules: !opts.lockfileOnly,
|
||||
projectDir: opts.projectDir,
|
||||
storeDir: opts.storeDir,
|
||||
virtualStoreDir: opts.virtualStoreDir,
|
||||
})
|
||||
if (isInnerLink === true) {
|
||||
nonLinkedDependencies.push(dependency)
|
||||
continue
|
||||
}
|
||||
// This info-log might be better to be moved to the reporter
|
||||
logger.info({
|
||||
message: `${dependency.alias} is linked to ${opts.modulesDir} from ${isInnerLink}`,
|
||||
prefix: opts.projectDir,
|
||||
})
|
||||
linkedAliases.add(dependency.alias)
|
||||
}
|
||||
return {
|
||||
linkedAliases,
|
||||
nonLinkedDependencies,
|
||||
}
|
||||
}
|
||||
|
||||
// If the specifier is new, the old resolution probably does not satisfy it anymore.
|
||||
// By removing these resolutions we ensure that they are resolved again using the new specs.
|
||||
function forgetResolutionsOfPrevWantedDeps (importer: ProjectSnapshot, wantedDeps: WantedDependency[]) {
|
||||
@@ -664,7 +617,7 @@ export type ImporterToUpdate = {
|
||||
pruneDirectDependencies: boolean
|
||||
removePackages?: string[]
|
||||
updatePackageManifest: boolean
|
||||
wantedDependencies: Array<WantedDependency & { isNew?: Boolean, updateSpec?: Boolean }>
|
||||
wantedDependencies: Array<WantedDependency & { isNew?: boolean, updateSpec?: boolean }>
|
||||
} & DependenciesMutation
|
||||
|
||||
type InstallFunction = (
|
||||
@@ -736,16 +689,6 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
opts.force ||
|
||||
opts.needsFullResolution ||
|
||||
ctx.lockfileHadConflicts
|
||||
const _toResolveImporter = toResolveImporter.bind(null, {
|
||||
defaultUpdateDepth: (opts.update || (opts.updateMatching != null)) ? opts.depth : -1,
|
||||
lockfileOnly: opts.lockfileOnly,
|
||||
preferredVersions,
|
||||
storeDir: ctx.storeDir,
|
||||
updateAll: Boolean(opts.updateMatching),
|
||||
virtualStoreDir: ctx.virtualStoreDir,
|
||||
workspacePackages: opts.workspacePackages,
|
||||
})
|
||||
const projectsToResolve = await Promise.all(projects.map(async (project) => _toResolveImporter(project)))
|
||||
|
||||
// Ignore some fields when fixing lockfile, so these fields can be regenerated
|
||||
// and make sure it's up-to-date
|
||||
@@ -776,9 +719,10 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
wantedToBeSkippedPackageIds,
|
||||
waitTillAllFetchingsFinish,
|
||||
} = await resolveDependencies(
|
||||
projectsToResolve,
|
||||
projects,
|
||||
{
|
||||
currentLockfile: ctx.currentLockfile,
|
||||
defaultUpdateDepth: (opts.update || (opts.updateMatching != null)) ? opts.depth : -1,
|
||||
dryRun: opts.lockfileOnly,
|
||||
engineStrict: opts.engineStrict,
|
||||
force: opts.force,
|
||||
@@ -790,6 +734,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
nodeVersion: opts.nodeVersion,
|
||||
pnpmVersion: opts.packageManager.name === 'pnpm' ? opts.packageManager.version : '',
|
||||
preferWorkspacePackages: opts.preferWorkspacePackages,
|
||||
preferredVersions,
|
||||
preserveWorkspaceProtocol: opts.preserveWorkspaceProtocol,
|
||||
registries: ctx.registries,
|
||||
saveWorkspaceProtocol: opts.saveWorkspaceProtocol,
|
||||
@@ -819,7 +764,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
const lockfileOpts = { forceSharedFormat: opts.forceSharedLockfile }
|
||||
if (!opts.lockfileOnly && opts.enableModulesDir) {
|
||||
const result = await linkPackages(
|
||||
projectsToResolve,
|
||||
projects,
|
||||
dependenciesGraph,
|
||||
{
|
||||
currentLockfile: ctx.currentLockfile,
|
||||
@@ -910,7 +855,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
})
|
||||
}
|
||||
|
||||
await Promise.all(projectsToResolve.map(async (project, index) => {
|
||||
await Promise.all(projects.map(async (project, index) => {
|
||||
let linkedPackages!: string[]
|
||||
if (ctx.publicHoistPattern?.length && path.relative(project.rootDir, opts.lockfileDir) === '') {
|
||||
const nodeExecPathByAlias = Object.entries(project.manifest.dependenciesMeta ?? {})
|
||||
@@ -1018,7 +963,7 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
|
||||
return {
|
||||
newLockfile,
|
||||
projects: projectsToResolve.map(({ manifest, rootDir }) => ({ rootDir, manifest })),
|
||||
projects: projects.map(({ manifest, rootDir }) => ({ rootDir, manifest })),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1040,68 +985,6 @@ const installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
}
|
||||
}
|
||||
|
||||
async function toResolveImporter (
|
||||
opts: {
|
||||
defaultUpdateDepth: number
|
||||
lockfileOnly: boolean
|
||||
preferredVersions?: PreferredVersions
|
||||
storeDir: string
|
||||
updateAll: boolean
|
||||
virtualStoreDir: string
|
||||
workspacePackages: WorkspacePackages
|
||||
},
|
||||
project: ImporterToUpdate
|
||||
) {
|
||||
const allDeps = getWantedDependencies(project.manifest)
|
||||
const { nonLinkedDependencies } = await partitionLinkedPackages(allDeps, {
|
||||
lockfileOnly: opts.lockfileOnly,
|
||||
modulesDir: project.modulesDir,
|
||||
projectDir: project.rootDir,
|
||||
storeDir: opts.storeDir,
|
||||
virtualStoreDir: opts.virtualStoreDir,
|
||||
workspacePackages: opts.workspacePackages,
|
||||
})
|
||||
const existingDeps = nonLinkedDependencies
|
||||
.filter(({ alias }) => !project.wantedDependencies.some((wantedDep) => wantedDep.alias === alias))
|
||||
let wantedDependencies!: Array<WantedDependency & { isNew?: boolean, updateDepth: number }>
|
||||
if (!project.manifest) {
|
||||
wantedDependencies = [
|
||||
...project.wantedDependencies,
|
||||
...existingDeps,
|
||||
]
|
||||
.map((dep) => ({
|
||||
...dep,
|
||||
updateDepth: opts.defaultUpdateDepth,
|
||||
}))
|
||||
} else {
|
||||
// Direct local tarballs are always checked,
|
||||
// so their update depth should be at least 0
|
||||
const updateLocalTarballs = (dep: WantedDependency) => ({
|
||||
...dep,
|
||||
updateDepth: opts.updateAll
|
||||
? opts.defaultUpdateDepth
|
||||
: (prefIsLocalTarball(dep.pref) ? 0 : -1),
|
||||
})
|
||||
wantedDependencies = [
|
||||
...project.wantedDependencies.map(
|
||||
opts.defaultUpdateDepth < 0
|
||||
? updateLocalTarballs
|
||||
: (dep) => ({ ...dep, updateDepth: opts.defaultUpdateDepth })),
|
||||
...existingDeps.map(updateLocalTarballs),
|
||||
]
|
||||
}
|
||||
return {
|
||||
...project,
|
||||
hasRemovedDependencies: Boolean(project.removePackages?.length),
|
||||
preferredVersions: opts.preferredVersions ?? (project.manifest && getPreferredVersionsFromPackage(project.manifest)) ?? {},
|
||||
wantedDependencies,
|
||||
}
|
||||
}
|
||||
|
||||
function prefIsLocalTarball (pref: string) {
|
||||
return pref.startsWith('file:') && pref.endsWith('.tgz')
|
||||
}
|
||||
|
||||
const limitLinking = pLimit(16)
|
||||
|
||||
async function linkAllBins (
|
||||
|
||||
@@ -19,7 +19,6 @@ import {
|
||||
DependenciesGraph,
|
||||
DependenciesGraphNode,
|
||||
LinkedDependency,
|
||||
ImporterToResolve,
|
||||
} from '@pnpm/resolve-dependencies'
|
||||
import { StoreController } from '@pnpm/store-controller-types'
|
||||
import symlinkDependency, { symlinkDirectRootDependency } from '@pnpm/symlink-dependency'
|
||||
@@ -34,11 +33,12 @@ import equals from 'ramda/src/equals'
|
||||
import difference from 'ramda/src/difference'
|
||||
import omit from 'ramda/src/omit'
|
||||
import props from 'ramda/src/props'
|
||||
import { ImporterToUpdate } from './index'
|
||||
|
||||
const brokenModulesLogger = logger('_broken_node_modules')
|
||||
|
||||
export default async function linkPackages (
|
||||
projects: ImporterToResolve[],
|
||||
projects: ImporterToUpdate[],
|
||||
depGraph: DependenciesGraph,
|
||||
opts: {
|
||||
currentLockfile: Lockfile
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import parseWantedDependency from '@pnpm/parse-wanted-dependency'
|
||||
import { Dependencies } from '@pnpm/types'
|
||||
import guessPinnedVersionFromExistingSpec from './guessPinnedVersionFromExistingSpec'
|
||||
import { PinnedVersion, WantedDependency } from './install/getWantedDependencies'
|
||||
import whichVersionIsPinned from '@pnpm/which-version-is-pinned'
|
||||
import { PinnedVersion, WantedDependency } from '@pnpm/resolve-dependencies/lib/getWantedDependencies'
|
||||
|
||||
export default function parseWantedDependencies (
|
||||
rawWantedDependencies: string[],
|
||||
@@ -34,7 +34,7 @@ export default function parseWantedDependencies (
|
||||
? 'workspace:*'
|
||||
: opts.currentPrefs[alias]
|
||||
}
|
||||
pinnedVersion = guessPinnedVersionFromExistingSpec(opts.currentPrefs[alias])
|
||||
pinnedVersion = whichVersionIsPinned(opts.currentPrefs[alias])
|
||||
}
|
||||
const result = {
|
||||
alias,
|
||||
|
||||
@@ -125,6 +125,9 @@
|
||||
},
|
||||
{
|
||||
"path": "../types"
|
||||
},
|
||||
{
|
||||
"path": "../which-version-is-pinned"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -42,9 +42,12 @@
|
||||
"@pnpm/resolver-base": "workspace:8.1.1",
|
||||
"@pnpm/store-controller-types": "workspace:11.0.7",
|
||||
"@pnpm/types": "workspace:7.6.0",
|
||||
"@pnpm/which-version-is-pinned": "workspace:0.0.0",
|
||||
"dependency-path": "workspace:8.0.6",
|
||||
"encode-registry": "^3.0.0",
|
||||
"get-npm-tarball-url": "^2.0.3",
|
||||
"is-inner-link": "^4.0.0",
|
||||
"is-subdir": "^1.1.1",
|
||||
"path-exists": "^4.0.0",
|
||||
"ramda": "^0.27.1",
|
||||
"replace-string": "^3.1.0",
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
IncludedDependencies,
|
||||
ProjectManifest,
|
||||
} from '@pnpm/types'
|
||||
import guessPinnedVersionFromExistingSpec from '../guessPinnedVersionFromExistingSpec'
|
||||
import whichVersionIsPinned from '@pnpm/which-version-is-pinned'
|
||||
|
||||
export type PinnedVersion = 'major' | 'minor' | 'patch' | 'none'
|
||||
|
||||
@@ -71,7 +71,7 @@ function getWantedDependenciesFromGivenSet (
|
||||
injected: opts.dependenciesMeta[alias]?.injected,
|
||||
optional: depType === 'optional',
|
||||
nodeExecPath: opts.nodeExecPath ?? opts.dependenciesMeta[alias]?.node,
|
||||
pinnedVersion: guessPinnedVersionFromExistingSpec(deps[alias]),
|
||||
pinnedVersion: whichVersionIsPinned(deps[alias]),
|
||||
pref,
|
||||
raw: `${alias}@${pref}`,
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import resolvePeers, {
|
||||
GenericDependenciesGraph,
|
||||
GenericDependenciesGraphNode,
|
||||
} from './resolvePeers'
|
||||
import toResolveImporter from './toResolveImporter'
|
||||
import updateLockfile from './updateLockfile'
|
||||
import updateProjectManifest from './updateProjectManifest'
|
||||
|
||||
@@ -73,27 +74,37 @@ export type ImporterToResolve = Importer<{
|
||||
export default async function (
|
||||
importers: ImporterToResolve[],
|
||||
opts: ResolveDependenciesOptions & {
|
||||
defaultUpdateDepth: number
|
||||
preserveWorkspaceProtocol: boolean
|
||||
saveWorkspaceProtocol: boolean
|
||||
strictPeerDependencies: boolean
|
||||
}
|
||||
) {
|
||||
const _toResolveImporter = toResolveImporter.bind(null, {
|
||||
defaultUpdateDepth: opts.defaultUpdateDepth,
|
||||
lockfileOnly: opts.dryRun,
|
||||
preferredVersions: opts.preferredVersions,
|
||||
updateAll: Boolean(opts.updateMatching),
|
||||
virtualStoreDir: opts.virtualStoreDir,
|
||||
workspacePackages: opts.workspacePackages,
|
||||
})
|
||||
const projectsToResolve = await Promise.all(importers.map(async (project) => _toResolveImporter(project)))
|
||||
const {
|
||||
dependenciesTree,
|
||||
outdatedDependencies,
|
||||
resolvedImporters,
|
||||
resolvedPackagesByDepPath,
|
||||
wantedToBeSkippedPackageIds,
|
||||
} = await resolveDependencyTree(importers, opts)
|
||||
} = await resolveDependencyTree(projectsToResolve, opts)
|
||||
|
||||
const linkedDependenciesByProjectId: Record<string, LinkedDependency[]> = {}
|
||||
const projectsToLink = await Promise.all<ProjectToLink>(importers.map(async (project, index) => {
|
||||
const projectsToLink = await Promise.all<ProjectToLink>(projectsToResolve.map(async (project, index) => {
|
||||
const resolvedImporter = resolvedImporters[project.id]
|
||||
linkedDependenciesByProjectId[project.id] = resolvedImporter.linkedDependencies
|
||||
let updatedManifest: ProjectManifest | undefined = project.manifest
|
||||
let updatedOriginalManifest: ProjectManifest | undefined = project.originalManifest
|
||||
if (project.updatePackageManifest) {
|
||||
const manifests = await updateProjectManifest(importers[index], {
|
||||
const manifests = await updateProjectManifest(project, {
|
||||
directDependencies: resolvedImporter.directDependencies,
|
||||
preserveWorkspaceProtocol: opts.preserveWorkspaceProtocol,
|
||||
saveWorkspaceProtocol: opts.saveWorkspaceProtocol,
|
||||
@@ -130,13 +141,14 @@ export default async function (
|
||||
)
|
||||
: []
|
||||
|
||||
project.manifest = updatedOriginalManifest ?? project.originalManifest ?? project.manifest
|
||||
const manifest = updatedOriginalManifest ?? project.originalManifest ?? project.manifest
|
||||
importers[index].manifest = manifest
|
||||
return {
|
||||
binsDir: project.binsDir,
|
||||
directNodeIdsByAlias: resolvedImporter.directNodeIdsByAlias,
|
||||
id: project.id,
|
||||
linkedDependencies: resolvedImporter.linkedDependencies,
|
||||
manifest: project.manifest,
|
||||
manifest,
|
||||
modulesDir: project.modulesDir,
|
||||
rootDir: project.rootDir,
|
||||
topParents,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Lockfile } from '@pnpm/lockfile-types'
|
||||
import { PreferredVersions, Resolution, WorkspacePackages } from '@pnpm/resolver-base'
|
||||
import { StoreController } from '@pnpm/store-controller-types'
|
||||
import {
|
||||
ProjectManifest,
|
||||
ReadPackageHook,
|
||||
Registries,
|
||||
} from '@pnpm/types'
|
||||
@@ -37,10 +38,16 @@ export interface ResolvedDirectDependency {
|
||||
|
||||
export interface Importer<T> {
|
||||
id: string
|
||||
hasRemovedDependencies?: boolean
|
||||
manifest: ProjectManifest
|
||||
modulesDir: string
|
||||
preferredVersions?: PreferredVersions
|
||||
removePackages?: string[]
|
||||
rootDir: string
|
||||
wantedDependencies: Array<T & WantedDependency>
|
||||
}
|
||||
|
||||
export interface ImporterToResolveGeneric<T> extends Importer<T> {
|
||||
hasRemovedDependencies?: boolean
|
||||
preferredVersions?: PreferredVersions
|
||||
wantedDependencies: Array<T & WantedDependency & { updateDepth: number }>
|
||||
}
|
||||
|
||||
@@ -57,6 +64,7 @@ export interface ResolveDependenciesOptions {
|
||||
nodeVersion: string
|
||||
registries: Registries
|
||||
pnpmVersion: string
|
||||
preferredVersions?: PreferredVersions
|
||||
preferWorkspacePackages?: boolean
|
||||
updateMatching?: (pkgName: string) => boolean
|
||||
linkWorkspacePackagesDepth?: number
|
||||
@@ -69,7 +77,7 @@ export interface ResolveDependenciesOptions {
|
||||
}
|
||||
|
||||
export default async function<T> (
|
||||
importers: Array<Importer<T>>,
|
||||
importers: Array<ImporterToResolveGeneric<T>>,
|
||||
opts: ResolveDependenciesOptions
|
||||
) {
|
||||
const directDepsByImporterId = {} as {[id: string]: Array<PkgAddress | LinkedDependency>}
|
||||
|
||||
@@ -10,7 +10,6 @@ export default async function safeIsInnerLink (
|
||||
opts: {
|
||||
hideAlienModules: boolean
|
||||
projectDir: string
|
||||
storeDir: string
|
||||
virtualStoreDir: string
|
||||
}
|
||||
): Promise<true | string> {
|
||||
@@ -19,7 +18,7 @@ export default async function safeIsInnerLink (
|
||||
|
||||
if (link.isInner) return true
|
||||
|
||||
if (isSubdir(opts.virtualStoreDir, link.target) || isSubdir(opts.storeDir, link.target)) return true
|
||||
if (isSubdir(opts.virtualStoreDir, link.target)) return true
|
||||
|
||||
return link.target as string
|
||||
} catch (err: any) { // eslint-disable-line
|
||||
141
packages/resolve-dependencies/src/toResolveImporter.ts
Normal file
141
packages/resolve-dependencies/src/toResolveImporter.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import logger from '@pnpm/logger'
|
||||
import { getAllDependenciesFromManifest } from '@pnpm/manifest-utils'
|
||||
import {
|
||||
PreferredVersions,
|
||||
WorkspacePackages,
|
||||
} from '@pnpm/resolver-base'
|
||||
import { Dependencies, ProjectManifest } from '@pnpm/types'
|
||||
import getVerSelType from 'version-selector-type'
|
||||
import { ImporterToResolve } from '.'
|
||||
import getWantedDependencies, { WantedDependency } from './getWantedDependencies'
|
||||
import safeIsInnerLink from './safeIsInnerLink'
|
||||
|
||||
export default async function toResolveImporter (
|
||||
opts: {
|
||||
defaultUpdateDepth: number
|
||||
lockfileOnly: boolean
|
||||
preferredVersions?: PreferredVersions
|
||||
updateAll: boolean
|
||||
virtualStoreDir: string
|
||||
workspacePackages: WorkspacePackages
|
||||
},
|
||||
project: ImporterToResolve
|
||||
) {
|
||||
const allDeps = getWantedDependencies(project.manifest)
|
||||
const nonLinkedDependencies = await partitionLinkedPackages(allDeps, {
|
||||
lockfileOnly: opts.lockfileOnly,
|
||||
modulesDir: project.modulesDir,
|
||||
projectDir: project.rootDir,
|
||||
virtualStoreDir: opts.virtualStoreDir,
|
||||
workspacePackages: opts.workspacePackages,
|
||||
})
|
||||
const existingDeps = nonLinkedDependencies
|
||||
.filter(({ alias }) => !project.wantedDependencies.some((wantedDep) => wantedDep.alias === alias))
|
||||
let wantedDependencies!: Array<WantedDependency & { isNew?: boolean, updateDepth: number }>
|
||||
if (!project.manifest) {
|
||||
wantedDependencies = [
|
||||
...project.wantedDependencies,
|
||||
...existingDeps,
|
||||
]
|
||||
.map((dep) => ({
|
||||
...dep,
|
||||
updateDepth: opts.defaultUpdateDepth,
|
||||
}))
|
||||
} else {
|
||||
// Direct local tarballs are always checked,
|
||||
// so their update depth should be at least 0
|
||||
const updateLocalTarballs = (dep: WantedDependency) => ({
|
||||
...dep,
|
||||
updateDepth: opts.updateAll
|
||||
? opts.defaultUpdateDepth
|
||||
: (prefIsLocalTarball(dep.pref) ? 0 : -1),
|
||||
})
|
||||
wantedDependencies = [
|
||||
...project.wantedDependencies.map(
|
||||
opts.defaultUpdateDepth < 0
|
||||
? updateLocalTarballs
|
||||
: (dep) => ({ ...dep, updateDepth: opts.defaultUpdateDepth })),
|
||||
...existingDeps.map(updateLocalTarballs),
|
||||
]
|
||||
}
|
||||
return {
|
||||
...project,
|
||||
hasRemovedDependencies: Boolean(project.removePackages?.length),
|
||||
preferredVersions: opts.preferredVersions ?? (project.manifest && getPreferredVersionsFromPackage(project.manifest)) ?? {},
|
||||
wantedDependencies,
|
||||
}
|
||||
}
|
||||
|
||||
function prefIsLocalTarball (pref: string) {
|
||||
return pref.startsWith('file:') && pref.endsWith('.tgz')
|
||||
}
|
||||
|
||||
async function partitionLinkedPackages (
|
||||
dependencies: WantedDependency[],
|
||||
opts: {
|
||||
projectDir: string
|
||||
lockfileOnly: boolean
|
||||
modulesDir: string
|
||||
virtualStoreDir: string
|
||||
workspacePackages?: WorkspacePackages
|
||||
}
|
||||
): Promise<WantedDependency[]> {
|
||||
const nonLinkedDependencies: WantedDependency[] = []
|
||||
const linkedAliases = new Set<string>()
|
||||
for (const dependency of dependencies) {
|
||||
if (
|
||||
!dependency.alias ||
|
||||
opts.workspacePackages?.[dependency.alias] != null ||
|
||||
dependency.pref.startsWith('workspace:')
|
||||
) {
|
||||
nonLinkedDependencies.push(dependency)
|
||||
continue
|
||||
}
|
||||
const isInnerLink = await safeIsInnerLink(opts.modulesDir, dependency.alias, {
|
||||
hideAlienModules: !opts.lockfileOnly,
|
||||
projectDir: opts.projectDir,
|
||||
virtualStoreDir: opts.virtualStoreDir,
|
||||
})
|
||||
if (isInnerLink === true) {
|
||||
nonLinkedDependencies.push(dependency)
|
||||
continue
|
||||
}
|
||||
// This info-log might be better to be moved to the reporter
|
||||
logger.info({
|
||||
message: `${dependency.alias} is linked to ${opts.modulesDir} from ${isInnerLink}`,
|
||||
prefix: opts.projectDir,
|
||||
})
|
||||
linkedAliases.add(dependency.alias)
|
||||
}
|
||||
return nonLinkedDependencies
|
||||
}
|
||||
|
||||
function getPreferredVersionsFromPackage (
|
||||
pkg: Pick<ProjectManifest, 'devDependencies' | 'dependencies' | 'optionalDependencies'>
|
||||
): PreferredVersions {
|
||||
return getVersionSpecsByRealNames(getAllDependenciesFromManifest(pkg))
|
||||
}
|
||||
|
||||
function getVersionSpecsByRealNames (deps: Dependencies) {
|
||||
return Object.keys(deps)
|
||||
.reduce((acc, depName) => {
|
||||
if (deps[depName].startsWith('npm:')) {
|
||||
const pref = deps[depName].substr(4)
|
||||
const index = pref.lastIndexOf('@')
|
||||
const spec = pref.substr(index + 1)
|
||||
const selector = getVerSelType(spec)
|
||||
if (selector != null) {
|
||||
const pkgName = pref.substr(0, index)
|
||||
acc[pkgName] = acc[pkgName] || {}
|
||||
acc[pkgName][selector.normalized] = selector.type
|
||||
}
|
||||
} else if (!deps[depName].includes(':')) { // we really care only about semver specs
|
||||
const selector = getVerSelType(deps[depName])
|
||||
if (selector != null) {
|
||||
acc[depName] = acc[depName] || {}
|
||||
acc[depName][selector.normalized] = selector.type
|
||||
}
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
@@ -50,6 +50,9 @@
|
||||
},
|
||||
{
|
||||
"path": "../types"
|
||||
},
|
||||
{
|
||||
"path": "../which-version-is-pinned"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
22
packages/which-version-is-pinned/README.md
Normal file
22
packages/which-version-is-pinned/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# @pnpm/which-version-is-pinned
|
||||
|
||||
> Takes a version spec and returns which version is pinned by it
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
pnpm add @pnpm/which-version-is-pinned
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import whichVersionIsPinned from '@pnpm/which-version-is-pinned'
|
||||
|
||||
whichVersionIsPinned('^1.0.0')
|
||||
// major
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
1
packages/which-version-is-pinned/jest.config.js
Normal file
1
packages/which-version-is-pinned/jest.config.js
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = require('../../jest.config')
|
||||
34
packages/which-version-is-pinned/package.json
Normal file
34
packages/which-version-is-pinned/package.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "@pnpm/which-version-is-pinned",
|
||||
"description": "Takes a version spec and returns which version is pinned by it",
|
||||
"version": "0.0.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pnpm/pnpm/issues"
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"files": [
|
||||
"lib",
|
||||
"!*.map"
|
||||
],
|
||||
"keywords": [
|
||||
"pnpm6"
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.17"
|
||||
},
|
||||
"repository": "https://github.com/pnpm/pnpm/blob/master/packages/which-version-is-pinned",
|
||||
"scripts": {
|
||||
"_test": "jest",
|
||||
"test": "pnpm run compile && pnpm run _test",
|
||||
"lint": "eslint src/**/*.ts test/**/*.ts",
|
||||
"prepublishOnly": "pnpm run compile",
|
||||
"compile": "rimraf lib tsconfig.tsbuildinfo && tsc --build && pnpm run lint -- --fix"
|
||||
},
|
||||
"homepage": "https://github.com/pnpm/pnpm/blob/master/packages/which-version-is-pinned#readme",
|
||||
"funding": "https://opencollective.com/pnpm",
|
||||
"dependencies": {
|
||||
"semver-utils": "^1.1.4"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { parseRange } from 'semver-utils'
|
||||
|
||||
export default function guessPinnedVersionFromExistingSpec (spec: string) {
|
||||
export default function whichVersionIsPinned (spec: string) {
|
||||
if (spec.startsWith('workspace:')) spec = spec.substr('workspace:'.length)
|
||||
if (spec === '*') return 'none'
|
||||
const parsedRange = parseRange(spec)
|
||||
11
packages/which-version-is-pinned/test/index.ts
Normal file
11
packages/which-version-is-pinned/test/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import whichVersionIsPinned from '@pnpm/which-version-is-pinned'
|
||||
|
||||
test.each([
|
||||
['^1.0.0', 'major'],
|
||||
['~1.0.0', 'minor'],
|
||||
['1.0.0', 'patch'],
|
||||
['*', 'none'],
|
||||
['workspace:^1.0.0', 'major'],
|
||||
])('whichVersionIsPinned()', (spec, expectedResult) => {
|
||||
expect(whichVersionIsPinned(spec)).toEqual(expectedResult)
|
||||
})
|
||||
12
packages/which-version-is-pinned/tsconfig.json
Normal file
12
packages/which-version-is-pinned/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "@pnpm/tsconfig",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"../../typings/**/*.d.ts"
|
||||
],
|
||||
"references": []
|
||||
}
|
||||
8
packages/which-version-is-pinned/tsconfig.lint.json
Normal file
8
packages/which-version-is-pinned/tsconfig.lint.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"test/**/*.ts",
|
||||
"../../typings/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
21
pnpm-lock.yaml
generated
21
pnpm-lock.yaml
generated
@@ -414,6 +414,7 @@ importers:
|
||||
'@pnpm/symlink-dependency': workspace:4.0.8
|
||||
'@pnpm/test-fixtures': workspace:*
|
||||
'@pnpm/types': workspace:7.6.0
|
||||
'@pnpm/which-version-is-pinned': workspace:0.0.0
|
||||
'@types/fs-extra': ^9.0.5
|
||||
'@types/is-ci': ^3.0.0
|
||||
'@types/is-windows': ^1.0.0
|
||||
@@ -432,7 +433,6 @@ importers:
|
||||
graph-sequencer: 2.0.0
|
||||
is-ci: ^3.0.0
|
||||
is-inner-link: ^4.0.0
|
||||
is-subdir: ^1.1.1
|
||||
is-windows: ^1.0.2
|
||||
load-json-file: ^6.2.0
|
||||
ncp: ^2.0.0
|
||||
@@ -449,7 +449,6 @@ importers:
|
||||
resolve-link-target: ^2.0.0
|
||||
run-groups: ^3.0.1
|
||||
semver: ^7.3.4
|
||||
semver-utils: ^1.1.4
|
||||
sinon: ^11.1.1
|
||||
symlink-dir: ^5.0.0
|
||||
version-selector-type: ^3.0.0
|
||||
@@ -487,12 +486,12 @@ importers:
|
||||
'@pnpm/store-controller-types': link:../store-controller-types
|
||||
'@pnpm/symlink-dependency': link:../symlink-dependency
|
||||
'@pnpm/types': link:../types
|
||||
'@pnpm/which-version-is-pinned': link:../which-version-is-pinned
|
||||
'@zkochan/npm-package-arg': 2.0.1
|
||||
'@zkochan/rimraf': 2.1.1
|
||||
dependency-path: link:../dependency-path
|
||||
graph-sequencer: 2.0.0
|
||||
is-inner-link: 4.0.0
|
||||
is-subdir: 1.2.0
|
||||
load-json-file: 6.2.0
|
||||
normalize-path: 3.0.0
|
||||
p-every: 2.0.0
|
||||
@@ -503,7 +502,6 @@ importers:
|
||||
ramda: 0.27.1
|
||||
run-groups: 3.0.1
|
||||
semver: 7.3.5
|
||||
semver-utils: 1.1.4
|
||||
version-selector-type: 3.0.0
|
||||
devDependencies:
|
||||
'@pnpm/assert-project': link:../../privatePackages/assert-project
|
||||
@@ -3067,11 +3065,14 @@ importers:
|
||||
'@pnpm/resolver-base': workspace:8.1.1
|
||||
'@pnpm/store-controller-types': workspace:11.0.7
|
||||
'@pnpm/types': workspace:7.6.0
|
||||
'@pnpm/which-version-is-pinned': workspace:0.0.0
|
||||
'@types/ramda': 0.27.39
|
||||
'@types/semver': ^7.3.4
|
||||
dependency-path: workspace:8.0.6
|
||||
encode-registry: ^3.0.0
|
||||
get-npm-tarball-url: ^2.0.3
|
||||
is-inner-link: ^4.0.0
|
||||
is-subdir: ^1.1.1
|
||||
path-exists: ^4.0.0
|
||||
ramda: ^0.27.1
|
||||
replace-string: ^3.1.0
|
||||
@@ -3091,9 +3092,12 @@ importers:
|
||||
'@pnpm/resolver-base': link:../resolver-base
|
||||
'@pnpm/store-controller-types': link:../store-controller-types
|
||||
'@pnpm/types': link:../types
|
||||
'@pnpm/which-version-is-pinned': link:../which-version-is-pinned
|
||||
dependency-path: link:../dependency-path
|
||||
encode-registry: 3.0.0
|
||||
get-npm-tarball-url: 2.0.3
|
||||
is-inner-link: 4.0.0
|
||||
is-subdir: 1.2.0
|
||||
path-exists: 4.0.0
|
||||
ramda: 0.27.1
|
||||
replace-string: 3.1.0
|
||||
@@ -3305,6 +3309,15 @@ importers:
|
||||
devDependencies:
|
||||
'@pnpm/types': 'link:'
|
||||
|
||||
packages/which-version-is-pinned:
|
||||
specifiers:
|
||||
'@pnpm/which-version-is-pinned': 'link:'
|
||||
semver-utils: ^1.1.4
|
||||
dependencies:
|
||||
semver-utils: 1.1.4
|
||||
devDependencies:
|
||||
'@pnpm/which-version-is-pinned': 'link:'
|
||||
|
||||
packages/write-project-manifest:
|
||||
specifiers:
|
||||
'@pnpm/types': workspace:7.6.0
|
||||
|
||||
Reference in New Issue
Block a user