fix: only validate modules directory if required (e.g. for install) (#8657)

This commit is contained in:
Jordan
2024-10-25 02:45:49 +11:00
committed by GitHub
parent 477e0c1f74
commit 9ea8fa457b
21 changed files with 303 additions and 286 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/get-context": major
---
Don't validate (and possibly purge) modules directory as a side effect of `getContext` and `getContextForSingleImporter` [#8657](https://github.com/pnpm/pnpm/pull/8657).

View File

@@ -0,0 +1,5 @@
---
"pnpm": patch
---
Don't validate (and possibly purge) `node_modules` in commands which should not modify it (e.g. `pnpm install --lockfile-only`) [#8657](https://github.com/pnpm/pnpm/pull/8657).

View File

@@ -0,0 +1,5 @@
---
"@pnpm/get-context": major
---
`PnpmContext.hoistPattern` and `PnpmContext.publicHoistPattern` are no longer affected by modules directory state [#8657](https://github.com/pnpm/pnpm/pull/8657). Prior behavior can be recreated with the new properties `PnpmContext.currentHoistPattern` (`_.currentHoistPattern ?? _.hoistPattern`) and `PnpmContext.currentPublicHoistPattern` (`_.currentPublicHoistPattern ?? _.publicHoistPattern`).

View File

@@ -0,0 +1,5 @@
---
"@pnpm/get-context": major
---
`PnpmSingleContext.hoistPattern` and `PnpmSingleContext.publicHoistPattern` are no longer affected by modules directory state [#8657](https://github.com/pnpm/pnpm/pull/8657).

View File

@@ -0,0 +1,5 @@
---
"@pnpm/core": patch
---
Don't validate (and possibly purge) modules directory in operations that do not mutate the structure (e.g. `mutateModules({ ... }, { ..., lockfileOnly: true })`) [#8657](https://github.com/pnpm/pnpm/pull/8657).

View File

@@ -0,0 +1,5 @@
---
"@pnpm/get-context": major
---
`UnexpectedStoreError` and `UnexpectedVirtualStoreDirError` are no longer exported [#8657](https://github.com/pnpm/pnpm/pull/8657). They can be imported from `@pnpm/core` instead.

View File

@@ -0,0 +1,5 @@
---
"@pnpm/get-context": major
---
Argument `alreadyPurged` removed from `getContextForSingleImporter` [#8657](https://github.com/pnpm/pnpm/pull/8657).

View File

@@ -64,6 +64,8 @@
"@pnpm/types": "workspace:*",
"@pnpm/which-version-is-pinned": "workspace:*",
"@zkochan/rimraf": "catalog:",
"ci-info": "catalog:",
"enquirer": "catalog:",
"is-inner-link": "catalog:",
"is-subdir": "catalog:",
"load-json-file": "catalog:",

View File

@@ -9,7 +9,9 @@ export type {
export type { HoistingLimits } from '@pnpm/headless'
export * from './api'
export { type ProjectOptions, UnexpectedStoreError, UnexpectedVirtualStoreDirError } from '@pnpm/get-context'
export { type ProjectOptions } from '@pnpm/get-context'
export { UnexpectedStoreError } from './install/checkCompatibility/UnexpectedStoreError'
export { UnexpectedVirtualStoreDirError } from './install/checkCompatibility/UnexpectedVirtualStoreDirError'
export type { InstallOptions } from './install/extendInstallOptions'
export type { WorkspacePackages } from '@pnpm/resolver-base'

View File

@@ -84,6 +84,8 @@ import {
} from './extendInstallOptions'
import { linkPackages } from './link'
import { reportPeerDependencyIssues } from './reportPeerDependencyIssues'
import { validateModules } from './validateModules'
import { isCI } from 'ci-info'
class LockfileConfigMismatchError extends PnpmError {
constructor (outdatedLockfileSettingName: string) {
@@ -234,14 +236,38 @@ export async function mutateModules (
const installsOnly = allMutationsAreInstalls(projects)
if (!installsOnly) opts.strictPeerDependencies = false
// @ts-expect-error
opts['forceNewModules'] = installsOnly
const rootProjectManifest = opts.allProjects.find(({ rootDir }) => rootDir === opts.lockfileDir)?.manifest ??
// When running install/update on a subset of projects, the root project might not be included,
// so reading its manifest explicitly here.
await safeReadProjectManifestOnly(opts.lockfileDir)
const ctx = await getContext(opts)
let ctx = await getContext(opts)
if (!opts.lockfileOnly && ctx.modulesFile != null) {
const { purged } = await validateModules(ctx.modulesFile, Object.values(ctx.projects), {
forceNewModules: installsOnly,
include: opts.include,
lockfileDir: opts.lockfileDir,
modulesDir: opts.modulesDir ?? 'node_modules',
registries: opts.registries,
storeDir: opts.storeDir,
virtualStoreDir: ctx.virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
confirmModulesPurge: opts.confirmModulesPurge && !isCI,
forceHoistPattern: opts.forceHoistPattern,
hoistPattern: opts.hoistPattern,
currentHoistPattern: ctx.currentHoistPattern,
forcePublicHoistPattern: opts.forcePublicHoistPattern,
publicHoistPattern: opts.publicHoistPattern,
currentPublicHoistPattern: ctx.currentPublicHoistPattern,
global: opts.global,
})
if (purged) {
ctx = await getContext(opts)
}
}
if (opts.hooks.preResolution) {
await opts.hooks.preResolution({

View File

@@ -0,0 +1,202 @@
import { promises as fs } from 'fs'
import path from 'path'
import { PnpmError } from '@pnpm/error'
import { logger } from '@pnpm/logger'
import {
type IncludedDependencies,
type Modules,
} from '@pnpm/modules-yaml'
import {
DEPENDENCIES_FIELDS,
type Registries,
type ProjectRootDir,
} from '@pnpm/types'
import rimraf from '@zkochan/rimraf'
import enquirer from 'enquirer'
import equals from 'ramda/src/equals'
import { checkCompatibility } from './checkCompatibility'
interface ImporterToPurge {
modulesDir: string
rootDir: ProjectRootDir
}
export async function validateModules (
modules: Modules,
projects: Array<{
modulesDir: string
id: string
rootDir: ProjectRootDir
}>,
opts: {
currentHoistPattern?: string[]
currentPublicHoistPattern?: string[]
forceNewModules: boolean
include?: IncludedDependencies
lockfileDir: string
modulesDir: string
registries: Registries
storeDir: string
virtualStoreDir: string
virtualStoreDirMaxLength: number
confirmModulesPurge?: boolean
hoistPattern?: string[] | undefined
forceHoistPattern?: boolean
publicHoistPattern?: string[] | undefined
forcePublicHoistPattern?: boolean
global?: boolean
}
): Promise<{ purged: boolean }> {
const rootProject = projects.find(({ id }) => id === '.')
if (opts.virtualStoreDirMaxLength !== modules.virtualStoreDirMaxLength) {
if (opts.forceNewModules && (rootProject != null)) {
await purgeModulesDirsOfImporter(opts, rootProject)
return { purged: true }
}
throw new PnpmError(
'VIRTUAL_STORE_DIR_MAX_LENGTH_DIFF',
'This modules directory was created using a different virtual-store-dir-max-length value.' +
' Run "pnpm install" to recreate the modules directory.'
)
}
if (
opts.forcePublicHoistPattern &&
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
!equals(modules.publicHoistPattern, opts.publicHoistPattern || undefined)
) {
if (opts.forceNewModules && (rootProject != null)) {
await purgeModulesDirsOfImporter(opts, rootProject)
return { purged: true }
}
throw new PnpmError(
'PUBLIC_HOIST_PATTERN_DIFF',
'This modules directory was created using a different public-hoist-pattern value.' +
' Run "pnpm install" to recreate the modules directory.'
)
}
const importersToPurge: ImporterToPurge[] = []
if (opts.forceHoistPattern && (rootProject != null)) {
try {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
if (!equals(opts.currentHoistPattern, opts.hoistPattern || undefined)) {
throw new PnpmError(
'HOIST_PATTERN_DIFF',
'This modules directory was created using a different hoist-pattern value.' +
' Run "pnpm install" to recreate the modules directory.'
)
}
} catch (err: any) { // eslint-disable-line
if (!opts.forceNewModules) throw err
importersToPurge.push(rootProject)
}
}
for (const project of projects) {
try {
checkCompatibility(modules, {
modulesDir: project.modulesDir,
storeDir: opts.storeDir,
virtualStoreDir: opts.virtualStoreDir,
})
if (opts.lockfileDir !== project.rootDir && (opts.include != null) && modules.included) {
for (const depsField of DEPENDENCIES_FIELDS) {
if (opts.include[depsField] !== modules.included[depsField]) {
throw new PnpmError('INCLUDED_DEPS_CONFLICT',
`modules directory (at "${opts.lockfileDir}") was installed with ${stringifyIncludedDeps(modules.included)}. ` +
`Current install wants ${stringifyIncludedDeps(opts.include)}.`
)
}
}
}
} catch (err: any) { // eslint-disable-line
if (!opts.forceNewModules) throw err
importersToPurge.push(project)
}
}
if (importersToPurge.length > 0 && (rootProject == null)) {
importersToPurge.push({
modulesDir: path.join(opts.lockfileDir, opts.modulesDir),
rootDir: opts.lockfileDir as ProjectRootDir,
})
}
const purged = importersToPurge.length > 0
if (purged) {
await purgeModulesDirsOfImporters(opts, importersToPurge)
}
return { purged }
}
async function purgeModulesDirsOfImporter (
opts: {
confirmModulesPurge?: boolean
virtualStoreDir: string
},
importer: ImporterToPurge
): Promise<void> {
return purgeModulesDirsOfImporters(opts, [importer])
}
async function purgeModulesDirsOfImporters (
opts: {
confirmModulesPurge?: boolean
virtualStoreDir: string
},
importers: ImporterToPurge[]
): Promise<void> {
if (opts.confirmModulesPurge ?? true) {
const confirmed = await enquirer.prompt<{ question: boolean }>({
type: 'confirm',
name: 'question',
message: importers.length === 1
? `The modules directory at "${importers[0].modulesDir}" will be removed and reinstalled from scratch. Proceed?`
: 'The modules directories will be removed and reinstalled from scratch. Proceed?',
initial: true,
})
if (!confirmed.question) {
throw new PnpmError('ABORTED_REMOVE_MODULES_DIR', 'Aborted removal of modules directory')
}
}
await Promise.all(importers.map(async (importer) => {
logger.info({
message: `Recreating ${importer.modulesDir}`,
prefix: importer.rootDir,
})
try {
// We don't remove the actual modules directory, just the contents of it.
// 1. we will need the directory anyway.
// 2. in some setups, pnpm won't even have permission to remove the modules directory.
await removeContentsOfDir(importer.modulesDir, opts.virtualStoreDir)
} catch (err: any) { // eslint-disable-line
if (err.code !== 'ENOENT') throw err
}
}))
}
async function removeContentsOfDir (dir: string, virtualStoreDir: string): Promise<void> {
const items = await fs.readdir(dir)
await Promise.all(items.map(async (item) => {
// The non-pnpm related hidden files are kept
if (
item.startsWith('.') &&
item !== '.bin' &&
item !== '.modules.yaml' &&
!dirsAreEqual(path.join(dir, item), virtualStoreDir)
) {
return
}
await rimraf(path.join(dir, item))
}))
}
function dirsAreEqual (dir1: string, dir2: string): boolean {
return path.relative(dir1, dir2) === ''
}
function stringifyIncludedDeps (included: IncludedDependencies): string {
return DEPENDENCIES_FIELDS.filter((depsField) => included[depsField]).join(', ')
}

View File

@@ -40,15 +40,12 @@
"dependencies": {
"@pnpm/constants": "workspace:*",
"@pnpm/core-loggers": "workspace:*",
"@pnpm/error": "workspace:*",
"@pnpm/lockfile.fs": "workspace:*",
"@pnpm/modules-yaml": "workspace:*",
"@pnpm/read-projects-context": "workspace:*",
"@pnpm/resolver-base": "workspace:*",
"@pnpm/types": "workspace:*",
"@zkochan/rimraf": "catalog:",
"ci-info": "catalog:",
"enquirer": "catalog:",
"path-absolute": "catalog:",
"ramda": "catalog:"
},

View File

@@ -1,9 +1,7 @@
import { promises as fs } from 'fs'
import path from 'path'
import { contextLogger, packageManifestLogger } from '@pnpm/core-loggers'
import { PnpmError } from '@pnpm/error'
import { type Lockfile } from '@pnpm/lockfile.fs'
import { logger } from '@pnpm/logger'
import {
type IncludedDependencies,
type Modules,
@@ -12,7 +10,6 @@ import { readProjectsContext } from '@pnpm/read-projects-context'
import { type WorkspacePackages } from '@pnpm/resolver-base'
import {
type DepPath,
DEPENDENCIES_FIELDS,
type HoistedDependencies,
type ProjectId,
type ProjectManifest,
@@ -22,19 +19,14 @@ import {
type ProjectRootDir,
type ProjectRootDirRealPath,
} from '@pnpm/types'
import rimraf from '@zkochan/rimraf'
import { isCI } from 'ci-info'
import enquirer from 'enquirer'
import pathAbsolute from 'path-absolute'
import clone from 'ramda/src/clone'
import equals from 'ramda/src/equals'
import { checkCompatibility } from './checkCompatibility'
import { UnexpectedStoreError } from './checkCompatibility/UnexpectedStoreError'
import { UnexpectedVirtualStoreDirError } from './checkCompatibility/UnexpectedVirtualStoreDirError'
import { readLockfiles } from './readLockfiles'
export { UnexpectedStoreError, UnexpectedVirtualStoreDirError }
/**
* 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: Lockfile
currentLockfileIsUpToDate: boolean
@@ -42,9 +34,11 @@ export interface PnpmContext {
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[]
@@ -54,11 +48,17 @@ export interface PnpmContext {
} & 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: Lockfile
@@ -87,7 +87,6 @@ export interface GetContextOptions {
allProjects: Array<ProjectOptions & HookOptions>
confirmModulesPurge?: boolean
force: boolean
forceNewModules?: boolean
frozenLockfile?: boolean
extraBinPaths: string[]
extendNodePath?: boolean
@@ -112,47 +111,14 @@ export interface GetContextOptions {
forcePublicHoistPattern?: boolean
global?: boolean
}
interface ImporterToPurge {
modulesDir: string
rootDir: ProjectRootDir
}
export async function getContext (
opts: GetContextOptions
): Promise<PnpmContext> {
const modulesDir = opts.modulesDir ?? 'node_modules'
let importersContext = await readProjectsContext(opts.allProjects, { lockfileDir: opts.lockfileDir, modulesDir })
const importersContext = await readProjectsContext(opts.allProjects, { lockfileDir: opts.lockfileDir, modulesDir })
const virtualStoreDir = pathAbsolute(opts.virtualStoreDir ?? path.join(modulesDir, '.pnpm'), opts.lockfileDir)
if (importersContext.modules != null) {
const { purged } = await validateModules(importersContext.modules, importersContext.projects, {
currentHoistPattern: importersContext.currentHoistPattern,
currentPublicHoistPattern: importersContext.currentPublicHoistPattern,
forceNewModules: opts.forceNewModules === true,
include: opts.include,
lockfileDir: opts.lockfileDir,
modulesDir,
registries: opts.registries,
storeDir: opts.storeDir,
virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
confirmModulesPurge: opts.confirmModulesPurge && !isCI,
forceHoistPattern: opts.forceHoistPattern,
hoistPattern: opts.hoistPattern,
forcePublicHoistPattern: opts.forcePublicHoistPattern,
publicHoistPattern: opts.publicHoistPattern,
global: opts.global,
})
if (purged) {
importersContext = await readProjectsContext(opts.allProjects, {
lockfileDir: opts.lockfileDir,
modulesDir,
})
}
}
await fs.mkdir(opts.storeDir, { recursive: true })
for (const project of opts.allProjects) {
@@ -175,19 +141,20 @@ export async function getContext (
if (opts.hoistPattern?.length) {
extraBinPaths.unshift(path.join(hoistedModulesDir, '.bin'))
}
const hoistPattern = importersContext.currentHoistPattern ?? opts.hoistPattern
const ctx: PnpmContext = {
extraBinPaths,
extraNodePaths: getExtraNodePaths({ extendNodePath: opts.extendNodePath, nodeLinker: opts.nodeLinker, hoistPattern, virtualStoreDir }),
extraNodePaths: getExtraNodePaths({ extendNodePath: opts.extendNodePath, nodeLinker: opts.nodeLinker, hoistPattern: importersContext.currentHoistPattern ?? opts.hoistPattern, virtualStoreDir }),
hoistedDependencies: importersContext.hoistedDependencies,
hoistedModulesDir,
hoistPattern,
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: importersContext.currentPublicHoistPattern ?? opts.publicHoistPattern,
publicHoistPattern: opts.publicHoistPattern,
currentPublicHoistPattern: importersContext.currentPublicHoistPattern,
registries: opts.registries,
rootModulesDir: importersContext.rootModulesDir,
skipped: importersContext.skipped,
@@ -218,192 +185,13 @@ export async function getContext (
return ctx
}
async function validateModules (
modules: Modules,
projects: Array<{
modulesDir: string
id: string
rootDir: ProjectRootDir
}>,
opts: {
currentHoistPattern?: string[]
currentPublicHoistPattern?: string[]
forceNewModules: boolean
include?: IncludedDependencies
lockfileDir: string
modulesDir: string
registries: Registries
storeDir: string
virtualStoreDir: string
virtualStoreDirMaxLength: number
confirmModulesPurge?: boolean
hoistPattern?: string[] | undefined
forceHoistPattern?: boolean
publicHoistPattern?: string[] | undefined
forcePublicHoistPattern?: boolean
global?: boolean
}
): Promise<{ purged: boolean }> {
const rootProject = projects.find(({ id }) => id === '.')
if (opts.virtualStoreDirMaxLength !== modules.virtualStoreDirMaxLength) {
if (opts.forceNewModules && (rootProject != null)) {
await purgeModulesDirsOfImporter(opts, rootProject)
return { purged: true }
}
throw new PnpmError(
'VIRTUAL_STORE_DIR_MAX_LENGTH_DIFF',
'This modules directory was created using a different virtual-store-dir-max-length value.' +
' Run "pnpm install" to recreate the modules directory.'
)
}
if (
opts.forcePublicHoistPattern &&
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
!equals(modules.publicHoistPattern, opts.publicHoistPattern || undefined)
) {
if (opts.forceNewModules && (rootProject != null)) {
await purgeModulesDirsOfImporter(opts, rootProject)
return { purged: true }
}
throw new PnpmError(
'PUBLIC_HOIST_PATTERN_DIFF',
'This modules directory was created using a different public-hoist-pattern value.' +
' Run "pnpm install" to recreate the modules directory.'
)
}
const importersToPurge: ImporterToPurge[] = []
if (opts.forceHoistPattern && (rootProject != null)) {
try {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
if (!equals(opts.currentHoistPattern, opts.hoistPattern || undefined)) {
throw new PnpmError(
'HOIST_PATTERN_DIFF',
'This modules directory was created using a different hoist-pattern value.' +
' Run "pnpm install" to recreate the modules directory.'
)
}
} catch (err: any) { // eslint-disable-line
if (!opts.forceNewModules) throw err
importersToPurge.push(rootProject)
}
}
for (const project of projects) {
try {
checkCompatibility(modules, {
modulesDir: project.modulesDir,
storeDir: opts.storeDir,
virtualStoreDir: opts.virtualStoreDir,
})
if (opts.lockfileDir !== project.rootDir && (opts.include != null) && modules.included) {
for (const depsField of DEPENDENCIES_FIELDS) {
if (opts.include[depsField] !== modules.included[depsField]) {
throw new PnpmError('INCLUDED_DEPS_CONFLICT',
`modules directory (at "${opts.lockfileDir}") was installed with ${stringifyIncludedDeps(modules.included)}. ` +
`Current install wants ${stringifyIncludedDeps(opts.include)}.`
)
}
}
}
} catch (err: any) { // eslint-disable-line
if (!opts.forceNewModules) throw err
importersToPurge.push(project)
}
}
if (importersToPurge.length > 0 && (rootProject == null)) {
importersToPurge.push({
modulesDir: path.join(opts.lockfileDir, opts.modulesDir),
rootDir: opts.lockfileDir as ProjectRootDir,
})
}
const purged = importersToPurge.length > 0
if (purged) {
await purgeModulesDirsOfImporters(opts, importersToPurge)
}
return { purged }
}
async function purgeModulesDirsOfImporter (
opts: {
confirmModulesPurge?: boolean
virtualStoreDir: string
},
importer: ImporterToPurge
): Promise<void> {
return purgeModulesDirsOfImporters(opts, [importer])
}
async function purgeModulesDirsOfImporters (
opts: {
confirmModulesPurge?: boolean
virtualStoreDir: string
},
importers: ImporterToPurge[]
): Promise<void> {
if (opts.confirmModulesPurge ?? true) {
const confirmed = await enquirer.prompt<{ question: boolean }>({
type: 'confirm',
name: 'question',
message: importers.length === 1
? `The modules directory at "${importers[0].modulesDir}" will be removed and reinstalled from scratch. Proceed?`
: 'The modules directories will be removed and reinstalled from scratch. Proceed?',
initial: true,
})
if (!confirmed.question) {
throw new PnpmError('ABORTED_REMOVE_MODULES_DIR', 'Aborted removal of modules directory')
}
}
await Promise.all(importers.map(async (importer) => {
logger.info({
message: `Recreating ${importer.modulesDir}`,
prefix: importer.rootDir,
})
try {
// We don't remove the actual modules directory, just the contents of it.
// 1. we will need the directory anyway.
// 2. in some setups, pnpm won't even have permission to remove the modules directory.
await removeContentsOfDir(importer.modulesDir, opts.virtualStoreDir)
} catch (err: any) { // eslint-disable-line
if (err.code !== 'ENOENT') throw err
}
}))
}
async function removeContentsOfDir (dir: string, virtualStoreDir: string): Promise<void> {
const items = await fs.readdir(dir)
await Promise.all(items.map(async (item) => {
// The non-pnpm related hidden files are kept
if (
item.startsWith('.') &&
item !== '.bin' &&
item !== '.modules.yaml' &&
!dirsAreEqual(path.join(dir, item), virtualStoreDir)
) {
return
}
await rimraf(path.join(dir, item))
}))
}
function dirsAreEqual (dir1: string, dir2: string): boolean {
return path.relative(dir1, dir2) === ''
}
function stringifyIncludedDeps (included: IncludedDependencies): string {
return DEPENDENCIES_FIELDS.filter((depsField) => included[depsField]).join(', ')
}
export interface PnpmSingleContext {
currentLockfile: Lockfile
currentLockfileIsUpToDate: boolean
existsCurrentLockfile: boolean
existsWantedLockfile: boolean
existsNonEmptyWantedLockfile: boolean
/** Affected by existing modules directory, if it exists. */
extraBinPaths: string[]
extraNodePaths: string[]
lockfileHadConflicts: boolean
@@ -414,6 +202,7 @@ export interface PnpmSingleContext {
modulesDir: string
importerId: string
prefix: string
/** Required included dependencies or dependencies currently included by the modules directory. */
include: IncludedDependencies
modulesFile: Modules | null
pendingBuilds: string[]
@@ -422,6 +211,7 @@ export interface PnpmSingleContext {
rootModulesDir: string
lockfileDir: string
virtualStoreDir: string
/** As applied to existing modules directory, if it exists. */
skipped: Set<string>
storeDir: string
wantedLockfile: Lockfile
@@ -435,7 +225,6 @@ export async function getContextForSingleImporter (
excludeLinksFromLockfile: boolean
peersSuffixMaxLength: number
force: boolean
forceNewModules?: boolean
confirmModulesPurge?: boolean
extraBinPaths: string[]
extendNodePath?: boolean
@@ -458,12 +247,10 @@ export async function getContextForSingleImporter (
publicHoistPattern?: string[] | undefined
forcePublicHoistPattern?: boolean
},
alreadyPurged: boolean = false
}
): Promise<PnpmSingleContext> {
const {
currentHoistPattern,
currentPublicHoistPattern,
hoistedDependencies,
projects,
include,
@@ -491,31 +278,6 @@ export async function getContextForSingleImporter (
const importerId = importer.id
const virtualStoreDir = pathAbsolute(opts.virtualStoreDir ?? 'node_modules/.pnpm', opts.lockfileDir)
if ((modules != null) && !alreadyPurged) {
const { purged } = await validateModules(modules, projects, {
currentHoistPattern,
currentPublicHoistPattern,
forceNewModules: opts.forceNewModules === true,
include: opts.include,
lockfileDir: opts.lockfileDir,
modulesDir: opts.modulesDir ?? 'node_modules',
registries: opts.registries,
storeDir: opts.storeDir,
virtualStoreDir,
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
confirmModulesPurge: opts.confirmModulesPurge && !isCI,
forceHoistPattern: opts.forceHoistPattern,
hoistPattern: opts.hoistPattern,
forcePublicHoistPattern: opts.forcePublicHoistPattern,
publicHoistPattern: opts.publicHoistPattern,
})
if (purged) {
return getContextForSingleImporter(manifest, opts, true)
}
}
await fs.mkdir(storeDir, { recursive: true })
const extraBinPaths = [
...opts.extraBinPaths || [],
@@ -524,13 +286,12 @@ export async function getContextForSingleImporter (
if (opts.hoistPattern?.length) {
extraBinPaths.unshift(path.join(hoistedModulesDir, '.bin'))
}
const hoistPattern = currentHoistPattern ?? opts.hoistPattern
const ctx: PnpmSingleContext = {
extraBinPaths,
extraNodePaths: getExtraNodePaths({ extendNodePath: opts.extendNodePath, nodeLinker: opts.nodeLinker, hoistPattern, virtualStoreDir }),
extraNodePaths: getExtraNodePaths({ extendNodePath: opts.extendNodePath, nodeLinker: opts.nodeLinker, hoistPattern: currentHoistPattern ?? opts.hoistPattern, virtualStoreDir }),
hoistedDependencies,
hoistedModulesDir,
hoistPattern,
hoistPattern: opts.hoistPattern,
importerId,
include: opts.include ?? include,
lockfileDir: opts.lockfileDir,
@@ -539,7 +300,7 @@ export async function getContextForSingleImporter (
modulesFile: modules,
pendingBuilds,
prefix: opts.dir,
publicHoistPattern: currentPublicHoistPattern ?? opts.publicHoistPattern,
publicHoistPattern: opts.publicHoistPattern,
registries: {
...opts.registries,
...registries,

View File

@@ -18,9 +18,6 @@
{
"path": "../../packages/core-loggers"
},
{
"path": "../../packages/error"
},
{
"path": "../../packages/logger"
},

19
pnpm-lock.yaml generated
View File

@@ -4219,6 +4219,12 @@ importers:
'@zkochan/rimraf':
specifier: 'catalog:'
version: 3.0.2
ci-info:
specifier: 'catalog:'
version: 3.9.0
enquirer:
specifier: 'catalog:'
version: 2.4.1
is-inner-link:
specifier: 'catalog:'
version: 4.0.0
@@ -4316,9 +4322,6 @@ importers:
'@yarnpkg/core':
specifier: 'catalog:'
version: 4.0.5(typanion@3.14.0)
ci-info:
specifier: 'catalog:'
version: 3.9.0
deep-require-cwd:
specifier: 'catalog:'
version: 1.0.0
@@ -4395,9 +4398,6 @@ importers:
'@pnpm/core-loggers':
specifier: workspace:*
version: link:../../packages/core-loggers
'@pnpm/error':
specifier: workspace:*
version: link:../../packages/error
'@pnpm/lockfile.fs':
specifier: workspace:*
version: link:../../lockfile/fs
@@ -4413,15 +4413,9 @@ importers:
'@pnpm/types':
specifier: workspace:*
version: link:../../packages/types
'@zkochan/rimraf':
specifier: 'catalog:'
version: 3.0.2
ci-info:
specifier: 'catalog:'
version: 3.9.0
enquirer:
specifier: 'catalog:'
version: 2.4.1
path-absolute:
specifier: 'catalog:'
version: 1.0.1
@@ -13879,6 +13873,7 @@ packages:
uid-number@0.0.6:
resolution: {integrity: sha512-c461FXIljswCuscZn67xq9PpszkPT6RjheWFQTgCyabJrTUozElanb0YEqv2UGgk247YpcJkFBuSGNvBlpXM9w==}
deprecated: This package is no longer supported.
umask@1.1.0:
resolution: {integrity: sha512-lE/rxOhmiScJu9L6RTNVgB/zZbF+vGC0/p6D3xnkAePI2o0sMyFG966iR5Ki50OI/0mNi2yaRnxfLsPmEZF/JA==}