mirror of
https://github.com/pnpm/pnpm.git
synced 2026-05-24 16:46:06 -04:00
refactor(config): split Config interface into settings + runtime context (#11197)
* refactor(config): split Config interface into settings + runtime context
Create ConfigContext for runtime state (hooks, finders, workspace graph,
CLI metadata) and keep Config for user-facing settings only. Functions
use Pick<Config, ...> & Pick<ConfigContext, ...> to express which fields
they need from each interface.
getConfig() now returns { config, context, warnings }. The CLI wrapper
returns { config, context } and spreads both when calling command
handlers (to be refactored to separate params in follow-up PRs).
Closes #11195
* fix: address review feedback
- Initialize cliOptions on pnpmConfig so context.cliOptions is never undefined
- Move rootProjectManifestDir assignment before ignoreLocalSettings guard
- Add allProjectsGraph to INTERNAL_CONFIG_KEYS
* refactor: remove INTERNAL_CONFIG_KEYS from configToRecord
configToRecord now accepts Config and ConfigContext separately, so
context fields are never in scope. Only auth-related Config fields
(authConfig, authInfos, sslConfigs) need filtering.
* refactor: eliminate INTERNAL_CONFIG_KEYS from configToRecord
configToRecord now receives the clean Config object and explicitlySetKeys
separately (via opts.config and opts.context), so context fields are
never in scope. main.ts passes the original split objects alongside
the spread for command handlers that need them.
* fix: spelling
* fix: import sorting
* fix: --config.xxx nconf overrides conflicting with --config CLI flag
When `pnpm add` registers `config: Boolean`, nopt captures
--config.xxx=yyy as the --config flag value instead of treating it
as a nconf-style config override. Fix by extracting --config.xxx args
before nopt parsing and re-parsing them separately.
Also rename the split config/context properties on the command opts
object to _config/_context to avoid clashing with the --config CLI option.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import { DEFAULT_REGISTRIES, normalizeRegistries } from '@pnpm/config.normalize-registries'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import type { LogBase } from '@pnpm/logger'
|
||||
import type { StoreController } from '@pnpm/store.controller-types'
|
||||
import type { Registries } from '@pnpm/types'
|
||||
@@ -55,7 +55,7 @@ export type StrictBuildOptions = {
|
||||
} & Pick<Config, 'sslConfigs' | 'allowBuilds'>
|
||||
|
||||
export type BuildOptions = Partial<StrictBuildOptions> &
|
||||
Pick<StrictBuildOptions, 'storeDir' | 'storeController'> & Pick<Config, 'rootProjectManifest' | 'rootProjectManifestDir'>
|
||||
Pick<StrictBuildOptions, 'storeDir' | 'storeController'> & Pick<ConfigContext, 'rootProjectManifest' | 'rootProjectManifestDir'>
|
||||
|
||||
const defaults = async (opts: BuildOptions): Promise<StrictBuildOptions> => {
|
||||
const packageManager = opts.packageManager ??
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
} from '@pnpm/building.after-install'
|
||||
import { FILTERING, UNIVERSAL_OPTIONS } from '@pnpm/cli.common-cli-options-help'
|
||||
import { docsUrl, readProjectManifestOnly } from '@pnpm/cli.utils'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import type { LogBase } from '@pnpm/logger'
|
||||
import {
|
||||
createStoreController,
|
||||
@@ -75,23 +75,24 @@ For options that may be used with `-r`, see "pnpm help recursive"',
|
||||
}
|
||||
|
||||
export type RebuildCommandOpts = Pick<Config,
|
||||
| 'allProjects'
|
||||
| 'dir'
|
||||
| 'engineStrict'
|
||||
| 'hooks'
|
||||
| 'lockfileDir'
|
||||
| 'nodeLinker'
|
||||
| 'rawLocalConfig'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'registries'
|
||||
| 'scriptShell'
|
||||
| 'selectedProjectsGraph'
|
||||
| 'sideEffectsCache'
|
||||
| 'sideEffectsCacheReadonly'
|
||||
| 'scriptsPrependNodePath'
|
||||
| 'shellEmulator'
|
||||
| 'workspaceDir'
|
||||
> & Pick<ConfigContext,
|
||||
| 'allProjects'
|
||||
| 'hooks'
|
||||
| 'rawLocalConfig'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'selectedProjectsGraph'
|
||||
> &
|
||||
CreateStoreControllerOptions &
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from '@pnpm/cli.utils'
|
||||
import {
|
||||
type Config,
|
||||
type ConfigContext,
|
||||
createProjectConfigRecord,
|
||||
getWorkspaceConcurrency,
|
||||
} from '@pnpm/config.reader'
|
||||
@@ -19,18 +20,19 @@ import pLimit from 'p-limit'
|
||||
|
||||
type RecursiveRebuildOpts = CreateStoreControllerOptions & Pick<Config,
|
||||
| 'hoistPattern'
|
||||
| 'hooks'
|
||||
| 'ignorePnpmfile'
|
||||
| 'ignoreScripts'
|
||||
| 'lockfileDir'
|
||||
| 'lockfileOnly'
|
||||
| 'nodeLinker'
|
||||
| 'packageConfigs'
|
||||
| 'rawLocalConfig'
|
||||
| 'registries'
|
||||
| 'sharedWorkspaceLockfile'
|
||||
> & Pick<ConfigContext,
|
||||
| 'hooks'
|
||||
| 'rawLocalConfig'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'sharedWorkspaceLockfile'
|
||||
> & {
|
||||
pending?: boolean
|
||||
} & Partial<Pick<Config, 'bail' | 'sort' | 'workspaceConcurrency'>>
|
||||
@@ -40,7 +42,7 @@ export async function recursiveRebuild (
|
||||
params: string[],
|
||||
opts: RecursiveRebuildOpts & {
|
||||
ignoredPackages?: Set<string>
|
||||
} & Required<Pick<Config, 'selectedProjectsGraph' | 'workspaceDir'>>
|
||||
} & Required<Pick<ConfigContext, 'selectedProjectsGraph'>> & Required<Pick<Config, 'workspaceDir'>>
|
||||
): Promise<void> {
|
||||
if (allProjects.length === 0) {
|
||||
// It might make sense to throw an exception in this case
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { CommandHandlerMap } from '@pnpm/cli.command'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import { writeSettings } from '@pnpm/config.writer'
|
||||
import { parse } from '@pnpm/deps.path'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
@@ -14,7 +14,7 @@ import { renderHelp } from 'render-help'
|
||||
import { rebuild, type RebuildCommandOpts } from '../build/index.js'
|
||||
import { getAutomaticallyIgnoredBuilds } from './getAutomaticallyIgnoredBuilds.js'
|
||||
|
||||
export type ApproveBuildsCommandOpts = Pick<Config, 'modulesDir' | 'dir' | 'rootProjectManifest' | 'rootProjectManifestDir' | 'allowBuilds' | 'enableGlobalVirtualStore'> & { all?: boolean, global?: boolean }
|
||||
export type ApproveBuildsCommandOpts = Pick<Config, 'modulesDir' | 'dir' | 'allowBuilds' | 'enableGlobalVirtualStore'> & Pick<ConfigContext, 'rootProjectManifest' | 'rootProjectManifestDir'> & { all?: boolean, global?: boolean }
|
||||
|
||||
export const commandNames = ['approve-builds']
|
||||
|
||||
|
||||
@@ -41,11 +41,13 @@ async function getApproveBuildsConfig () {
|
||||
dir: process.cwd(),
|
||||
registry: `http://localhost:${REGISTRY_MOCK_PORT}`,
|
||||
}
|
||||
const { config, context } = await getConfig({
|
||||
cliOptions,
|
||||
packageManager: { name: 'pnpm', version: '' },
|
||||
})
|
||||
return {
|
||||
...omit(['reporter'], (await getConfig({
|
||||
cliOptions,
|
||||
packageManager: { name: 'pnpm', version: '' },
|
||||
})).config),
|
||||
...omit(['reporter'], config),
|
||||
...context,
|
||||
storeDir: path.resolve('store'),
|
||||
cacheDir: path.resolve('cache'),
|
||||
pnpmfile: [], // this is only needed because the pnpmfile returned by getConfig is string | string[]
|
||||
|
||||
4
cache/commands/src/cache.cmd.ts
vendored
4
cache/commands/src/cache.cmd.ts
vendored
@@ -7,7 +7,7 @@ import {
|
||||
cacheView,
|
||||
} from '@pnpm/cache.api'
|
||||
import { docsUrl } from '@pnpm/cli.utils'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { ABBREVIATED_META_DIR, FULL_FILTERED_META_DIR } from '@pnpm/constants'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { getStorePath } from '@pnpm/store.path'
|
||||
@@ -59,7 +59,7 @@ export function help (): string {
|
||||
})
|
||||
}
|
||||
|
||||
export type CacheCommandOptions = Pick<Config, 'cacheDir' | 'storeDir' | 'pnpmHomeDir' | 'cliOptions' | 'resolutionMode' | 'registrySupportsTimeField'>
|
||||
export type CacheCommandOptions = Pick<Config, 'cacheDir' | 'storeDir' | 'pnpmHomeDir' | 'resolutionMode' | 'registrySupportsTimeField'> & Pick<ConfigContext, 'cliOptions'>
|
||||
|
||||
export async function handler (opts: CacheCommandOptions, params: string[]): Promise<string | undefined> {
|
||||
const cacheType = (opts.resolutionMode === 'time-based' && !opts.registrySupportsTimeField)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import type * as logs from '@pnpm/core-loggers'
|
||||
import type { LogLevel, StreamParser } from '@pnpm/logger'
|
||||
import createDiffer from 'ansi-diff'
|
||||
@@ -33,7 +33,7 @@ export function initDefaultReporter (
|
||||
}
|
||||
context: {
|
||||
argv: string[]
|
||||
config?: Config
|
||||
config?: Config & ConfigContext
|
||||
env?: NodeJS.ProcessEnv
|
||||
process?: NodeJS.Process
|
||||
}
|
||||
@@ -111,7 +111,7 @@ export function toOutput$ (
|
||||
}
|
||||
context: {
|
||||
argv: string[]
|
||||
config?: Config
|
||||
config?: Config & ConfigContext
|
||||
env?: NodeJS.ProcessEnv
|
||||
process?: NodeJS.Process
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import type * as logs from '@pnpm/core-loggers'
|
||||
import type { LogLevel } from '@pnpm/logger'
|
||||
import type * as Rx from 'rxjs'
|
||||
@@ -66,7 +66,7 @@ export function reporterForClient (
|
||||
process: NodeJS.Process
|
||||
isRecursive: boolean
|
||||
logLevel?: LogLevel
|
||||
pnpmConfig?: Config
|
||||
pnpmConfig?: Config & ConfigContext
|
||||
streamLifecycleOutput?: boolean
|
||||
aggregateOutput?: boolean
|
||||
throttleProgress?: number
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import type { IgnoredScriptsLog } from '@pnpm/core-loggers'
|
||||
import { lexCompare } from '@pnpm/util.lex-comparator'
|
||||
import boxen from 'boxen'
|
||||
@@ -10,7 +10,7 @@ export function reportIgnoredBuilds (
|
||||
ignoredScripts: Rx.Observable<IgnoredScriptsLog>
|
||||
},
|
||||
opts: {
|
||||
pnpmConfig?: Config
|
||||
pnpmConfig?: Config & ConfigContext
|
||||
// This is used by Bit CLI
|
||||
approveBuildsInstructionText?: string
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import { toOutput$ } from '@pnpm/cli.default-reporter'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import {
|
||||
deprecationLogger,
|
||||
hookLogger,
|
||||
@@ -43,7 +43,7 @@ test('prints summary (of current package only)', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: prefix } as Config,
|
||||
config: { dir: prefix } as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -235,7 +235,7 @@ test('prints summary without the filtered out entries', async () => {
|
||||
argv: ['install'],
|
||||
config: {
|
||||
dir: prefix,
|
||||
} as Config,
|
||||
} as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
filterPkgsDiff: (diff) => diff.name !== 'bar',
|
||||
@@ -304,7 +304,7 @@ test('does not print deprecation message when log level is set to error', async
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: prefix } as Config,
|
||||
config: { dir: prefix } as Config & ConfigContext,
|
||||
},
|
||||
reportingOptions: {
|
||||
logLevel: 'error',
|
||||
@@ -362,7 +362,7 @@ test('prints summary for global installation', async () => {
|
||||
config: {
|
||||
dir: prefix,
|
||||
global: true,
|
||||
} as Config,
|
||||
} as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -419,7 +419,7 @@ test('prints added peer dependency', async () => {
|
||||
argv: ['install'],
|
||||
config: {
|
||||
dir: prefix,
|
||||
} as Config,
|
||||
} as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -460,7 +460,7 @@ test('prints summary correctly when the same package is specified both in option
|
||||
argv: ['install'],
|
||||
config: {
|
||||
dir: prefix,
|
||||
} as Config,
|
||||
} as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -522,7 +522,7 @@ test('in the installation summary report which dependency types are skipped', as
|
||||
production: true,
|
||||
dev: false,
|
||||
optional: false,
|
||||
} as Config,
|
||||
} as Config & ConfigContext,
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
},
|
||||
@@ -583,7 +583,7 @@ ${h1('devDependencies:')} skipped
|
||||
|
||||
test('prints summary when some packages fail', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: { argv: ['run'], config: { recursive: true } as Config },
|
||||
context: { argv: ['run'], config: { recursive: true } as Config & ConfigContext },
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
|
||||
@@ -822,7 +822,7 @@ test('prints added/removed stats and warnings during recursive installation', as
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: rootPrefix, recursive: true } as Config,
|
||||
config: { dir: rootPrefix, recursive: true } as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -881,7 +881,7 @@ test('recursive installation: prints only the added stats if nothing was removed
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['recursive'],
|
||||
config: { dir: '/home/jane/repo' } as Config,
|
||||
config: { dir: '/home/jane/repo' } as Config & ConfigContext,
|
||||
},
|
||||
reportingOptions: { outputMaxWidth: 60 },
|
||||
streamParser: createStreamParser(),
|
||||
@@ -900,7 +900,7 @@ test('recursive installation: prints only the removed stats if nothing was added
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['recursive'],
|
||||
config: { dir: '/home/jane/repo' } as Config,
|
||||
config: { dir: '/home/jane/repo' } as Config & ConfigContext,
|
||||
},
|
||||
reportingOptions: { outputMaxWidth: 60 },
|
||||
streamParser: createStreamParser(),
|
||||
@@ -919,7 +919,7 @@ test('recursive installation: prints at least one remove sign when removed !== 0
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['recursive'],
|
||||
config: { dir: '/home/jane/repo' } as Config,
|
||||
config: { dir: '/home/jane/repo' } as Config & ConfigContext,
|
||||
},
|
||||
reportingOptions: { outputMaxWidth: 62 },
|
||||
streamParser: createStreamParser(),
|
||||
@@ -938,7 +938,7 @@ test('recursive installation: prints at least one add sign when added !== 0', as
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['recursive'],
|
||||
config: { dir: '/home/jane/repo' } as Config,
|
||||
config: { dir: '/home/jane/repo' } as Config & ConfigContext,
|
||||
},
|
||||
reportingOptions: { outputMaxWidth: 62 },
|
||||
streamParser: createStreamParser(),
|
||||
@@ -957,7 +957,7 @@ test('recursive uninstall: prints removed packages number', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['remove'],
|
||||
config: { dir: '/home/jane/repo', recursive: true } as Config,
|
||||
config: { dir: '/home/jane/repo', recursive: true } as Config & ConfigContext,
|
||||
},
|
||||
reportingOptions: { outputMaxWidth: 62 },
|
||||
streamParser: createStreamParser(),
|
||||
@@ -975,7 +975,7 @@ test('install: print hook message', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: '/home/jane/repo' } as Config,
|
||||
config: { dir: '/home/jane/repo' } as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -997,7 +997,7 @@ test('recursive: print hook message', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['recursive'],
|
||||
config: { dir: '/home/jane/repo' } as Config,
|
||||
config: { dir: '/home/jane/repo' } as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -1020,7 +1020,7 @@ test('prints skipped optional dependency info message', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: prefix } as Config,
|
||||
config: { dir: prefix } as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -1049,7 +1049,7 @@ test('logLevel=default', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: prefix } as Config,
|
||||
config: { dir: prefix } as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -1072,7 +1072,7 @@ test('logLevel=warn', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: prefix } as Config,
|
||||
config: { dir: prefix } as Config & ConfigContext,
|
||||
},
|
||||
reportingOptions: {
|
||||
logLevel: 'warn',
|
||||
@@ -1097,7 +1097,7 @@ test('logLevel=error', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: prefix } as Config,
|
||||
config: { dir: prefix } as Config & ConfigContext,
|
||||
},
|
||||
reportingOptions: {
|
||||
logLevel: 'error',
|
||||
@@ -1121,7 +1121,7 @@ test('warnings are collapsed', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: prefix } as Config,
|
||||
config: { dir: prefix } as Config & ConfigContext,
|
||||
},
|
||||
reportingOptions: {
|
||||
logLevel: 'warn',
|
||||
@@ -1153,7 +1153,7 @@ test('warnings are not collapsed when append-only is true', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: prefix } as Config,
|
||||
config: { dir: prefix } as Config & ConfigContext,
|
||||
},
|
||||
reportingOptions: {
|
||||
appendOnly: true,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { toOutput$ } from '@pnpm/cli.default-reporter'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import {
|
||||
deprecationLogger,
|
||||
stageLogger,
|
||||
@@ -17,7 +17,7 @@ test('prints summary of deprecated subdependencies', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: prefix } as Config,
|
||||
config: { dir: prefix } as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { toOutput$ } from '@pnpm/cli.default-reporter'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import {
|
||||
fetchingProgressLogger,
|
||||
progressLogger,
|
||||
@@ -25,7 +25,7 @@ test('prints progress beginning', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: '/src/project' } as Config,
|
||||
config: { dir: '/src/project' } as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -50,7 +50,7 @@ test('prints progress without added packages stats', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: '/src/project' } as Config,
|
||||
config: { dir: '/src/project' } as Config & ConfigContext,
|
||||
},
|
||||
reportingOptions: {
|
||||
hideAddedPkgsProgress: true,
|
||||
@@ -78,7 +78,7 @@ test('prints all progress stats', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: '/src/project' } as Config,
|
||||
config: { dir: '/src/project' } as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -119,7 +119,7 @@ test('prints progress beginning of node_modules from not cwd', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: '/src/projects' } as Config,
|
||||
config: { dir: '/src/projects' } as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -144,7 +144,7 @@ test('prints progress beginning of node_modules from not cwd, when progress pref
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: '/src/projects' } as Config,
|
||||
config: { dir: '/src/projects' } as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
reportingOptions: {
|
||||
@@ -172,7 +172,7 @@ test('prints progress beginning when appendOnly is true', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: '/src/project' } as Config,
|
||||
config: { dir: '/src/project' } as Config & ConfigContext,
|
||||
},
|
||||
reportingOptions: {
|
||||
appendOnly: true,
|
||||
@@ -203,7 +203,7 @@ test('prints progress beginning during recursive install', async () => {
|
||||
config: {
|
||||
dir: '/src/project',
|
||||
recursive: true,
|
||||
} as Config,
|
||||
} as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -230,7 +230,7 @@ test('prints progress on first download', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: '/src/project' } as Config,
|
||||
config: { dir: '/src/project' } as Config & ConfigContext,
|
||||
},
|
||||
reportingOptions: { throttleProgress: 0 },
|
||||
streamParser: createStreamParser(),
|
||||
@@ -264,7 +264,7 @@ test('moves fixed line to the end', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: prefix } as Config,
|
||||
config: { dir: prefix } as Config & ConfigContext,
|
||||
},
|
||||
reportingOptions: { throttleProgress: 0 },
|
||||
streamParser: createStreamParser(),
|
||||
@@ -326,7 +326,7 @@ test('prints progress of big files download', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { dir: '/src/project' } as Config,
|
||||
config: { dir: '/src/project' } as Config & ConfigContext,
|
||||
},
|
||||
reportingOptions: { throttleProgress: 0 },
|
||||
streamParser: createStreamParser(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { setTimeout } from 'node:timers/promises'
|
||||
|
||||
import { toOutput$ } from '@pnpm/cli.default-reporter'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import { scopeLogger } from '@pnpm/core-loggers'
|
||||
import { createStreamParser } from '@pnpm/logger'
|
||||
import { firstValueFrom } from 'rxjs'
|
||||
@@ -33,7 +33,7 @@ test('prints scope of recursive install in a workspace when not all packages are
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { recursive: true } as Config,
|
||||
config: { recursive: true } as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -54,7 +54,7 @@ test('prints scope of recursive install in a workspace when all packages are sel
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { recursive: true } as Config,
|
||||
config: { recursive: true } as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -75,7 +75,7 @@ test('prints scope of recursive install not in a workspace when not all packages
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { recursive: true } as Config,
|
||||
config: { recursive: true } as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
@@ -95,7 +95,7 @@ test('prints scope of recursive install not in a workspace when all packages are
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { recursive: true } as Config,
|
||||
config: { recursive: true } as Config & ConfigContext,
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ import { setTimeout } from 'node:timers/promises'
|
||||
import { stripVTControlCharacters as stripAnsi } from 'node:util'
|
||||
|
||||
import { toOutput$ } from '@pnpm/cli.default-reporter'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import { updateCheckLogger } from '@pnpm/core-loggers'
|
||||
import { createStreamParser } from '@pnpm/logger'
|
||||
import { firstValueFrom } from 'rxjs'
|
||||
@@ -35,7 +35,7 @@ test('print update notification if the latest version is greater than the curren
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { recursive: true } as Config,
|
||||
config: { recursive: true } as Config & ConfigContext,
|
||||
env: {},
|
||||
},
|
||||
streamParser: createStreamParser(),
|
||||
@@ -56,7 +56,7 @@ test('print update notification for Corepack if the latest version is greater th
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { recursive: true } as Config,
|
||||
config: { recursive: true } as Config & ConfigContext,
|
||||
env: {
|
||||
COREPACK_ROOT: '/usr/bin/corepack',
|
||||
},
|
||||
@@ -79,7 +79,7 @@ test('print update notification that suggests to use the standalone scripts for
|
||||
const output$ = toOutput$({
|
||||
context: {
|
||||
argv: ['install'],
|
||||
config: { recursive: true } as Config,
|
||||
config: { recursive: true } as Config & ConfigContext,
|
||||
env: {
|
||||
PNPM_HOME: '/home/user/.local/share/pnpm',
|
||||
},
|
||||
|
||||
@@ -129,6 +129,22 @@ export async function parseCliArgs (
|
||||
return [noptExploratoryResults.argv.remain[indexOfRunScriptName]]
|
||||
}
|
||||
|
||||
// When "config" is a registered CLI option (e.g. `pnpm add --config`),
|
||||
// nopt captures --config.xxx=yyy as the "config" flag value instead of
|
||||
// treating it as the nconf-style config override syntax. Work around this
|
||||
// by rewriting --config.xxx=yyy to a placeholder before nopt, then restoring.
|
||||
const hasConfigOption = 'config' in types
|
||||
const configDotArgs: string[] = []
|
||||
const filteredArgv = hasConfigOption
|
||||
? inputArgv.map(arg => {
|
||||
if (arg.startsWith('--config.')) {
|
||||
configDotArgs.push(arg)
|
||||
return undefined
|
||||
}
|
||||
return arg
|
||||
}).filter((arg): arg is string => arg !== undefined)
|
||||
: inputArgv
|
||||
|
||||
const { argv, ...options } = nopt(
|
||||
{
|
||||
recursive: Boolean,
|
||||
@@ -138,10 +154,17 @@ export async function parseCliArgs (
|
||||
...opts.universalShorthands,
|
||||
...opts.shorthandsByCommandName[commandName],
|
||||
},
|
||||
inputArgv,
|
||||
filteredArgv,
|
||||
0,
|
||||
{ escapeArgs: getEscapeArgsWithSpecialCases() }
|
||||
)
|
||||
|
||||
// Re-parse extracted --config.xxx args through nopt so they get proper
|
||||
// type coercion (e.g. "false" → false for Boolean settings).
|
||||
if (configDotArgs.length > 0) {
|
||||
const { argv: _, ...configOptions } = nopt({}, {}, configDotArgs, 0)
|
||||
Object.assign(options, configOptions)
|
||||
}
|
||||
const workspaceDir = await getWorkspaceDir(options)
|
||||
|
||||
// For the run command, it's not clear whether --help should be passed to the
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
|
||||
export type ConfigCommandOptions = Pick<Config,
|
||||
| 'configDir'
|
||||
| 'cliOptions'
|
||||
| 'dir'
|
||||
| 'global'
|
||||
| 'authConfig'
|
||||
| 'workspaceDir'
|
||||
> & Pick<ConfigContext,
|
||||
| 'cliOptions'
|
||||
> & {
|
||||
_config: Config
|
||||
_context: ConfigContext
|
||||
json?: boolean
|
||||
location?: 'global' | 'project'
|
||||
// The config commands receive the full Config object at runtime
|
||||
// and read arbitrary typed properties for display.
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type Config, isIniConfigKey, types } from '@pnpm/config.reader'
|
||||
import { isIniConfigKey, types } from '@pnpm/config.reader'
|
||||
import { getObjectValueByPropertyPath } from '@pnpm/object.property-path'
|
||||
import { isCamelCase } from '@pnpm/text.naming-cases'
|
||||
import camelcase from 'camelcase'
|
||||
@@ -28,9 +28,9 @@ function lookupConfig (opts: ConfigCommandOptions, key: string, isScopedKey: boo
|
||||
// then fall back to authConfig (for keys like registry set in .npmrc)
|
||||
if (Object.hasOwn(types, kebabKey)) {
|
||||
const camelKey = camelcase(kebabKey, { locale: 'en-US' })
|
||||
const explicit = (opts as unknown as Config).explicitlySetKeys
|
||||
const explicit = opts._context.explicitlySetKeys
|
||||
if (!explicit || explicit.has(camelKey)) {
|
||||
return { value: (opts as unknown as Record<string, unknown>)[camelKey] }
|
||||
return { value: (opts._config as unknown as Record<string, unknown>)[camelKey] }
|
||||
}
|
||||
// Fall back to authConfig for INI keys (registry, ca, etc.)
|
||||
if (kebabKey in opts.authConfig) {
|
||||
@@ -45,7 +45,7 @@ function lookupConfig (opts: ConfigCommandOptions, key: string, isScopedKey: boo
|
||||
// For keys not in types (e.g., package-extensions), look up via configToRecord
|
||||
// which excludes internal/sensitive fields.
|
||||
const camelKey = camelcase(key, { locale: 'en-US' })
|
||||
const record = configToRecord(opts as unknown as Config)
|
||||
const record = configToRecord(opts._config, opts._context.explicitlySetKeys)
|
||||
if (Object.hasOwn(record, camelKey)) {
|
||||
return { value: record[camelKey] }
|
||||
}
|
||||
@@ -55,9 +55,9 @@ function lookupConfig (opts: ConfigCommandOptions, key: string, isScopedKey: boo
|
||||
function lookupByPropertyPath (opts: ConfigCommandOptions, propertyPath: string): Found<unknown> {
|
||||
const parsedPropertyPath = Array.from(parseConfigPropertyPath(propertyPath))
|
||||
if (parsedPropertyPath.length === 0) {
|
||||
return { value: configToRecord(opts as unknown as Config) }
|
||||
return { value: configToRecord(opts._config, opts._context.explicitlySetKeys) }
|
||||
}
|
||||
const record = configToRecord(opts as unknown as Config)
|
||||
const record = configToRecord(opts._config, opts._context.explicitlySetKeys)
|
||||
return {
|
||||
value: getObjectValueByPropertyPath(record, parsedPropertyPath),
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
|
||||
import type { ConfigCommandOptions } from './ConfigCommandOptions.js'
|
||||
import { configToRecord } from './configToRecord.js'
|
||||
|
||||
export async function configList (opts: ConfigCommandOptions): Promise<string> {
|
||||
return JSON.stringify(configToRecord(opts as unknown as Config), undefined, 2)
|
||||
return JSON.stringify(configToRecord(opts._config, opts._context.explicitlySetKeys), undefined, 2)
|
||||
}
|
||||
|
||||
@@ -4,26 +4,25 @@ import camelcase from 'camelcase'
|
||||
|
||||
import { censorProtectedSettings } from './protectedSettings.js'
|
||||
|
||||
const INTERNAL_CONFIG_KEYS = new Set([
|
||||
'authConfig', 'authInfos', 'rawLocalConfig', 'cliOptions',
|
||||
'explicitlySetKeys',
|
||||
'hooks', 'finders', 'allProjects', 'selectedProjectsGraph',
|
||||
'packageManager', 'wantedPackageManager', 'rootProjectManifest',
|
||||
'storeController', 'rootProjectManifestDir', 'sslConfigs',
|
||||
// Auth-related Config fields that are internal objects, not user settings.
|
||||
const NON_SETTING_CONFIG_KEYS = new Set([
|
||||
'authConfig', 'authInfos', 'sslConfigs',
|
||||
])
|
||||
|
||||
/**
|
||||
* Convert a Config object to a camelCase record for display.
|
||||
* Only includes explicitly set values (from CLI, env vars, or workspace yaml),
|
||||
* not default values. Auth/registry keys from authConfig are always included.
|
||||
*
|
||||
* Accepts a clean Config object (without ConfigContext fields mixed in),
|
||||
* so no INTERNAL_CONFIG_KEYS exclusion list is needed.
|
||||
*/
|
||||
export function configToRecord (config: Config): Record<string, unknown> {
|
||||
export function configToRecord (config: Config, explicitlySetKeys: Set<string>): Record<string, unknown> {
|
||||
const result: Record<string, unknown> = {}
|
||||
const explicit = config.explicitlySetKeys
|
||||
// Add typed settings (only explicitly set ones if tracking is available)
|
||||
for (const kebabKey of Object.keys(types)) {
|
||||
const camelKey = camelcase(kebabKey, { locale: 'en-US' })
|
||||
if (explicit && !explicit.has(camelKey)) continue
|
||||
if (!explicitlySetKeys.has(camelKey)) continue
|
||||
const value = (config as unknown as Record<string, unknown>)[camelKey]
|
||||
if (value !== undefined) {
|
||||
result[camelKey] = value
|
||||
@@ -31,8 +30,8 @@ export function configToRecord (config: Config): Record<string, unknown> {
|
||||
}
|
||||
// Add non-types config properties (e.g., packageExtensions, overrides)
|
||||
for (const [key, value] of Object.entries(config)) {
|
||||
if (value === undefined || INTERNAL_CONFIG_KEYS.has(key)) continue
|
||||
if (!(key in result) && (!explicit || explicit.has(key))) {
|
||||
if (value === undefined || NON_SETTING_CONFIG_KEYS.has(key)) continue
|
||||
if (!(key in result) && explicitlySetKeys.has(key)) {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,19 +7,21 @@ import { readIniFileSync } from 'read-ini-file'
|
||||
import { readYamlFileSync } from 'read-yaml-file'
|
||||
import { writeYamlFileSync } from 'write-yaml-file'
|
||||
|
||||
import { createConfigCommandOpts } from './utils/index.js'
|
||||
|
||||
test('config delete on registry key not set', async () => {
|
||||
const tmp = tempDir()
|
||||
const configDir = path.join(tmp, 'global-config')
|
||||
fs.mkdirSync(configDir, { recursive: true })
|
||||
fs.writeFileSync(path.join(configDir, 'auth.ini'), '@my-company:registry=https://registry.my-company.example.com/')
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['delete', 'registry'])
|
||||
}), ['delete', 'registry'])
|
||||
|
||||
expect(readIniFileSync(path.join(configDir, 'auth.ini'))).toEqual({
|
||||
'@my-company:registry': 'https://registry.my-company.example.com/',
|
||||
@@ -32,13 +34,13 @@ test('config delete on registry key set', async () => {
|
||||
fs.mkdirSync(configDir, { recursive: true })
|
||||
fs.writeFileSync(path.join(configDir, 'auth.ini'), 'registry=https://registry.my-company.example.com/')
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['delete', 'registry'])
|
||||
}), ['delete', 'registry'])
|
||||
|
||||
expect(readIniFileSync(path.join(configDir, 'auth.ini'))).toEqual({})
|
||||
})
|
||||
@@ -49,13 +51,13 @@ test('config delete on npm-compatible key not set', async () => {
|
||||
fs.mkdirSync(configDir, { recursive: true })
|
||||
fs.writeFileSync(path.join(configDir, 'auth.ini'), '@my-company:registry=https://registry.my-company.example.com/')
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['delete', 'cafile'])
|
||||
}), ['delete', 'cafile'])
|
||||
|
||||
expect(readIniFileSync(path.join(configDir, 'auth.ini'))).toEqual({
|
||||
'@my-company:registry': 'https://registry.my-company.example.com/',
|
||||
@@ -68,13 +70,13 @@ test('config delete on npm-compatible key set', async () => {
|
||||
fs.mkdirSync(configDir, { recursive: true })
|
||||
fs.writeFileSync(path.join(configDir, 'auth.ini'), 'cafile=some-cafile')
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['delete', 'cafile'])
|
||||
}), ['delete', 'cafile'])
|
||||
|
||||
// NOTE: pnpm currently does not delete empty rc files.
|
||||
// TODO: maybe we should?
|
||||
@@ -89,13 +91,13 @@ test('config delete on pnpm-specific key not set', async () => {
|
||||
cacheDir: '~/cache',
|
||||
})
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['delete', 'store-dir'])
|
||||
}), ['delete', 'store-dir'])
|
||||
|
||||
expect(readYamlFileSync(path.join(configDir, 'config.yaml'))).toStrictEqual({
|
||||
cacheDir: '~/cache',
|
||||
@@ -110,13 +112,13 @@ test('config delete on pnpm-specific key set', async () => {
|
||||
cacheDir: '~/cache',
|
||||
})
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['delete', 'cache-dir'])
|
||||
}), ['delete', 'cache-dir'])
|
||||
|
||||
expect(fs.readdirSync(configDir)).not.toContain('config.yaml')
|
||||
})
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
import { config } from '@pnpm/config.commands'
|
||||
|
||||
import { getOutputString } from './utils/index.js'
|
||||
import { createConfigCommandOpts, getOutputString } from './utils/index.js'
|
||||
|
||||
test('config get', async () => {
|
||||
const getResult = await config.handler({
|
||||
const getResult = await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
authConfig: {},
|
||||
storeDir: '~/store',
|
||||
}, ['get', 'store-dir'])
|
||||
}), ['get', 'store-dir'])
|
||||
|
||||
expect(getOutputString(getResult)).toBe('~/store')
|
||||
})
|
||||
|
||||
test('config get works with camelCase', async () => {
|
||||
const getResult = await config.handler({
|
||||
const getResult = await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
authConfig: {},
|
||||
storeDir: '~/store',
|
||||
}, ['get', 'storeDir'])
|
||||
}), ['get', 'storeDir'])
|
||||
|
||||
expect(getOutputString(getResult)).toBe('~/store')
|
||||
})
|
||||
|
||||
test('config get a boolean should return string format', async () => {
|
||||
const getResult = await config.handler({
|
||||
const getResult = await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
authConfig: {},
|
||||
updateNotifier: true,
|
||||
}, ['get', 'update-notifier'])
|
||||
}), ['get', 'update-notifier'])
|
||||
|
||||
expect(getOutputString(getResult)).toBe('true')
|
||||
})
|
||||
|
||||
test('config get on array should return a comma-separated list', async () => {
|
||||
const getResult = await config.handler({
|
||||
const getResult = await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
@@ -52,7 +52,7 @@ test('config get on array should return a comma-separated list', async () => {
|
||||
'*eslint*',
|
||||
'*prettier*',
|
||||
],
|
||||
}, ['get', 'public-hoist-pattern'])
|
||||
}), ['get', 'public-hoist-pattern'])
|
||||
|
||||
expect(JSON.parse(getOutputString(getResult))).toStrictEqual([
|
||||
'*eslint*',
|
||||
@@ -61,7 +61,7 @@ test('config get on array should return a comma-separated list', async () => {
|
||||
})
|
||||
|
||||
test('config get on object should return a JSON string', async () => {
|
||||
const getResult = await config.handler({
|
||||
const getResult = await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
@@ -70,7 +70,7 @@ test('config get on object should return a JSON string', async () => {
|
||||
catalog: {
|
||||
react: '^19.0.0',
|
||||
},
|
||||
}, ['get', 'catalog'])
|
||||
}), ['get', 'catalog'])
|
||||
|
||||
expect(JSON.parse(getOutputString(getResult))).toStrictEqual({ react: '^19.0.0' })
|
||||
})
|
||||
@@ -80,20 +80,15 @@ test('config get without key show list all settings', async () => {
|
||||
'store-dir': '~/store',
|
||||
'fetch-retries': '2',
|
||||
}
|
||||
const getOutput = await config.handler({
|
||||
const baseOpts = {
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
authConfig,
|
||||
}, ['get'])
|
||||
}
|
||||
const getOutput = await config.handler(createConfigCommandOpts(baseOpts), ['get'])
|
||||
|
||||
const listOutput = await config.handler({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
authConfig,
|
||||
}, ['list'])
|
||||
const listOutput = await config.handler(createConfigCommandOpts(baseOpts), ['list'])
|
||||
|
||||
expect(getOutput).toStrictEqual(listOutput)
|
||||
})
|
||||
@@ -116,14 +111,14 @@ describe('config get with a property path', () => {
|
||||
trustPolicyExclude: ['foo', 'bar'],
|
||||
packageExtensions,
|
||||
}
|
||||
const baseOpts = {
|
||||
const baseOpts = createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
authConfig: {},
|
||||
...configData,
|
||||
}
|
||||
})
|
||||
|
||||
describe('anything with --json', () => {
|
||||
test('«»', async () => {
|
||||
@@ -211,7 +206,7 @@ describe('config get with a property path', () => {
|
||||
})
|
||||
|
||||
test('config get with scoped registry key (global: false)', async () => {
|
||||
const getResult = await config.handler({
|
||||
const getResult = await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
@@ -219,13 +214,13 @@ test('config get with scoped registry key (global: false)', async () => {
|
||||
authConfig: {
|
||||
'@scope:registry': 'https://custom-registry.example.com/',
|
||||
},
|
||||
}, ['get', '@scope:registry'])
|
||||
}), ['get', '@scope:registry'])
|
||||
|
||||
expect(getOutputString(getResult)).toBe('https://custom-registry.example.com/')
|
||||
})
|
||||
|
||||
test('config get with scoped registry key (global: true)', async () => {
|
||||
const getResult = await config.handler({
|
||||
const getResult = await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
@@ -233,19 +228,19 @@ test('config get with scoped registry key (global: true)', async () => {
|
||||
authConfig: {
|
||||
'@scope:registry': 'https://custom-registry.example.com/',
|
||||
},
|
||||
}, ['get', '@scope:registry'])
|
||||
}), ['get', '@scope:registry'])
|
||||
|
||||
expect(getOutputString(getResult)).toBe('https://custom-registry.example.com/')
|
||||
})
|
||||
|
||||
test('config get with scoped registry key that does not exist', async () => {
|
||||
const getResult = await config.handler({
|
||||
const getResult = await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: false,
|
||||
authConfig: {},
|
||||
}, ['get', '@scope:registry'])
|
||||
}), ['get', '@scope:registry'])
|
||||
|
||||
expect(getOutputString(getResult)).toBe('undefined')
|
||||
})
|
||||
@@ -261,13 +256,13 @@ describe('does not traverse the prototype chain (#10296)', () => {
|
||||
'valueOf',
|
||||
'__proto__',
|
||||
])('%s', async key => {
|
||||
const getResult = await config.handler({
|
||||
const getResult = await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['get', key])
|
||||
}), ['get', key])
|
||||
|
||||
expect(getOutputString(getResult)).toBe('undefined')
|
||||
})
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { config } from '@pnpm/config.commands'
|
||||
|
||||
import { getOutputString } from './utils/index.js'
|
||||
import { createConfigCommandOpts, getOutputString } from './utils/index.js'
|
||||
|
||||
test('config list', async () => {
|
||||
const output = await config.handler({
|
||||
const output = await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
authConfig: {},
|
||||
storeDir: '~/store',
|
||||
fetchRetries: '2',
|
||||
}, ['list'])
|
||||
}), ['list'])
|
||||
|
||||
expect(JSON.parse(getOutputString(output))).toMatchObject({
|
||||
fetchRetries: '2',
|
||||
@@ -19,7 +19,7 @@ test('config list', async () => {
|
||||
})
|
||||
|
||||
test('config list --json', async () => {
|
||||
const output = await config.handler({
|
||||
const output = await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
@@ -27,7 +27,7 @@ test('config list --json', async () => {
|
||||
authConfig: {},
|
||||
storeDir: '~/store',
|
||||
fetchRetries: '2',
|
||||
}, ['list'])
|
||||
}), ['list'])
|
||||
|
||||
const parsed = JSON.parse(output as string)
|
||||
expect(parsed).toMatchObject({
|
||||
@@ -43,14 +43,14 @@ test('config list censors protected settings', async () => {
|
||||
'//my-org.example.com:username': 'my-username-in-my-org',
|
||||
}
|
||||
|
||||
const output = await config.handler({
|
||||
const output = await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
storeDir: '~/store',
|
||||
fetchRetries: '2',
|
||||
authConfig,
|
||||
}, ['list'])
|
||||
}), ['list'])
|
||||
|
||||
expect(JSON.parse(getOutputString(output))).toMatchObject({
|
||||
storeDir: '~/store',
|
||||
@@ -68,7 +68,7 @@ test('config list --json censors protected settings', async () => {
|
||||
'//my-org.example.com:username': 'my-username-in-my-org',
|
||||
}
|
||||
|
||||
const output = await config.handler({
|
||||
const output = await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
json: true,
|
||||
cliOptions: {},
|
||||
@@ -76,7 +76,7 @@ test('config list --json censors protected settings', async () => {
|
||||
storeDir: '~/store',
|
||||
fetchRetries: '2',
|
||||
authConfig,
|
||||
}, ['list'])
|
||||
}), ['list'])
|
||||
|
||||
expect(JSON.parse(getOutputString(output))).toMatchObject({
|
||||
storeDir: '~/store',
|
||||
|
||||
@@ -7,7 +7,7 @@ import { tempDir } from '@pnpm/prepare'
|
||||
import { readIniFileSync } from 'read-ini-file'
|
||||
import { readYamlFileSync } from 'read-yaml-file'
|
||||
|
||||
import { type ConfigFilesData, readConfigFiles, writeConfigFiles } from './utils/index.js'
|
||||
import { type ConfigFilesData, createConfigCommandOpts, readConfigFiles, writeConfigFiles } from './utils/index.js'
|
||||
|
||||
test('config set registry setting using the global option', async () => {
|
||||
const tmp = tempDir()
|
||||
@@ -24,13 +24,13 @@ test('config set registry setting using the global option', async () => {
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['set', 'registry', 'https://npm-registry.example.com/'])
|
||||
}), ['set', 'registry', 'https://npm-registry.example.com/'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -56,13 +56,13 @@ test('config set npm-compatible setting using the global option', async () => {
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['set', 'cafile', 'some-cafile'])
|
||||
}), ['set', 'cafile', 'some-cafile'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -88,13 +88,13 @@ test('config set pnpm-specific key using the global option', async () => {
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['set', 'fetch-retries', '1'])
|
||||
}), ['set', 'fetch-retries', '1'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -120,13 +120,13 @@ test('config set using the location=global option', async () => {
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'global',
|
||||
authConfig: {},
|
||||
}, ['set', 'fetchRetries', '1'])
|
||||
}), ['set', 'fetchRetries', '1'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -152,13 +152,13 @@ test('config set pnpm-specific setting using the location=project option', async
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['set', 'virtual-store-dir', '.pnpm'])
|
||||
}), ['set', 'virtual-store-dir', '.pnpm'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -174,25 +174,25 @@ test('config delete with location=project, when delete the last setting from pnp
|
||||
const configDir = path.join(tmp, 'global-config')
|
||||
fs.mkdirSync(configDir, { recursive: true })
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['set', 'virtual-store-dir', '.pnpm'])
|
||||
}), ['set', 'virtual-store-dir', '.pnpm'])
|
||||
|
||||
expect(readYamlFileSync(path.join(tmp, 'pnpm-workspace.yaml'))).toEqual({
|
||||
virtualStoreDir: '.pnpm',
|
||||
})
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['delete', 'virtual-store-dir'])
|
||||
}), ['delete', 'virtual-store-dir'])
|
||||
|
||||
expect(fs.existsSync(path.join(tmp, 'pnpm-workspace.yaml'))).toBeFalsy()
|
||||
})
|
||||
@@ -213,13 +213,13 @@ test('config set registry setting using the location=project option', async () =
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['set', 'registry', 'https://npm-registry.example.com/'])
|
||||
}), ['set', 'registry', 'https://npm-registry.example.com/'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -246,13 +246,13 @@ test('config set npm-compatible setting using the location=project option', asyn
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['set', 'cafile', 'some-cafile'])
|
||||
}), ['set', 'cafile', 'some-cafile'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -268,13 +268,13 @@ test('config set saves the setting in the right format to pnpm-workspace.yaml',
|
||||
const configDir = path.join(tmp, 'global-config')
|
||||
fs.mkdirSync(configDir, { recursive: true })
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['set', 'fetch-timeout', '1000'])
|
||||
}), ['set', 'fetch-timeout', '1000'])
|
||||
|
||||
expect(readYamlFileSync(path.join(tmp, 'pnpm-workspace.yaml'))).toEqual({
|
||||
fetchTimeout: 1000,
|
||||
@@ -300,14 +300,14 @@ test('config set registry setting in project .npmrc file', async () => {
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: false,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['set', 'registry', 'https://npm-registry.example.com/'])
|
||||
}), ['set', 'registry', 'https://npm-registry.example.com/'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -337,14 +337,14 @@ test('config set npm-compatible setting in project .npmrc file', async () => {
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: false,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['set', 'cafile', 'some-cafile'])
|
||||
}), ['set', 'cafile', 'some-cafile'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -374,14 +374,14 @@ test('config set pnpm-specific setting in project pnpm-workspace.yaml file', asy
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: false,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['set', 'fetch-retries', '1'])
|
||||
}), ['set', 'fetch-retries', '1'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -411,13 +411,13 @@ test('config set key=value', async () => {
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['set', 'fetch-retries=1'])
|
||||
}), ['set', 'fetch-retries=1'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -447,13 +447,13 @@ test('config set key=value, when value contains a "="', async () => {
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['set', 'lockfile-dir=foo=bar'])
|
||||
}), ['set', 'lockfile-dir=foo=bar'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -470,21 +470,21 @@ test('config set or delete throws missing params error', async () => {
|
||||
fs.mkdirSync(configDir, { recursive: true })
|
||||
fs.writeFileSync(path.join(tmp, '.npmrc'), 'store-dir=~/store')
|
||||
|
||||
await expect(config.handler({
|
||||
await expect(config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['set'])).rejects.toThrow(new PnpmError('CONFIG_NO_PARAMS', '`pnpm config set` requires the config key'))
|
||||
}), ['set'])).rejects.toThrow(new PnpmError('CONFIG_NO_PARAMS', '`pnpm config set` requires the config key'))
|
||||
|
||||
await expect(config.handler({
|
||||
await expect(config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['delete'])).rejects.toThrow(new PnpmError('CONFIG_NO_PARAMS', '`pnpm config delete` requires the config key'))
|
||||
}), ['delete'])).rejects.toThrow(new PnpmError('CONFIG_NO_PARAMS', '`pnpm config delete` requires the config key'))
|
||||
})
|
||||
|
||||
test('config set with dot leading key', async () => {
|
||||
@@ -500,13 +500,13 @@ test('config set with dot leading key', async () => {
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['set', '.fetchRetries', '1'])
|
||||
}), ['set', '.fetchRetries', '1'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -530,13 +530,13 @@ test('config set with subscripted key', async () => {
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['set', '["fetch-retries"]', '1'])
|
||||
}), ['set', '["fetch-retries"]', '1'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -553,13 +553,13 @@ test('config set rejects complex property path', async () => {
|
||||
fs.mkdirSync(configDir, { recursive: true })
|
||||
fs.writeFileSync(path.join(configDir, 'auth.ini'), 'store-dir=~/store')
|
||||
|
||||
await expect(config.handler({
|
||||
await expect(config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['set', '.catalog.react', '19'])).rejects.toMatchObject({
|
||||
}), ['set', '.catalog.react', '19'])).rejects.toMatchObject({
|
||||
code: 'ERR_PNPM_CONFIG_SET_DEEP_KEY',
|
||||
})
|
||||
})
|
||||
@@ -569,14 +569,14 @@ test('config set with location=project and json=true', async () => {
|
||||
const configDir = path.join(tmp, 'global-config')
|
||||
fs.mkdirSync(configDir, { recursive: true })
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
json: true,
|
||||
authConfig: {},
|
||||
}, ['set', 'catalog', '{ "react": "19" }'])
|
||||
}), ['set', 'catalog', '{ "react": "19" }'])
|
||||
|
||||
expect(readYamlFileSync(path.join(tmp, 'pnpm-workspace.yaml'))).toStrictEqual({
|
||||
catalog: {
|
||||
@@ -584,14 +584,14 @@ test('config set with location=project and json=true', async () => {
|
||||
},
|
||||
})
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
json: true,
|
||||
authConfig: {},
|
||||
}, ['set', 'packageExtensions', JSON.stringify({
|
||||
}), ['set', 'packageExtensions', JSON.stringify({
|
||||
'@babel/parser': {
|
||||
peerDependencies: {
|
||||
'@babel/types': '*',
|
||||
@@ -636,26 +636,26 @@ test('config set refuses writing workspace-specific settings to the global confi
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await expect(config.handler({
|
||||
await expect(config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'global',
|
||||
json: true,
|
||||
authConfig: {},
|
||||
}, ['set', 'catalog', '{ "react": "19" }'])).rejects.toMatchObject({
|
||||
}), ['set', 'catalog', '{ "react": "19" }'])).rejects.toMatchObject({
|
||||
code: 'ERR_PNPM_CONFIG_SET_UNSUPPORTED_YAML_CONFIG_KEY',
|
||||
key: 'catalog',
|
||||
})
|
||||
|
||||
await expect(config.handler({
|
||||
await expect(config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'global',
|
||||
json: true,
|
||||
authConfig: {},
|
||||
}, ['set', 'packageExtensions', JSON.stringify({
|
||||
}), ['set', 'packageExtensions', JSON.stringify({
|
||||
'@babel/parser': {
|
||||
peerDependencies: {
|
||||
'@babel/types': '*',
|
||||
@@ -671,14 +671,14 @@ test('config set refuses writing workspace-specific settings to the global confi
|
||||
key: 'packageExtensions',
|
||||
})
|
||||
|
||||
await expect(config.handler({
|
||||
await expect(config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'global',
|
||||
json: true,
|
||||
authConfig: {},
|
||||
}, ['set', 'package-extensions', JSON.stringify({
|
||||
}), ['set', 'package-extensions', JSON.stringify({
|
||||
'@babel/parser': {
|
||||
peerDependencies: {
|
||||
'@babel/types': '*',
|
||||
@@ -709,14 +709,14 @@ test('config set writes workspace-specific settings to pnpm-workspace.yaml', asy
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
const catalog = { react: '19' }
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
json: true,
|
||||
authConfig: {},
|
||||
}, ['set', 'catalog', JSON.stringify(catalog)])
|
||||
}), ['set', 'catalog', JSON.stringify(catalog)])
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
localYaml: {
|
||||
@@ -737,14 +737,14 @@ test('config set writes workspace-specific settings to pnpm-workspace.yaml', asy
|
||||
},
|
||||
},
|
||||
}
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
json: true,
|
||||
authConfig: {},
|
||||
}, ['set', 'packageExtensions', JSON.stringify(packageExtensions)])
|
||||
}), ['set', 'packageExtensions', JSON.stringify(packageExtensions)])
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
localYaml: {
|
||||
@@ -760,14 +760,14 @@ test('config set refuses kebab-case workspace-specific settings', async () => {
|
||||
const configDir = path.join(tmp, 'global-config')
|
||||
fs.mkdirSync(configDir, { recursive: true })
|
||||
|
||||
await expect(config.handler({
|
||||
await expect(config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
json: true,
|
||||
authConfig: {},
|
||||
}, ['set', 'package-extensions', JSON.stringify({
|
||||
}), ['set', 'package-extensions', JSON.stringify({
|
||||
'@babel/parser': {
|
||||
peerDependencies: {
|
||||
'@babel/types': '*',
|
||||
@@ -789,13 +789,13 @@ test('config set registry-specific setting with --location=project should create
|
||||
const configDir = path.join(tmp, 'global-config')
|
||||
fs.mkdirSync(configDir, { recursive: true })
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['set', '//registry.example.com/:_auth', 'test-auth-value'])
|
||||
}), ['set', '//registry.example.com/:_auth', 'test-auth-value'])
|
||||
|
||||
expect(readIniFileSync(path.join(tmp, '.npmrc'))).toEqual({
|
||||
'//registry.example.com/:_auth': 'test-auth-value',
|
||||
@@ -808,13 +808,13 @@ test('config set scoped registry with --location=project should create .npmrc',
|
||||
const configDir = path.join(tmp, 'global-config')
|
||||
fs.mkdirSync(configDir, { recursive: true })
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['set', '@myorg:registry', 'https://test-registry.example.com/'])
|
||||
}), ['set', '@myorg:registry', 'https://test-registry.example.com/'])
|
||||
|
||||
expect(readIniFileSync(path.join(tmp, '.npmrc'))).toEqual({
|
||||
'@myorg:registry': 'https://test-registry.example.com/',
|
||||
@@ -831,13 +831,13 @@ test('config set when both pnpm-workspace.yaml and .npmrc exist, pnpm-workspace.
|
||||
fs.writeFileSync(path.join(tmp, '.npmrc'), 'store-dir=~/store')
|
||||
fs.writeFileSync(path.join(tmp, 'pnpm-workspace.yaml'), 'fetchRetries: 5')
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['set', 'fetch-timeout', '2000'])
|
||||
}), ['set', 'fetch-timeout', '2000'])
|
||||
|
||||
expect(readYamlFileSync(path.join(tmp, 'pnpm-workspace.yaml'))).toEqual({
|
||||
fetchRetries: 5,
|
||||
@@ -856,13 +856,13 @@ test('config set when only pnpm-workspace.yaml exists, writes to it', async () =
|
||||
fs.mkdirSync(configDir, { recursive: true })
|
||||
fs.writeFileSync(path.join(tmp, 'pnpm-workspace.yaml'), 'fetchRetries: 5')
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
authConfig: {},
|
||||
}, ['set', 'fetch-timeout', '3000'])
|
||||
}), ['set', 'fetch-timeout', '3000'])
|
||||
|
||||
expect(readYamlFileSync(path.join(tmp, 'pnpm-workspace.yaml'))).toEqual({
|
||||
fetchRetries: 5,
|
||||
|
||||
@@ -3,6 +3,7 @@ import path from 'node:path'
|
||||
import { config } from '@pnpm/config.commands'
|
||||
import { tempDir } from '@pnpm/prepare'
|
||||
|
||||
import { createConfigCommandOpts } from './utils/index.js'
|
||||
import { type ConfigFilesData, readConfigFiles, writeConfigFiles } from './utils/index.js'
|
||||
|
||||
describe.each(
|
||||
@@ -27,13 +28,13 @@ describe.each(
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: tmp,
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['set', `${key}=123`])
|
||||
}), ['set', `${key}=123`])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -51,13 +52,13 @@ describe.each(
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: tmp,
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['delete', key])
|
||||
}), ['delete', key])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -78,14 +79,14 @@ describe.each(
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
json: true,
|
||||
dir: tmp,
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['set', key, '"123"'])
|
||||
}), ['set', key, '"123"'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -103,14 +104,14 @@ describe.each(
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
json: true,
|
||||
dir: tmp,
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['delete', key])
|
||||
}), ['delete', key])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -136,13 +137,13 @@ describe.each(
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: tmp,
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['set', `${key}=https://registry.example.com/`])
|
||||
}), ['set', `${key}=https://registry.example.com/`])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -160,13 +161,13 @@ describe.each(
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: tmp,
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['delete', key])
|
||||
}), ['delete', key])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -189,14 +190,14 @@ describe.each(
|
||||
const tmp = tempDir()
|
||||
const configDir = path.join(tmp, 'global-config')
|
||||
it(`${key} should reject a non-string value`, async () => {
|
||||
await expect(config.handler({
|
||||
await expect(config.handler(createConfigCommandOpts({
|
||||
json: true,
|
||||
dir: tmp,
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['set', key, '{}'])).rejects.toMatchObject({
|
||||
}), ['set', key, '{}'])).rejects.toMatchObject({
|
||||
code: 'ERR_PNPM_CONFIG_SET_AUTH_NON_STRING',
|
||||
})
|
||||
})
|
||||
@@ -219,13 +220,13 @@ describe.each(
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: tmp,
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['set', propertyPath, '123'])
|
||||
}), ['set', propertyPath, '123'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -243,13 +244,13 @@ describe.each(
|
||||
} satisfies ConfigFilesData
|
||||
writeConfigFiles(configDir, tmp, initConfig)
|
||||
|
||||
await config.handler({
|
||||
await config.handler(createConfigCommandOpts({
|
||||
dir: tmp,
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
authConfig: {},
|
||||
}, ['delete', propertyPath])
|
||||
}), ['delete', propertyPath])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
|
||||
@@ -1,13 +1,46 @@
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import { readIniFileSync } from 'read-ini-file'
|
||||
import { readYamlFileSync } from 'read-yaml-file'
|
||||
import { writeIniFileSync } from 'write-ini-file'
|
||||
import { writeYamlFileSync } from 'write-yaml-file'
|
||||
|
||||
import type { ConfigCommandOptions } from '../../src/ConfigCommandOptions.js'
|
||||
import type { config } from '../../src/index.js'
|
||||
|
||||
/**
|
||||
* Build a {@link ConfigCommandOptions} object for tests.
|
||||
*
|
||||
* Accepts the flat shape that tests already use (settings like `storeDir`,
|
||||
* `authConfig`, etc. mixed into a single object) and builds the `config`
|
||||
* and `context` properties that the refactored config commands now expect.
|
||||
*/
|
||||
export function createConfigCommandOpts (
|
||||
opts: Record<string, unknown> & {
|
||||
dir: string
|
||||
configDir: string
|
||||
cliOptions: Record<string, unknown>
|
||||
authConfig: Record<string, unknown>
|
||||
global?: boolean
|
||||
json?: boolean
|
||||
location?: 'global' | 'project'
|
||||
}
|
||||
): ConfigCommandOptions {
|
||||
return {
|
||||
...opts,
|
||||
_config: opts as unknown as Config,
|
||||
_context: {
|
||||
cliOptions: opts.cliOptions ?? {},
|
||||
explicitlySetKeys: new Set(Object.keys(opts)),
|
||||
rawLocalConfig: {},
|
||||
rootProjectManifestDir: opts.dir,
|
||||
packageManager: { name: 'pnpm', version: '0.0.0' },
|
||||
} as ConfigContext,
|
||||
} as ConfigCommandOptions
|
||||
}
|
||||
|
||||
export function getOutputString (result: config.ConfigHandlerResult): string {
|
||||
if (result == null) throw new Error('output is null or undefined')
|
||||
if (typeof result === 'string') return result
|
||||
|
||||
@@ -14,22 +14,50 @@ import type {
|
||||
import type { OptionsFromRootManifest } from './getOptionsFromRootManifest.js'
|
||||
import type { AuthInfo } from './parseAuthInfo.js'
|
||||
|
||||
export type UniversalOptions = Pick<Config, 'color' | 'dir' | 'authConfig' | 'rawLocalConfig'>
|
||||
export type UniversalOptions = Pick<Config, 'color' | 'dir' | 'authConfig'> & Pick<ConfigContext, 'rawLocalConfig'>
|
||||
|
||||
|
||||
export type VerifyDepsBeforeRun = 'install' | 'warn' | 'error' | 'prompt' | false
|
||||
|
||||
export interface Config extends AuthInfo, OptionsFromRootManifest {
|
||||
/**
|
||||
* Runtime state, workspace context, and CLI metadata.
|
||||
* These fields are NOT user-facing settings — they are computed at startup
|
||||
* or populated later by the CLI harness (e.g. workspace filtering, hook loading).
|
||||
*/
|
||||
export interface ConfigContext {
|
||||
// -- Runtime state --
|
||||
hooks?: Hooks
|
||||
finders?: Record<string, Finder>
|
||||
|
||||
// -- Workspace context --
|
||||
allProjects?: Project[]
|
||||
selectedProjectsGraph?: ProjectsGraph
|
||||
allProjectsGraph?: ProjectsGraph
|
||||
rootProjectManifest?: ProjectManifest
|
||||
rootProjectManifestDir: string
|
||||
|
||||
// -- CLI metadata --
|
||||
cliOptions: Record<string, any> // eslint-disable-line
|
||||
rawLocalConfig: Record<string, any> // eslint-disable-line
|
||||
/** Keys explicitly set from workspace yaml, CLI, or env vars (not defaults). */
|
||||
explicitlySetKeys: Set<string>
|
||||
packageManager: {
|
||||
name: string
|
||||
version: string
|
||||
}
|
||||
wantedPackageManager?: EngineDependency
|
||||
}
|
||||
|
||||
/**
|
||||
* User-facing settings + auth/network config.
|
||||
* Does NOT include runtime state — see {@link ConfigContext} for that.
|
||||
*/
|
||||
export interface Config extends AuthInfo, OptionsFromRootManifest {
|
||||
allowNew: boolean
|
||||
autoConfirmAllPrompts?: boolean
|
||||
autoInstallPeers?: boolean
|
||||
bail: boolean
|
||||
color: 'always' | 'auto' | 'never'
|
||||
cliOptions: Record<string, any>, // eslint-disable-line
|
||||
useBetaCli: boolean
|
||||
excludeLinksFromLockfile: boolean
|
||||
extraBinPaths: string[]
|
||||
@@ -37,10 +65,7 @@ export interface Config extends AuthInfo, OptionsFromRootManifest {
|
||||
failIfNoMatch: boolean
|
||||
filter: string[]
|
||||
filterProd: string[]
|
||||
rawLocalConfig: Record<string, any>, // eslint-disable-line
|
||||
authConfig: Record<string, any>, // eslint-disable-line
|
||||
/** Keys explicitly set from workspace yaml, CLI, or env vars (not defaults). */
|
||||
explicitlySetKeys: Set<string>
|
||||
dryRun?: boolean // This option might be not supported ever
|
||||
global?: boolean
|
||||
dir: string
|
||||
@@ -86,11 +111,6 @@ export interface Config extends AuthInfo, OptionsFromRootManifest {
|
||||
frozenLockfile?: boolean
|
||||
preferFrozenLockfile?: boolean
|
||||
only?: 'prod' | 'production' | 'dev' | 'development'
|
||||
packageManager: {
|
||||
name: string
|
||||
version: string
|
||||
}
|
||||
wantedPackageManager?: EngineDependency
|
||||
preferOffline?: boolean
|
||||
sideEffectsCache?: boolean // for backward compatibility
|
||||
sideEffectsCacheReadonly?: boolean // for backward compatibility
|
||||
@@ -144,8 +164,6 @@ export interface Config extends AuthInfo, OptionsFromRootManifest {
|
||||
ignorePnpmfile?: boolean
|
||||
pnpmfile: string[] | string
|
||||
tryLoadDefaultPnpmfile?: boolean
|
||||
hooks?: Hooks
|
||||
finders?: Record<string, Finder>
|
||||
packageImportMethod?: 'auto' | 'hardlink' | 'copy' | 'clone' | 'clone-or-copy'
|
||||
hoistPattern?: string[]
|
||||
publicHoistPattern?: string[] | string
|
||||
@@ -203,8 +221,6 @@ export interface Config extends AuthInfo, OptionsFromRootManifest {
|
||||
|
||||
testPattern?: string[]
|
||||
changedFilesIgnorePattern?: string[]
|
||||
rootProjectManifestDir: string
|
||||
rootProjectManifest?: ProjectManifest
|
||||
userConfig: Record<string, string>
|
||||
|
||||
hoist: boolean
|
||||
|
||||
@@ -1,40 +1,48 @@
|
||||
import { inheritAuthConfig } from './auth.js'
|
||||
import type { InheritableConfig } from './inheritPickedConfig.js'
|
||||
import type { InheritableConfigPair } from './inheritPickedConfig.js'
|
||||
|
||||
test('inheritAuthConfig copies only auth keys from source to target', () => {
|
||||
const target: InheritableConfig = {
|
||||
bin: 'foo',
|
||||
cacheDir: '/path/to/cache/dir',
|
||||
registry: 'https://npmjs.com/registry/',
|
||||
authConfig: {
|
||||
'cache-dir': '/path/to/cache/dir',
|
||||
registry: 'https://npmjs.com/registry/',
|
||||
},
|
||||
rawLocalConfig: {
|
||||
const target: InheritableConfigPair = {
|
||||
config: {
|
||||
bin: 'foo',
|
||||
cacheDir: '/path/to/cache/dir',
|
||||
registry: 'https://npmjs.com/registry/',
|
||||
authConfig: {
|
||||
'cache-dir': '/path/to/cache/dir',
|
||||
registry: 'https://npmjs.com/registry/',
|
||||
},
|
||||
} as any, // eslint-disable-line
|
||||
context: {
|
||||
rawLocalConfig: {
|
||||
bin: 'foo',
|
||||
registry: 'https://npmjs.com/registry/',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
inheritAuthConfig(target, {
|
||||
bin: 'bar',
|
||||
cacheDir: '/path/to/another/cache/dir',
|
||||
storeDir: '/path/to/custom/store/dir',
|
||||
registry: 'https://example.com/local-registry/',
|
||||
authConfig: {
|
||||
registry: 'https://example.com/global-registry/',
|
||||
'//example.com/global-registry/:_auth': 'MY_SECRET_GLOBAL_AUTH',
|
||||
},
|
||||
rawLocalConfig: {
|
||||
config: {
|
||||
bin: 'bar',
|
||||
'cache-dir': '/path/to/another/cache/dir',
|
||||
'store-dir': '/path/to/custom/store/dir',
|
||||
cacheDir: '/path/to/another/cache/dir',
|
||||
storeDir: '/path/to/custom/store/dir',
|
||||
registry: 'https://example.com/local-registry/',
|
||||
'//example.com/local-registry/:_authToken': 'MY_SECRET_LOCAL_AUTH',
|
||||
authConfig: {
|
||||
registry: 'https://example.com/global-registry/',
|
||||
'//example.com/global-registry/:_auth': 'MY_SECRET_GLOBAL_AUTH',
|
||||
},
|
||||
} as any, // eslint-disable-line
|
||||
context: {
|
||||
rawLocalConfig: {
|
||||
bin: 'bar',
|
||||
'cache-dir': '/path/to/another/cache/dir',
|
||||
'store-dir': '/path/to/custom/store/dir',
|
||||
registry: 'https://example.com/local-registry/',
|
||||
'//example.com/local-registry/:_authToken': 'MY_SECRET_LOCAL_AUTH',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(target).toStrictEqual({
|
||||
expect(target.config).toMatchObject({
|
||||
bin: 'foo',
|
||||
cacheDir: '/path/to/cache/dir',
|
||||
registry: 'https://example.com/local-registry/',
|
||||
@@ -43,10 +51,10 @@ test('inheritAuthConfig copies only auth keys from source to target', () => {
|
||||
registry: 'https://example.com/global-registry/',
|
||||
'//example.com/global-registry/:_auth': 'MY_SECRET_GLOBAL_AUTH',
|
||||
},
|
||||
rawLocalConfig: {
|
||||
bin: 'foo',
|
||||
registry: 'https://example.com/local-registry/',
|
||||
'//example.com/local-registry/:_authToken': 'MY_SECRET_LOCAL_AUTH',
|
||||
},
|
||||
})
|
||||
expect(target.context.rawLocalConfig).toStrictEqual({
|
||||
bin: 'foo',
|
||||
registry: 'https://example.com/local-registry/',
|
||||
'//example.com/local-registry/:_authToken': 'MY_SECRET_LOCAL_AUTH',
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Config } from './Config.js'
|
||||
import { type InheritableConfig, inheritPickedConfig } from './inheritPickedConfig.js'
|
||||
import { type InheritableConfigPair, inheritPickedConfig } from './inheritPickedConfig.js'
|
||||
import type { types } from './types.js'
|
||||
|
||||
const RAW_AUTH_CFG_KEYS = [
|
||||
@@ -83,8 +83,8 @@ function pickAuthConfig (localCfg: Partial<Config>): Partial<Config> {
|
||||
return result as Partial<Config>
|
||||
}
|
||||
|
||||
export function inheritAuthConfig (targetCfg: InheritableConfig, authSrcCfg: InheritableConfig): void {
|
||||
inheritPickedConfig(targetCfg, authSrcCfg, pickAuthConfig, pickRawAuthConfig)
|
||||
export function inheritAuthConfig (target: InheritableConfigPair, src: InheritableConfigPair): void {
|
||||
inheritPickedConfig(target, src, pickAuthConfig, pickRawAuthConfig)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,7 @@ import { checkGlobalBinDir } from './checkGlobalBinDir.js'
|
||||
import { getDefaultWorkspaceConcurrency, getWorkspaceConcurrency } from './concurrency.js'
|
||||
import type {
|
||||
Config,
|
||||
ConfigContext,
|
||||
ConfigWithDeprecatedSettings,
|
||||
ProjectConfig,
|
||||
UniversalOptions,
|
||||
@@ -63,7 +64,7 @@ export {
|
||||
ProjectConfigUnsupportedFieldError,
|
||||
} from './projectConfig.js'
|
||||
|
||||
export type { Config, ProjectConfig, UniversalOptions, VerifyDepsBeforeRun }
|
||||
export type { Config, ConfigContext, ProjectConfig, UniversalOptions, VerifyDepsBeforeRun }
|
||||
|
||||
export { isIniConfigKey } from './auth.js'
|
||||
export { type ConfigFileKey, isConfigFileKey } from './configFileKey.js'
|
||||
@@ -89,7 +90,7 @@ export async function getConfig (opts: {
|
||||
env?: Record<string, string | undefined>
|
||||
ignoreNonAuthSettingsFromLocal?: boolean
|
||||
ignoreLocalSettings?: boolean
|
||||
}): Promise<{ config: Config, warnings: string[] }> {
|
||||
}): Promise<{ config: Config, context: ConfigContext, warnings: string[] }> {
|
||||
if (opts.ignoreNonAuthSettingsFromLocal) {
|
||||
const { ignoreNonAuthSettingsFromLocal: _, ...authOpts } = opts
|
||||
const globalCfgOpts: typeof authOpts = {
|
||||
@@ -101,7 +102,7 @@ export async function getConfig (opts: {
|
||||
},
|
||||
}
|
||||
const [final, authSrc] = await Promise.all([getConfig(globalCfgOpts), getConfig(authOpts)])
|
||||
inheritAuthConfig(final.config, authSrc.config)
|
||||
inheritAuthConfig(final, authSrc)
|
||||
final.warnings.push(...authSrc.warnings)
|
||||
return final
|
||||
}
|
||||
@@ -241,7 +242,7 @@ export async function getConfig (opts: {
|
||||
const pnpmConfig = Object.fromEntries(
|
||||
Object.entries(defaultOptions)
|
||||
.map(([key, value]) => [camelcase(key, { locale: 'en-US' }), value])
|
||||
) as unknown as ConfigWithDeprecatedSettings
|
||||
) as unknown as (ConfigWithDeprecatedSettings & ConfigContext)
|
||||
|
||||
for (const [key, value] of Object.entries(npmrcResult.mergedConfig)) {
|
||||
if (Object.hasOwn(types, key)) {
|
||||
@@ -254,6 +255,7 @@ export async function getConfig (opts: {
|
||||
// Track which keys are explicitly set (not defaults)
|
||||
const explicitlySetKeys = new Set<string>(Object.keys(configFromCliOpts))
|
||||
pnpmConfig.explicitlySetKeys = explicitlySetKeys
|
||||
pnpmConfig.cliOptions = cliOptions
|
||||
|
||||
Object.assign(pnpmConfig, configFromCliOpts)
|
||||
// Resolving the current working directory to its actual location is crucial.
|
||||
@@ -377,8 +379,8 @@ export async function getConfig (opts: {
|
||||
}
|
||||
pnpmConfig.packageManager = packageManager
|
||||
|
||||
pnpmConfig.rootProjectManifestDir = pnpmConfig.lockfileDir ?? pnpmConfig.workspaceDir ?? pnpmConfig.dir
|
||||
if (!opts.ignoreLocalSettings) {
|
||||
pnpmConfig.rootProjectManifestDir = pnpmConfig.lockfileDir ?? pnpmConfig.workspaceDir ?? pnpmConfig.dir
|
||||
pnpmConfig.rootProjectManifest = await safeReadProjectManifestOnly(pnpmConfig.rootProjectManifestDir) ?? undefined
|
||||
if (pnpmConfig.rootProjectManifest != null) {
|
||||
if (pnpmConfig.rootProjectManifest.workspaces?.length && !pnpmConfig.workspaceDir) {
|
||||
@@ -622,7 +624,24 @@ export async function getConfig (opts: {
|
||||
}
|
||||
}
|
||||
|
||||
return { config: pnpmConfig, warnings }
|
||||
const {
|
||||
hooks, finders,
|
||||
allProjects, selectedProjectsGraph, allProjectsGraph,
|
||||
rootProjectManifest, rootProjectManifestDir,
|
||||
cliOptions: ctxCliOptions, rawLocalConfig: ctxRawLocalConfig,
|
||||
explicitlySetKeys: ctxExplicitlySetKeys,
|
||||
packageManager: ctxPackageManager, wantedPackageManager,
|
||||
...config
|
||||
} = pnpmConfig as Config & ConfigContext
|
||||
const context: ConfigContext = {
|
||||
hooks, finders,
|
||||
allProjects, selectedProjectsGraph, allProjectsGraph,
|
||||
rootProjectManifest, rootProjectManifestDir,
|
||||
cliOptions: ctxCliOptions, rawLocalConfig: ctxRawLocalConfig,
|
||||
explicitlySetKeys: ctxExplicitlySetKeys,
|
||||
packageManager: ctxPackageManager, wantedPackageManager,
|
||||
}
|
||||
return { config, context, warnings }
|
||||
}
|
||||
|
||||
function getProcessEnv (env: string): string | undefined {
|
||||
@@ -719,7 +738,7 @@ function getNodeVersionFromEnginesRuntime (manifest: ProjectManifest): string |
|
||||
return undefined
|
||||
}
|
||||
|
||||
function addSettingsFromWorkspaceManifestToConfig (pnpmConfig: Config, {
|
||||
function addSettingsFromWorkspaceManifestToConfig (pnpmConfig: Config & ConfigContext, {
|
||||
configFromCliOpts,
|
||||
projectManifest,
|
||||
workspaceManifest,
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import type { Config } from './Config.js'
|
||||
import type { Config, ConfigContext } from './Config.js'
|
||||
|
||||
export type InheritableConfig = Partial<Config> & Pick<Config, 'authConfig' | 'rawLocalConfig'>
|
||||
export interface InheritableConfigPair {
|
||||
config: Partial<Config> & Pick<Config, 'authConfig'>
|
||||
context: Pick<ConfigContext, 'rawLocalConfig'>
|
||||
}
|
||||
export type PickConfig = (cfg: Partial<Config>) => Partial<Config>
|
||||
export type PickRawConfig = (cfg: Record<string, unknown>) => Record<string, unknown>
|
||||
|
||||
export function inheritPickedConfig (
|
||||
targetCfg: InheritableConfig,
|
||||
srcCfg: InheritableConfig,
|
||||
target: InheritableConfigPair,
|
||||
src: InheritableConfigPair,
|
||||
pickConfig: PickConfig,
|
||||
pickRawConfig: PickRawConfig,
|
||||
pickRawLocalConfig: PickRawConfig = pickRawConfig
|
||||
): void {
|
||||
Object.assign(targetCfg, pickConfig(srcCfg))
|
||||
Object.assign(targetCfg.authConfig, pickRawConfig(srcCfg.authConfig))
|
||||
Object.assign(targetCfg.rawLocalConfig, pickRawLocalConfig(srcCfg.rawLocalConfig))
|
||||
Object.assign(target.config, pickConfig(src.config))
|
||||
Object.assign(target.config.authConfig, pickRawConfig(src.config.authConfig))
|
||||
Object.assign(target.context.rawLocalConfig, pickRawLocalConfig(src.context.rawLocalConfig))
|
||||
}
|
||||
|
||||
@@ -678,7 +678,7 @@ test.skip('rawLocalConfig in a workspace', async () => {
|
||||
fs.writeFileSync('.npmrc', 'hoist-pattern=eslint-*', 'utf8')
|
||||
|
||||
{
|
||||
const { config } = await getConfig({
|
||||
const { context } = await getConfig({
|
||||
cliOptions: {
|
||||
'save-exact': true,
|
||||
},
|
||||
@@ -689,7 +689,7 @@ test.skip('rawLocalConfig in a workspace', async () => {
|
||||
workspaceDir,
|
||||
})
|
||||
|
||||
expect(config.rawLocalConfig).toStrictEqual({
|
||||
expect(context.rawLocalConfig).toStrictEqual({
|
||||
'hoist-pattern': 'eslint-*',
|
||||
'save-exact': true,
|
||||
})
|
||||
@@ -699,7 +699,7 @@ test.skip('rawLocalConfig in a workspace', async () => {
|
||||
fs.mkdirSync('package2')
|
||||
process.chdir('package2')
|
||||
{
|
||||
const { config } = await getConfig({
|
||||
const { context } = await getConfig({
|
||||
cliOptions: {
|
||||
'save-exact': true,
|
||||
},
|
||||
@@ -710,7 +710,7 @@ test.skip('rawLocalConfig in a workspace', async () => {
|
||||
workspaceDir,
|
||||
})
|
||||
|
||||
expect(config.rawLocalConfig).toStrictEqual({
|
||||
expect(context.rawLocalConfig).toStrictEqual({
|
||||
'hoist-pattern': '*',
|
||||
'save-exact': true,
|
||||
})
|
||||
@@ -722,7 +722,7 @@ test.skip('rawLocalConfig', async () => {
|
||||
|
||||
fs.writeFileSync('.npmrc', 'modules-dir=modules', 'utf8')
|
||||
|
||||
const { config } = await getConfig({
|
||||
const { context } = await getConfig({
|
||||
cliOptions: {
|
||||
'save-exact': true,
|
||||
},
|
||||
@@ -732,7 +732,7 @@ test.skip('rawLocalConfig', async () => {
|
||||
},
|
||||
})
|
||||
|
||||
expect(config.rawLocalConfig).toStrictEqual({
|
||||
expect(context.rawLocalConfig).toStrictEqual({
|
||||
'modules-dir': 'modules',
|
||||
'save-exact': true,
|
||||
})
|
||||
|
||||
7
deps/compliance/commands/src/audit/audit.ts
vendored
7
deps/compliance/commands/src/audit/audit.ts
vendored
@@ -1,5 +1,5 @@
|
||||
import { docsUrl, TABLE_OPTIONS } from '@pnpm/cli.utils'
|
||||
import { type Config, types as allTypes, type UniversalOptions } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes, type UniversalOptions } from '@pnpm/config.reader'
|
||||
import { WANTED_LOCKFILE } from '@pnpm/constants'
|
||||
import { audit, type AuditAdvisory, type AuditLevelNumber, type AuditLevelString, type AuditReport, type AuditVulnerabilityCounts, type IgnoredAuditVulnerabilityCounts } from '@pnpm/deps.compliance.audit'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
@@ -166,10 +166,11 @@ export type AuditOptions = Pick<UniversalOptions, 'dir'> & {
|
||||
| 'optional'
|
||||
| 'userConfig'
|
||||
| 'authConfig'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'virtualStoreDirMaxLength'
|
||||
| 'workspaceDir'
|
||||
> & Pick<ConfigContext,
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
> & InstallCommandOptions
|
||||
|
||||
const DEFAULT_FIX_METHOD = 'override'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import { readProjectManifestOnly } from '@pnpm/cli.utils'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import { WANTED_LOCKFILE } from '@pnpm/constants'
|
||||
import { findDependencyLicenses } from '@pnpm/deps.compliance.license-scanner'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
@@ -28,11 +28,12 @@ export type LicensesCommandOptions = {
|
||||
| 'virtualStoreDir'
|
||||
| 'modulesDir'
|
||||
| 'pnpmHomeDir'
|
||||
| 'supportedArchitectures'
|
||||
| 'virtualStoreDirMaxLength'
|
||||
> & Pick<ConfigContext,
|
||||
| 'selectedProjectsGraph'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'supportedArchitectures'
|
||||
| 'virtualStoreDirMaxLength'
|
||||
> &
|
||||
Partial<Pick<Config, 'userConfig'>>
|
||||
|
||||
|
||||
9
deps/compliance/commands/src/sbom/sbom.ts
vendored
9
deps/compliance/commands/src/sbom/sbom.ts
vendored
@@ -1,7 +1,7 @@
|
||||
import { FILTERING } from '@pnpm/cli.common-cli-options-help'
|
||||
import { packageManager } from '@pnpm/cli.meta'
|
||||
import { docsUrl, readProjectManifestOnly } from '@pnpm/cli.utils'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { WANTED_LOCKFILE } from '@pnpm/constants'
|
||||
import {
|
||||
collectSbomComponents,
|
||||
@@ -34,10 +34,11 @@ export type SbomCommandOptions = {
|
||||
| 'virtualStoreDir'
|
||||
| 'modulesDir'
|
||||
| 'pnpmHomeDir'
|
||||
| 'selectedProjectsGraph'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'virtualStoreDirMaxLength'
|
||||
> & Pick<ConfigContext,
|
||||
| 'selectedProjectsGraph'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
> &
|
||||
Partial<Pick<Config, 'userConfig'>>
|
||||
|
||||
|
||||
11
deps/inspection/commands/src/listing/list.ts
vendored
11
deps/inspection/commands/src/listing/list.ts
vendored
@@ -1,6 +1,6 @@
|
||||
import { FILTERING, OPTIONS, UNIVERSAL_OPTIONS } from '@pnpm/cli.common-cli-options-help'
|
||||
import { docsUrl } from '@pnpm/cli.utils'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { list, listForPackages } from '@pnpm/deps.inspection.list'
|
||||
import { listGlobalPackages } from '@pnpm/global.commands'
|
||||
import type { Finder, IncludedDependencies } from '@pnpm/types'
|
||||
@@ -84,16 +84,17 @@ For example: pnpm ls babel-* eslint-*',
|
||||
}
|
||||
|
||||
export type ListCommandOptions = Pick<Config,
|
||||
| 'allProjects'
|
||||
| 'dev'
|
||||
| 'dir'
|
||||
| 'finders'
|
||||
| 'optional'
|
||||
| 'production'
|
||||
| 'selectedProjectsGraph'
|
||||
| 'modulesDir'
|
||||
| 'virtualStoreDirMaxLength'
|
||||
> & Partial<Pick<Config, 'cliOptions'>> & {
|
||||
> & Pick<ConfigContext,
|
||||
| 'allProjects'
|
||||
| 'finders'
|
||||
| 'selectedProjectsGraph'
|
||||
> & Partial<Pick<ConfigContext, 'cliOptions'>> & {
|
||||
alwaysPrintRootPackage?: boolean
|
||||
depth?: number
|
||||
excludePeers?: boolean
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
TABLE_OPTIONS,
|
||||
} from '@pnpm/cli.utils'
|
||||
import colorizeSemverDiff from '@pnpm/colorize-semver-diff'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import {
|
||||
outdatedDepsOfProjects,
|
||||
type OutdatedPackage,
|
||||
@@ -139,7 +139,6 @@ export type OutdatedCommandOptions = {
|
||||
format?: 'table' | 'list' | 'json'
|
||||
sortBy?: 'name'
|
||||
} & Pick<Config,
|
||||
| 'allProjects'
|
||||
| 'ca'
|
||||
| 'cacheDir'
|
||||
| 'catalogs'
|
||||
@@ -167,11 +166,13 @@ export type OutdatedCommandOptions = {
|
||||
| 'production'
|
||||
| 'authConfig'
|
||||
| 'registries'
|
||||
| 'selectedProjectsGraph'
|
||||
| 'strictSsl'
|
||||
| 'tag'
|
||||
| 'userAgent'
|
||||
| 'updateConfig'
|
||||
> & Pick<ConfigContext,
|
||||
| 'allProjects'
|
||||
| 'selectedProjectsGraph'
|
||||
> & Partial<Pick<Config, 'globalPkgDir' | 'userConfig'>>
|
||||
|
||||
export async function handler (
|
||||
|
||||
6
deps/inspection/commands/src/peers.ts
vendored
6
deps/inspection/commands/src/peers.ts
vendored
@@ -1,6 +1,6 @@
|
||||
import { FILTERING, UNIVERSAL_OPTIONS } from '@pnpm/cli.common-cli-options-help'
|
||||
import { docsUrl } from '@pnpm/cli.utils'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { checkPeerDependencies } from '@pnpm/deps.inspection.peers-checker'
|
||||
import type { PeerDependencyIssuesByProjects } from '@pnpm/types'
|
||||
import chalk from 'chalk'
|
||||
@@ -66,8 +66,8 @@ export type PeersCommandOptions = Pick<Config,
|
||||
| 'dir'
|
||||
| 'modulesDir'
|
||||
| 'peerDependencyRules'
|
||||
| 'selectedProjectsGraph'
|
||||
> & Partial<Pick<Config, 'cliOptions'>> & {
|
||||
> & Pick<ConfigContext, 'selectedProjectsGraph'>
|
||||
& Partial<Pick<ConfigContext, 'cliOptions'>> & {
|
||||
json?: boolean
|
||||
lockfileDir?: string
|
||||
lockfileOnly?: boolean
|
||||
|
||||
4
deps/inspection/commands/src/view/index.ts
vendored
4
deps/inspection/commands/src/view/index.ts
vendored
@@ -1,5 +1,5 @@
|
||||
import { pickRegistryForPackage } from '@pnpm/config.pick-registry-for-package'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { createGetAuthHeaderByURI } from '@pnpm/network.auth-header'
|
||||
import { createFetchFromRegistry } from '@pnpm/network.fetch'
|
||||
@@ -49,7 +49,7 @@ export function help (): string {
|
||||
}
|
||||
|
||||
export async function handler (
|
||||
opts: Config & {
|
||||
opts: Config & ConfigContext & {
|
||||
json?: boolean
|
||||
},
|
||||
params: string[]
|
||||
|
||||
42
deps/inspection/commands/test/view.ts
vendored
42
deps/inspection/commands/test/view.ts
vendored
@@ -1,4 +1,4 @@
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import { view } from '@pnpm/deps.inspection.commands'
|
||||
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
|
||||
|
||||
@@ -37,79 +37,79 @@ test('view: rcOptionsTypes should return object', () => {
|
||||
|
||||
test('view: missing package name throws error', async () => {
|
||||
await expect(
|
||||
view.handler(VIEW_OPTIONS as unknown as Config, [])
|
||||
view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, [])
|
||||
).rejects.toMatchObject({ code: 'ERR_PNPM_MISSING_PACKAGE_NAME' })
|
||||
})
|
||||
|
||||
test('view: non-registry spec throws error', async () => {
|
||||
await expect(
|
||||
view.handler(VIEW_OPTIONS as unknown as Config, ['github:user/repo'])
|
||||
view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['github:user/repo'])
|
||||
).rejects.toMatchObject({ code: 'ERR_PNPM_INVALID_PACKAGE_NAME' })
|
||||
})
|
||||
|
||||
test('view: successful lookup of package', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative'])
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['is-negative'])
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result).toContain('is-negative')
|
||||
})
|
||||
|
||||
test('view: package not found throws an error', async () => {
|
||||
await expect(
|
||||
view.handler(VIEW_OPTIONS as unknown as Config, ['not-a-real-package-123456789'])
|
||||
view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['not-a-real-package-123456789'])
|
||||
).rejects.toMatchObject({ code: 'ERR_PNPM_FETCH_404' })
|
||||
})
|
||||
|
||||
test('view: no matching version throws an error', async () => {
|
||||
await expect(
|
||||
view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@99999.0.0'])
|
||||
view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['is-negative@99999.0.0'])
|
||||
).rejects.toMatchObject({ code: 'ERR_PNPM_PACKAGE_NOT_FOUND' })
|
||||
})
|
||||
|
||||
test('view: with --json option', async () => {
|
||||
const result = await view.handler({ ...VIEW_OPTIONS, json: true } as unknown as Config, ['is-negative'])
|
||||
const result = await view.handler({ ...VIEW_OPTIONS, json: true } as unknown as Config & ConfigContext, ['is-negative'])
|
||||
expect(typeof result).toBe('string')
|
||||
const parsed = JSON.parse(result as string)
|
||||
expect(parsed.name).toBe('is-negative')
|
||||
})
|
||||
|
||||
test('view: accessing a specific field', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative', 'name'])
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['is-negative', 'name'])
|
||||
expect(result).toBe('is-negative')
|
||||
})
|
||||
|
||||
test('view: accessing a specific version', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@1.0.0', 'version'])
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['is-negative@1.0.0', 'version'])
|
||||
expect(result).toBe('1.0.0')
|
||||
})
|
||||
|
||||
test('view: accessing multiple fields adds quotes for strings', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@1.0.0', 'name', 'version'])
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['is-negative@1.0.0', 'name', 'version'])
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result).toContain("name = 'is-negative'")
|
||||
expect(result).toContain("version = '1.0.0'")
|
||||
})
|
||||
|
||||
test('view: version range resolves to matching version', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@^1.0.0', 'version'])
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['is-negative@^1.0.0', 'version'])
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result).toMatch(/^1\./)
|
||||
})
|
||||
|
||||
test('view: dist-tag resolves correctly', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@latest', 'version'])
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['is-negative@latest', 'version'])
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result).toMatch(/^\d+\.\d+\.\d+/)
|
||||
})
|
||||
|
||||
test('view: nested field selection', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@1.0.0', 'dist.shasum'])
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['is-negative@1.0.0', 'dist.shasum'])
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result!.length).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
test('view: field selection with --json', async () => {
|
||||
const result = await view.handler(
|
||||
{ ...VIEW_OPTIONS, json: true } as unknown as Config,
|
||||
{ ...VIEW_OPTIONS, json: true } as unknown as Config & ConfigContext,
|
||||
['is-negative@1.0.0', 'name', 'version']
|
||||
)
|
||||
const parsed = JSON.parse(result as string)
|
||||
@@ -118,43 +118,43 @@ test('view: field selection with --json', async () => {
|
||||
})
|
||||
|
||||
test('view: text output includes header with name@version', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@1.0.0']) as string
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['is-negative@1.0.0']) as string
|
||||
const firstLine = result.split('\n')[0]
|
||||
expect(firstLine).toContain('is-negative@1.0.0')
|
||||
})
|
||||
|
||||
test('view: text output includes dist section', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@1.0.0']) as string
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['is-negative@1.0.0']) as string
|
||||
expect(result).toContain('.tarball:')
|
||||
expect(result).toContain('.shasum:')
|
||||
})
|
||||
|
||||
test('view: text output includes dist-tags', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative']) as string
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['is-negative']) as string
|
||||
expect(result).toContain('dist-tags:')
|
||||
expect(result).toContain('latest:')
|
||||
})
|
||||
|
||||
test('view: text output for package with dependencies shows deps count', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['@pnpm.e2e/pkg-with-1-dep@100.0.0']) as string
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['@pnpm.e2e/pkg-with-1-dep@100.0.0']) as string
|
||||
const firstLine = result.split('\n')[0]
|
||||
expect(firstLine).toContain('deps: ')
|
||||
expect(firstLine).not.toContain('deps: none')
|
||||
})
|
||||
|
||||
test('view: text output for package without dependencies shows deps: none', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@1.0.0']) as string
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['is-negative@1.0.0']) as string
|
||||
const firstLine = result.split('\n')[0]
|
||||
expect(firstLine).toContain('deps: none')
|
||||
})
|
||||
|
||||
test('view: scoped package lookup', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['@pnpm.e2e/pkg-with-1-dep@100.0.0', 'name'])
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['@pnpm.e2e/pkg-with-1-dep@100.0.0', 'name'])
|
||||
expect(result).toBe('@pnpm.e2e/pkg-with-1-dep')
|
||||
})
|
||||
|
||||
test('view: object field renders as JSON', async () => {
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config, ['is-negative@1.0.0', 'dist'])
|
||||
const result = await view.handler(VIEW_OPTIONS as unknown as Config & ConfigContext, ['is-negative@1.0.0', 'dist'])
|
||||
expect(typeof result).toBe('string')
|
||||
const parsed = JSON.parse(result as string)
|
||||
expect(parsed.tarball).toBeDefined()
|
||||
|
||||
11
deps/status/src/checkDepsStatus.ts
vendored
11
deps/status/src/checkDepsStatus.ts
vendored
@@ -3,7 +3,7 @@ import path from 'node:path'
|
||||
import util from 'node:util'
|
||||
|
||||
import { parseOverrides } from '@pnpm/config.parse-overrides'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import { MANIFEST_BASE_NAMES, WANTED_LOCKFILE } from '@pnpm/constants'
|
||||
import { hashObjectNullableWithPrefix } from '@pnpm/crypto.object-hasher'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
@@ -42,18 +42,14 @@ import { safeStat, safeStatSync } from './safeStat.js'
|
||||
import { statManifestFile } from './statManifestFile.js'
|
||||
|
||||
export type CheckDepsStatusOptions = Pick<Config,
|
||||
| 'allProjects'
|
||||
| 'autoInstallPeers'
|
||||
| 'catalogs'
|
||||
| 'excludeLinksFromLockfile'
|
||||
| 'injectWorkspacePackages'
|
||||
| 'linkWorkspacePackages'
|
||||
| 'nodeLinker'
|
||||
| 'hooks'
|
||||
| 'patchedDependencies'
|
||||
| 'peersSuffixMaxLength'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'sharedWorkspaceLockfile'
|
||||
| 'workspaceDir'
|
||||
| 'patchesDir'
|
||||
@@ -61,6 +57,11 @@ export type CheckDepsStatusOptions = Pick<Config,
|
||||
| 'overrides'
|
||||
| 'packageExtensions'
|
||||
| 'ignoredOptionalDependencies'
|
||||
> & Pick<ConfigContext,
|
||||
| 'allProjects'
|
||||
| 'hooks'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
> & {
|
||||
ignoreFilteredInstallCache?: boolean
|
||||
ignoredWorkspaceStateSettings?: Array<keyof WorkspaceStateSettings>
|
||||
|
||||
@@ -3,7 +3,7 @@ import path from 'node:path'
|
||||
import { linkBins } from '@pnpm/bins.linker'
|
||||
import { isExecutedByCorepack, packageManager } from '@pnpm/cli.meta'
|
||||
import { docsUrl } from '@pnpm/cli.utils'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { createResolver } from '@pnpm/installing.client'
|
||||
import { resolvePackageManagerIntegrities } from '@pnpm/installing.env-installer'
|
||||
@@ -52,6 +52,7 @@ export type SelfUpdateCommandOptions = CreateStoreControllerOptions & Pick<Confi
|
||||
| 'managePackageManagerVersions'
|
||||
| 'modulesDir'
|
||||
| 'pnpmHomeDir'
|
||||
> & Pick<ConfigContext,
|
||||
| 'rootProjectManifestDir'
|
||||
| 'wantedPackageManager'
|
||||
>
|
||||
|
||||
7
engine/runtime/commands/src/env/node.ts
vendored
7
engine/runtime/commands/src/env/node.ts
vendored
@@ -1,4 +1,4 @@
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
|
||||
export type NvmNodeCommandOptions = Pick<Config,
|
||||
| 'bin'
|
||||
@@ -24,11 +24,9 @@ export type NvmNodeCommandOptions = Pick<Config,
|
||||
> & Partial<Pick<Config,
|
||||
| 'cacheDir'
|
||||
| 'configDir'
|
||||
| 'cliOptions'
|
||||
| 'sslConfigs'
|
||||
// Fields needed to forward opts to add.handler for env use
|
||||
| 'registries'
|
||||
| 'rawLocalConfig'
|
||||
| 'lockfileDir'
|
||||
| 'nodeLinker'
|
||||
| 'modulesDir'
|
||||
@@ -38,6 +36,9 @@ export type NvmNodeCommandOptions = Pick<Config,
|
||||
| 'sideEffectsCache'
|
||||
| 'sideEffectsCacheReadonly'
|
||||
| 'supportedArchitectures'
|
||||
>> & Partial<Pick<ConfigContext,
|
||||
| 'cliOptions'
|
||||
| 'rawLocalConfig'
|
||||
>> & {
|
||||
remote?: boolean
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import path from 'node:path'
|
||||
|
||||
import { FILTERING, UNIVERSAL_OPTIONS } from '@pnpm/cli.common-cli-options-help'
|
||||
import { docsUrl, readProjectManifestOnly, type RecursiveSummary, throwOnCommandFail } from '@pnpm/cli.utils'
|
||||
import { type Config, getWorkspaceConcurrency, types } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, getWorkspaceConcurrency, types } from '@pnpm/config.reader'
|
||||
import { lifecycleLogger, type LifecycleMessage } from '@pnpm/core-loggers'
|
||||
import type { CheckDepsStatusOptions } from '@pnpm/deps.status'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
@@ -136,7 +136,7 @@ export function getExecutionDuration (start: [number, number]): number {
|
||||
return (end[0] * 1e9 + end[1]) / 1e6
|
||||
}
|
||||
|
||||
export type ExecOpts = Required<Pick<Config, 'selectedProjectsGraph'>> & {
|
||||
export type ExecOpts = Required<Pick<ConfigContext, 'selectedProjectsGraph'>> & {
|
||||
bail?: boolean
|
||||
unsafePerm?: boolean
|
||||
reverse?: boolean
|
||||
@@ -148,7 +148,6 @@ export type ExecOpts = Required<Pick<Config, 'selectedProjectsGraph'>> & {
|
||||
implicitlyFellbackFromRun?: boolean
|
||||
} & Pick<Config,
|
||||
| 'bin'
|
||||
| 'cliOptions'
|
||||
| 'dir'
|
||||
| 'extraBinPaths'
|
||||
| 'extraEnv'
|
||||
@@ -161,7 +160,7 @@ export type ExecOpts = Required<Pick<Config, 'selectedProjectsGraph'>> & {
|
||||
| 'userAgent'
|
||||
| 'verifyDepsBeforeRun'
|
||||
| 'workspaceDir'
|
||||
> & CheckDepsStatusOptions
|
||||
> & Pick<ConfigContext, 'cliOptions'> & CheckDepsStatusOptions
|
||||
|
||||
export async function handler (
|
||||
opts: ExecOpts,
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
readProjectManifestOnly,
|
||||
tryReadProjectManifest,
|
||||
} from '@pnpm/cli.utils'
|
||||
import { type Config, getWorkspaceConcurrency, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, getWorkspaceConcurrency, types as allTypes } from '@pnpm/config.reader'
|
||||
import type { CheckDepsStatusOptions } from '@pnpm/deps.status'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import {
|
||||
@@ -161,7 +161,6 @@ export type RunOpts =
|
||||
& { recursive?: boolean }
|
||||
& Pick<Config,
|
||||
| 'bin'
|
||||
| 'cliOptions'
|
||||
| 'verifyDepsBeforeRun'
|
||||
| 'dir'
|
||||
| 'enablePrePostScripts'
|
||||
@@ -177,9 +176,10 @@ export type RunOpts =
|
||||
| 'syncInjectedDepsAfterScripts'
|
||||
| 'userAgent'
|
||||
>
|
||||
& Pick<ConfigContext, 'cliOptions'>
|
||||
& (
|
||||
| { recursive?: false } & Partial<Pick<Config, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir'>>
|
||||
| { recursive: true } & Required<Pick<Config, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir'>>
|
||||
| { recursive?: false } & Partial<Pick<ConfigContext, 'allProjects' | 'selectedProjectsGraph'> & Pick<Config, 'workspaceDir'>>
|
||||
| { recursive: true } & Required<Pick<ConfigContext, 'allProjects' | 'selectedProjectsGraph'> & Pick<Config, 'workspaceDir'>>
|
||||
)
|
||||
& {
|
||||
argv?: {
|
||||
|
||||
@@ -3,7 +3,7 @@ import path from 'node:path'
|
||||
import util from 'node:util'
|
||||
|
||||
import { throwOnCommandFail } from '@pnpm/cli.utils'
|
||||
import { type Config, getWorkspaceConcurrency } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, getWorkspaceConcurrency } from '@pnpm/config.reader'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import {
|
||||
makeNodeRequireOption,
|
||||
@@ -28,14 +28,13 @@ export type RecursiveRunOpts = Pick<Config,
|
||||
| 'pnpmHomeDir'
|
||||
| 'requiredScripts'
|
||||
| 'userAgent'
|
||||
| 'rootProjectManifest'
|
||||
| 'scriptsPrependNodePath'
|
||||
| 'scriptShell'
|
||||
| 'shellEmulator'
|
||||
| 'stream'
|
||||
| 'syncInjectedDepsAfterScripts'
|
||||
| 'workspaceDir'
|
||||
> & Required<Pick<Config, 'allProjects' | 'selectedProjectsGraph' | 'workspaceDir' | 'dir'>> &
|
||||
> & Pick<ConfigContext, 'rootProjectManifest'> & Required<Pick<ConfigContext, 'allProjects' | 'selectedProjectsGraph'> & Pick<Config, 'workspaceDir' | 'dir'>> &
|
||||
Partial<Pick<Config, 'extraBinPaths' | 'extraEnv' | 'bail' | 'reporter' | 'reverse' | 'sort' | 'workspaceConcurrency'>> &
|
||||
{
|
||||
ifPresent?: boolean
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { UNIVERSAL_OPTIONS } from '@pnpm/cli.common-cli-options-help'
|
||||
import { docsUrl } from '@pnpm/cli.utils'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import { type InstallOptions, mutateModulesInSingleProject } from '@pnpm/installing.deps-installer'
|
||||
import { createStoreController, type CreateStoreControllerOptions } from '@pnpm/store.connection-manager'
|
||||
import type { ProjectRootDir } from '@pnpm/types'
|
||||
@@ -46,7 +46,7 @@ export function help (): string {
|
||||
})
|
||||
}
|
||||
|
||||
type FetchCommandOptions = Pick<Config, 'production' | 'dev' | 'enableGlobalVirtualStore' | 'patchedDependencies' | 'rootProjectManifest' | 'rootProjectManifestDir'> & CreateStoreControllerOptions
|
||||
type FetchCommandOptions = Pick<Config, 'production' | 'dev' | 'enableGlobalVirtualStore' | 'patchedDependencies'> & Pick<ConfigContext, 'rootProjectManifest' | 'rootProjectManifestDir'> & CreateStoreControllerOptions
|
||||
|
||||
export async function handler (opts: FetchCommandOptions): Promise<void> {
|
||||
const store = await createStoreController(opts)
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
import { docsUrl } from '@pnpm/cli.utils'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import { WANTED_LOCKFILE } from '@pnpm/constants'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import gfs from '@pnpm/fs.graceful-fs'
|
||||
@@ -99,14 +99,15 @@ export const commandNames = ['import']
|
||||
export const recursiveByDefault = true
|
||||
|
||||
export type ImportCommandOptions = Pick<Config,
|
||||
| 'allProjects'
|
||||
| 'allProjectsGraph'
|
||||
| 'selectedProjectsGraph'
|
||||
| 'workspaceDir'
|
||||
| 'ignoreWorkspaceCycles'
|
||||
| 'disallowWorkspaceCycles'
|
||||
| 'sharedWorkspaceLockfile'
|
||||
| 'workspacePackagePatterns'
|
||||
> & Pick<ConfigContext,
|
||||
| 'allProjects'
|
||||
| 'allProjectsGraph'
|
||||
| 'selectedProjectsGraph'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
> & CreateStoreControllerOptions & Omit<InstallOptions, 'storeController' | 'lockfileOnly' | 'preferredVersions'>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { CommandHandlerMap } from '@pnpm/cli.command'
|
||||
import { FILTERING, OPTIONS, OUTPUT_OPTIONS, UNIVERSAL_OPTIONS } from '@pnpm/cli.common-cli-options-help'
|
||||
import { docsUrl } from '@pnpm/cli.utils'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { WANTED_LOCKFILE } from '@pnpm/constants'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import type { CreateStoreControllerOptions } from '@pnpm/store.connection-manager'
|
||||
@@ -265,12 +265,10 @@ Install all optionalDependencies even when they don\'t satisfy the current envir
|
||||
}
|
||||
|
||||
export type InstallCommandOptions = Pick<Config,
|
||||
| 'allProjects'
|
||||
| 'autoInstallPeers'
|
||||
| 'bail'
|
||||
| 'bin'
|
||||
| 'catalogs'
|
||||
| 'cliOptions'
|
||||
| 'configDependencies'
|
||||
| 'dedupeInjectedDeps'
|
||||
| 'dedupeDirectDeps'
|
||||
@@ -285,12 +283,10 @@ export type InstallCommandOptions = Pick<Config,
|
||||
| 'frozenLockfile'
|
||||
| 'global'
|
||||
| 'globalPnpmfile'
|
||||
| 'hooks'
|
||||
| 'ignorePnpmfile'
|
||||
| 'ignoreScripts'
|
||||
| 'injectWorkspacePackages'
|
||||
| 'linkWorkspacePackages'
|
||||
| 'rawLocalConfig'
|
||||
| 'lockfileDir'
|
||||
| 'lockfileOnly'
|
||||
| 'modulesDir'
|
||||
@@ -300,8 +296,6 @@ export type InstallCommandOptions = Pick<Config,
|
||||
| 'preferWorkspacePackages'
|
||||
| 'production'
|
||||
| 'registries'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'save'
|
||||
| 'saveDev'
|
||||
| 'saveExact'
|
||||
@@ -312,8 +306,6 @@ export type InstallCommandOptions = Pick<Config,
|
||||
| 'saveCatalogName'
|
||||
| 'saveWorkspaceProtocol'
|
||||
| 'lockfileIncludeTarballUrl'
|
||||
| 'allProjectsGraph'
|
||||
| 'selectedProjectsGraph'
|
||||
| 'sideEffectsCache'
|
||||
| 'sideEffectsCacheReadonly'
|
||||
| 'sort'
|
||||
@@ -334,6 +326,15 @@ export type InstallCommandOptions = Pick<Config,
|
||||
| 'packageExtensions'
|
||||
| 'supportedArchitectures'
|
||||
| 'packageConfigs'
|
||||
> & Pick<ConfigContext,
|
||||
| 'allProjects'
|
||||
| 'cliOptions'
|
||||
| 'hooks'
|
||||
| 'rawLocalConfig'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'allProjectsGraph'
|
||||
| 'selectedProjectsGraph'
|
||||
> & CreateStoreControllerOptions & Partial<Pick<Config, 'globalPkgDir'>> & {
|
||||
argv: {
|
||||
original: string[]
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
readProjectManifestOnly,
|
||||
tryReadProjectManifest,
|
||||
} from '@pnpm/cli.utils'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import { checkDepsStatus } from '@pnpm/deps.status'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { arrayOfWorkspacePackagesToMap } from '@pnpm/installing.context'
|
||||
@@ -57,15 +57,12 @@ const OVERWRITE_UPDATE_OPTIONS = {
|
||||
}
|
||||
|
||||
export type InstallDepsOptions = Pick<Config,
|
||||
| 'allProjects'
|
||||
| 'allProjectsGraph'
|
||||
| 'autoInstallPeers'
|
||||
| 'bail'
|
||||
| 'bin'
|
||||
| 'catalogs'
|
||||
| 'catalogMode'
|
||||
| 'cleanupUnusedCatalogs'
|
||||
| 'cliOptions'
|
||||
| 'dedupePeerDependents'
|
||||
| 'dedupePeers'
|
||||
| 'depth'
|
||||
@@ -76,7 +73,6 @@ export type InstallDepsOptions = Pick<Config,
|
||||
| 'excludeLinksFromLockfile'
|
||||
| 'global'
|
||||
| 'globalPnpmfile'
|
||||
| 'hooks'
|
||||
| 'ignoreCurrentSpecifiers'
|
||||
| 'ignorePnpmfile'
|
||||
| 'ignoreScripts'
|
||||
@@ -86,10 +82,7 @@ export type InstallDepsOptions = Pick<Config,
|
||||
| 'lockfileOnly'
|
||||
| 'production'
|
||||
| 'preferWorkspacePackages'
|
||||
| 'rawLocalConfig'
|
||||
| 'registries'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'rootProjectManifest'
|
||||
| 'save'
|
||||
| 'saveDev'
|
||||
| 'saveExact'
|
||||
@@ -101,7 +94,6 @@ export type InstallDepsOptions = Pick<Config,
|
||||
| 'lockfileIncludeTarballUrl'
|
||||
| 'scriptsPrependNodePath'
|
||||
| 'scriptShell'
|
||||
| 'selectedProjectsGraph'
|
||||
| 'sideEffectsCache'
|
||||
| 'sideEffectsCacheReadonly'
|
||||
| 'sort'
|
||||
@@ -119,6 +111,15 @@ export type InstallDepsOptions = Pick<Config,
|
||||
| 'configDependencies'
|
||||
| 'packageExtensions'
|
||||
| 'updateConfig'
|
||||
> & Pick<ConfigContext,
|
||||
| 'allProjects'
|
||||
| 'allProjectsGraph'
|
||||
| 'cliOptions'
|
||||
| 'hooks'
|
||||
| 'rawLocalConfig'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'rootProjectManifest'
|
||||
| 'selectedProjectsGraph'
|
||||
> & CreateStoreControllerOptions & {
|
||||
argv: {
|
||||
original: string[]
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
docsUrl,
|
||||
tryReadProjectManifest,
|
||||
} from '@pnpm/cli.utils'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { writeSettings } from '@pnpm/config.writer'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { arrayOfWorkspacePackagesToMap } from '@pnpm/installing.context'
|
||||
@@ -29,10 +29,7 @@ const isFilespec = isWindows ? /^(?:[./\\]|~\/|[a-z]:)/i : /^(?:[./]|~\/|[a-z]:)
|
||||
|
||||
type LinkOpts = Pick<Config,
|
||||
| 'bin'
|
||||
| 'cliOptions'
|
||||
| 'engineStrict'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'overrides'
|
||||
| 'saveDev'
|
||||
| 'saveOptional'
|
||||
@@ -40,6 +37,10 @@ type LinkOpts = Pick<Config,
|
||||
| 'workspaceDir'
|
||||
| 'workspacePackagePatterns'
|
||||
| 'sharedWorkspaceLockfile'
|
||||
> & Pick<ConfigContext,
|
||||
| 'cliOptions'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
> & Partial<Pick<Config, 'linkWorkspacePackages'>> & install.InstallCommandOptions
|
||||
|
||||
export const rcOptionsTypes = cliOptionsTypes
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
import { createMatcherWithIndex } from '@pnpm/config.matcher'
|
||||
import {
|
||||
type Config,
|
||||
type ConfigContext,
|
||||
createProjectConfigRecord,
|
||||
getWorkspaceConcurrency,
|
||||
type OptionsFromRootManifest,
|
||||
@@ -63,7 +64,6 @@ export type RecursiveOptions = CreateStoreControllerOptions & Pick<Config,
|
||||
| 'depth'
|
||||
| 'globalPnpmfile'
|
||||
| 'hoistPattern'
|
||||
| 'hooks'
|
||||
| 'ignorePnpmfile'
|
||||
| 'ignoreScripts'
|
||||
| 'linkWorkspacePackages'
|
||||
@@ -71,10 +71,7 @@ export type RecursiveOptions = CreateStoreControllerOptions & Pick<Config,
|
||||
| 'lockfileOnly'
|
||||
| 'modulesDir'
|
||||
| 'allowBuilds'
|
||||
| 'rawLocalConfig'
|
||||
| 'registries'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'save'
|
||||
| 'saveCatalogName'
|
||||
| 'saveDev'
|
||||
@@ -90,6 +87,11 @@ export type RecursiveOptions = CreateStoreControllerOptions & Pick<Config,
|
||||
| 'cleanupUnusedCatalogs'
|
||||
| 'packageConfigs'
|
||||
| 'updateConfig'
|
||||
> & Pick<ConfigContext,
|
||||
| 'hooks'
|
||||
| 'rawLocalConfig'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
> & {
|
||||
rebuildHandler?: CommandHandler
|
||||
include?: IncludedDependencies
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
readDepNameCompletions,
|
||||
readProjectManifest,
|
||||
} from '@pnpm/cli.utils'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { handleGlobalRemove } from '@pnpm/global.commands'
|
||||
import { arrayOfWorkspacePackagesToMap } from '@pnpm/installing.context'
|
||||
@@ -126,32 +126,33 @@ export const completion: CompletionFunc = async (cliOpts) => {
|
||||
|
||||
export async function handler (
|
||||
opts: CreateStoreControllerOptions & Pick<Config,
|
||||
| 'allProjects'
|
||||
| 'allProjectsGraph'
|
||||
| 'bail'
|
||||
| 'bin'
|
||||
| 'configDependencies'
|
||||
| 'dev'
|
||||
| 'engineStrict'
|
||||
| 'globalPnpmfile'
|
||||
| 'hooks'
|
||||
| 'ignorePnpmfile'
|
||||
| 'linkWorkspacePackages'
|
||||
| 'lockfileDir'
|
||||
| 'optional'
|
||||
| 'production'
|
||||
| 'rawLocalConfig'
|
||||
| 'registries'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'saveDev'
|
||||
| 'saveOptional'
|
||||
| 'saveProd'
|
||||
| 'selectedProjectsGraph'
|
||||
| 'workspaceDir'
|
||||
| 'workspacePackagePatterns'
|
||||
| 'sharedWorkspaceLockfile'
|
||||
| 'cleanupUnusedCatalogs'
|
||||
> & Pick<ConfigContext,
|
||||
| 'allProjects'
|
||||
| 'allProjectsGraph'
|
||||
| 'hooks'
|
||||
| 'rawLocalConfig'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'selectedProjectsGraph'
|
||||
> & {
|
||||
recursive?: boolean
|
||||
pnpmfile: string[]
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
import { docsUrl } from '@pnpm/cli.utils'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import type { LogBase } from '@pnpm/logger'
|
||||
import { applyPatchToDir } from '@pnpm/patching.apply-patch'
|
||||
@@ -63,11 +63,12 @@ export type PatchCommandOptions = Pick<Config,
|
||||
| 'registries'
|
||||
| 'tag'
|
||||
| 'storeDir'
|
||||
| 'rootProjectManifest'
|
||||
| 'lockfileDir'
|
||||
| 'modulesDir'
|
||||
| 'virtualStoreDir'
|
||||
| 'sharedWorkspaceLockfile'
|
||||
> & Pick<ConfigContext,
|
||||
| 'rootProjectManifest'
|
||||
> & CreateStoreControllerOptions & {
|
||||
editDir?: string
|
||||
reporter?: (logObj: LogBase) => void
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
import { docsUrl } from '@pnpm/cli.utils'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { createShortHash } from '@pnpm/crypto.hash'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { packlist } from '@pnpm/fs.packlist'
|
||||
@@ -52,7 +52,7 @@ export function help (): string {
|
||||
})
|
||||
}
|
||||
|
||||
type PatchCommitCommandOptions = install.InstallCommandOptions & Pick<Config, 'patchesDir' | 'rootProjectManifest' | 'rootProjectManifestDir' | 'patchedDependencies'>
|
||||
type PatchCommitCommandOptions = install.InstallCommandOptions & Pick<Config, 'patchesDir' | 'patchedDependencies'> & Pick<ConfigContext, 'rootProjectManifest' | 'rootProjectManifestDir'>
|
||||
|
||||
export async function handler (opts: PatchCommitCommandOptions, params: string[]): Promise<string | undefined> {
|
||||
const userDir = params[0]
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
|
||||
import { docsUrl } from '@pnpm/cli.utils'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { install } from '@pnpm/installing.commands'
|
||||
import enquirer from 'enquirer'
|
||||
@@ -31,7 +31,7 @@ export function help (): string {
|
||||
})
|
||||
}
|
||||
|
||||
export type PatchRemoveCommandOptions = install.InstallCommandOptions & Pick<Config, 'dir' | 'lockfileDir' | 'patchesDir' | 'rootProjectManifest' | 'patchedDependencies'>
|
||||
export type PatchRemoveCommandOptions = install.InstallCommandOptions & Pick<Config, 'dir' | 'lockfileDir' | 'patchesDir' | 'patchedDependencies'> & Pick<ConfigContext, 'rootProjectManifest'>
|
||||
|
||||
export async function handler (opts: PatchRemoveCommandOptions, params: string[]): Promise<void> {
|
||||
let patchesToRemove = params
|
||||
|
||||
@@ -3,7 +3,7 @@ import path from 'node:path'
|
||||
|
||||
import { formatWarn } from '@pnpm/cli.default-reporter'
|
||||
import { packageManager } from '@pnpm/cli.meta'
|
||||
import { type CliOptions, type Config, getConfig as _getConfig } from '@pnpm/config.reader'
|
||||
import { type CliOptions, type Config, type ConfigContext, getConfig as _getConfig } from '@pnpm/config.reader'
|
||||
import { requireHooks } from '@pnpm/hooks.pnpmfile'
|
||||
import { resolveAndInstallConfigDeps } from '@pnpm/installing.env-installer'
|
||||
import { createStoreController } from '@pnpm/store.connection-manager'
|
||||
@@ -18,15 +18,15 @@ export async function getConfig (
|
||||
workspaceDir: string | undefined
|
||||
ignoreNonAuthSettingsFromLocal?: boolean
|
||||
}
|
||||
): Promise<Config> {
|
||||
let { config, warnings } = await _getConfig({
|
||||
): Promise<{ config: Config, context: ConfigContext }> {
|
||||
const { config, context, warnings } = await _getConfig({
|
||||
cliOptions,
|
||||
globalDirShouldAllowWrite: opts.globalDirShouldAllowWrite,
|
||||
packageManager,
|
||||
workspaceDir: opts.workspaceDir,
|
||||
ignoreNonAuthSettingsFromLocal: opts.ignoreNonAuthSettingsFromLocal,
|
||||
})
|
||||
config.cliOptions = cliOptions
|
||||
context.cliOptions = cliOptions
|
||||
applyDerivedConfig(config)
|
||||
|
||||
if (opts.excludeReporter) {
|
||||
@@ -37,18 +37,22 @@ export async function getConfig (
|
||||
console.warn(warnings.map((warning) => formatWarn(warning)).join('\n'))
|
||||
}
|
||||
|
||||
return config
|
||||
return { config, context }
|
||||
}
|
||||
|
||||
export async function installConfigDepsAndLoadHooks (config: Config): Promise<Config> {
|
||||
export async function installConfigDepsAndLoadHooks (
|
||||
config: Config,
|
||||
context: ConfigContext
|
||||
): Promise<{ config: Config, context: ConfigContext }> {
|
||||
if (config.configDependencies) {
|
||||
const store = await createStoreController(config)
|
||||
const store = await createStoreController({ ...config, ...context })
|
||||
try {
|
||||
await resolveAndInstallConfigDeps(config.configDependencies, {
|
||||
...config,
|
||||
...context,
|
||||
store: store.ctrl,
|
||||
storeDir: store.dir,
|
||||
rootDir: config.lockfileDir ?? config.rootProjectManifestDir,
|
||||
rootDir: config.lockfileDir ?? context.rootProjectManifestDir,
|
||||
frozenLockfile: config.frozenLockfile,
|
||||
})
|
||||
} finally {
|
||||
@@ -59,7 +63,7 @@ export async function installConfigDepsAndLoadHooks (config: Config): Promise<Co
|
||||
config.tryLoadDefaultPnpmfile = config.pnpmfile == null
|
||||
const pnpmfiles = config.pnpmfile == null ? [] : Array.isArray(config.pnpmfile) ? config.pnpmfile : [config.pnpmfile]
|
||||
if (config.configDependencies) {
|
||||
const configModulesDir = path.join(config.lockfileDir ?? config.rootProjectManifestDir, 'node_modules/.pnpm-config')
|
||||
const configModulesDir = path.join(config.lockfileDir ?? context.rootProjectManifestDir, 'node_modules/.pnpm-config')
|
||||
pnpmfiles.unshift(...calcPnpmfilePathsOfPluginDeps(configModulesDir, config.configDependencies))
|
||||
}
|
||||
const { hooks, finders, resolvedPnpmfilePaths } = await requireHooks(config.lockfileDir ?? config.dir, {
|
||||
@@ -67,17 +71,17 @@ export async function installConfigDepsAndLoadHooks (config: Config): Promise<Co
|
||||
pnpmfiles,
|
||||
tryLoadDefaultPnpmfile: config.tryLoadDefaultPnpmfile,
|
||||
})
|
||||
config.hooks = hooks
|
||||
config.finders = finders
|
||||
context.hooks = hooks
|
||||
context.finders = finders
|
||||
config.pnpmfile = resolvedPnpmfilePaths
|
||||
if (config.hooks?.updateConfig) {
|
||||
for (const updateConfig of config.hooks.updateConfig) {
|
||||
if (context.hooks?.updateConfig) {
|
||||
for (const updateConfig of context.hooks.updateConfig) {
|
||||
const updateConfigResult = updateConfig(config)
|
||||
config = updateConfigResult instanceof Promise ? await updateConfigResult : updateConfigResult // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
}
|
||||
}
|
||||
return config
|
||||
return { config, context }
|
||||
}
|
||||
|
||||
export function * calcPnpmfilePathsOfPluginDeps (configModulesDir: string, configDependencies: ConfigDependencies): Generator<string> {
|
||||
|
||||
@@ -10,7 +10,7 @@ import path from 'node:path'
|
||||
import { stripVTControlCharacters as stripAnsi } from 'node:util'
|
||||
|
||||
import { isExecutedByCorepack, packageManager } from '@pnpm/cli.meta'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import { executionTimeLogger, scopeLogger } from '@pnpm/core-loggers'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { globalWarn, logger } from '@pnpm/logger'
|
||||
@@ -87,6 +87,7 @@ export async function main (inputArgv: string[]): Promise<void> {
|
||||
parseable?: boolean
|
||||
json?: boolean
|
||||
}
|
||||
let context: ConfigContext
|
||||
try {
|
||||
// When we just want to print the location of the global bin directory,
|
||||
// we don't need the write permission to it. Related issue: #2700
|
||||
@@ -95,16 +96,16 @@ export async function main (inputArgv: string[]): Promise<void> {
|
||||
if (cmd === 'link' && cliParams.length === 0) {
|
||||
cliOptions.global = true
|
||||
}
|
||||
config = await getConfig(cliOptions, {
|
||||
;({ config, context } = await getConfig(cliOptions, {
|
||||
excludeReporter: false,
|
||||
globalDirShouldAllowWrite,
|
||||
workspaceDir,
|
||||
ignoreNonAuthSettingsFromLocal: isDlxOrCreateCommand,
|
||||
}) as typeof config
|
||||
if (!isExecutedByCorepack() && cmd !== 'setup' && config.wantedPackageManager != null) {
|
||||
const pm = config.wantedPackageManager
|
||||
}) as { config: typeof config, context: ConfigContext })
|
||||
if (!isExecutedByCorepack() && cmd !== 'setup' && context.wantedPackageManager != null) {
|
||||
const pm = context.wantedPackageManager
|
||||
if (pm.onFail === 'download' && pm.name === 'pnpm' && cmd !== 'self-update') {
|
||||
await switchCliVersion(config)
|
||||
await switchCliVersion(config, context)
|
||||
} else if (pm.onFail !== 'ignore' && (!cmd || !skipPackageManagerCheckForCommand.has(cmd))) {
|
||||
if (cliOptions.global) {
|
||||
globalWarn('Using --global skips the package manager check for this project')
|
||||
@@ -113,7 +114,7 @@ export async function main (inputArgv: string[]): Promise<void> {
|
||||
}
|
||||
}
|
||||
}
|
||||
config = await installConfigDepsAndLoadHooks(config) as typeof config
|
||||
;({ config, context } = await installConfigDepsAndLoadHooks(config, context) as { config: typeof config, context: ConfigContext })
|
||||
if (isDlxOrCreateCommand || cmd === 'sbom') {
|
||||
config.useStderr = true
|
||||
}
|
||||
@@ -167,7 +168,7 @@ export async function main (inputArgv: string[]): Promise<void> {
|
||||
if (printLogs) {
|
||||
initReporter(reporterType, {
|
||||
cmd,
|
||||
config,
|
||||
config: { ...config, ...context },
|
||||
})
|
||||
global[REPORTER_INITIALIZED] = reporterType
|
||||
}
|
||||
@@ -176,8 +177,8 @@ export async function main (inputArgv: string[]): Promise<void> {
|
||||
// script with the same name, run the script instead of the built-in command.
|
||||
const typedCommandName = argv.remain[0]
|
||||
if (cmd != null && !builtInCommandForced && overridableByScriptCommands.has(typedCommandName) && !cliOptions.global) {
|
||||
const currentDirManifest = config.dir === config.rootProjectManifestDir
|
||||
? config.rootProjectManifest
|
||||
const currentDirManifest = config.dir === context.rootProjectManifestDir
|
||||
? context.rootProjectManifest
|
||||
: await safeReadProjectManifestOnly(config.dir)
|
||||
if (currentDirManifest?.scripts?.[typedCommandName]) {
|
||||
// Redirect to "pnpm run <cmd>"
|
||||
@@ -191,8 +192,8 @@ export async function main (inputArgv: string[]): Promise<void> {
|
||||
}
|
||||
} else if (
|
||||
workspaceDir &&
|
||||
config.dir !== config.rootProjectManifestDir &&
|
||||
config.rootProjectManifest?.scripts?.[typedCommandName]
|
||||
config.dir !== context.rootProjectManifestDir &&
|
||||
context.rootProjectManifest?.scripts?.[typedCommandName]
|
||||
) {
|
||||
throw new PnpmError(
|
||||
'SCRIPT_OVERRIDE_IN_WORKSPACE_ROOT',
|
||||
@@ -261,9 +262,9 @@ export async function main (inputArgv: string[]): Promise<void> {
|
||||
process.exitCode = config.failIfNoMatch ? 1 : 0
|
||||
return
|
||||
}
|
||||
config.allProjectsGraph = filterResults.allProjectsGraph
|
||||
config.selectedProjectsGraph = filterResults.selectedProjectsGraph
|
||||
if (isEmpty(config.selectedProjectsGraph)) {
|
||||
context.allProjectsGraph = filterResults.allProjectsGraph
|
||||
context.selectedProjectsGraph = filterResults.selectedProjectsGraph
|
||||
if (isEmpty(context.selectedProjectsGraph)) {
|
||||
if (printLogs) {
|
||||
console.log(`No projects matched the filters in "${wsDir}"`)
|
||||
}
|
||||
@@ -279,7 +280,7 @@ export async function main (inputArgv: string[]): Promise<void> {
|
||||
if (filterResults.unmatchedFilters.length !== 0 && printLogs) {
|
||||
console.log(`No projects matched the filters "${filterResults.unmatchedFilters.join(', ')}" in "${wsDir}"`)
|
||||
}
|
||||
config.allProjects = filterResults.allProjects
|
||||
context.allProjects = filterResults.allProjects
|
||||
config.workspaceDir = wsDir
|
||||
}
|
||||
|
||||
@@ -313,16 +314,18 @@ export async function main (inputArgv: string[]): Promise<void> {
|
||||
!cliOptions['recursive']
|
||||
? { selected: 1 }
|
||||
: {
|
||||
selected: Object.keys(config.selectedProjectsGraph!).length,
|
||||
total: config.allProjects!.length,
|
||||
selected: Object.keys(context.selectedProjectsGraph!).length,
|
||||
total: context.allProjects!.length,
|
||||
}
|
||||
),
|
||||
...(workspaceDir ? { workspacePrefix: workspaceDir } : {}),
|
||||
})
|
||||
let result = pnpmCmds[cmd ?? 'help'](
|
||||
// TypeScript doesn't currently infer that the type of config
|
||||
// is `Omit<typeof config, 'reporter'>` after the `delete config.reporter` statement
|
||||
config as Omit<typeof config, 'reporter'>,
|
||||
// Spread config (settings) and context (runtime state) into a single
|
||||
// options object for command handlers. The original split objects are
|
||||
// also passed for handlers that need them separated (e.g. config commands).
|
||||
// Named "_config"/"_context" to avoid clashing with the "--config" CLI option.
|
||||
{ ...config, ...context, _config: config, _context: context } as Omit<typeof config & ConfigContext, 'reporter'>,
|
||||
cliParams,
|
||||
pnpmCmds
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { initDefaultReporter } from '@pnpm/cli.default-reporter'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import type { Log } from '@pnpm/core-loggers'
|
||||
import { type LogLevel, type StreamParser, streamParser, writeToConsole } from '@pnpm/logger'
|
||||
|
||||
@@ -11,7 +11,7 @@ export function initReporter (
|
||||
reporterType: ReporterType,
|
||||
opts: {
|
||||
cmd: string | null
|
||||
config: Config
|
||||
config: Config & ConfigContext
|
||||
}
|
||||
): void {
|
||||
switch (reporterType) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import { packageManager } from '@pnpm/cli.meta'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import { installPnpmToStore } from '@pnpm/engine.pm.commands'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { isPackageManagerResolved, resolvePackageManagerIntegrities } from '@pnpm/installing.env-installer'
|
||||
@@ -12,22 +12,22 @@ import { createStoreController } from '@pnpm/store.connection-manager'
|
||||
import spawn from 'cross-spawn'
|
||||
import semver from 'semver'
|
||||
|
||||
export async function switchCliVersion (config: Config): Promise<void> {
|
||||
const pm = config.wantedPackageManager
|
||||
export async function switchCliVersion (config: Config, context: ConfigContext): Promise<void> {
|
||||
const pm = context.wantedPackageManager
|
||||
if (pm == null || pm.name !== 'pnpm' || pm.version == null) return
|
||||
|
||||
let envLockfile = await readEnvLockfile(config.rootProjectManifestDir) ?? undefined
|
||||
let envLockfile = await readEnvLockfile(context.rootProjectManifestDir) ?? undefined
|
||||
let storeToUse: Awaited<ReturnType<typeof createStoreController>> | undefined
|
||||
|
||||
// Check if the env lockfile already has a resolved version that satisfies the wanted version/range.
|
||||
let pmVersion = envLockfile?.importers['.'].packageManagerDependencies?.['pnpm']?.version
|
||||
if (!pmVersion || !semver.satisfies(pmVersion, pm.version, { includePrerelease: true })) {
|
||||
// Resolve to an exact version from the registry.
|
||||
storeToUse = await createStoreController(config)
|
||||
storeToUse = await createStoreController({ ...config, ...context })
|
||||
envLockfile = await resolvePackageManagerIntegrities(pm.version, {
|
||||
envLockfile,
|
||||
registries: config.registries,
|
||||
rootDir: config.rootProjectManifestDir,
|
||||
rootDir: context.rootProjectManifestDir,
|
||||
storeController: storeToUse.ctrl,
|
||||
storeDir: storeToUse.dir,
|
||||
})
|
||||
@@ -38,11 +38,11 @@ export async function switchCliVersion (config: Config): Promise<void> {
|
||||
return
|
||||
}
|
||||
} else if (!isPackageManagerResolved(envLockfile, pmVersion)) {
|
||||
storeToUse = await createStoreController(config)
|
||||
storeToUse = await createStoreController({ ...config, ...context })
|
||||
envLockfile = await resolvePackageManagerIntegrities(pmVersion, {
|
||||
envLockfile,
|
||||
registries: config.registries,
|
||||
rootDir: config.rootProjectManifestDir,
|
||||
rootDir: context.rootProjectManifestDir,
|
||||
storeController: storeToUse.ctrl,
|
||||
storeDir: storeToUse.dir,
|
||||
})
|
||||
@@ -57,7 +57,7 @@ export async function switchCliVersion (config: Config): Promise<void> {
|
||||
// We need a store controller to install pnpm. If it wasn't created during
|
||||
// integrity resolution (because integrities were already cached), create it now.
|
||||
if (!storeToUse) {
|
||||
storeToUse = await createStoreController(config)
|
||||
storeToUse = await createStoreController({ ...config, ...context })
|
||||
}
|
||||
|
||||
if (!envLockfile) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import type {
|
||||
LogBase,
|
||||
ReadPackageHook,
|
||||
} from '@pnpm/types'
|
||||
|
||||
export type PnpmOptions = Omit<Config, 'reporter' | 'pnpmfile'> & {
|
||||
export type PnpmOptions = Omit<Config & ConfigContext, 'reporter' | 'pnpmfile'> & {
|
||||
argv: {
|
||||
cooked: string[]
|
||||
original: string[]
|
||||
|
||||
@@ -64,7 +64,7 @@ describe('calcPnpmfilePathsOfPluginDeps', () => {
|
||||
test('hoist: false removes hoistPattern', async () => {
|
||||
prepare()
|
||||
|
||||
const config = await getConfig({
|
||||
const { config } = await getConfig({
|
||||
hoist: false,
|
||||
}, {
|
||||
workspaceDir: '.',
|
||||
|
||||
@@ -6,7 +6,7 @@ import { getBinsFromPackageManifest } from '@pnpm/bins.resolver'
|
||||
import type { Catalogs } from '@pnpm/catalogs.types'
|
||||
import { FILTERING } from '@pnpm/cli.common-cli-options-help'
|
||||
import { readProjectManifest } from '@pnpm/cli.utils'
|
||||
import { type Config, getDefaultWorkspaceConcurrency, getWorkspaceConcurrency, types as allTypes, type UniversalOptions } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, getDefaultWorkspaceConcurrency, getWorkspaceConcurrency, types as allTypes, type UniversalOptions } from '@pnpm/config.reader'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { packlist } from '@pnpm/fs.packlist'
|
||||
import type { Hooks } from '@pnpm/hooks.pnpmfile'
|
||||
@@ -102,11 +102,12 @@ export type PackOptions = Pick<UniversalOptions, 'dir'> & Pick<Config, 'catalogs
|
||||
| 'userAgent'
|
||||
> & Partial<Pick<Config, 'extraBinPaths'
|
||||
| 'extraEnv'
|
||||
| 'hooks'
|
||||
| 'recursive'
|
||||
| 'selectedProjectsGraph'
|
||||
| 'workspaceConcurrency'
|
||||
| 'workspaceDir'
|
||||
>> & Partial<Pick<ConfigContext,
|
||||
| 'hooks'
|
||||
| 'selectedProjectsGraph'
|
||||
>> & {
|
||||
argv: {
|
||||
original: string[]
|
||||
|
||||
@@ -2,7 +2,7 @@ import path from 'node:path'
|
||||
|
||||
import { FILTERING } from '@pnpm/cli.common-cli-options-help'
|
||||
import { docsUrl, readProjectManifest } from '@pnpm/cli.utils'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { runLifecycleHook, type RunLifecycleHookOptions } from '@pnpm/exec.lifecycle'
|
||||
import { getCurrentBranch, isGitRepo, isRemoteHistoryClean, isWorkingTreeClean } from '@pnpm/network.git-utils'
|
||||
@@ -122,7 +122,8 @@ export async function handler (
|
||||
engineStrict?: boolean
|
||||
recursive?: boolean
|
||||
workspaceDir?: string
|
||||
} & Pick<Config, 'allProjects' | 'bin' | 'gitChecks' | 'ignoreScripts' | 'pnpmHomeDir' | 'publishBranch' | 'embedReadme'>,
|
||||
} & Pick<Config, 'bin' | 'gitChecks' | 'ignoreScripts' | 'pnpmHomeDir' | 'publishBranch' | 'embedReadme'>
|
||||
& Pick<ConfigContext, 'allProjects'>,
|
||||
params: string[]
|
||||
): Promise<{ exitCode?: number } | undefined> {
|
||||
const result = await publish(opts, params)
|
||||
@@ -143,7 +144,8 @@ export async function publish (
|
||||
engineStrict?: boolean
|
||||
recursive?: boolean
|
||||
workspaceDir?: string
|
||||
} & Pick<Config, 'allProjects' | 'bin' | 'gitChecks' | 'ignoreScripts' | 'pnpmHomeDir' | 'publishBranch' | 'embedReadme' | 'packGzipLevel'>,
|
||||
} & Pick<Config, 'bin' | 'gitChecks' | 'ignoreScripts' | 'pnpmHomeDir' | 'publishBranch' | 'embedReadme' | 'packGzipLevel'>
|
||||
& Pick<ConfigContext, 'allProjects'>,
|
||||
params: string[]
|
||||
): Promise<PublishResult> {
|
||||
if (opts.gitChecks !== false && await isGitRepo()) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import { pickRegistryForPackage } from '@pnpm/config.pick-registry-for-package'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import { createResolver } from '@pnpm/installing.client'
|
||||
import { logger } from '@pnpm/logger'
|
||||
import type { ResolveFunction } from '@pnpm/resolving.resolver-base'
|
||||
@@ -17,13 +17,15 @@ import type { PublishPackedPkgOptions } from './publishPackedPkg.js'
|
||||
export type PublishRecursiveOpts = Required<Pick<Config,
|
||||
| 'bin'
|
||||
| 'cacheDir'
|
||||
| 'cliOptions'
|
||||
| 'dir'
|
||||
| 'pnpmHomeDir'
|
||||
| 'authConfig'
|
||||
| 'registries'
|
||||
| 'workspaceDir'
|
||||
>> &
|
||||
Required<Pick<ConfigContext,
|
||||
| 'cliOptions'
|
||||
>> &
|
||||
Partial<Pick<Config,
|
||||
| 'tag'
|
||||
| 'ca'
|
||||
@@ -46,12 +48,14 @@ Partial<Pick<Config,
|
||||
| 'noProxy'
|
||||
| 'npmPath'
|
||||
| 'offline'
|
||||
| 'selectedProjectsGraph'
|
||||
| 'strictSsl'
|
||||
| 'unsafePerm'
|
||||
| 'userAgent'
|
||||
| 'userConfig'
|
||||
| 'verifyStoreIntegrity'
|
||||
>> &
|
||||
Partial<Pick<ConfigContext,
|
||||
| 'selectedProjectsGraph'
|
||||
>> & {
|
||||
access?: 'public' | 'restricted'
|
||||
argv: {
|
||||
@@ -61,7 +65,7 @@ Partial<Pick<Config,
|
||||
} & PublishPackedPkgOptions
|
||||
|
||||
export async function recursivePublish (
|
||||
opts: PublishRecursiveOpts & Required<Pick<Config, 'selectedProjectsGraph'>>
|
||||
opts: PublishRecursiveOpts & Required<Pick<ConfigContext, 'selectedProjectsGraph'>>
|
||||
): Promise<{ exitCode: number }> {
|
||||
const pkgs = Object.values(opts.selectedProjectsGraph).map((wsPkg) => wsPkg.package)
|
||||
const { resolve } = createResolver({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { promises as fs } from 'node:fs'
|
||||
|
||||
import { packageManager } from '@pnpm/cli.meta'
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
import type { Config, ConfigContext } from '@pnpm/config.reader'
|
||||
import { type ClientOptions, createClient } from '@pnpm/installing.client'
|
||||
import { type CafsLocker, createPackageStore, type StoreController } from '@pnpm/store.controller'
|
||||
import { StoreIndex } from '@pnpm/store.index'
|
||||
@@ -28,7 +28,6 @@ export type CreateNewStoreControllerOptions = CreateResolverOptions & Pick<Confi
|
||||
| 'fetchMinSpeedKiBps'
|
||||
| 'gitShallowHosts'
|
||||
| 'ignoreScripts'
|
||||
| 'hooks'
|
||||
| 'httpProxy'
|
||||
| 'httpsProxy'
|
||||
| 'key'
|
||||
@@ -51,7 +50,7 @@ export type CreateNewStoreControllerOptions = CreateResolverOptions & Pick<Confi
|
||||
| 'userAgent'
|
||||
| 'verifyStoreIntegrity'
|
||||
| 'virtualStoreDirMaxLength'
|
||||
> & {
|
||||
> & Pick<ConfigContext, 'hooks'> & {
|
||||
cafsLocker?: CafsLocker
|
||||
ignoreFile?: (filename: string) => boolean
|
||||
fetchFullMetadata?: boolean
|
||||
|
||||
@@ -3,7 +3,7 @@ import path from 'node:path'
|
||||
|
||||
import { packageManager } from '@pnpm/cli.meta'
|
||||
import { docsUrl } from '@pnpm/cli.utils'
|
||||
import { type Config, types as allTypes } from '@pnpm/config.reader'
|
||||
import { type Config, type ConfigContext, types as allTypes } from '@pnpm/config.reader'
|
||||
import { PnpmError } from '@pnpm/error'
|
||||
import { sortKeysByPriority } from '@pnpm/object.key-sorting'
|
||||
import type { ProjectManifest } from '@pnpm/types'
|
||||
@@ -55,7 +55,7 @@ export function help (): string {
|
||||
}
|
||||
|
||||
export type InitOptions =
|
||||
& Pick<Config, 'cliOptions'>
|
||||
& Pick<ConfigContext, 'cliOptions'>
|
||||
& Partial<Pick<Config,
|
||||
| 'initPackageManager'
|
||||
| 'initType'
|
||||
|
||||
Reference in New Issue
Block a user