mirror of
https://github.com/pnpm/pnpm.git
synced 2025-12-25 08:08:14 -05:00
perf: faster repeat install (#8838)
This commit is contained in:
9
.changeset/plenty-mayflies-float.md
Normal file
9
.changeset/plenty-mayflies-float.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-installation": minor
|
||||
"pnpm": minor
|
||||
"@pnpm/pnpmfile": minor
|
||||
"@pnpm/workspace.state": major
|
||||
"@pnpm/deps.status": major
|
||||
---
|
||||
|
||||
On repeat install perform a fast check if `node_modules` is up to date [#8838](https://github.com/pnpm/pnpm/pull/8838).
|
||||
1
deps/status/package.json
vendored
1
deps/status/package.json
vendored
@@ -40,6 +40,7 @@
|
||||
"@pnpm/lockfile.settings-checker": "workspace:*",
|
||||
"@pnpm/lockfile.verification": "workspace:*",
|
||||
"@pnpm/parse-overrides": "workspace:*",
|
||||
"@pnpm/pnpmfile": "workspace:*",
|
||||
"@pnpm/resolver-base": "workspace:*",
|
||||
"@pnpm/types": "workspace:*",
|
||||
"@pnpm/workspace.find-packages": "workspace:*",
|
||||
|
||||
170
deps/status/src/checkDepsStatus.ts
vendored
170
deps/status/src/checkDepsStatus.ts
vendored
@@ -28,6 +28,7 @@ import {
|
||||
} from '@pnpm/lockfile.verification'
|
||||
import { globalWarn, logger } from '@pnpm/logger'
|
||||
import { parseOverrides } from '@pnpm/parse-overrides'
|
||||
import { getPnpmfilePath } from '@pnpm/pnpmfile'
|
||||
import { type WorkspacePackages } from '@pnpm/resolver-base'
|
||||
import {
|
||||
type DependencyManifest,
|
||||
@@ -39,7 +40,8 @@ import { findWorkspacePackages } from '@pnpm/workspace.find-packages'
|
||||
import { readWorkspaceManifest } from '@pnpm/workspace.read-manifest'
|
||||
import { loadWorkspaceState, updateWorkspaceState } from '@pnpm/workspace.state'
|
||||
import { assertLockfilesEqual } from './assertLockfilesEqual'
|
||||
import { statManifestFile } from './statManifestFile'
|
||||
import { safeStat, safeStatSync, statManifestFile } from './statManifestFile'
|
||||
import { type WorkspaceStateSettings } from '@pnpm/workspace.state/src/types'
|
||||
|
||||
export type CheckDepsStatusOptions = Pick<Config,
|
||||
| 'allProjects'
|
||||
@@ -55,9 +57,13 @@ export type CheckDepsStatusOptions = Pick<Config,
|
||||
| 'sharedWorkspaceLockfile'
|
||||
| 'virtualStoreDir'
|
||||
| 'workspaceDir'
|
||||
>
|
||||
| 'patchesDir'
|
||||
| 'pnpmfile'
|
||||
> & {
|
||||
ignoreFilteredInstallCache?: boolean
|
||||
} & WorkspaceStateSettings
|
||||
|
||||
export async function checkDepsStatus (opts: CheckDepsStatusOptions): Promise<{ upToDate: boolean, issue?: string }> {
|
||||
export async function checkDepsStatus (opts: CheckDepsStatusOptions): Promise<{ upToDate: boolean | undefined, issue?: string }> {
|
||||
try {
|
||||
return await _checkDepsStatus(opts)
|
||||
} catch (error) {
|
||||
@@ -71,7 +77,7 @@ export async function checkDepsStatus (opts: CheckDepsStatusOptions): Promise<{
|
||||
}
|
||||
}
|
||||
|
||||
async function _checkDepsStatus (opts: CheckDepsStatusOptions): Promise<{ upToDate: boolean, issue?: string }> {
|
||||
async function _checkDepsStatus (opts: CheckDepsStatusOptions): Promise<{ upToDate: boolean | undefined, issue?: string }> {
|
||||
const {
|
||||
allProjects,
|
||||
autoInstallPeers,
|
||||
@@ -89,17 +95,31 @@ async function _checkDepsStatus (opts: CheckDepsStatusOptions): Promise<{ upToDa
|
||||
? getOptionsFromRootManifest(rootProjectManifestDir, rootProjectManifest)
|
||||
: undefined
|
||||
|
||||
if (allProjects && workspaceDir) {
|
||||
const workspaceState = loadWorkspaceState(workspaceDir)
|
||||
if (!workspaceState) {
|
||||
const workspaceState = loadWorkspaceState(workspaceDir ?? rootProjectManifestDir)
|
||||
if (!workspaceState) {
|
||||
return {
|
||||
upToDate: false,
|
||||
issue: 'Cannot check whether dependencies are outdated',
|
||||
}
|
||||
}
|
||||
if (opts.ignoreFilteredInstallCache && workspaceState.filteredInstall) {
|
||||
return { upToDate: undefined }
|
||||
}
|
||||
|
||||
for (const [settingName, settingValue] of Object.entries(workspaceState.settings)) {
|
||||
if (settingName === 'catalogs') continue
|
||||
// @ts-expect-error
|
||||
if (!equals(settingValue, opts[settingName])) {
|
||||
return {
|
||||
upToDate: false,
|
||||
issue: 'Cannot check whether dependencies are outdated',
|
||||
issue: `The value of the ${settingName} setting has changed`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allProjects && workspaceDir) {
|
||||
if (!equals(
|
||||
filter(value => value != null, workspaceState.catalogs ?? {}),
|
||||
filter(value => value != null, workspaceState.settings.catalogs ?? {}),
|
||||
filter(value => value != null, catalogs ?? {})
|
||||
)) {
|
||||
return {
|
||||
@@ -108,8 +128,13 @@ async function _checkDepsStatus (opts: CheckDepsStatusOptions): Promise<{ upToDa
|
||||
}
|
||||
}
|
||||
|
||||
const currentProjectRootDirs = allProjects.map(project => project.rootDir).sort()
|
||||
if (!equals(workspaceState.projectRootDirs, currentProjectRootDirs)) {
|
||||
if (allProjects.length !== Object.keys(workspaceState.projects).length ||
|
||||
!allProjects.every((currentProject) => {
|
||||
const prevProject = workspaceState.projects[currentProject.rootDir]
|
||||
if (!prevProject) return false
|
||||
return prevProject.name === currentProject.manifest.name && (prevProject.version ?? '0.0.0') === (currentProject.manifest.version ?? '0.0.0')
|
||||
})
|
||||
) {
|
||||
return {
|
||||
upToDate: false,
|
||||
issue: 'The workspace structure has changed since last install',
|
||||
@@ -135,6 +160,17 @@ async function _checkDepsStatus (opts: CheckDepsStatusOptions): Promise<{ upToDa
|
||||
return { upToDate: true }
|
||||
}
|
||||
|
||||
const issue = await patchesAreModified({
|
||||
rootManifestOptions,
|
||||
rootDir: rootProjectManifestDir,
|
||||
lastValidatedTimestamp: workspaceState.lastValidatedTimestamp,
|
||||
pnpmfile: opts.pnpmfile,
|
||||
hadPnpmfile: workspaceState.pnpmfileExists,
|
||||
})
|
||||
if (issue) {
|
||||
return { upToDate: false, issue }
|
||||
}
|
||||
|
||||
logger.debug({ msg: 'Some manifest files were modified since the last validation. Continuing check.' })
|
||||
|
||||
let readWantedLockfileAndDir: (projectDir: string) => Promise<{
|
||||
@@ -204,23 +240,28 @@ async function _checkDepsStatus (opts: CheckDepsStatusOptions): Promise<{ upToDa
|
||||
rootManifestOptions,
|
||||
}
|
||||
|
||||
await Promise.all(modifiedProjects.map(async ({ project }) => {
|
||||
const { wantedLockfile, wantedLockfileDir } = await readWantedLockfileAndDir(project.rootDir)
|
||||
await assertWantedLockfileUpToDate(assertCtx, {
|
||||
projectDir: project.rootDir,
|
||||
projectId: getProjectId(project),
|
||||
projectManifest: project.manifest,
|
||||
wantedLockfile,
|
||||
wantedLockfileDir,
|
||||
})
|
||||
}))
|
||||
try {
|
||||
await Promise.all(modifiedProjects.map(async ({ project }) => {
|
||||
const { wantedLockfile, wantedLockfileDir } = await readWantedLockfileAndDir(project.rootDir)
|
||||
await assertWantedLockfileUpToDate(assertCtx, {
|
||||
projectDir: project.rootDir,
|
||||
projectId: getProjectId(project),
|
||||
projectManifest: project.manifest,
|
||||
wantedLockfile,
|
||||
wantedLockfileDir,
|
||||
})
|
||||
}))
|
||||
} catch (err) {
|
||||
return { upToDate: false, issue: (util.types.isNativeError(err) && 'message' in err) ? err.message : undefined }
|
||||
}
|
||||
|
||||
// update lastValidatedTimestamp to prevent pointless repeat
|
||||
await updateWorkspaceState({
|
||||
allProjects,
|
||||
catalogs,
|
||||
lastValidatedTimestamp: Date.now(),
|
||||
workspaceDir,
|
||||
pnpmfileExists: workspaceState.pnpmfileExists,
|
||||
settings: opts,
|
||||
filteredInstall: workspaceState.filteredInstall,
|
||||
})
|
||||
|
||||
return { upToDate: true }
|
||||
@@ -260,6 +301,17 @@ async function _checkDepsStatus (opts: CheckDepsStatusOptions): Promise<{ upToDa
|
||||
|
||||
if (!wantedLockfileStats) return throwLockfileNotFound(rootProjectManifestDir)
|
||||
|
||||
const issue = await patchesAreModified({
|
||||
rootManifestOptions,
|
||||
rootDir: rootProjectManifestDir,
|
||||
lastValidatedTimestamp: wantedLockfileStats.mtime.valueOf(),
|
||||
pnpmfile: opts.pnpmfile,
|
||||
hadPnpmfile: workspaceState.pnpmfileExists,
|
||||
})
|
||||
if (issue) {
|
||||
return { upToDate: false, issue }
|
||||
}
|
||||
|
||||
if (currentLockfileStats && wantedLockfileStats.mtime.valueOf() > currentLockfileStats.mtime.valueOf()) {
|
||||
const currentLockfile = await currentLockfilePromise
|
||||
const wantedLockfile = (await wantedLockfilePromise) ?? throwLockfileNotFound(rootProjectManifestDir)
|
||||
@@ -273,23 +325,27 @@ async function _checkDepsStatus (opts: CheckDepsStatusOptions): Promise<{ upToDa
|
||||
|
||||
if (manifestStats.mtime.valueOf() > wantedLockfileStats.mtime.valueOf()) {
|
||||
logger.debug({ msg: 'The manifest is newer than the lockfile. Continuing check.' })
|
||||
await assertWantedLockfileUpToDate({
|
||||
autoInstallPeers,
|
||||
injectWorkspacePackages,
|
||||
config: opts,
|
||||
excludeLinksFromLockfile,
|
||||
linkWorkspacePackages,
|
||||
getManifestsByDir: () => ({}),
|
||||
getWorkspacePackages: () => undefined,
|
||||
rootDir: rootProjectManifestDir,
|
||||
rootManifestOptions,
|
||||
}, {
|
||||
projectDir: rootProjectManifestDir,
|
||||
projectId: '.' as ProjectId,
|
||||
projectManifest: rootProjectManifest,
|
||||
wantedLockfile: (await wantedLockfilePromise) ?? throwLockfileNotFound(rootProjectManifestDir),
|
||||
wantedLockfileDir: rootProjectManifestDir,
|
||||
})
|
||||
try {
|
||||
await assertWantedLockfileUpToDate({
|
||||
autoInstallPeers,
|
||||
injectWorkspacePackages,
|
||||
config: opts,
|
||||
excludeLinksFromLockfile,
|
||||
linkWorkspacePackages,
|
||||
getManifestsByDir: () => ({}),
|
||||
getWorkspacePackages: () => undefined,
|
||||
rootDir: rootProjectManifestDir,
|
||||
rootManifestOptions,
|
||||
}, {
|
||||
projectDir: rootProjectManifestDir,
|
||||
projectId: '.' as ProjectId,
|
||||
projectManifest: rootProjectManifest,
|
||||
wantedLockfile: (await wantedLockfilePromise) ?? throwLockfileNotFound(rootProjectManifestDir),
|
||||
wantedLockfileDir: rootProjectManifestDir,
|
||||
})
|
||||
} catch (err) {
|
||||
return { upToDate: false, issue: (util.types.isNativeError(err) && 'message' in err) ? err.message : undefined }
|
||||
}
|
||||
} else if (currentLockfileStats) {
|
||||
logger.debug({ msg: 'The manifest file is not newer than the lockfile. Exiting check.' })
|
||||
} else {
|
||||
@@ -308,7 +364,7 @@ async function _checkDepsStatus (opts: CheckDepsStatusOptions): Promise<{ upToDa
|
||||
// `rootProjectManifest` being `undefined` means that there's no root manifest.
|
||||
// Both means that `pnpm run` would fail, so checking lockfiles here is pointless.
|
||||
globalWarn('Skipping check.')
|
||||
return { upToDate: true }
|
||||
return { upToDate: undefined }
|
||||
}
|
||||
|
||||
interface AssertWantedLockfileUpToDateContext {
|
||||
@@ -428,3 +484,35 @@ function throwLockfileNotFound (wantedLockfileDir: string): never {
|
||||
hint: 'Run `pnpm install` to create the lockfile',
|
||||
})
|
||||
}
|
||||
|
||||
async function patchesAreModified (opts: {
|
||||
rootManifestOptions: OptionsFromRootManifest | undefined
|
||||
rootDir: string
|
||||
lastValidatedTimestamp: number
|
||||
pnpmfile: string
|
||||
hadPnpmfile: boolean
|
||||
}): Promise<string | undefined> {
|
||||
if (opts.rootManifestOptions?.patchedDependencies) {
|
||||
const allPatchStats = await Promise.all(Object.values(opts.rootManifestOptions.patchedDependencies).map((patchFile) => {
|
||||
return safeStat(path.relative(opts.rootDir, patchFile))
|
||||
}))
|
||||
if (allPatchStats.some(
|
||||
(patch) =>
|
||||
patch && patch.mtime.valueOf() > opts.lastValidatedTimestamp
|
||||
)) {
|
||||
return 'Patches were modified'
|
||||
}
|
||||
}
|
||||
const pnpmfilePath = getPnpmfilePath(opts.rootDir, opts.pnpmfile)
|
||||
const pnpmfileStats = safeStatSync(pnpmfilePath)
|
||||
if (pnpmfileStats != null && pnpmfileStats.mtime.valueOf() > opts.lastValidatedTimestamp) {
|
||||
return `pnpmfile at "${pnpmfilePath}" was modified`
|
||||
}
|
||||
if (opts.hadPnpmfile && pnpmfileStats == null) {
|
||||
return `pnpmfile at "${pnpmfilePath}" was removed`
|
||||
}
|
||||
if (!opts.hadPnpmfile && pnpmfileStats != null) {
|
||||
return `pnpmfile at "${pnpmfilePath}" was added`
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
35
deps/status/src/statManifestFile.ts
vendored
35
deps/status/src/statManifestFile.ts
vendored
@@ -4,18 +4,31 @@ import util from 'util'
|
||||
import { MANIFEST_BASE_NAMES } from '@pnpm/constants'
|
||||
|
||||
export async function statManifestFile (projectRootDir: string): Promise<fs.Stats | undefined> {
|
||||
const attempts = await Promise.all(MANIFEST_BASE_NAMES.map(async baseName => {
|
||||
const attempts = await Promise.all(MANIFEST_BASE_NAMES.map((baseName) => {
|
||||
const manifestPath = path.join(projectRootDir, baseName)
|
||||
let stats: fs.Stats
|
||||
try {
|
||||
stats = await fs.promises.stat(manifestPath)
|
||||
} catch (error) {
|
||||
if (util.types.isNativeError(error) && 'code' in error && error.code === 'ENOENT') {
|
||||
return undefined
|
||||
}
|
||||
throw error
|
||||
}
|
||||
return stats
|
||||
return safeStat(manifestPath)
|
||||
}))
|
||||
return attempts.find(stats => stats != null)
|
||||
}
|
||||
|
||||
export async function safeStat (filePath: string): Promise<fs.Stats | undefined> {
|
||||
try {
|
||||
return await fs.promises.stat(filePath)
|
||||
} catch (error) {
|
||||
if (util.types.isNativeError(error) && 'code' in error && error.code === 'ENOENT') {
|
||||
return undefined
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export function safeStatSync (filePath: string): fs.Stats | undefined {
|
||||
try {
|
||||
return fs.statSync(filePath)
|
||||
} catch (error) {
|
||||
if (util.types.isNativeError(error) && 'code' in error && error.code === 'ENOENT') {
|
||||
return undefined
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
3
deps/status/tsconfig.json
vendored
3
deps/status/tsconfig.json
vendored
@@ -21,6 +21,9 @@
|
||||
{
|
||||
"path": "../../crypto/object-hasher"
|
||||
},
|
||||
{
|
||||
"path": "../../hooks/pnpmfile"
|
||||
},
|
||||
{
|
||||
"path": "../../lockfile/fs"
|
||||
},
|
||||
|
||||
@@ -39,6 +39,7 @@ export const DEFAULT_OPTS = {
|
||||
pending: false,
|
||||
pnpmfile: './.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
rawLocalConfig: {},
|
||||
@@ -67,6 +68,7 @@ export const DLX_DEFAULT_OPTS = {
|
||||
bail: false,
|
||||
bin: 'node_modules/.bin',
|
||||
cacheDir: path.join(tmp, 'cache'),
|
||||
excludeLinksFromLockfile: false,
|
||||
extraEnv: {},
|
||||
extraBinPaths: [],
|
||||
cliOptions: {},
|
||||
@@ -80,6 +82,7 @@ export const DLX_DEFAULT_OPTS = {
|
||||
lock: true,
|
||||
pnpmfile: '.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
rawLocalConfig: { registry: REGISTRY_URL },
|
||||
registries: {
|
||||
|
||||
@@ -56,7 +56,7 @@ test('throw an error if verifyDepsBeforeRun is set to error', async () => {
|
||||
} catch (_err) {
|
||||
err = _err as Error
|
||||
}
|
||||
expect(err.message).toContain('Cannot find a lockfile in')
|
||||
expect(err.message).toContain('Cannot check whether dependencies are outdated')
|
||||
})
|
||||
|
||||
test('install the dependencies if verifyDepsBeforeRun is set to install', async () => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { CookedHooks } from './requireHooks'
|
||||
|
||||
export { getPnpmfilePath } from './getPnpmfilePath'
|
||||
export { requireHooks } from './requireHooks'
|
||||
export { requirePnpmfile, BadReadPackageHookError } from './requirePnpmfile'
|
||||
export type { HookContext } from './Hooks'
|
||||
|
||||
@@ -23,6 +23,7 @@ export const DEFAULT_OPTS = {
|
||||
ca: undefined,
|
||||
cacheDir: '../cache',
|
||||
cert: undefined,
|
||||
excludeLinksFromLockfile: false,
|
||||
extraEnv: {},
|
||||
cliOptions: {},
|
||||
fetchRetries: 2,
|
||||
@@ -46,6 +47,7 @@ export const DEFAULT_OPTS = {
|
||||
pending: false,
|
||||
pnpmfile: './.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
rawConfig,
|
||||
rawLocalConfig: {},
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"meta-updater": "pnpm --filter=@pnpm-private/updater compile && pnpm exec meta-updater",
|
||||
"lint:meta": "pnpm run meta-updater --test",
|
||||
"copy-artifacts": "ts-node __utils__/scripts/src/copy-artifacts.ts",
|
||||
"make-release-description": "pnpm --filter=@pnpm/get-release-text run write-release-text",
|
||||
"make-release-description": "pnpm --filter=@pnpm/get-release-text run write-release-ext",
|
||||
"release": "pnpm --filter=@pnpm/exe publish --tag=next-10 --access=public && pnpm publish --filter=!pnpm --filter=!@pnpm/exe --access=public && pnpm publish --filter=pnpm --tag=next-10 --access=public",
|
||||
"dev-setup": "pnpm -C=./pnpm/dev link -g"
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@ export const DEFAULT_OPTS = {
|
||||
ca: undefined,
|
||||
cacheDir: '../cache',
|
||||
cert: undefined,
|
||||
excludeLinksFromLockfile: false,
|
||||
extraEnv: {},
|
||||
cliOptions: {},
|
||||
fetchRetries: 2,
|
||||
@@ -34,6 +35,7 @@ export const DEFAULT_OPTS = {
|
||||
pending: false,
|
||||
pnpmfile: './.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
"@pnpm/constants": "workspace:*",
|
||||
"@pnpm/core": "workspace:*",
|
||||
"@pnpm/dedupe.check": "workspace:*",
|
||||
"@pnpm/deps.status": "workspace:*",
|
||||
"@pnpm/error": "workspace:*",
|
||||
"@pnpm/filter-workspace-packages": "workspace:*",
|
||||
"@pnpm/find-workspace-dir": "workspace:*",
|
||||
|
||||
@@ -260,6 +260,7 @@ export type InstallCommandOptions = Pick<Config,
|
||||
| 'depth'
|
||||
| 'dev'
|
||||
| 'engineStrict'
|
||||
| 'excludeLinksFromLockfile'
|
||||
| 'frozenLockfile'
|
||||
| 'global'
|
||||
| 'globalPnpmfile'
|
||||
@@ -274,6 +275,7 @@ export type InstallCommandOptions = Pick<Config,
|
||||
| 'nodeLinker'
|
||||
| 'pnpmfile'
|
||||
| 'preferFrozenLockfile'
|
||||
| 'preferWorkspacePackages'
|
||||
| 'production'
|
||||
| 'registries'
|
||||
| 'rootProjectManifest'
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
tryReadProjectManifest,
|
||||
} from '@pnpm/cli-utils'
|
||||
import { type Config, getOptionsFromRootManifest } from '@pnpm/config'
|
||||
import { checkDepsStatus } from '@pnpm/deps.status'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { arrayOfWorkspacePackagesToMap } from '@pnpm/get-context'
|
||||
import { filterPkgsBySelectorObjects } from '@pnpm/filter-workspace-packages'
|
||||
@@ -19,10 +20,10 @@ import {
|
||||
type MutateModulesOptions,
|
||||
type WorkspacePackages,
|
||||
} from '@pnpm/core'
|
||||
import { logger } from '@pnpm/logger'
|
||||
import { globalInfo, logger } from '@pnpm/logger'
|
||||
import { sequenceGraph } from '@pnpm/sort-packages'
|
||||
import { createPkgGraph } from '@pnpm/workspace.pkgs-graph'
|
||||
import { updateWorkspaceState } from '@pnpm/workspace.state'
|
||||
import { updateWorkspaceState, type WorkspaceStateSettings } from '@pnpm/workspace.state'
|
||||
import isSubdir from 'is-subdir'
|
||||
import { getPinnedVersion } from './getPinnedVersion'
|
||||
import { getSaveType } from './getSaveType'
|
||||
@@ -55,6 +56,7 @@ export type InstallDepsOptions = Pick<Config,
|
||||
| 'depth'
|
||||
| 'dev'
|
||||
| 'engineStrict'
|
||||
| 'excludeLinksFromLockfile'
|
||||
| 'global'
|
||||
| 'globalPnpmfile'
|
||||
| 'hooks'
|
||||
@@ -66,6 +68,7 @@ export type InstallDepsOptions = Pick<Config,
|
||||
| 'lockfileOnly'
|
||||
| 'pnpmfile'
|
||||
| 'production'
|
||||
| 'preferWorkspacePackages'
|
||||
| 'rawLocalConfig'
|
||||
| 'registries'
|
||||
| 'rootProjectManifestDir'
|
||||
@@ -133,6 +136,16 @@ export async function installDeps (
|
||||
opts: InstallDepsOptions,
|
||||
params: string[]
|
||||
): Promise<void> {
|
||||
if (!opts.update && !opts.dedupe && params.length === 0) {
|
||||
const { upToDate } = await checkDepsStatus({
|
||||
...opts,
|
||||
ignoreFilteredInstallCache: true,
|
||||
})
|
||||
if (upToDate) {
|
||||
globalInfo('Already up to date')
|
||||
return
|
||||
}
|
||||
}
|
||||
if (opts.workspace) {
|
||||
if (opts.latest) {
|
||||
throw new PnpmError('BAD_OPTIONS', 'Cannot use --latest with --workspace simultaneously')
|
||||
@@ -308,6 +321,15 @@ when running add/update with the --workspace option')
|
||||
if (opts.save !== false) {
|
||||
await writeProjectManifest(updatedImporter.manifest)
|
||||
}
|
||||
if (!opts.lockfileOnly) {
|
||||
await updateWorkspaceState({
|
||||
allProjects,
|
||||
settings: opts,
|
||||
workspaceDir: opts.workspaceDir ?? opts.lockfileDir ?? opts.dir,
|
||||
pnpmfileExists: opts.hooks?.calculatePnpmfileChecksum != null,
|
||||
filteredInstall: allProjects.length !== Object.keys(opts.selectedProjectsGraph ?? {}).length,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -351,6 +373,16 @@ when running add/update with the --workspace option')
|
||||
skipIfHasSideEffectsCache: true,
|
||||
}
|
||||
)
|
||||
} else {
|
||||
if (!opts.lockfileOnly) {
|
||||
await updateWorkspaceState({
|
||||
allProjects,
|
||||
settings: opts,
|
||||
workspaceDir: opts.workspaceDir ?? opts.lockfileDir ?? opts.dir,
|
||||
pnpmfileExists: opts.hooks?.calculatePnpmfileChecksum != null,
|
||||
filteredInstall: allProjects.length !== Object.keys(opts.selectedProjectsGraph ?? {}).length,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,15 +395,18 @@ function selectProjectByDir (projects: Project[], searchedDir: string): Projects
|
||||
async function recursiveInstallThenUpdateWorkspaceState (
|
||||
allProjects: Project[],
|
||||
params: string[],
|
||||
opts: RecursiveOptions & Pick<InstallDepsOptions, 'catalogs' | 'workspaceDir'>,
|
||||
opts: RecursiveOptions & WorkspaceStateSettings,
|
||||
cmdFullName: CommandFullName
|
||||
): Promise<boolean | string> {
|
||||
const recursiveResult = await recursive(allProjects, params, opts, cmdFullName)
|
||||
await updateWorkspaceState({
|
||||
allProjects,
|
||||
catalogs: opts.catalogs,
|
||||
lastValidatedTimestamp: Date.now(),
|
||||
workspaceDir: opts.workspaceDir,
|
||||
})
|
||||
if (!opts.lockfileOnly) {
|
||||
await updateWorkspaceState({
|
||||
allProjects,
|
||||
settings: opts,
|
||||
workspaceDir: opts.workspaceDir,
|
||||
pnpmfileExists: opts.hooks?.calculatePnpmfileChecksum != null,
|
||||
filteredInstall: allProjects.length !== Object.keys(opts.selectedProjectsGraph ?? {}).length,
|
||||
})
|
||||
}
|
||||
return recursiveResult
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ const DEFAULT_OPTIONS = {
|
||||
bail: false,
|
||||
bin: 'node_modules/.bin',
|
||||
cacheDir: path.join(tmp, 'cache'),
|
||||
excludeLinksFromLockfile: false,
|
||||
extraEnv: {},
|
||||
cliOptions: {},
|
||||
deployAllFiles: false,
|
||||
@@ -25,6 +26,7 @@ const DEFAULT_OPTIONS = {
|
||||
optionalDependencies: true,
|
||||
},
|
||||
lock: true,
|
||||
preferWorkspacePackages: true,
|
||||
pnpmfile: '.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
|
||||
@@ -14,6 +14,7 @@ const DEFAULT_OPTIONS = {
|
||||
bin: 'node_modules/.bin',
|
||||
cliOptions: {},
|
||||
deployAllFiles: false,
|
||||
excludeLinksFromLockfile: false,
|
||||
extraEnv: {},
|
||||
include: {
|
||||
dependencies: true,
|
||||
@@ -21,6 +22,7 @@ const DEFAULT_OPTIONS = {
|
||||
optionalDependencies: true,
|
||||
},
|
||||
lock: true,
|
||||
preferWorkspacePackages: true,
|
||||
pnpmfile: '.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
|
||||
@@ -16,6 +16,7 @@ const DEFAULT_OPTIONS = {
|
||||
bail: false,
|
||||
bin: 'node_modules/.bin',
|
||||
cacheDir: path.join(tmp, 'cache'),
|
||||
excludeLinksFromLockfile: false,
|
||||
extraEnv: {},
|
||||
cliOptions: {},
|
||||
deployAllFiles: false,
|
||||
@@ -26,6 +27,7 @@ const DEFAULT_OPTIONS = {
|
||||
},
|
||||
lock: true,
|
||||
pnpmfile: '.pnpmfile.cjs',
|
||||
preferWorkspacePackages: true,
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
rawLocalConfig: { registry: REGISTRY_URL },
|
||||
registries: {
|
||||
|
||||
@@ -28,6 +28,7 @@ const DEFAULT_OPTS = {
|
||||
lockStaleDuration: 90,
|
||||
networkConcurrency: 16,
|
||||
offline: false,
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: { registry: REGISTRY },
|
||||
|
||||
@@ -26,6 +26,7 @@ const DEFAULT_OPTS = {
|
||||
lockStaleDuration: 90,
|
||||
networkConcurrency: 16,
|
||||
offline: false,
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: { registry: REGISTRY },
|
||||
|
||||
@@ -14,6 +14,7 @@ const DEFAULT_OPTIONS = {
|
||||
bail: false,
|
||||
bin: 'node_modules/.bin',
|
||||
cacheDir: path.join(TMP, 'cache'),
|
||||
excludeLinksFromLockfile: false,
|
||||
extraEnv: {},
|
||||
cliOptions: {},
|
||||
deployAllFiles: false,
|
||||
@@ -25,6 +26,7 @@ const DEFAULT_OPTIONS = {
|
||||
lock: true,
|
||||
pnpmfile: '.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
rawLocalConfig: { registry: REGISTRY_URL },
|
||||
registries: {
|
||||
|
||||
@@ -16,6 +16,7 @@ const DEFAULT_OPTIONS = {
|
||||
},
|
||||
bail: false,
|
||||
bin: 'node_modules/.bin',
|
||||
excludeLinksFromLockfile: false,
|
||||
extraEnv: {},
|
||||
cliOptions: {},
|
||||
deployAllFiles: false,
|
||||
@@ -28,6 +29,7 @@ const DEFAULT_OPTIONS = {
|
||||
linkWorkspacePackages: true,
|
||||
pnpmfile: '.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
rawLocalConfig: { registry: REGISTRY_URL },
|
||||
registries: {
|
||||
|
||||
@@ -21,6 +21,7 @@ const DEFAULT_OPTIONS = {
|
||||
},
|
||||
bail: false,
|
||||
bin: 'node_modules/.bin',
|
||||
excludeLinksFromLockfile: false,
|
||||
extraEnv: {},
|
||||
cliOptions: {},
|
||||
deployAllFiles: false,
|
||||
@@ -32,6 +33,7 @@ const DEFAULT_OPTIONS = {
|
||||
lock: true,
|
||||
pnpmfile: '.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
rawLocalConfig: { registry: REGISTRY_URL },
|
||||
registries: {
|
||||
|
||||
@@ -18,6 +18,7 @@ const DEFAULT_OPTIONS = {
|
||||
},
|
||||
bail: false,
|
||||
bin: 'node_modules/.bin',
|
||||
excludeLinksFromLockfile: false,
|
||||
extraEnv: {},
|
||||
cliOptions: {},
|
||||
deployAllFiles: false,
|
||||
@@ -29,6 +30,7 @@ const DEFAULT_OPTIONS = {
|
||||
lock: true,
|
||||
pnpmfile: '.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
rawLocalConfig: { registry: REGISTRY_URL },
|
||||
registries: {
|
||||
|
||||
@@ -11,6 +11,7 @@ export const DEFAULT_OPTS = {
|
||||
ca: undefined,
|
||||
cacheDir: '../cache',
|
||||
cert: undefined,
|
||||
excludeLinksFromLockfile: false,
|
||||
extraEnv: {},
|
||||
cliOptions: {},
|
||||
deployAllFiles: false,
|
||||
@@ -35,6 +36,7 @@ export const DEFAULT_OPTS = {
|
||||
pending: false,
|
||||
pnpmfile: './.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
{
|
||||
"path": "../../dedupe/check"
|
||||
},
|
||||
{
|
||||
"path": "../../deps/status"
|
||||
},
|
||||
{
|
||||
"path": "../../env/plugin-commands-env"
|
||||
},
|
||||
|
||||
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@@ -1824,6 +1824,9 @@ importers:
|
||||
'@pnpm/parse-overrides':
|
||||
specifier: workspace:*
|
||||
version: link:../../config/parse-overrides
|
||||
'@pnpm/pnpmfile':
|
||||
specifier: workspace:*
|
||||
version: link:../../hooks/pnpmfile
|
||||
'@pnpm/resolver-base':
|
||||
specifier: workspace:*
|
||||
version: link:../../resolving/resolver-base
|
||||
@@ -5088,6 +5091,9 @@ importers:
|
||||
'@pnpm/dedupe.check':
|
||||
specifier: workspace:*
|
||||
version: link:../../dedupe/check
|
||||
'@pnpm/deps.status':
|
||||
specifier: workspace:*
|
||||
version: link:../../deps/status
|
||||
'@pnpm/error':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/error
|
||||
@@ -7901,6 +7907,9 @@ importers:
|
||||
'@pnpm/catalogs.types':
|
||||
specifier: workspace:*
|
||||
version: link:../../catalogs/types
|
||||
'@pnpm/config':
|
||||
specifier: workspace:*
|
||||
version: link:../../config/config
|
||||
'@pnpm/logger':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/logger
|
||||
@@ -7917,6 +7926,9 @@ importers:
|
||||
'@pnpm/workspace.state':
|
||||
specifier: workspace:*
|
||||
version: 'link:'
|
||||
'@types/ramda':
|
||||
specifier: 'catalog:'
|
||||
version: 0.29.12
|
||||
|
||||
packages:
|
||||
|
||||
|
||||
@@ -179,14 +179,14 @@ test('use node versions specified by pnpm.executionEnv.nodeVersion in workspace
|
||||
name: 'node-version-unset',
|
||||
version: '1.0.0',
|
||||
scripts: {
|
||||
install: 'node -v > node-version.txt',
|
||||
test: 'node -v > node-version.txt',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'node-version-18',
|
||||
version: '1.0.0',
|
||||
scripts: {
|
||||
install: 'node -v > node-version.txt',
|
||||
test: 'node -v > node-version.txt',
|
||||
},
|
||||
pnpm: {
|
||||
executionEnv: {
|
||||
@@ -198,7 +198,7 @@ test('use node versions specified by pnpm.executionEnv.nodeVersion in workspace
|
||||
name: 'node-version-20',
|
||||
version: '1.0.0',
|
||||
scripts: {
|
||||
install: 'node -v > node-version.txt',
|
||||
test: 'node -v > node-version.txt',
|
||||
},
|
||||
pnpm: {
|
||||
executionEnv: {
|
||||
@@ -212,7 +212,7 @@ test('use node versions specified by pnpm.executionEnv.nodeVersion in workspace
|
||||
packages: ['*'],
|
||||
})
|
||||
|
||||
execPnpmSync(['install'])
|
||||
execPnpmSync(['-r', 'test'])
|
||||
expect(
|
||||
['node-version-unset', 'node-version-18', 'node-version-20'].map(name => {
|
||||
const filePath = path.join(projects[name].dir(), 'node-version.txt')
|
||||
@@ -220,7 +220,7 @@ test('use node versions specified by pnpm.executionEnv.nodeVersion in workspace
|
||||
})
|
||||
).toStrictEqual([process.version, 'v18.0.0', 'v20.0.0'])
|
||||
|
||||
execPnpmSync(['--config.use-node-version=19.0.0', 'install'])
|
||||
execPnpmSync(['--config.use-node-version=19.0.0', '-r', 'test'])
|
||||
expect(
|
||||
['node-version-unset', 'node-version-18', 'node-version-20'].map(name => {
|
||||
const filePath = path.join(projects[name].dir(), 'node-version.txt')
|
||||
|
||||
@@ -549,7 +549,7 @@ test('installation with --link-workspace-packages links packages even if they we
|
||||
},
|
||||
])
|
||||
|
||||
await execPnpm(['recursive', 'install', '--no-link-workspace-packages'])
|
||||
await execPnpm(['-r', 'install', '--no-link-workspace-packages'])
|
||||
|
||||
{
|
||||
const lockfile = projects.project.readLockfile()
|
||||
@@ -557,7 +557,7 @@ test('installation with --link-workspace-packages links packages even if they we
|
||||
expect(lockfile.importers['.'].dependencies?.negative.version).toBe('is-negative@1.0.0')
|
||||
}
|
||||
|
||||
await execPnpm(['recursive', 'install', '--link-workspace-packages'])
|
||||
await execPnpm(['-r', 'install', '--link-workspace-packages'])
|
||||
|
||||
{
|
||||
const lockfile = projects.project.readLockfile()
|
||||
@@ -590,7 +590,7 @@ test('shared-workspace-lockfile: installation with --link-workspace-packages lin
|
||||
writeYamlFile('pnpm-workspace.yaml', { packages: ['**', '!store/**'] })
|
||||
fs.writeFileSync('.npmrc', 'shared-workspace-lockfile = true\nlink-workspace-packages = true', 'utf8')
|
||||
|
||||
await execPnpm(['recursive', 'install'])
|
||||
await execPnpm(['install'])
|
||||
|
||||
{
|
||||
const lockfile = readYamlFile<LockfileFile>(WANTED_LOCKFILE)
|
||||
@@ -608,7 +608,7 @@ test('shared-workspace-lockfile: installation with --link-workspace-packages lin
|
||||
version: '1.0.0',
|
||||
})
|
||||
|
||||
await execPnpm(['recursive', 'install'])
|
||||
await execPnpm(['install'])
|
||||
|
||||
{
|
||||
const lockfile = readYamlFile<LockfileFile>(WANTED_LOCKFILE)
|
||||
@@ -1243,8 +1243,8 @@ test('dependencies of workspace projects are built during headless installation'
|
||||
fs.writeFileSync('.npmrc', 'shared-workspace-lockfile=false', 'utf8')
|
||||
writeYamlFile('pnpm-workspace.yaml', { packages: ['**', '!store/**'] })
|
||||
|
||||
await execPnpm(['recursive', 'install', '--lockfile-only'])
|
||||
await execPnpm(['recursive', 'install', '--frozen-lockfile'])
|
||||
await execPnpm(['install', '--lockfile-only'])
|
||||
await execPnpm(['install', '--frozen-lockfile'])
|
||||
|
||||
{
|
||||
const generatedByPreinstall = projects['project-1'].requireModule('@pnpm.e2e/pre-and-postinstall-scripts-example/generated-by-preinstall')
|
||||
@@ -1277,14 +1277,15 @@ test("linking the package's bin to another workspace package in a monorepo", asy
|
||||
|
||||
writeYamlFile('pnpm-workspace.yaml', { packages: ['**', '!store/**'] })
|
||||
|
||||
await execPnpm(['recursive', 'install'])
|
||||
await execPnpm(['install'])
|
||||
|
||||
projects.main.isExecutable('.bin/hello')
|
||||
|
||||
expect(fs.existsSync('main/node_modules')).toBeTruthy()
|
||||
rimraf('main/node_modules')
|
||||
rimraf('node_modules')
|
||||
|
||||
await execPnpm(['recursive', 'install', '--frozen-lockfile'])
|
||||
await execPnpm(['install', '--frozen-lockfile'])
|
||||
|
||||
projects.main.isExecutable('.bin/hello')
|
||||
})
|
||||
@@ -1433,6 +1434,7 @@ test('custom virtual store directory in a workspace with not shared lockfile', a
|
||||
|
||||
rimraf('project-1/virtual-store')
|
||||
rimraf('project-1/node_modules')
|
||||
rimraf('node_modules')
|
||||
|
||||
await execPnpm(['install', '--frozen-lockfile'])
|
||||
|
||||
|
||||
@@ -25,16 +25,14 @@ test('single package workspace', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, ...EXEC])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('Cannot find a lockfile')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
}
|
||||
|
||||
await execPnpm([...CONFIG, 'install'])
|
||||
|
||||
// installing dependencies on a single package workspace should not create a packages list cache
|
||||
{
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toBeUndefined()
|
||||
}
|
||||
// installing dependencies on a single package workspace should create a packages list cache
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toBeDefined()
|
||||
|
||||
// should be able to execute a command after dependencies have been installed
|
||||
{
|
||||
@@ -62,7 +60,7 @@ test('single package workspace', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, ...EXEC])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
}
|
||||
|
||||
await execPnpm([...CONFIG, 'install'])
|
||||
@@ -82,7 +80,7 @@ test('single package workspace', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, ...EXEC])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
}
|
||||
|
||||
await execPnpm([...CONFIG, 'install'])
|
||||
@@ -167,15 +165,16 @@ test('multi-project workspace', async () => {
|
||||
// pnpm install should create a packages list cache
|
||||
{
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toStrictEqual({
|
||||
catalogs: {},
|
||||
expect(workspaceState).toStrictEqual(expect.objectContaining({
|
||||
lastValidatedTimestamp: expect.any(Number),
|
||||
projectRootDirs: [
|
||||
path.resolve('.'),
|
||||
path.resolve('foo'),
|
||||
path.resolve('bar'),
|
||||
].sort(),
|
||||
})
|
||||
pnpmfileExists: false,
|
||||
filteredInstall: false,
|
||||
projects: {
|
||||
[path.resolve('.')]: { name: 'root', version: '0.0.0' },
|
||||
[path.resolve('foo')]: { name: 'foo', version: '0.0.0' },
|
||||
[path.resolve('bar')]: { name: 'bar', version: '0.0.0' },
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
// should be able to execute a command in root after dependencies have been installed
|
||||
@@ -229,7 +228,7 @@ test('multi-project workspace', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, ...EXEC])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).toContain('project of id foo')
|
||||
}
|
||||
// attempting to execute a command in any workspace package without updating dependencies should fail
|
||||
@@ -238,7 +237,7 @@ test('multi-project workspace', async () => {
|
||||
cwd: projects.foo.dir(),
|
||||
})
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).toContain('project of id foo')
|
||||
}
|
||||
{
|
||||
@@ -246,21 +245,21 @@ test('multi-project workspace', async () => {
|
||||
cwd: projects.bar.dir(),
|
||||
})
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).toContain('project of id foo')
|
||||
}
|
||||
// attempting to execute a command recursively without updating dependencies should fail
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, '--recursive', ...EXEC])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).toContain('project of id foo')
|
||||
}
|
||||
// attempting to execute a command with filter without updating dependencies should fail
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, '--filter=foo', ...EXEC])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).toContain('project of id foo')
|
||||
}
|
||||
|
||||
@@ -300,7 +299,7 @@ test('multi-project workspace', async () => {
|
||||
}
|
||||
|
||||
manifests.baz = {
|
||||
name: 'bar',
|
||||
name: 'baz',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'@pnpm.e2e/foo': '=100.0.0',
|
||||
@@ -321,16 +320,17 @@ test('multi-project workspace', async () => {
|
||||
// pnpm install should update the packages list cache
|
||||
{
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toStrictEqual({
|
||||
catalogs: {},
|
||||
expect(workspaceState).toStrictEqual(expect.objectContaining({
|
||||
lastValidatedTimestamp: expect.any(Number),
|
||||
projectRootDirs: [
|
||||
path.resolve('.'),
|
||||
path.resolve('foo'),
|
||||
path.resolve('bar'),
|
||||
path.resolve('baz'),
|
||||
].sort(),
|
||||
})
|
||||
pnpmfileExists: false,
|
||||
filteredInstall: false,
|
||||
projects: {
|
||||
[path.resolve('.')]: { name: 'root', version: '0.0.0' },
|
||||
[path.resolve('foo')]: { name: 'foo' },
|
||||
[path.resolve('bar')]: { name: 'bar', version: '0.0.0' },
|
||||
[path.resolve('baz')]: { name: 'baz' },
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
// should be able to execute a command after projects list have been updated
|
||||
|
||||
@@ -90,15 +90,16 @@ test('single dependency', async () => {
|
||||
// pnpm install should create a packages list cache
|
||||
{
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toStrictEqual({
|
||||
catalogs: {},
|
||||
expect(workspaceState).toStrictEqual(expect.objectContaining({
|
||||
lastValidatedTimestamp: expect.any(Number),
|
||||
projectRootDirs: [
|
||||
path.resolve('.'),
|
||||
path.resolve('foo'),
|
||||
path.resolve('bar'),
|
||||
].sort(),
|
||||
})
|
||||
pnpmfileExists: false,
|
||||
filteredInstall: false,
|
||||
projects: {
|
||||
[path.resolve('.')]: { name: 'root', version: '0.0.0' },
|
||||
[path.resolve('foo')]: { name: 'foo', version: '0.0.0' },
|
||||
[path.resolve('bar')]: { name: 'bar', version: '0.0.0' },
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
// should be able to execute a script in root after dependencies have been installed
|
||||
@@ -159,7 +160,7 @@ test('single dependency', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, '--reporter=ndjson', 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).toContain('project of id foo')
|
||||
expect(stdout.toString()).not.toContain('No manifest files were modified since the last validation. Exiting check.')
|
||||
expect(stdout.toString()).toContain('Some manifest files were modified since the last validation. Continuing check.')
|
||||
@@ -170,7 +171,7 @@ test('single dependency', async () => {
|
||||
cwd: projects.foo.dir(),
|
||||
})
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).toContain('project of id foo')
|
||||
}
|
||||
{
|
||||
@@ -178,21 +179,21 @@ test('single dependency', async () => {
|
||||
cwd: projects.bar.dir(),
|
||||
})
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).toContain('project of id foo')
|
||||
}
|
||||
// attempting to execute a script recursively without updating dependencies should fail
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, '--recursive', 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).toContain('project of id foo')
|
||||
}
|
||||
// attempting to execute a script with filter without updating dependencies should fail
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, '--filter=foo', 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).toContain('project of id foo')
|
||||
}
|
||||
|
||||
@@ -233,7 +234,7 @@ test('single dependency', async () => {
|
||||
}
|
||||
|
||||
manifests.baz = {
|
||||
name: 'bar',
|
||||
name: 'baz',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'@pnpm.e2e/foo': '=100.0.0',
|
||||
@@ -258,16 +259,17 @@ test('single dependency', async () => {
|
||||
// pnpm install should update the packages list cache
|
||||
{
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toStrictEqual({
|
||||
catalogs: {},
|
||||
expect(workspaceState).toStrictEqual(expect.objectContaining({
|
||||
lastValidatedTimestamp: expect.any(Number),
|
||||
projectRootDirs: [
|
||||
path.resolve('.'),
|
||||
path.resolve('foo'),
|
||||
path.resolve('bar'),
|
||||
path.resolve('baz'),
|
||||
].sort(),
|
||||
})
|
||||
pnpmfileExists: false,
|
||||
filteredInstall: false,
|
||||
projects: {
|
||||
[path.resolve('.')]: { name: 'root', version: '0.0.0' },
|
||||
[path.resolve('foo')]: { name: 'foo' },
|
||||
[path.resolve('bar')]: { name: 'bar', version: '0.0.0' },
|
||||
[path.resolve('baz')]: { name: 'baz' },
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
// should be able to execute a script after projects list have been updated
|
||||
@@ -362,15 +364,16 @@ test('multiple lockfiles', async () => {
|
||||
// pnpm install should create a packages list cache
|
||||
{
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toStrictEqual({
|
||||
catalogs: {},
|
||||
expect(workspaceState).toStrictEqual(expect.objectContaining({
|
||||
lastValidatedTimestamp: expect.any(Number),
|
||||
projectRootDirs: [
|
||||
path.resolve('.'),
|
||||
path.resolve('foo'),
|
||||
path.resolve('bar'),
|
||||
].sort(),
|
||||
})
|
||||
pnpmfileExists: false,
|
||||
filteredInstall: false,
|
||||
projects: {
|
||||
[path.resolve('.')]: { name: 'root', version: '0.0.0' },
|
||||
[path.resolve('foo')]: { name: 'foo', version: '0.0.0' },
|
||||
[path.resolve('bar')]: { name: 'bar', version: '0.0.0' },
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
// should be able to execute a script in root after dependencies have been installed
|
||||
@@ -431,7 +434,7 @@ test('multiple lockfiles', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...config, 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).toContain(`The lockfile in ${path.resolve('foo')} does not satisfy project of id .`)
|
||||
}
|
||||
// attempting to execute a script in any workspace package without updating dependencies should fail
|
||||
@@ -440,7 +443,7 @@ test('multiple lockfiles', async () => {
|
||||
cwd: projects.foo.dir(),
|
||||
})
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).toContain(`The lockfile in ${path.resolve('foo')} does not satisfy project of id .`)
|
||||
}
|
||||
{
|
||||
@@ -448,21 +451,21 @@ test('multiple lockfiles', async () => {
|
||||
cwd: projects.bar.dir(),
|
||||
})
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).toContain(`The lockfile in ${path.resolve('foo')} does not satisfy project of id .`)
|
||||
}
|
||||
// attempting to execute a script recursively without updating dependencies should fail
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...config, '--recursive', 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).toContain(`The lockfile in ${path.resolve('foo')} does not satisfy project of id .`)
|
||||
}
|
||||
// attempting to execute a script with filter without updating dependencies should fail
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...config, '--filter=foo', 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).toContain(`The lockfile in ${path.resolve('foo')} does not satisfy project of id .`)
|
||||
}
|
||||
|
||||
@@ -501,7 +504,7 @@ test('multiple lockfiles', async () => {
|
||||
}
|
||||
|
||||
manifests.baz = {
|
||||
name: 'bar',
|
||||
name: 'baz',
|
||||
private: true,
|
||||
dependencies: {
|
||||
'@pnpm.e2e/foo': '=100.0.0',
|
||||
@@ -525,16 +528,17 @@ test('multiple lockfiles', async () => {
|
||||
// pnpm install should update the packages list cache
|
||||
{
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toStrictEqual({
|
||||
catalogs: {},
|
||||
expect(workspaceState).toStrictEqual(expect.objectContaining({
|
||||
lastValidatedTimestamp: expect.any(Number),
|
||||
projectRootDirs: [
|
||||
path.resolve('.'),
|
||||
path.resolve('foo'),
|
||||
path.resolve('bar'),
|
||||
path.resolve('baz'),
|
||||
].sort(),
|
||||
})
|
||||
pnpmfileExists: false,
|
||||
filteredInstall: false,
|
||||
projects: {
|
||||
[path.resolve('.')]: { name: 'root', version: '0.0.0' },
|
||||
[path.resolve('foo')]: { name: 'foo' },
|
||||
[path.resolve('bar')]: { name: 'bar', version: '0.0.0' },
|
||||
[path.resolve('baz')]: { name: 'baz' },
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
// should be able to execute a script after projects list have been updated
|
||||
@@ -611,7 +615,7 @@ test('filtered install', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, '--filter=foo', 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
}
|
||||
|
||||
await execPnpm([...CONFIG, '--filter=foo', 'install'])
|
||||
@@ -671,15 +675,16 @@ test('no dependencies', async () => {
|
||||
// pnpm install should create a packages list cache
|
||||
{
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toStrictEqual({
|
||||
catalogs: {},
|
||||
expect(workspaceState).toStrictEqual(expect.objectContaining({
|
||||
lastValidatedTimestamp: expect.any(Number),
|
||||
projectRootDirs: [
|
||||
path.resolve('.'),
|
||||
path.resolve('foo'),
|
||||
path.resolve('bar'),
|
||||
].sort(),
|
||||
})
|
||||
pnpmfileExists: false,
|
||||
filteredInstall: false,
|
||||
projects: {
|
||||
[path.resolve('.')]: { name: 'root', version: '0.0.0' },
|
||||
[path.resolve('foo')]: { name: 'foo', version: '0.0.0' },
|
||||
[path.resolve('bar')]: { name: 'bar', version: '0.0.0' },
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
// should be able to execute a script after `pnpm install`
|
||||
@@ -761,7 +766,7 @@ test('nested `pnpm run` should not check for mutated manifest', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, '--recursive', 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
}
|
||||
|
||||
await execPnpm([...CONFIG, 'install'])
|
||||
@@ -849,15 +854,19 @@ test('should check for outdated catalogs', async () => {
|
||||
{
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toStrictEqual({
|
||||
catalogs: {
|
||||
default: workspaceManifest.catalog,
|
||||
},
|
||||
settings: expect.objectContaining({
|
||||
catalogs: {
|
||||
default: workspaceManifest.catalog,
|
||||
},
|
||||
}),
|
||||
pnpmfileExists: false,
|
||||
filteredInstall: false,
|
||||
lastValidatedTimestamp: expect.any(Number),
|
||||
projectRootDirs: [
|
||||
path.resolve('.'),
|
||||
path.resolve('foo'),
|
||||
path.resolve('bar'),
|
||||
].sort(),
|
||||
projects: {
|
||||
[path.resolve('.')]: { name: 'root', version: '0.0.0' },
|
||||
[path.resolve('foo')]: { name: 'foo', version: '0.0.0' },
|
||||
[path.resolve('bar')]: { name: 'bar', version: '0.0.0' },
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -954,6 +963,6 @@ test('failed to install dependencies', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -26,16 +26,14 @@ test('single dependency', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('Cannot find a lockfile in')
|
||||
expect(stdout.toString()).toContain('Cannot check whether dependencies are outdated')
|
||||
}
|
||||
|
||||
await execPnpm([...CONFIG, 'install'])
|
||||
|
||||
// installing dependencies on a single package workspace should not create a packages list cache
|
||||
{
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toBeUndefined()
|
||||
}
|
||||
// installing dependencies on a single package workspace should create a packages list cache
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toBeDefined()
|
||||
|
||||
// should be able to execute a script after dependencies have been installed
|
||||
{
|
||||
@@ -65,7 +63,7 @@ test('single dependency', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, '--reporter=ndjson', 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
expect(stdout.toString()).not.toContain('The manifest file is not newer than the lockfile. Exiting check.')
|
||||
expect(stdout.toString()).toContain('The manifest is newer than the lockfile. Continuing check.')
|
||||
}
|
||||
@@ -89,7 +87,7 @@ test('single dependency', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
}
|
||||
|
||||
await execPnpm([...CONFIG, 'install'])
|
||||
@@ -122,16 +120,14 @@ test('deleting node_modules after install', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('Cannot find a lockfile in')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
}
|
||||
|
||||
await execPnpm([...CONFIG, 'install'])
|
||||
|
||||
// installing dependencies on a single package workspace should not create a packages list cache
|
||||
{
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toBeUndefined()
|
||||
}
|
||||
// installing dependencies on a single package workspace should create a packages list cache
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toBeDefined()
|
||||
|
||||
// should be able to execute a script after dependencies have been installed
|
||||
{
|
||||
@@ -145,7 +141,7 @@ test('deleting node_modules after install', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_NO_DEPS')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -164,16 +160,14 @@ test('no dependencies', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('Cannot find a lockfile in')
|
||||
expect(stdout.toString()).toContain('Cannot check whether dependencies are outdated')
|
||||
}
|
||||
|
||||
await execPnpm([...CONFIG, 'install'])
|
||||
|
||||
// installing dependencies on a single package workspace should not create a packages list cache
|
||||
{
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toBeUndefined()
|
||||
}
|
||||
// installing dependencies on a single package workspace should create a packages list cache
|
||||
const workspaceState = loadWorkspaceState(process.cwd())
|
||||
expect(workspaceState).toBeDefined()
|
||||
|
||||
// should be able to execute a script after the lockfile has been created
|
||||
{
|
||||
@@ -220,7 +214,7 @@ test('nested `pnpm run` should not check for mutated manifest', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('Cannot find a lockfile in')
|
||||
expect(stdout.toString()).toContain('Cannot check whether dependencies are outdated')
|
||||
}
|
||||
|
||||
await execPnpm([...CONFIG, 'install'])
|
||||
@@ -236,7 +230,7 @@ test('nested `pnpm run` should not check for mutated manifest', async () => {
|
||||
{
|
||||
const { status, stdout } = execPnpmSync([...CONFIG, 'start'])
|
||||
expect(status).not.toBe(0)
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_RUN_CHECK_DEPS_UNSATISFIED_PKG_MANIFEST')
|
||||
expect(stdout.toString()).toContain('ERR_PNPM_VERIFY_DEPS_BEFORE_RUN')
|
||||
}
|
||||
|
||||
await execPnpm([...CONFIG, 'install'])
|
||||
|
||||
@@ -12,6 +12,7 @@ export const DEFAULT_OPTS = {
|
||||
cacheDir: '../cache',
|
||||
cert: undefined,
|
||||
extraEnv: {},
|
||||
excludeLinksFromLockfile: false,
|
||||
cliOptions: {},
|
||||
deployAllFiles: false,
|
||||
fetchRetries: 2,
|
||||
@@ -35,6 +36,7 @@ export const DEFAULT_OPTS = {
|
||||
pending: false,
|
||||
pnpmfile: './.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
|
||||
@@ -9,6 +9,7 @@ export const DEFAULT_OPTS = {
|
||||
ca: undefined,
|
||||
cacheDir: '../cache',
|
||||
cert: undefined,
|
||||
excludeLinksFromLockfile: false,
|
||||
extraEnv: {},
|
||||
cliOptions: {},
|
||||
fetchRetries: 2,
|
||||
@@ -32,6 +33,7 @@ export const DEFAULT_OPTS = {
|
||||
pending: false,
|
||||
pnpmfile: './.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
|
||||
@@ -10,6 +10,7 @@ export const DEFAULT_OPTS = {
|
||||
bin: 'node_modules/.bin',
|
||||
ca: undefined,
|
||||
cert: undefined,
|
||||
excludeLinksFromLockfile: false,
|
||||
extraEnv: {},
|
||||
cliOptions: {},
|
||||
fetchRetries: 2,
|
||||
@@ -34,6 +35,7 @@ export const DEFAULT_OPTS = {
|
||||
pnpmfile: './.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
proxy: undefined,
|
||||
preferWorkspacePackages: true,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
registries: { default: REGISTRY },
|
||||
|
||||
@@ -11,6 +11,7 @@ export const DEFAULT_OPTS = {
|
||||
ca: undefined,
|
||||
cacheDir: '../cache',
|
||||
cert: undefined,
|
||||
excludeLinksFromLockfile: false,
|
||||
extraEnv: {},
|
||||
cliOptions: {},
|
||||
deployAllFiles: false,
|
||||
@@ -36,6 +37,7 @@ export const DEFAULT_OPTS = {
|
||||
pending: false,
|
||||
pnpmfile: './.pnpmfile.cjs',
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
|
||||
@@ -38,9 +38,11 @@ function prepareOptions (dir: string) {
|
||||
original: [],
|
||||
},
|
||||
cliOptions: {},
|
||||
excludeLinksFromLockfile: false,
|
||||
linkWorkspacePackages: true,
|
||||
bail: true,
|
||||
pnpmHomeDir: dir,
|
||||
preferWorkspacePackages: true,
|
||||
registries: {
|
||||
default: 'https://registry.npmjs.org/',
|
||||
},
|
||||
|
||||
@@ -32,13 +32,15 @@
|
||||
"funding": "https://opencollective.com/pnpm",
|
||||
"dependencies": {
|
||||
"@pnpm/catalogs.types": "workspace:*",
|
||||
"@pnpm/config": "workspace:*",
|
||||
"@pnpm/logger": "workspace:*",
|
||||
"@pnpm/types": "workspace:*",
|
||||
"ramda": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pnpm/prepare": "workspace:*",
|
||||
"@pnpm/workspace.state": "workspace:*"
|
||||
"@pnpm/workspace.state": "workspace:*",
|
||||
"@types/ramda": "catalog:"
|
||||
},
|
||||
"exports": {
|
||||
".": "./lib/index.js"
|
||||
|
||||
@@ -1,14 +1,41 @@
|
||||
import { type Catalogs } from '@pnpm/catalogs.types'
|
||||
import { type WorkspaceState, type ProjectsList } from './types'
|
||||
import pick from 'ramda/src/pick'
|
||||
import { type WorkspaceState, type WorkspaceStateSettings, type ProjectsList } from './types'
|
||||
|
||||
export interface CreateWorkspaceStateOptions {
|
||||
allProjects: ProjectsList
|
||||
catalogs: Catalogs | undefined
|
||||
lastValidatedTimestamp: number
|
||||
pnpmfileExists: boolean
|
||||
filteredInstall: boolean
|
||||
settings: WorkspaceStateSettings
|
||||
}
|
||||
|
||||
export const createWorkspaceState = (opts: CreateWorkspaceStateOptions): WorkspaceState => ({
|
||||
catalogs: opts.catalogs,
|
||||
lastValidatedTimestamp: opts.lastValidatedTimestamp,
|
||||
projectRootDirs: opts.allProjects.map(project => project.rootDir).sort(),
|
||||
lastValidatedTimestamp: Date.now(),
|
||||
projects: Object.fromEntries(opts.allProjects.map(project => [
|
||||
project.rootDir,
|
||||
{
|
||||
name: project.manifest.name,
|
||||
version: project.manifest.version,
|
||||
},
|
||||
])),
|
||||
pnpmfileExists: opts.pnpmfileExists,
|
||||
settings: pick([
|
||||
'autoInstallPeers',
|
||||
'catalogs',
|
||||
'dedupeDirectDeps',
|
||||
'dedupeInjectedDeps',
|
||||
'dedupePeerDependents',
|
||||
'dev',
|
||||
'excludeLinksFromLockfile',
|
||||
'hoistPattern',
|
||||
'hoistWorkspacePackages',
|
||||
'injectWorkspacePackages',
|
||||
'linkWorkspacePackages',
|
||||
'nodeLinker',
|
||||
'optional',
|
||||
'preferWorkspacePackages',
|
||||
'production',
|
||||
'publicHoistPattern',
|
||||
'workspacePackagePatterns',
|
||||
], opts.settings),
|
||||
filteredInstall: opts.filteredInstall,
|
||||
})
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { loadWorkspaceState } from './loadWorkspaceState'
|
||||
export { type UpdateWorkspaceStateOptions, updateWorkspaceState } from './updateWorkspaceState'
|
||||
export { type WorkspaceState, type ProjectsList } from './types'
|
||||
export { type WorkspaceState, type WorkspaceStateSettings, type ProjectsList } from './types'
|
||||
|
||||
@@ -1,10 +1,35 @@
|
||||
import { type Catalogs } from '@pnpm/catalogs.types'
|
||||
import { type Config } from '@pnpm/config'
|
||||
import { type Project, type ProjectRootDir } from '@pnpm/types'
|
||||
|
||||
export type ProjectsList = Array<Pick<Project, 'rootDir'>>
|
||||
export type ProjectsList = Array<Pick<Project, 'rootDir' | 'manifest'>>
|
||||
|
||||
export interface WorkspaceState {
|
||||
catalogs: Catalogs | undefined
|
||||
lastValidatedTimestamp: number
|
||||
projectRootDirs: ProjectRootDir[]
|
||||
projects: Record<ProjectRootDir, {
|
||||
name?: string
|
||||
version?: string
|
||||
}>
|
||||
pnpmfileExists: boolean
|
||||
filteredInstall: boolean
|
||||
settings: WorkspaceStateSettings
|
||||
}
|
||||
|
||||
export type WorkspaceStateSettings = Pick<Config,
|
||||
| 'autoInstallPeers'
|
||||
| 'catalogs'
|
||||
| 'dedupeDirectDeps'
|
||||
| 'dedupeInjectedDeps'
|
||||
| 'dedupePeerDependents'
|
||||
| 'dev'
|
||||
| 'excludeLinksFromLockfile'
|
||||
| 'hoistPattern'
|
||||
| 'hoistWorkspacePackages'
|
||||
| 'injectWorkspacePackages'
|
||||
| 'linkWorkspacePackages'
|
||||
| 'nodeLinker'
|
||||
| 'optional'
|
||||
| 'preferWorkspacePackages'
|
||||
| 'production'
|
||||
| 'publicHoistPattern'
|
||||
| 'workspacePackagePatterns'
|
||||
>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { type Catalogs } from '@pnpm/catalogs.types'
|
||||
import { logger } from '@pnpm/logger'
|
||||
import { getFilePath } from './filePath'
|
||||
import { createWorkspaceState } from './createWorkspaceState'
|
||||
import { type ProjectsList } from './types'
|
||||
import { type WorkspaceStateSettings, type ProjectsList } from './types'
|
||||
|
||||
export interface UpdateWorkspaceStateOptions {
|
||||
allProjects: ProjectsList
|
||||
catalogs: Catalogs | undefined
|
||||
lastValidatedTimestamp: number
|
||||
settings: WorkspaceStateSettings
|
||||
workspaceDir: string
|
||||
pnpmfileExists: boolean
|
||||
filteredInstall: boolean
|
||||
}
|
||||
|
||||
export async function updateWorkspaceState (opts: UpdateWorkspaceStateOptions): Promise<void> {
|
||||
|
||||
@@ -3,22 +3,28 @@ import { prepareEmpty, preparePackages } from '@pnpm/prepare'
|
||||
import { type ProjectRootDir } from '@pnpm/types'
|
||||
import { createWorkspaceState } from '../src/createWorkspaceState'
|
||||
|
||||
const lastValidatedTimestamp = Date.now()
|
||||
|
||||
test('createWorkspaceState() on empty list', () => {
|
||||
prepareEmpty()
|
||||
|
||||
expect(
|
||||
createWorkspaceState({
|
||||
allProjects: [],
|
||||
catalogs: undefined,
|
||||
lastValidatedTimestamp,
|
||||
pnpmfileExists: true,
|
||||
filteredInstall: false,
|
||||
settings: {
|
||||
autoInstallPeers: true,
|
||||
dedupeDirectDeps: true,
|
||||
excludeLinksFromLockfile: false,
|
||||
preferWorkspacePackages: false,
|
||||
linkWorkspacePackages: false,
|
||||
injectWorkspacePackages: false,
|
||||
},
|
||||
})
|
||||
).toStrictEqual({
|
||||
catalogs: undefined,
|
||||
lastValidatedTimestamp,
|
||||
projectRootDirs: [],
|
||||
})
|
||||
).toStrictEqual(expect.objectContaining({
|
||||
projects: {},
|
||||
pnpmfileExists: true,
|
||||
lastValidatedTimestamp: expect.any(Number),
|
||||
}))
|
||||
})
|
||||
|
||||
test('createWorkspaceState() on non-empty list', () => {
|
||||
@@ -30,30 +36,42 @@ test('createWorkspaceState() on non-empty list', () => {
|
||||
expect(
|
||||
createWorkspaceState({
|
||||
allProjects: [
|
||||
{ rootDir: path.resolve('packages/c') as ProjectRootDir },
|
||||
{ rootDir: path.resolve('packages/b') as ProjectRootDir },
|
||||
{ rootDir: path.resolve('packages/a') as ProjectRootDir },
|
||||
{ rootDir: path.resolve('packages/d') as ProjectRootDir },
|
||||
{ rootDir: path.resolve('packages/c') as ProjectRootDir, manifest: {} },
|
||||
{ rootDir: path.resolve('packages/b') as ProjectRootDir, manifest: {} },
|
||||
{ rootDir: path.resolve('packages/a') as ProjectRootDir, manifest: {} },
|
||||
{ rootDir: path.resolve('packages/d') as ProjectRootDir, manifest: {} },
|
||||
],
|
||||
lastValidatedTimestamp,
|
||||
settings: {
|
||||
autoInstallPeers: true,
|
||||
dedupeDirectDeps: true,
|
||||
excludeLinksFromLockfile: false,
|
||||
preferWorkspacePackages: false,
|
||||
linkWorkspacePackages: false,
|
||||
injectWorkspacePackages: false,
|
||||
catalogs: {
|
||||
default: {
|
||||
foo: '0.1.2',
|
||||
},
|
||||
},
|
||||
},
|
||||
pnpmfileExists: false,
|
||||
filteredInstall: false,
|
||||
})
|
||||
).toStrictEqual(expect.objectContaining({
|
||||
settings: expect.objectContaining({
|
||||
catalogs: {
|
||||
default: {
|
||||
foo: '0.1.2',
|
||||
},
|
||||
},
|
||||
})
|
||||
).toStrictEqual({
|
||||
catalogs: {
|
||||
default: {
|
||||
foo: '0.1.2',
|
||||
},
|
||||
}),
|
||||
lastValidatedTimestamp: expect.any(Number),
|
||||
projects: {
|
||||
[path.resolve('packages/a')]: {},
|
||||
[path.resolve('packages/b')]: {},
|
||||
[path.resolve('packages/c')]: {},
|
||||
[path.resolve('packages/d')]: {},
|
||||
},
|
||||
lastValidatedTimestamp,
|
||||
projectRootDirs: [
|
||||
path.resolve('packages/a'),
|
||||
path.resolve('packages/b'),
|
||||
path.resolve('packages/c'),
|
||||
path.resolve('packages/d'),
|
||||
],
|
||||
})
|
||||
pnpmfileExists: false,
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -41,18 +41,28 @@ test('loadWorkspaceState() when cache file exists and is correct', async () => {
|
||||
const cacheFile = getFilePath(workspaceDir)
|
||||
fs.mkdirSync(path.dirname(cacheFile), { recursive: true })
|
||||
const workspaceState: WorkspaceState = {
|
||||
catalogs: {
|
||||
default: {
|
||||
foo: '0.1.2',
|
||||
settings: {
|
||||
autoInstallPeers: true,
|
||||
dedupeDirectDeps: true,
|
||||
excludeLinksFromLockfile: false,
|
||||
preferWorkspacePackages: false,
|
||||
injectWorkspacePackages: false,
|
||||
catalogs: {
|
||||
default: {
|
||||
foo: '0.1.2',
|
||||
},
|
||||
},
|
||||
linkWorkspacePackages: true,
|
||||
},
|
||||
lastValidatedTimestamp,
|
||||
projectRootDirs: [
|
||||
path.resolve('packages/a') as ProjectRootDir,
|
||||
path.resolve('packages/b') as ProjectRootDir,
|
||||
path.resolve('packages/c') as ProjectRootDir,
|
||||
path.resolve('packages/d') as ProjectRootDir,
|
||||
],
|
||||
projects: {
|
||||
[path.resolve('packages/a') as ProjectRootDir]: {},
|
||||
[path.resolve('packages/b') as ProjectRootDir]: {},
|
||||
[path.resolve('packages/c') as ProjectRootDir]: {},
|
||||
[path.resolve('packages/d') as ProjectRootDir]: {},
|
||||
},
|
||||
pnpmfileExists: false,
|
||||
filteredInstall: false,
|
||||
}
|
||||
fs.writeFileSync(cacheFile, JSON.stringify(workspaceState))
|
||||
expect(loadWorkspaceState(workspaceDir)).toStrictEqual(workspaceState)
|
||||
|
||||
@@ -4,8 +4,6 @@ import { preparePackages } from '@pnpm/prepare'
|
||||
import { type ProjectRootDir } from '@pnpm/types'
|
||||
import { loadWorkspaceState, updateWorkspaceState } from '../src/index'
|
||||
|
||||
const lastValidatedTimestamp = Date.now()
|
||||
|
||||
const originalLoggerDebug = logger.debug
|
||||
afterEach(() => {
|
||||
logger.debug = originalLoggerDebug
|
||||
@@ -23,46 +21,65 @@ test('updateWorkspaceState()', async () => {
|
||||
|
||||
logger.debug = jest.fn(originalLoggerDebug)
|
||||
await updateWorkspaceState({
|
||||
lastValidatedTimestamp,
|
||||
pnpmfileExists: true,
|
||||
workspaceDir,
|
||||
catalogs: undefined,
|
||||
allProjects: [],
|
||||
filteredInstall: false,
|
||||
settings: {
|
||||
autoInstallPeers: true,
|
||||
dedupeDirectDeps: true,
|
||||
excludeLinksFromLockfile: false,
|
||||
preferWorkspacePackages: false,
|
||||
linkWorkspacePackages: false,
|
||||
injectWorkspacePackages: false,
|
||||
},
|
||||
})
|
||||
expect((logger.debug as jest.Mock).mock.calls).toStrictEqual([[{ msg: 'updating workspace state' }]])
|
||||
expect(loadWorkspaceState(workspaceDir)).toStrictEqual({
|
||||
lastValidatedTimestamp,
|
||||
projectRootDirs: [],
|
||||
})
|
||||
expect(loadWorkspaceState(workspaceDir)).toStrictEqual(expect.objectContaining({
|
||||
lastValidatedTimestamp: expect.any(Number),
|
||||
projects: {},
|
||||
}))
|
||||
|
||||
logger.debug = jest.fn(originalLoggerDebug)
|
||||
await updateWorkspaceState({
|
||||
lastValidatedTimestamp,
|
||||
pnpmfileExists: false,
|
||||
workspaceDir,
|
||||
catalogs: {
|
||||
default: {
|
||||
foo: '0.1.2',
|
||||
settings: {
|
||||
autoInstallPeers: true,
|
||||
dedupeDirectDeps: true,
|
||||
excludeLinksFromLockfile: false,
|
||||
preferWorkspacePackages: false,
|
||||
injectWorkspacePackages: false,
|
||||
catalogs: {
|
||||
default: {
|
||||
foo: '0.1.2',
|
||||
},
|
||||
},
|
||||
linkWorkspacePackages: true,
|
||||
},
|
||||
allProjects: [
|
||||
{ rootDir: path.resolve('packages/c') as ProjectRootDir },
|
||||
{ rootDir: path.resolve('packages/a') as ProjectRootDir },
|
||||
{ rootDir: path.resolve('packages/d') as ProjectRootDir },
|
||||
{ rootDir: path.resolve('packages/b') as ProjectRootDir },
|
||||
{ rootDir: path.resolve('packages/c') as ProjectRootDir, manifest: {} },
|
||||
{ rootDir: path.resolve('packages/a') as ProjectRootDir, manifest: {} },
|
||||
{ rootDir: path.resolve('packages/d') as ProjectRootDir, manifest: {} },
|
||||
{ rootDir: path.resolve('packages/b') as ProjectRootDir, manifest: {} },
|
||||
],
|
||||
filteredInstall: false,
|
||||
})
|
||||
expect((logger.debug as jest.Mock).mock.calls).toStrictEqual([[{ msg: 'updating workspace state' }]])
|
||||
expect(loadWorkspaceState(workspaceDir)).toStrictEqual({
|
||||
catalogs: {
|
||||
default: {
|
||||
foo: '0.1.2',
|
||||
expect(loadWorkspaceState(workspaceDir)).toStrictEqual(expect.objectContaining({
|
||||
settings: expect.objectContaining({
|
||||
catalogs: {
|
||||
default: {
|
||||
foo: '0.1.2',
|
||||
},
|
||||
},
|
||||
}),
|
||||
lastValidatedTimestamp: expect.any(Number),
|
||||
projects: {
|
||||
[path.resolve('packages/a')]: {},
|
||||
[path.resolve('packages/b')]: {},
|
||||
[path.resolve('packages/c')]: {},
|
||||
[path.resolve('packages/d')]: {},
|
||||
},
|
||||
lastValidatedTimestamp,
|
||||
projectRootDirs: [
|
||||
path.resolve('packages/a'),
|
||||
path.resolve('packages/b'),
|
||||
path.resolve('packages/c'),
|
||||
path.resolve('packages/d'),
|
||||
],
|
||||
})
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
{
|
||||
"path": "../../catalogs/types"
|
||||
},
|
||||
{
|
||||
"path": "../../config/config"
|
||||
},
|
||||
{
|
||||
"path": "../../packages/logger"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user