mirror of
https://github.com/pnpm/pnpm.git
synced 2026-06-28 09:55:39 -04:00
rawLocalConfig detected whether hoist settings were explicitly set. In v11, config values are always authoritative. - Remove rawLocalConfig from ConfigContext, config reader, inheritPickedConfig, UniversalOptions - Remove forceHoistPattern, forcePublicHoistPattern, forceShamefullyHoist — validateModules always checks now - Simplify save-workspace-protocol check - Remove dead rawLocalConfig overrides in deploy/patchCommit
400 lines
12 KiB
TypeScript
400 lines
12 KiB
TypeScript
import { promises as fs } from 'node:fs'
|
|
import path from 'node:path'
|
|
|
|
import { contextLogger, packageManifestLogger } from '@pnpm/core-loggers'
|
|
import type {
|
|
IncludedDependencies,
|
|
Modules,
|
|
} from '@pnpm/installing.modules-yaml'
|
|
import { readProjectsContext } from '@pnpm/installing.read-projects-context'
|
|
import type { LockfileObject } from '@pnpm/lockfile.fs'
|
|
import type { WorkspacePackages } from '@pnpm/resolving.resolver-base'
|
|
import { registerProject } from '@pnpm/store.controller'
|
|
import type {
|
|
DependencyManifest,
|
|
DepPath,
|
|
HoistedDependencies,
|
|
ProjectId,
|
|
ProjectManifest,
|
|
ProjectRootDir,
|
|
ProjectRootDirRealPath,
|
|
ReadPackageHook,
|
|
Registries,
|
|
} from '@pnpm/types'
|
|
import { pathAbsolute } from 'path-absolute'
|
|
import { clone } from 'ramda'
|
|
|
|
import { readLockfiles } from './readLockfiles.js'
|
|
|
|
/**
|
|
* Note that some fields are affected by modules directory state. Such fields should be used for
|
|
* mutating the modules directory only or in a manner that does not influence dependency resolution.
|
|
*/
|
|
export interface PnpmContext {
|
|
currentLockfile: LockfileObject
|
|
currentLockfileIsUpToDate: boolean
|
|
existsCurrentLockfile: boolean
|
|
existsWantedLockfile: boolean
|
|
existsNonEmptyWantedLockfile: boolean
|
|
extraBinPaths: string[]
|
|
/** Affected by existing modules directory, if it exists. */
|
|
extraNodePaths: string[]
|
|
lockfileHadConflicts: boolean
|
|
hoistedDependencies: HoistedDependencies
|
|
/** Required included dependencies or dependencies currently included by the modules directory. */
|
|
include: IncludedDependencies
|
|
modulesFile: Modules | null
|
|
pendingBuilds: string[]
|
|
projects: Record<string, {
|
|
modulesDir: string
|
|
id: ProjectId
|
|
} & HookOptions & Required<ProjectOptions>>
|
|
rootModulesDir: string
|
|
hoistPattern: string[] | undefined
|
|
/** As applied to existing modules directory, if it exists. */
|
|
currentHoistPattern: string[] | undefined
|
|
hoistedModulesDir: string
|
|
publicHoistPattern: string[] | undefined
|
|
/** As applied to existing modules directory, if it exists. */
|
|
currentPublicHoistPattern: string[] | undefined
|
|
lockfileDir: string
|
|
virtualStoreDir: string
|
|
/** As applied to existing modules directory, otherwise options. */
|
|
virtualStoreDirMaxLength: number
|
|
/** As applied to existing modules directory, if it exists. */
|
|
skipped: Set<DepPath>
|
|
storeDir: string
|
|
wantedLockfile: LockfileObject
|
|
wantedLockfileIsModified: boolean
|
|
workspacePackages: WorkspacePackages
|
|
registries: Registries
|
|
}
|
|
|
|
export interface ProjectOptions {
|
|
buildIndex: number
|
|
binsDir?: string
|
|
manifest: ProjectManifest
|
|
modulesDir?: string
|
|
rootDir: ProjectRootDir
|
|
rootDirRealPath?: ProjectRootDirRealPath
|
|
}
|
|
|
|
interface HookOptions {
|
|
originalManifest?: ProjectManifest
|
|
}
|
|
|
|
export interface GetContextOptions {
|
|
autoInstallPeers: boolean
|
|
ci?: boolean
|
|
excludeLinksFromLockfile: boolean
|
|
peersSuffixMaxLength: number
|
|
allProjects: Array<ProjectOptions & HookOptions>
|
|
confirmModulesPurge?: boolean
|
|
force: boolean
|
|
frozenLockfile?: boolean
|
|
enableGlobalVirtualStore?: boolean
|
|
extraBinPaths: string[]
|
|
extendNodePath?: boolean
|
|
lockfileDir: string
|
|
modulesDir?: string
|
|
nodeLinker: 'isolated' | 'hoisted' | 'pnp'
|
|
readPackageHook?: ReadPackageHook
|
|
include?: IncludedDependencies
|
|
registries: Registries
|
|
storeDir: string
|
|
useLockfile: boolean
|
|
useGitBranchLockfile?: boolean
|
|
mergeGitBranchLockfiles?: boolean
|
|
virtualStoreDir?: string
|
|
virtualStoreDirMaxLength: number
|
|
workspacePackages?: WorkspacePackages
|
|
|
|
hoistPattern?: string[] | undefined
|
|
|
|
publicHoistPattern?: string[] | undefined
|
|
global?: boolean
|
|
}
|
|
|
|
export async function getContext (
|
|
opts: GetContextOptions
|
|
): Promise<PnpmContext> {
|
|
const modulesDir = opts.modulesDir ?? 'node_modules'
|
|
const importersContext = await readProjectsContext(opts.allProjects, { lockfileDir: opts.lockfileDir, modulesDir })
|
|
const virtualStoreDir = pathAbsolute(opts.virtualStoreDir ?? path.join(modulesDir, '.pnpm'), opts.lockfileDir)
|
|
|
|
await fs.mkdir(opts.storeDir, { recursive: true })
|
|
|
|
// Register this project for store prune tracking
|
|
await registerProject(opts.storeDir, opts.lockfileDir)
|
|
|
|
for (const project of opts.allProjects) {
|
|
packageManifestLogger.debug({
|
|
initial: project.manifest,
|
|
prefix: project.rootDir,
|
|
})
|
|
}
|
|
if (opts.readPackageHook != null) {
|
|
await Promise.all(importersContext.projects.map(async (project) => {
|
|
project.originalManifest = project.manifest
|
|
project.manifest = await opts.readPackageHook!(clone(project.manifest), project.rootDir)
|
|
}))
|
|
}
|
|
|
|
const extraBinPaths = [
|
|
...opts.extraBinPaths || [],
|
|
]
|
|
const internalPnpmDir = path.join(importersContext.rootModulesDir, '.pnpm')
|
|
const hoistedModulesDir = path.join(
|
|
opts.enableGlobalVirtualStore ? internalPnpmDir : virtualStoreDir,
|
|
'node_modules'
|
|
)
|
|
if (opts.hoistPattern?.length) {
|
|
extraBinPaths.unshift(path.join(hoistedModulesDir, '.bin'))
|
|
}
|
|
const ctx: PnpmContext = {
|
|
extraBinPaths,
|
|
extraNodePaths: getExtraNodePaths({
|
|
extendNodePath: opts.extendNodePath,
|
|
nodeLinker: opts.nodeLinker,
|
|
hoistPattern: importersContext.currentHoistPattern ?? opts.hoistPattern,
|
|
hoistedModulesDir,
|
|
}),
|
|
hoistedDependencies: importersContext.hoistedDependencies,
|
|
hoistedModulesDir,
|
|
hoistPattern: opts.hoistPattern,
|
|
currentHoistPattern: importersContext.currentHoistPattern,
|
|
include: opts.include ?? importersContext.include,
|
|
lockfileDir: opts.lockfileDir,
|
|
modulesFile: importersContext.modules,
|
|
pendingBuilds: importersContext.pendingBuilds,
|
|
projects: Object.fromEntries(importersContext.projects.map((project) => [project.rootDir, project])),
|
|
publicHoistPattern: opts.publicHoistPattern,
|
|
currentPublicHoistPattern: importersContext.currentPublicHoistPattern,
|
|
registries: opts.registries,
|
|
rootModulesDir: importersContext.rootModulesDir,
|
|
skipped: importersContext.skipped,
|
|
storeDir: opts.storeDir,
|
|
virtualStoreDir,
|
|
virtualStoreDirMaxLength: importersContext.virtualStoreDirMaxLength ?? opts.virtualStoreDirMaxLength,
|
|
workspacePackages: opts.workspacePackages ?? arrayOfWorkspacePackagesToMap(opts.allProjects),
|
|
...await readLockfiles({
|
|
autoInstallPeers: opts.autoInstallPeers,
|
|
ci: opts.ci,
|
|
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
|
|
peersSuffixMaxLength: opts.peersSuffixMaxLength,
|
|
force: opts.force,
|
|
frozenLockfile: opts.frozenLockfile === true,
|
|
lockfileDir: opts.lockfileDir,
|
|
projects: importersContext.projects,
|
|
registry: opts.registries.default,
|
|
useLockfile: opts.useLockfile,
|
|
useGitBranchLockfile: opts.useGitBranchLockfile,
|
|
mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles,
|
|
internalPnpmDir,
|
|
}),
|
|
}
|
|
contextLogger.debug({
|
|
currentLockfileExists: ctx.existsCurrentLockfile,
|
|
storeDir: opts.storeDir,
|
|
virtualStoreDir,
|
|
})
|
|
return ctx
|
|
}
|
|
|
|
export interface PnpmSingleContext {
|
|
currentLockfile: LockfileObject
|
|
currentLockfileIsUpToDate: boolean
|
|
existsCurrentLockfile: boolean
|
|
existsWantedLockfile: boolean
|
|
existsNonEmptyWantedLockfile: boolean
|
|
/** Affected by existing modules directory, if it exists. */
|
|
extraBinPaths: string[]
|
|
extraNodePaths: string[]
|
|
lockfileHadConflicts: boolean
|
|
hoistedDependencies: HoistedDependencies
|
|
hoistedModulesDir: string
|
|
hoistPattern: string[] | undefined
|
|
manifest: ProjectManifest
|
|
modulesDir: string
|
|
importerId: string
|
|
prefix: string
|
|
/** Required included dependencies or dependencies currently included by the modules directory. */
|
|
include: IncludedDependencies
|
|
modulesFile: Modules | null
|
|
pendingBuilds: string[]
|
|
publicHoistPattern: string[] | undefined
|
|
registries: Registries
|
|
rootModulesDir: string
|
|
lockfileDir: string
|
|
virtualStoreDir: string
|
|
/** As applied to existing modules directory, if it exists. */
|
|
skipped: Set<string>
|
|
storeDir: string
|
|
wantedLockfile: LockfileObject
|
|
wantedLockfileIsModified: boolean
|
|
}
|
|
|
|
export async function getContextForSingleImporter (
|
|
manifest: ProjectManifest,
|
|
opts: {
|
|
autoInstallPeers: boolean
|
|
ci?: boolean
|
|
enableGlobalVirtualStore?: boolean
|
|
excludeLinksFromLockfile: boolean
|
|
peersSuffixMaxLength: number
|
|
force: boolean
|
|
confirmModulesPurge?: boolean
|
|
extraBinPaths: string[]
|
|
extendNodePath?: boolean
|
|
lockfileDir: string
|
|
nodeLinker: 'isolated' | 'hoisted' | 'pnp'
|
|
modulesDir?: string
|
|
readPackageHook?: ReadPackageHook
|
|
include?: IncludedDependencies
|
|
dir: string
|
|
registries: Registries
|
|
storeDir: string
|
|
useLockfile: boolean
|
|
useGitBranchLockfile?: boolean
|
|
mergeGitBranchLockfiles?: boolean
|
|
virtualStoreDir?: string
|
|
virtualStoreDirMaxLength: number
|
|
|
|
hoistPattern?: string[] | undefined
|
|
publicHoistPattern?: string[] | undefined
|
|
}
|
|
): Promise<PnpmSingleContext> {
|
|
const {
|
|
currentHoistPattern,
|
|
hoistedDependencies,
|
|
projects,
|
|
include,
|
|
modules,
|
|
pendingBuilds,
|
|
registries,
|
|
skipped,
|
|
rootModulesDir,
|
|
} = await readProjectsContext(
|
|
[
|
|
{
|
|
rootDir: opts.dir as ProjectRootDir,
|
|
},
|
|
],
|
|
{
|
|
lockfileDir: opts.lockfileDir,
|
|
modulesDir: opts.modulesDir,
|
|
}
|
|
)
|
|
|
|
const storeDir = opts.storeDir
|
|
|
|
const importer = projects[0]
|
|
const modulesDir = importer.modulesDir
|
|
const importerId = importer.id
|
|
const virtualStoreDir = pathAbsolute(opts.virtualStoreDir ?? 'node_modules/.pnpm', opts.lockfileDir)
|
|
|
|
await fs.mkdir(storeDir, { recursive: true })
|
|
|
|
// Register this project for store prune tracking
|
|
await registerProject(storeDir, opts.lockfileDir)
|
|
const extraBinPaths = [
|
|
...opts.extraBinPaths || [],
|
|
]
|
|
const internalPnpmDir = path.join(rootModulesDir, '.pnpm')
|
|
const hoistedModulesDir = path.join(
|
|
opts.enableGlobalVirtualStore ? internalPnpmDir : virtualStoreDir,
|
|
'node_modules'
|
|
)
|
|
if (opts.hoistPattern?.length) {
|
|
extraBinPaths.unshift(path.join(hoistedModulesDir, '.bin'))
|
|
}
|
|
const ctx: PnpmSingleContext = {
|
|
extraBinPaths,
|
|
extraNodePaths: getExtraNodePaths({
|
|
extendNodePath: opts.extendNodePath,
|
|
nodeLinker: opts.nodeLinker,
|
|
hoistPattern: currentHoistPattern ?? opts.hoistPattern,
|
|
hoistedModulesDir,
|
|
}),
|
|
hoistedDependencies,
|
|
hoistedModulesDir,
|
|
hoistPattern: opts.hoistPattern,
|
|
importerId,
|
|
include: opts.include ?? include,
|
|
lockfileDir: opts.lockfileDir,
|
|
manifest: await opts.readPackageHook?.(manifest) ?? manifest,
|
|
modulesDir,
|
|
modulesFile: modules,
|
|
pendingBuilds,
|
|
prefix: opts.dir,
|
|
publicHoistPattern: opts.publicHoistPattern,
|
|
registries: {
|
|
...opts.registries,
|
|
...registries,
|
|
},
|
|
rootModulesDir,
|
|
skipped,
|
|
storeDir,
|
|
virtualStoreDir,
|
|
...await readLockfiles({
|
|
autoInstallPeers: opts.autoInstallPeers,
|
|
ci: opts.ci,
|
|
excludeLinksFromLockfile: opts.excludeLinksFromLockfile,
|
|
peersSuffixMaxLength: opts.peersSuffixMaxLength,
|
|
force: opts.force,
|
|
frozenLockfile: false,
|
|
lockfileDir: opts.lockfileDir,
|
|
projects: [{ id: importerId, rootDir: opts.dir as ProjectRootDir }],
|
|
registry: opts.registries.default,
|
|
useLockfile: opts.useLockfile,
|
|
useGitBranchLockfile: opts.useGitBranchLockfile,
|
|
mergeGitBranchLockfiles: opts.mergeGitBranchLockfiles,
|
|
internalPnpmDir,
|
|
}),
|
|
}
|
|
packageManifestLogger.debug({
|
|
initial: manifest,
|
|
prefix: opts.dir,
|
|
})
|
|
contextLogger.debug({
|
|
currentLockfileExists: ctx.existsCurrentLockfile,
|
|
storeDir: opts.storeDir,
|
|
virtualStoreDir,
|
|
})
|
|
|
|
return ctx
|
|
}
|
|
|
|
function getExtraNodePaths (
|
|
{ extendNodePath = true, hoistPattern, nodeLinker, hoistedModulesDir }: {
|
|
extendNodePath?: boolean
|
|
hoistPattern?: string[]
|
|
nodeLinker: 'isolated' | 'hoisted' | 'pnp'
|
|
hoistedModulesDir: string
|
|
}
|
|
): string[] {
|
|
if (extendNodePath && nodeLinker === 'isolated' && hoistPattern?.length) {
|
|
return [hoistedModulesDir]
|
|
}
|
|
return []
|
|
}
|
|
|
|
export function arrayOfWorkspacePackagesToMap (
|
|
pkgs: Array<Pick<ProjectOptions, 'manifest' | 'rootDir'>>
|
|
): WorkspacePackages {
|
|
const workspacePkgs: WorkspacePackages = new Map()
|
|
for (const { manifest, rootDir } of pkgs) {
|
|
if (!manifest.name) continue
|
|
let workspacePkgsByVersion = workspacePkgs.get(manifest.name)
|
|
if (!workspacePkgsByVersion) {
|
|
workspacePkgsByVersion = new Map()
|
|
workspacePkgs.set(manifest.name, workspacePkgsByVersion)
|
|
}
|
|
workspacePkgsByVersion.set(manifest.version ?? '0.0.0', {
|
|
manifest: manifest as DependencyManifest,
|
|
rootDir,
|
|
})
|
|
}
|
|
return workspacePkgs
|
|
}
|