mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-29 11:34:18 -04:00
refactor(config): rename rawConfig to authConfig, add nodeDownloadMirrors, simplify config reader (#11194)
Major cleanup of the config system after migrating settings from `.npmrc` to `pnpm-workspace.yaml`.
### Config reader simplification
- Remove `checkUnknownSetting` (dead code, always `false`)
- Trim `npmConfigTypes` from ~127 to ~67 keys (remove unused npm config keys)
- Replace `rcOptions` iteration over all type keys with direct construction from defaults + auth overlay
- Remove `rcOptionsTypes` parameter from `getConfig()` and its assembly chain
### Rename `rawConfig` to `authConfig`
- `rawConfig` was a confusing mix of auth data and general settings
- Non-auth settings are already on the typed `Config` object — stop duplicating them in `rawConfig`
- Rename `rawConfig` → `authConfig` across the codebase to clarify it only contains auth/registry data from `.npmrc`
### Remove `rawConfig` from non-auth consumers
- **Lifecycle hooks**: replace `rawConfig: object` with `userAgent?: string` — only user-agent was read
- **Fetchers**: remove unused `rawConfig` from git fetcher, binary fetcher, tarball fetcher, prepare-package
- **Update command**: use `opts.production/dev/optional` instead of `rawConfig.*`
- **`pnpm init`**: accept typed init properties instead of parsing `rawConfig`
### Add `nodeDownloadMirrors` setting
- New `nodeDownloadMirrors?: Record<string, string>` on `PnpmSettings` and `Config`
- Replaces the `node-mirror:<channel>` pattern that was stored in `rawConfig`
- Configured in `pnpm-workspace.yaml`:
```yaml
nodeDownloadMirrors:
release: https://my-mirror.example.com/download/release/
```
- Remove unused `rawConfig` from deno-resolver and bun-resolver
### Refactor `pnpm config get/list`
- New `configToRecord()` builds display data from typed Config properties on the fly
- Excludes sensitive internals (`authInfos`, `sslConfigs`, etc.)
- Non-types keys (e.g., `package-extensions`) resolve through `configToRecord` instead of direct property access
- Delete `processConfig.ts` (replaced by `configToRecord.ts`)
### Pre-push hook improvement
- Add `compile-only` (`tsgo --build`) to pre-push hook to catch type errors before push
This commit is contained in:
29
.changeset/refactor-config-auth-config.md
Normal file
29
.changeset/refactor-config-auth-config.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
"@pnpm/config.reader": major
|
||||
"@pnpm/exec.lifecycle": minor
|
||||
"@pnpm/exec.prepare-package": minor
|
||||
"@pnpm/fetching.git-fetcher": minor
|
||||
"@pnpm/fetching.tarball-fetcher": minor
|
||||
"@pnpm/fetching.binary-fetcher": minor
|
||||
"@pnpm/installing.client": major
|
||||
"@pnpm/config.commands": minor
|
||||
"@pnpm/workspace.commands": major
|
||||
"@pnpm/engine.runtime.node-resolver": minor
|
||||
"@pnpm/resolving.default-resolver": minor
|
||||
"@pnpm/store.connection-manager": minor
|
||||
"pnpm": minor
|
||||
---
|
||||
|
||||
Renamed `rawConfig` to `authConfig` on the `Config` interface. This field now only contains auth/registry data from `.npmrc` files. Non-auth settings are no longer written to it.
|
||||
|
||||
Added `nodeDownloadMirrors` setting to configure custom Node.js download mirrors in `pnpm-workspace.yaml`:
|
||||
|
||||
```yaml
|
||||
nodeDownloadMirrors:
|
||||
release: https://my-mirror.example.com/download/release/
|
||||
nightly: https://my-mirror.example.com/download/nightly/
|
||||
```
|
||||
|
||||
Replaced `rawConfig: object` with `userAgent?: string` in lifecycle hook options. Removed unused `rawConfig` from fetcher and prepare-package options.
|
||||
|
||||
Removed support for the npm `init-module` setting. Custom init scripts via `.pnpm-init.js` are no longer executed by `pnpm init`.
|
||||
@@ -1 +1 @@
|
||||
pnpm run lint --quiet
|
||||
pnpm run compile-only && pnpm run lint --quiet
|
||||
|
||||
@@ -63,7 +63,7 @@ export type LoginCommandOptions = Pick<Config,
|
||||
| 'fetchRetryMaxtimeout'
|
||||
| 'fetchRetryMintimeout'
|
||||
| 'fetchTimeout'
|
||||
| 'rawConfig'
|
||||
| 'authConfig'
|
||||
> & {
|
||||
registry?: string
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ describe('login', () => {
|
||||
stdin: { isTTY: false },
|
||||
},
|
||||
})
|
||||
const opts = { configDir: '/mock/config', dir: '/mock', rawConfig: {} }
|
||||
const opts = { configDir: '/mock/config', dir: '/mock', authConfig: {} }
|
||||
const promise = login({ context, opts })
|
||||
await expect(promise).rejects.toHaveProperty(['code'], 'ERR_PNPM_LOGIN_NON_INTERACTIVE')
|
||||
await expect(promise).rejects.toHaveProperty(['message'], 'The login command requires an interactive terminal')
|
||||
@@ -130,7 +130,7 @@ describe('login', () => {
|
||||
throw new Error(`Unexpected call to fetch: ${url}`)
|
||||
},
|
||||
})
|
||||
const opts = { configDir: '/custom/config', dir: '/mock', rawConfig: {}, registry: 'https://example.com/npm/' }
|
||||
const opts = { configDir: '/custom/config', dir: '/mock', authConfig: {}, registry: 'https://example.com/npm/' }
|
||||
const result = await login({ context, opts })
|
||||
expect(result).toBe('Logged in on https://example.com/npm/')
|
||||
expect(fetchedUrls[0]).toBe('https://example.com/npm/-/v1/login')
|
||||
@@ -183,7 +183,7 @@ describe('login', () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
const opts = { configDir: '/other/config', dir: '/mock', rawConfig: {}, registry: 'https://example.org' }
|
||||
const opts = { configDir: '/other/config', dir: '/mock', authConfig: {}, registry: 'https://example.org' }
|
||||
const result = await login({ context, opts })
|
||||
expect(result).toBe('Logged in on https://example.org/')
|
||||
expect(fetchedUrls[0]).toBe('https://example.org/-/v1/login')
|
||||
@@ -240,7 +240,7 @@ describe('login', () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
const opts = { configDir: '/otp/config', dir: '/mock', rawConfig: {}, registry: 'https://example.org' }
|
||||
const opts = { configDir: '/otp/config', dir: '/mock', authConfig: {}, registry: 'https://example.org' }
|
||||
const result = await login({ context, opts })
|
||||
expect(result).toBe('Logged in on https://example.org/')
|
||||
expect(putCallCount).toBe(2)
|
||||
@@ -302,7 +302,7 @@ describe('login', () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
const opts = { configDir: '/otp/config', dir: '/mock', rawConfig: {}, registry: 'https://example.org' }
|
||||
const opts = { configDir: '/otp/config', dir: '/mock', authConfig: {}, registry: 'https://example.org' }
|
||||
const result = await login({ context, opts })
|
||||
expect(result).toBe('Logged in on https://example.org/')
|
||||
expect(putCallCount).toBe(2)
|
||||
@@ -338,7 +338,7 @@ describe('login', () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
const opts = { configDir: '/otp/config', dir: '/mock', rawConfig: {}, registry: 'https://example.org' }
|
||||
const opts = { configDir: '/otp/config', dir: '/mock', authConfig: {}, registry: 'https://example.org' }
|
||||
const promise = login({ context, opts })
|
||||
await expect(promise).rejects.toHaveProperty(['code'], 'ERR_PNPM_LOGIN_FAILED')
|
||||
await expect(promise).rejects.toHaveProperty(['message'], 'Login failed (HTTP 403): Forbidden')
|
||||
@@ -367,7 +367,7 @@ describe('login', () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
const opts = { configDir: '/mock/config', dir: '/mock', rawConfig: {}, registry: 'https://example.org' }
|
||||
const opts = { configDir: '/mock/config', dir: '/mock', authConfig: {}, registry: 'https://example.org' }
|
||||
const promise = login({ context, opts })
|
||||
await expect(promise).rejects.toHaveProperty(['code'], 'ERR_PNPM_LOGIN_MISSING_CREDENTIALS')
|
||||
await expect(promise).rejects.toHaveProperty(['message'], 'Username, password, and email are all required')
|
||||
@@ -403,7 +403,7 @@ describe('login', () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
const opts = { configDir: '/mock/config', dir: '/mock', rawConfig: {}, registry: 'https://example.org' }
|
||||
const opts = { configDir: '/mock/config', dir: '/mock', authConfig: {}, registry: 'https://example.org' }
|
||||
const promise = login({ context, opts })
|
||||
await expect(promise).rejects.toHaveProperty(['code'], 'ERR_PNPM_LOGIN_NO_TOKEN')
|
||||
await expect(promise).rejects.toHaveProperty(['message'], 'The registry did not return an authentication token')
|
||||
@@ -424,7 +424,7 @@ describe('login', () => {
|
||||
throw new Error(`Unexpected call to fetch: ${url}`)
|
||||
},
|
||||
})
|
||||
const opts = { configDir: '/mock/config', dir: '/mock', rawConfig: {}, registry: 'https://example.org' }
|
||||
const opts = { configDir: '/mock/config', dir: '/mock', authConfig: {}, registry: 'https://example.org' }
|
||||
const promise = login({ context, opts })
|
||||
await expect(promise).rejects.toHaveProperty(['code'], 'ERR_PNPM_LOGIN_INVALID_RESPONSE')
|
||||
await expect(promise).rejects.toHaveProperty(['message'], 'The registry returned an invalid response for web-based login')
|
||||
@@ -465,7 +465,7 @@ describe('login', () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
const opts = { configDir: '/mock/config', dir: '/mock', rawConfig: {}, registry: 'https://example.org' }
|
||||
const opts = { configDir: '/mock/config', dir: '/mock', authConfig: {}, registry: 'https://example.org' }
|
||||
const result = await login({ context, opts })
|
||||
expect(result).toBe('Logged in on https://example.org/')
|
||||
expect(savedSettings).toMatchObject({
|
||||
@@ -503,7 +503,7 @@ describe('login', () => {
|
||||
},
|
||||
},
|
||||
})
|
||||
const opts = { configDir: '/otp/config', dir: '/mock', rawConfig: {}, registry: 'https://example.org' }
|
||||
const opts = { configDir: '/otp/config', dir: '/mock', authConfig: {}, registry: 'https://example.org' }
|
||||
const promise = login({ context, opts })
|
||||
await expect(promise).rejects.toHaveProperty(['code'], 'ERR_PNPM_LOGIN_FAILED')
|
||||
await expect(promise).rejects.toHaveProperty(['message'], 'Login failed (HTTP 401): Unauthorized')
|
||||
@@ -538,7 +538,7 @@ describe('login', () => {
|
||||
})
|
||||
},
|
||||
})
|
||||
const opts = { configDir: '/nonexistent/config', dir: '/mock', rawConfig: {}, registry: 'https://example.org' }
|
||||
const opts = { configDir: '/nonexistent/config', dir: '/mock', authConfig: {}, registry: 'https://example.org' }
|
||||
const result = await login({ context, opts })
|
||||
expect(result).toBe('Logged in on https://example.org/')
|
||||
expect(savedSettings).toMatchObject({
|
||||
@@ -573,7 +573,7 @@ describe('login', () => {
|
||||
})
|
||||
},
|
||||
})
|
||||
const opts = { configDir: '/broken/config', dir: '/mock', rawConfig: {}, registry: 'https://example.org' }
|
||||
const opts = { configDir: '/broken/config', dir: '/mock', authConfig: {}, registry: 'https://example.org' }
|
||||
const promise = login({ context, opts })
|
||||
await expect(promise).rejects.toHaveProperty(['code'], 'EACCES')
|
||||
await expect(promise).rejects.toHaveProperty(['message'], 'EACCES: permission denied')
|
||||
|
||||
@@ -35,7 +35,7 @@ export type StrictBuildOptions = {
|
||||
production: boolean
|
||||
development: boolean
|
||||
optional: boolean
|
||||
rawConfig: object
|
||||
authConfig: object
|
||||
userConfig: Record<string, string>
|
||||
userAgent: string
|
||||
packageManager: {
|
||||
@@ -73,7 +73,7 @@ const defaults = async (opts: BuildOptions): Promise<StrictBuildOptions> => {
|
||||
packageManager,
|
||||
pending: false,
|
||||
production: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
registries: DEFAULT_REGISTRIES,
|
||||
scriptsPrependNodePath: false,
|
||||
shamefullyHoist: false,
|
||||
|
||||
@@ -198,12 +198,12 @@ export async function buildProjects (
|
||||
extraNodePaths: ctx.extraNodePaths,
|
||||
extraEnv: opts.extraEnv,
|
||||
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
||||
rawConfig: opts.rawConfig,
|
||||
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
||||
scriptShell: opts.scriptShell,
|
||||
shellEmulator: opts.shellEmulator,
|
||||
storeController: store.ctrl,
|
||||
unsafePerm: opts.unsafePerm || false,
|
||||
userAgent: opts.userAgent,
|
||||
}
|
||||
await runLifecycleHooksConcurrently(
|
||||
['preinstall', 'install', 'postinstall', 'prepublish', 'prepare'],
|
||||
@@ -386,11 +386,11 @@ async function _rebuild (
|
||||
extraEnv: opts.extraEnv,
|
||||
optional: pkgSnapshot.optional === true,
|
||||
pkgRoot,
|
||||
rawConfig: opts.rawConfig,
|
||||
rootModulesDir: ctx.rootModulesDir,
|
||||
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
||||
shellEmulator: opts.shellEmulator,
|
||||
unsafePerm: opts.unsafePerm || false,
|
||||
userAgent: opts.userAgent,
|
||||
})
|
||||
if (hasSideEffects && (opts.sideEffectsCacheWrite ?? true) && resolution.integrity) {
|
||||
builtDepPaths.add(depPath)
|
||||
|
||||
@@ -137,10 +137,6 @@ export async function recursiveRebuild (
|
||||
...localConfig,
|
||||
dir: rootDir,
|
||||
pending: opts.pending === true,
|
||||
rawConfig: {
|
||||
...rebuildOpts.rawConfig,
|
||||
...localConfig,
|
||||
},
|
||||
}
|
||||
)
|
||||
result[rootDir].status = 'passed'
|
||||
|
||||
@@ -35,7 +35,7 @@ export const DEFAULT_OPTS = {
|
||||
pnpmfile: ['./.pnpmfile.cjs'],
|
||||
pnpmHomeDir: '',
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
authConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
registries: { default: REGISTRY },
|
||||
registry: REGISTRY,
|
||||
|
||||
@@ -43,7 +43,6 @@ export async function buildModules<T extends string> (
|
||||
lockfileDir: string
|
||||
optional: boolean
|
||||
preferSymlinkedExecutables?: boolean
|
||||
rawConfig: object
|
||||
unsafePerm: boolean
|
||||
userAgent: string
|
||||
scriptsPrependNodePath?: boolean | 'warn-only'
|
||||
@@ -141,7 +140,6 @@ async function buildDependency<T extends string> (
|
||||
lockfileDir: string
|
||||
optional: boolean
|
||||
preferSymlinkedExecutables?: boolean
|
||||
rawConfig: object
|
||||
rootModulesDir: string
|
||||
scriptsPrependNodePath?: boolean | 'warn-only'
|
||||
scriptShell?: string
|
||||
@@ -149,6 +147,7 @@ async function buildDependency<T extends string> (
|
||||
sideEffectsCacheWrite: boolean
|
||||
storeController: StoreController
|
||||
unsafePerm: boolean
|
||||
userAgent?: string
|
||||
hoistedLocations?: Record<string, string[]>
|
||||
builtHoistedDeps?: Record<string, DeferredPromise<void>>
|
||||
enableGlobalVirtualStore?: boolean
|
||||
@@ -184,12 +183,12 @@ async function buildDependency<T extends string> (
|
||||
initCwd: opts.lockfileDir,
|
||||
optional: depNode.optional,
|
||||
pkgRoot: depNode.dir,
|
||||
rawConfig: opts.rawConfig,
|
||||
rootModulesDir: opts.rootModulesDir,
|
||||
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
||||
scriptShell: opts.scriptShell,
|
||||
shellEmulator: opts.shellEmulator,
|
||||
unsafePerm: opts.unsafePerm || false,
|
||||
userAgent: opts.userAgent,
|
||||
})
|
||||
// Remove the .pnpm-needs-build marker before uploading side effects,
|
||||
// so it doesn't get cached as part of the package's side effects diff.
|
||||
|
||||
@@ -420,7 +420,7 @@ function reportAuthError (
|
||||
config?: Config
|
||||
): ErrorInfo {
|
||||
const foundSettings = [] as string[]
|
||||
for (const [key, value] of Object.entries(config?.rawConfig ?? {})) {
|
||||
for (const [key, value] of Object.entries(config?.authConfig ?? {})) {
|
||||
if (key[0] === '@') {
|
||||
foundSettings.push(`${key}=${String(value)}`)
|
||||
continue
|
||||
|
||||
@@ -412,7 +412,7 @@ some hint`)
|
||||
})
|
||||
|
||||
test('prints authorization error with auth settings', async () => {
|
||||
const rawConfig = {
|
||||
const authConfig = {
|
||||
'//foo.bar:_auth': '9876543219',
|
||||
'//foo.bar:_authToken': '9876543219',
|
||||
'//foo.bar:_password': '9876543219',
|
||||
@@ -424,7 +424,7 @@ test('prints authorization error with auth settings', async () => {
|
||||
username: 'nagy.gabor',
|
||||
}
|
||||
const output$ = toOutput$({
|
||||
context: { argv: ['install'], config: { rawConfig } as any }, // eslint-disable-line
|
||||
context: { argv: ['install'], config: { authConfig } as any }, // eslint-disable-line
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
|
||||
@@ -452,7 +452,7 @@ ${ERROR_PAD}username=nagy.gabor`)
|
||||
|
||||
test('prints authorization error without auth settings, where there are none', async () => {
|
||||
const output$ = toOutput$({
|
||||
context: { argv: ['install'], config: { rawConfig: {} } as any }, // eslint-disable-line
|
||||
context: { argv: ['install'], config: { authConfig: {} } as any }, // eslint-disable-line
|
||||
streamParser: createStreamParser(),
|
||||
})
|
||||
|
||||
|
||||
@@ -5,9 +5,12 @@ export type ConfigCommandOptions = Pick<Config,
|
||||
| 'cliOptions'
|
||||
| 'dir'
|
||||
| 'global'
|
||||
| 'rawConfig'
|
||||
| 'authConfig'
|
||||
| 'workspaceDir'
|
||||
> & {
|
||||
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,15 +1,16 @@
|
||||
import { isIniConfigKey, types } from '@pnpm/config.reader'
|
||||
import { type Config, isIniConfigKey, types } from '@pnpm/config.reader'
|
||||
import { getObjectValueByPropertyPath } from '@pnpm/object.property-path'
|
||||
import { isCamelCase, isStrictlyKebabCase } from '@pnpm/text.naming-cases'
|
||||
import { isCamelCase } from '@pnpm/text.naming-cases'
|
||||
import camelcase from 'camelcase'
|
||||
import kebabCase from 'lodash.kebabcase'
|
||||
|
||||
import type { ConfigCommandOptions } from './ConfigCommandOptions.js'
|
||||
import { configToRecord } from './configToRecord.js'
|
||||
import { parseConfigPropertyPath } from './parseConfigPropertyPath.js'
|
||||
import { processConfig } from './processConfig.js'
|
||||
|
||||
export function configGet (opts: ConfigCommandOptions, key: string): { output: string, exitCode: number } {
|
||||
const isScopedKey = key.startsWith('@')
|
||||
const configResult = getRcConfig(opts.rawConfig, key, isScopedKey) ?? getConfigByPropertyPath(opts.rawConfig, key)
|
||||
const configResult = lookupConfig(opts, key, isScopedKey) ?? (isPropertyPath(key) ? lookupByPropertyPath(opts, key) : { value: undefined })
|
||||
const output = displayConfig(configResult?.value, opts)
|
||||
return { output, exitCode: 0 }
|
||||
}
|
||||
@@ -18,39 +19,54 @@ interface Found<Value> {
|
||||
value: Value
|
||||
}
|
||||
|
||||
function getRcConfig (rawConfig: Record<string, unknown>, key: string, isScopedKey: boolean): Found<unknown> | undefined {
|
||||
function lookupConfig (opts: ConfigCommandOptions, key: string, isScopedKey: boolean): Found<unknown> | undefined {
|
||||
if (isScopedKey) {
|
||||
const value = rawConfig[key]
|
||||
return { value }
|
||||
return { value: opts.authConfig[key] }
|
||||
}
|
||||
const rcKey = isCamelCase(key) ? kebabCase(key) : key
|
||||
if (Object.hasOwn(types, rcKey)) {
|
||||
const value = rawConfig[rcKey]
|
||||
return { value }
|
||||
}
|
||||
if (isStrictlyKebabCase(key)) {
|
||||
const value = rawConfig[key]
|
||||
return { value }
|
||||
const kebabKey = isCamelCase(key) ? kebabCase(key) : key
|
||||
// Resolve typed keys from Config — check explicitly set values first,
|
||||
// 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
|
||||
if (!explicit || explicit.has(camelKey)) {
|
||||
return { value: (opts as unknown as Record<string, unknown>)[camelKey] }
|
||||
}
|
||||
// Fall back to authConfig for INI keys (registry, ca, etc.)
|
||||
if (kebabKey in opts.authConfig) {
|
||||
return { value: opts.authConfig[kebabKey] }
|
||||
}
|
||||
return { value: undefined }
|
||||
}
|
||||
// Auth-specific INI keys (//host:_authToken, _auth, etc.) from authConfig
|
||||
if (isIniConfigKey(key)) {
|
||||
const value = rawConfig[key]
|
||||
return { value }
|
||||
return { value: opts.authConfig[key] }
|
||||
}
|
||||
// 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)
|
||||
if (Object.hasOwn(record, camelKey)) {
|
||||
return { value: record[camelKey] }
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function getConfigByPropertyPath (rawConfig: Record<string, unknown>, propertyPath: string): Found<unknown> {
|
||||
function lookupByPropertyPath (opts: ConfigCommandOptions, propertyPath: string): Found<unknown> {
|
||||
const parsedPropertyPath = Array.from(parseConfigPropertyPath(propertyPath))
|
||||
if (parsedPropertyPath.length === 0) {
|
||||
return {
|
||||
value: processConfig(rawConfig),
|
||||
}
|
||||
return { value: configToRecord(opts as unknown as Config) }
|
||||
}
|
||||
const record = configToRecord(opts as unknown as Config)
|
||||
return {
|
||||
value: getObjectValueByPropertyPath(rawConfig, parsedPropertyPath),
|
||||
value: getObjectValueByPropertyPath(record, parsedPropertyPath),
|
||||
}
|
||||
}
|
||||
|
||||
function isPropertyPath (key: string): boolean {
|
||||
return key === '' || key.includes('.') || key.includes('[')
|
||||
}
|
||||
|
||||
type DisplayConfigOptions = Pick<ConfigCommandOptions, 'json'>
|
||||
|
||||
function displayConfig (config: unknown, opts: DisplayConfigOptions): string {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
|
||||
import type { ConfigCommandOptions } from './ConfigCommandOptions.js'
|
||||
import { processConfig } from './processConfig.js'
|
||||
import { configToRecord } from './configToRecord.js'
|
||||
|
||||
export type ConfigListOptions = Pick<ConfigCommandOptions, 'rawConfig'>
|
||||
|
||||
export async function configList (opts: ConfigListOptions): Promise<string> {
|
||||
const processedConfig = processConfig(opts.rawConfig)
|
||||
return JSON.stringify(processedConfig, undefined, 2)
|
||||
export async function configList (opts: ConfigCommandOptions): Promise<string> {
|
||||
return JSON.stringify(configToRecord(opts as unknown as Config), undefined, 2)
|
||||
}
|
||||
|
||||
50
config/commands/src/configToRecord.ts
Normal file
50
config/commands/src/configToRecord.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { type Config, types } from '@pnpm/config.reader'
|
||||
import { sortDirectKeys } from '@pnpm/object.key-sorting'
|
||||
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',
|
||||
])
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export function configToRecord (config: Config): 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
|
||||
const value = (config as unknown as Record<string, unknown>)[camelKey]
|
||||
if (value !== undefined) {
|
||||
result[camelKey] = value
|
||||
}
|
||||
}
|
||||
// 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))) {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
// Add auth/registry keys (scoped keys, auth tokens) — keep original casing
|
||||
for (const [key, value] of Object.entries(config.authConfig)) {
|
||||
if (!(key in result)) {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
// Always include user-agent for debugging connectivity issues
|
||||
if (config.userAgent) {
|
||||
result.userAgent = config.userAgent
|
||||
}
|
||||
return censorProtectedSettings(sortDirectKeys(result))
|
||||
}
|
||||
@@ -1,30 +1,16 @@
|
||||
import { types } from '@pnpm/config.reader'
|
||||
import { parsePropertyPath } from '@pnpm/object.property-path'
|
||||
import kebabCase from 'lodash.kebabcase'
|
||||
import camelcase from 'camelcase'
|
||||
|
||||
/**
|
||||
* Just like {@link parsePropertyPath} but the first element may be converted into kebab-case
|
||||
* if it's part of {@link types}.
|
||||
* Just like {@link parsePropertyPath} but the first element is converted to camelCase
|
||||
* to match the camelCase keys produced by {@link configToRecord}.
|
||||
*/
|
||||
export function * parseConfigPropertyPath (propertyPath: string): Generator<string | number, void, void> {
|
||||
const iter = parsePropertyPath(propertyPath)
|
||||
|
||||
const first = iter.next()
|
||||
if (first.done) return
|
||||
yield normalizeTopLevelConfigName(first.value)
|
||||
yield typeof first.value === 'number' ? first.value : camelcase(first.value, { locale: 'en-US' })
|
||||
|
||||
yield * iter
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a top-level config name into kebab-case if it's part of {@link types}.
|
||||
* Otherwise, return the string as-is.
|
||||
*/
|
||||
function normalizeTopLevelConfigName (configName: string | number): string {
|
||||
if (typeof configName === 'number') return configName.toString()
|
||||
|
||||
const kebabKey = kebabCase(configName)
|
||||
if (Object.hasOwn(types, kebabKey)) return kebabKey
|
||||
|
||||
return configName
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { sortDirectKeys } from '@pnpm/object.key-sorting'
|
||||
import camelcase from 'camelcase'
|
||||
|
||||
import { censorProtectedSettings } from './protectedSettings.js'
|
||||
|
||||
const shouldChangeCase = (key: string): boolean => key[0] !== '@' && !key.startsWith('//')
|
||||
|
||||
function camelCaseConfig (rawConfig: Record<string, unknown>): Record<string, unknown> {
|
||||
const result: Record<string, unknown> = {}
|
||||
for (const key in rawConfig) {
|
||||
const targetKey = shouldChangeCase(key) ? camelcase(key) : key
|
||||
result[targetKey] = rawConfig[key]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export interface ProcessConfigOptions {
|
||||
json?: boolean
|
||||
}
|
||||
|
||||
export function processConfig (rawConfig: Record<string, unknown>): Record<string, unknown> {
|
||||
return camelCaseConfig(censorProtectedSettings(sortDirectKeys(rawConfig)))
|
||||
}
|
||||
@@ -18,7 +18,7 @@ test('config delete on registry key not set', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['delete', 'registry'])
|
||||
|
||||
expect(readIniFileSync(path.join(configDir, 'auth.ini'))).toEqual({
|
||||
@@ -37,7 +37,7 @@ test('config delete on registry key set', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['delete', 'registry'])
|
||||
|
||||
expect(readIniFileSync(path.join(configDir, 'auth.ini'))).toEqual({})
|
||||
@@ -54,7 +54,7 @@ test('config delete on npm-compatible key not set', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['delete', 'cafile'])
|
||||
|
||||
expect(readIniFileSync(path.join(configDir, 'auth.ini'))).toEqual({
|
||||
@@ -73,7 +73,7 @@ test('config delete on npm-compatible key set', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['delete', 'cafile'])
|
||||
|
||||
// NOTE: pnpm currently does not delete empty rc files.
|
||||
@@ -94,7 +94,7 @@ test('config delete on pnpm-specific key not set', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['delete', 'store-dir'])
|
||||
|
||||
expect(readYamlFileSync(path.join(configDir, 'config.yaml'))).toStrictEqual({
|
||||
@@ -115,7 +115,7 @@ test('config delete on pnpm-specific key set', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['delete', 'cache-dir'])
|
||||
|
||||
expect(fs.readdirSync(configDir)).not.toContain('config.yaml')
|
||||
|
||||
@@ -8,9 +8,8 @@ test('config get', async () => {
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
rawConfig: {
|
||||
'store-dir': '~/store',
|
||||
},
|
||||
authConfig: {},
|
||||
storeDir: '~/store',
|
||||
}, ['get', 'store-dir'])
|
||||
|
||||
expect(getOutputString(getResult)).toBe('~/store')
|
||||
@@ -22,9 +21,8 @@ test('config get works with camelCase', async () => {
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
rawConfig: {
|
||||
'store-dir': '~/store',
|
||||
},
|
||||
authConfig: {},
|
||||
storeDir: '~/store',
|
||||
}, ['get', 'storeDir'])
|
||||
|
||||
expect(getOutputString(getResult)).toBe('~/store')
|
||||
@@ -36,9 +34,8 @@ test('config get a boolean should return string format', async () => {
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
rawConfig: {
|
||||
'update-notifier': true,
|
||||
},
|
||||
authConfig: {},
|
||||
updateNotifier: true,
|
||||
}, ['get', 'update-notifier'])
|
||||
|
||||
expect(getOutputString(getResult)).toBe('true')
|
||||
@@ -50,12 +47,11 @@ test('config get on array should return a comma-separated list', async () => {
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
rawConfig: {
|
||||
'public-hoist-pattern': [
|
||||
'*eslint*',
|
||||
'*prettier*',
|
||||
],
|
||||
},
|
||||
authConfig: {},
|
||||
publicHoistPattern: [
|
||||
'*eslint*',
|
||||
'*prettier*',
|
||||
],
|
||||
}, ['get', 'public-hoist-pattern'])
|
||||
|
||||
expect(JSON.parse(getOutputString(getResult))).toStrictEqual([
|
||||
@@ -70,10 +66,9 @@ test('config get on object should return a JSON string', async () => {
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
rawConfig: {
|
||||
catalog: {
|
||||
react: '^19.0.0',
|
||||
},
|
||||
authConfig: {},
|
||||
catalog: {
|
||||
react: '^19.0.0',
|
||||
},
|
||||
}, ['get', 'catalog'])
|
||||
|
||||
@@ -81,7 +76,7 @@ test('config get on object should return a JSON string', async () => {
|
||||
})
|
||||
|
||||
test('config get without key show list all settings', async () => {
|
||||
const rawConfig = {
|
||||
const authConfig = {
|
||||
'store-dir': '~/store',
|
||||
'fetch-retries': '2',
|
||||
}
|
||||
@@ -90,78 +85,74 @@ test('config get without key show list all settings', async () => {
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
rawConfig,
|
||||
authConfig,
|
||||
}, ['get'])
|
||||
|
||||
const listOutput = await config.handler({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
rawConfig,
|
||||
authConfig,
|
||||
}, ['list'])
|
||||
|
||||
expect(getOutput).toStrictEqual(listOutput)
|
||||
})
|
||||
|
||||
describe('config get with a property path', () => {
|
||||
// TODO: change `rawConfig` into camelCase (to emulate pnpm-workspace.yaml)
|
||||
const rawConfig = {
|
||||
'dlx-cache-max-age': '1234',
|
||||
'trust-policy-exclude': ['foo', 'bar'],
|
||||
packageExtensions: {
|
||||
'@babel/parser': {
|
||||
peerDependencies: {
|
||||
'@babel/types': '*',
|
||||
},
|
||||
},
|
||||
'jest-circus': {
|
||||
dependencies: {
|
||||
slash: '3',
|
||||
},
|
||||
const packageExtensions = {
|
||||
'@babel/parser': {
|
||||
peerDependencies: {
|
||||
'@babel/types': '*',
|
||||
},
|
||||
},
|
||||
'jest-circus': {
|
||||
dependencies: {
|
||||
slash: '3',
|
||||
},
|
||||
},
|
||||
}
|
||||
const configData = {
|
||||
dlxCacheMaxAge: '1234',
|
||||
trustPolicyExclude: ['foo', 'bar'],
|
||||
packageExtensions,
|
||||
}
|
||||
const baseOpts = {
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
authConfig: {},
|
||||
...configData,
|
||||
}
|
||||
|
||||
describe('anything with --json', () => {
|
||||
test('«»', async () => {
|
||||
const getResult = await config.handler({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
...baseOpts,
|
||||
json: true,
|
||||
rawConfig,
|
||||
}, ['get', ''])
|
||||
|
||||
expect(JSON.parse(getOutputString(getResult))).toStrictEqual({
|
||||
dlxCacheMaxAge: rawConfig['dlx-cache-max-age'],
|
||||
trustPolicyExclude: rawConfig['trust-policy-exclude'],
|
||||
packageExtensions: rawConfig.packageExtensions,
|
||||
})
|
||||
expect(JSON.parse(getOutputString(getResult))).toMatchObject(configData)
|
||||
})
|
||||
|
||||
test.each([
|
||||
['dlx-cache-max-age', rawConfig['dlx-cache-max-age']],
|
||||
['dlxCacheMaxAge', rawConfig['dlx-cache-max-age']],
|
||||
['trust-policy-exclude', rawConfig['trust-policy-exclude']],
|
||||
['trustPolicyExclude', rawConfig['trust-policy-exclude']],
|
||||
['trustPolicyExclude[0]', rawConfig['trust-policy-exclude'][0]],
|
||||
['trustPolicyExclude[1]', rawConfig['trust-policy-exclude'][1]],
|
||||
['packageExtensions', rawConfig.packageExtensions],
|
||||
['packageExtensions["@babel/parser"]', rawConfig.packageExtensions['@babel/parser']],
|
||||
['packageExtensions["@babel/parser"].peerDependencies', rawConfig.packageExtensions['@babel/parser'].peerDependencies],
|
||||
['packageExtensions["@babel/parser"].peerDependencies["@babel/types"]', rawConfig.packageExtensions['@babel/parser'].peerDependencies['@babel/types']],
|
||||
['packageExtensions["jest-circus"]', rawConfig.packageExtensions['jest-circus']],
|
||||
['packageExtensions["jest-circus"].dependencies', rawConfig.packageExtensions['jest-circus'].dependencies],
|
||||
['packageExtensions["jest-circus"].dependencies.slash', rawConfig.packageExtensions['jest-circus'].dependencies.slash],
|
||||
['dlx-cache-max-age', configData.dlxCacheMaxAge],
|
||||
['dlxCacheMaxAge', configData.dlxCacheMaxAge],
|
||||
['trust-policy-exclude', configData.trustPolicyExclude],
|
||||
['trustPolicyExclude', configData.trustPolicyExclude],
|
||||
['trustPolicyExclude[0]', configData.trustPolicyExclude[0]],
|
||||
['trustPolicyExclude[1]', configData.trustPolicyExclude[1]],
|
||||
['packageExtensions', configData.packageExtensions],
|
||||
['packageExtensions["@babel/parser"]', configData.packageExtensions['@babel/parser']],
|
||||
['packageExtensions["@babel/parser"].peerDependencies', configData.packageExtensions['@babel/parser'].peerDependencies],
|
||||
['packageExtensions["@babel/parser"].peerDependencies["@babel/types"]', configData.packageExtensions['@babel/parser'].peerDependencies['@babel/types']],
|
||||
['packageExtensions["jest-circus"]', configData.packageExtensions['jest-circus']],
|
||||
['packageExtensions["jest-circus"].dependencies', configData.packageExtensions['jest-circus'].dependencies],
|
||||
['packageExtensions["jest-circus"].dependencies.slash', configData.packageExtensions['jest-circus'].dependencies.slash],
|
||||
] as Array<[string, unknown]>)('«%s»', async (propertyPath, expected) => {
|
||||
const getResult = await config.handler({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
...baseOpts,
|
||||
json: true,
|
||||
rawConfig,
|
||||
}, ['get', propertyPath])
|
||||
|
||||
expect(JSON.parse(getOutputString(getResult))).toStrictEqual(expected)
|
||||
@@ -169,27 +160,21 @@ describe('config get with a property path', () => {
|
||||
})
|
||||
|
||||
describe('object without --json', () => {
|
||||
test.each([
|
||||
// TODO: change `rawConfig` into camelCase and replace this object with just `rawConfig`.
|
||||
['', {
|
||||
dlxCacheMaxAge: rawConfig['dlx-cache-max-age'],
|
||||
trustPolicyExclude: rawConfig['trust-policy-exclude'],
|
||||
packageExtensions: rawConfig.packageExtensions,
|
||||
}],
|
||||
// Note: empty path returns all config including dir/global/configDir,
|
||||
// so we use toMatchObject for the empty-path case.
|
||||
test('«»', async () => {
|
||||
const getResult = await config.handler(baseOpts, ['get', ''])
|
||||
expect(JSON.parse(getOutputString(getResult))).toMatchObject(configData)
|
||||
})
|
||||
|
||||
['packageExtensions', rawConfig.packageExtensions],
|
||||
['packageExtensions["@babel/parser"]', rawConfig.packageExtensions['@babel/parser']],
|
||||
['packageExtensions["@babel/parser"].peerDependencies', rawConfig.packageExtensions['@babel/parser'].peerDependencies],
|
||||
['packageExtensions["jest-circus"]', rawConfig.packageExtensions['jest-circus']],
|
||||
['packageExtensions["jest-circus"].dependencies', rawConfig.packageExtensions['jest-circus'].dependencies],
|
||||
test.each([
|
||||
['packageExtensions', configData.packageExtensions],
|
||||
['packageExtensions["@babel/parser"]', configData.packageExtensions['@babel/parser']],
|
||||
['packageExtensions["@babel/parser"].peerDependencies', configData.packageExtensions['@babel/parser'].peerDependencies],
|
||||
['packageExtensions["jest-circus"]', configData.packageExtensions['jest-circus']],
|
||||
['packageExtensions["jest-circus"].dependencies', configData.packageExtensions['jest-circus'].dependencies],
|
||||
] as Array<[string, unknown]>)('«%s»', async (propertyPath, expected) => {
|
||||
const getResult = await config.handler({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
rawConfig,
|
||||
}, ['get', propertyPath])
|
||||
const getResult = await config.handler(baseOpts, ['get', propertyPath])
|
||||
|
||||
expect(JSON.parse(getOutputString(getResult))).toStrictEqual(expected)
|
||||
})
|
||||
@@ -197,35 +182,28 @@ describe('config get with a property path', () => {
|
||||
|
||||
describe('string without --json', () => {
|
||||
test.each([
|
||||
['dlx-cache-max-age', rawConfig['dlx-cache-max-age']],
|
||||
['dlxCacheMaxAge', rawConfig['dlx-cache-max-age']],
|
||||
['trustPolicyExclude[0]', rawConfig['trust-policy-exclude'][0]],
|
||||
['trustPolicyExclude[1]', rawConfig['trust-policy-exclude'][1]],
|
||||
['package-extensions', 'undefined'], // it cannot be defined by rc, it can't be kebab-case
|
||||
['packageExtensions["@babel/parser"].peerDependencies["@babel/types"]', rawConfig.packageExtensions['@babel/parser'].peerDependencies['@babel/types']],
|
||||
['packageExtensions["jest-circus"].dependencies.slash', rawConfig.packageExtensions['jest-circus'].dependencies.slash],
|
||||
['dlx-cache-max-age', configData.dlxCacheMaxAge],
|
||||
['dlxCacheMaxAge', configData.dlxCacheMaxAge],
|
||||
['trustPolicyExclude[0]', configData.trustPolicyExclude[0]],
|
||||
['trustPolicyExclude[1]', configData.trustPolicyExclude[1]],
|
||||
['packageExtensions["@babel/parser"].peerDependencies["@babel/types"]', configData.packageExtensions['@babel/parser'].peerDependencies['@babel/types']],
|
||||
['packageExtensions["jest-circus"].dependencies.slash', configData.packageExtensions['jest-circus'].dependencies.slash],
|
||||
] as Array<[string, string]>)('«%s»', async (propertyPath, expected) => {
|
||||
const getResult = await config.handler({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
rawConfig,
|
||||
}, ['get', propertyPath])
|
||||
const getResult = await config.handler(baseOpts, ['get', propertyPath])
|
||||
|
||||
expect(getOutputString(getResult)).toStrictEqual(expected)
|
||||
})
|
||||
})
|
||||
|
||||
describe('non-rc kebab-case keys', () => {
|
||||
test('«package-extensions»', async () => {
|
||||
const getResult = await config.handler({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
rawConfig,
|
||||
}, ['get', 'package-extensions'])
|
||||
test('«package-extensions» resolves to packageExtensions on Config', async () => {
|
||||
const getResult = await config.handler(baseOpts, ['get', 'package-extensions'])
|
||||
|
||||
expect(JSON.parse(getOutputString(getResult))).toStrictEqual(configData.packageExtensions)
|
||||
})
|
||||
|
||||
test('unknown kebab-case key returns undefined', async () => {
|
||||
const getResult = await config.handler(baseOpts, ['get', 'no-such-setting'])
|
||||
|
||||
expect(getOutputString(getResult)).toBe('undefined')
|
||||
})
|
||||
@@ -238,7 +216,7 @@ test('config get with scoped registry key (global: false)', async () => {
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: false,
|
||||
rawConfig: {
|
||||
authConfig: {
|
||||
'@scope:registry': 'https://custom-registry.example.com/',
|
||||
},
|
||||
}, ['get', '@scope:registry'])
|
||||
@@ -252,7 +230,7 @@ test('config get with scoped registry key (global: true)', async () => {
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
rawConfig: {
|
||||
authConfig: {
|
||||
'@scope:registry': 'https://custom-registry.example.com/',
|
||||
},
|
||||
}, ['get', '@scope:registry'])
|
||||
@@ -266,7 +244,7 @@ test('config get with scoped registry key that does not exist', async () => {
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: false,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['get', '@scope:registry'])
|
||||
|
||||
expect(getOutputString(getResult)).toBe('undefined')
|
||||
@@ -288,7 +266,7 @@ describe('does not traverse the prototype chain (#10296)', () => {
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['get', key])
|
||||
|
||||
expect(getOutputString(getResult)).toBe('undefined')
|
||||
|
||||
@@ -7,13 +7,12 @@ test('config list', async () => {
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
rawConfig: {
|
||||
'store-dir': '~/store',
|
||||
'fetch-retries': '2',
|
||||
},
|
||||
authConfig: {},
|
||||
storeDir: '~/store',
|
||||
fetchRetries: '2',
|
||||
}, ['list'])
|
||||
|
||||
expect(JSON.parse(getOutputString(output))).toStrictEqual({
|
||||
expect(JSON.parse(getOutputString(output))).toMatchObject({
|
||||
fetchRetries: '2',
|
||||
storeDir: '~/store',
|
||||
})
|
||||
@@ -25,22 +24,20 @@ test('config list --json', async () => {
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
json: true,
|
||||
rawConfig: {
|
||||
'store-dir': '~/store',
|
||||
'fetch-retries': '2',
|
||||
},
|
||||
authConfig: {},
|
||||
storeDir: '~/store',
|
||||
fetchRetries: '2',
|
||||
}, ['list'])
|
||||
|
||||
expect(output).toEqual(JSON.stringify({
|
||||
const parsed = JSON.parse(output as string)
|
||||
expect(parsed).toMatchObject({
|
||||
fetchRetries: '2',
|
||||
storeDir: '~/store',
|
||||
}, null, 2))
|
||||
})
|
||||
})
|
||||
|
||||
test('config list censors protected settings', async () => {
|
||||
const rawConfig = {
|
||||
'store-dir': '~/store',
|
||||
'fetch-retries': '2',
|
||||
const authConfig = {
|
||||
username: 'general-username',
|
||||
'@my-org:registry': 'https://my-org.example.com/registry',
|
||||
'//my-org.example.com:username': 'my-username-in-my-org',
|
||||
@@ -50,10 +47,12 @@ test('config list censors protected settings', async () => {
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
rawConfig,
|
||||
storeDir: '~/store',
|
||||
fetchRetries: '2',
|
||||
authConfig,
|
||||
}, ['list'])
|
||||
|
||||
expect(JSON.parse(getOutputString(output))).toStrictEqual({
|
||||
expect(JSON.parse(getOutputString(output))).toMatchObject({
|
||||
storeDir: '~/store',
|
||||
fetchRetries: '2',
|
||||
'@my-org:registry': 'https://my-org.example.com/registry',
|
||||
@@ -63,9 +62,7 @@ test('config list censors protected settings', async () => {
|
||||
})
|
||||
|
||||
test('config list --json censors protected settings', async () => {
|
||||
const rawConfig = {
|
||||
'store-dir': '~/store',
|
||||
'fetch-retries': '2',
|
||||
const authConfig = {
|
||||
username: 'general-username',
|
||||
'@my-org:registry': 'https://my-org.example.com/registry',
|
||||
'//my-org.example.com:username': 'my-username-in-my-org',
|
||||
@@ -76,14 +73,16 @@ test('config list --json censors protected settings', async () => {
|
||||
json: true,
|
||||
cliOptions: {},
|
||||
configDir: process.cwd(),
|
||||
rawConfig,
|
||||
storeDir: '~/store',
|
||||
fetchRetries: '2',
|
||||
authConfig,
|
||||
}, ['list'])
|
||||
|
||||
expect(JSON.parse(getOutputString(output))).toStrictEqual({
|
||||
storeDir: rawConfig['store-dir'],
|
||||
fetchRetries: rawConfig['fetch-retries'],
|
||||
expect(JSON.parse(getOutputString(output))).toMatchObject({
|
||||
storeDir: '~/store',
|
||||
fetchRetries: '2',
|
||||
username: '(protected)',
|
||||
'@my-org:registry': rawConfig['@my-org:registry'],
|
||||
'@my-org:registry': 'https://my-org.example.com/registry',
|
||||
'//my-org.example.com:username': '(protected)',
|
||||
})
|
||||
})
|
||||
|
||||
@@ -29,7 +29,7 @@ test('config set registry setting using the global option', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'registry', 'https://npm-registry.example.com/'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -61,7 +61,7 @@ test('config set npm-compatible setting using the global option', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'cafile', 'some-cafile'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -93,7 +93,7 @@ test('config set pnpm-specific key using the global option', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'fetch-retries', '1'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -125,7 +125,7 @@ test('config set using the location=global option', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'global',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'fetchRetries', '1'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -157,7 +157,7 @@ test('config set pnpm-specific setting using the location=project option', async
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'virtual-store-dir', '.pnpm'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -179,7 +179,7 @@ test('config delete with location=project, when delete the last setting from pnp
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'virtual-store-dir', '.pnpm'])
|
||||
|
||||
expect(readYamlFileSync(path.join(tmp, 'pnpm-workspace.yaml'))).toEqual({
|
||||
@@ -191,7 +191,7 @@ test('config delete with location=project, when delete the last setting from pnp
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['delete', 'virtual-store-dir'])
|
||||
|
||||
expect(fs.existsSync(path.join(tmp, 'pnpm-workspace.yaml'))).toBeFalsy()
|
||||
@@ -218,7 +218,7 @@ test('config set registry setting using the location=project option', async () =
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'registry', 'https://npm-registry.example.com/'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -251,7 +251,7 @@ test('config set npm-compatible setting using the location=project option', asyn
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'cafile', 'some-cafile'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -273,7 +273,7 @@ test('config set saves the setting in the right format to pnpm-workspace.yaml',
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'fetch-timeout', '1000'])
|
||||
|
||||
expect(readYamlFileSync(path.join(tmp, 'pnpm-workspace.yaml'))).toEqual({
|
||||
@@ -306,7 +306,7 @@ test('config set registry setting in project .npmrc file', async () => {
|
||||
configDir,
|
||||
global: false,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'registry', 'https://npm-registry.example.com/'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -343,7 +343,7 @@ test('config set npm-compatible setting in project .npmrc file', async () => {
|
||||
configDir,
|
||||
global: false,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'cafile', 'some-cafile'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -380,7 +380,7 @@ test('config set pnpm-specific setting in project pnpm-workspace.yaml file', asy
|
||||
configDir,
|
||||
global: false,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'fetch-retries', '1'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -416,7 +416,7 @@ test('config set key=value', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'fetch-retries=1'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -452,7 +452,7 @@ test('config set key=value, when value contains a "="', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'lockfile-dir=foo=bar'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -475,7 +475,7 @@ test('config set or delete throws missing params error', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set'])).rejects.toThrow(new PnpmError('CONFIG_NO_PARAMS', '`pnpm config set` requires the config key'))
|
||||
|
||||
await expect(config.handler({
|
||||
@@ -483,7 +483,7 @@ test('config set or delete throws missing params error', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['delete'])).rejects.toThrow(new PnpmError('CONFIG_NO_PARAMS', '`pnpm config delete` requires the config key'))
|
||||
})
|
||||
|
||||
@@ -505,7 +505,7 @@ test('config set with dot leading key', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', '.fetchRetries', '1'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -535,7 +535,7 @@ test('config set with subscripted key', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', '["fetch-retries"]', '1'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -558,7 +558,7 @@ test('config set rejects complex property path', async () => {
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', '.catalog.react', '19'])).rejects.toMatchObject({
|
||||
code: 'ERR_PNPM_CONFIG_SET_DEEP_KEY',
|
||||
})
|
||||
@@ -575,7 +575,7 @@ test('config set with location=project and json=true', async () => {
|
||||
configDir,
|
||||
location: 'project',
|
||||
json: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'catalog', '{ "react": "19" }'])
|
||||
|
||||
expect(readYamlFileSync(path.join(tmp, 'pnpm-workspace.yaml'))).toStrictEqual({
|
||||
@@ -590,7 +590,7 @@ test('config set with location=project and json=true', async () => {
|
||||
configDir,
|
||||
location: 'project',
|
||||
json: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'packageExtensions', JSON.stringify({
|
||||
'@babel/parser': {
|
||||
peerDependencies: {
|
||||
@@ -642,7 +642,7 @@ test('config set refuses writing workspace-specific settings to the global confi
|
||||
configDir,
|
||||
location: 'global',
|
||||
json: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'catalog', '{ "react": "19" }'])).rejects.toMatchObject({
|
||||
code: 'ERR_PNPM_CONFIG_SET_UNSUPPORTED_YAML_CONFIG_KEY',
|
||||
key: 'catalog',
|
||||
@@ -654,7 +654,7 @@ test('config set refuses writing workspace-specific settings to the global confi
|
||||
configDir,
|
||||
location: 'global',
|
||||
json: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'packageExtensions', JSON.stringify({
|
||||
'@babel/parser': {
|
||||
peerDependencies: {
|
||||
@@ -677,7 +677,7 @@ test('config set refuses writing workspace-specific settings to the global confi
|
||||
configDir,
|
||||
location: 'global',
|
||||
json: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'package-extensions', JSON.stringify({
|
||||
'@babel/parser': {
|
||||
peerDependencies: {
|
||||
@@ -715,7 +715,7 @@ test('config set writes workspace-specific settings to pnpm-workspace.yaml', asy
|
||||
configDir,
|
||||
location: 'project',
|
||||
json: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'catalog', JSON.stringify(catalog)])
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -743,7 +743,7 @@ test('config set writes workspace-specific settings to pnpm-workspace.yaml', asy
|
||||
configDir,
|
||||
location: 'project',
|
||||
json: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'packageExtensions', JSON.stringify(packageExtensions)])
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
...initConfig,
|
||||
@@ -766,7 +766,7 @@ test('config set refuses kebab-case workspace-specific settings', async () => {
|
||||
configDir,
|
||||
location: 'project',
|
||||
json: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'package-extensions', JSON.stringify({
|
||||
'@babel/parser': {
|
||||
peerDependencies: {
|
||||
@@ -794,7 +794,7 @@ test('config set registry-specific setting with --location=project should create
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', '//registry.example.com/:_auth', 'test-auth-value'])
|
||||
|
||||
expect(readIniFileSync(path.join(tmp, '.npmrc'))).toEqual({
|
||||
@@ -813,7 +813,7 @@ test('config set scoped registry with --location=project should create .npmrc',
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', '@myorg:registry', 'https://test-registry.example.com/'])
|
||||
|
||||
expect(readIniFileSync(path.join(tmp, '.npmrc'))).toEqual({
|
||||
@@ -836,7 +836,7 @@ test('config set when both pnpm-workspace.yaml and .npmrc exist, pnpm-workspace.
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'fetch-timeout', '2000'])
|
||||
|
||||
expect(readYamlFileSync(path.join(tmp, 'pnpm-workspace.yaml'))).toEqual({
|
||||
@@ -861,7 +861,7 @@ test('config set when only pnpm-workspace.yaml exists, writes to it', async () =
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
location: 'project',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', 'fetch-timeout', '3000'])
|
||||
|
||||
expect(readYamlFileSync(path.join(tmp, 'pnpm-workspace.yaml'))).toEqual({
|
||||
|
||||
@@ -32,7 +32,7 @@ describe.each(
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', `${key}=123`])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -56,7 +56,7 @@ describe.each(
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['delete', key])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -84,7 +84,7 @@ describe.each(
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', key, '"123"'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -109,7 +109,7 @@ describe.each(
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['delete', key])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -141,7 +141,7 @@ describe.each(
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', `${key}=https://registry.example.com/`])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -165,7 +165,7 @@ describe.each(
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['delete', key])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -195,7 +195,7 @@ describe.each(
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', key, '{}'])).rejects.toMatchObject({
|
||||
code: 'ERR_PNPM_CONFIG_SET_AUTH_NON_STRING',
|
||||
})
|
||||
@@ -224,7 +224,7 @@ describe.each(
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['set', propertyPath, '123'])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
@@ -248,7 +248,7 @@ describe.each(
|
||||
cliOptions: {},
|
||||
configDir,
|
||||
global: true,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['delete', propertyPath])
|
||||
|
||||
expect(readConfigFiles(configDir, tmp)).toEqual({
|
||||
|
||||
@@ -14,7 +14,7 @@ import type {
|
||||
import type { OptionsFromRootManifest } from './getOptionsFromRootManifest.js'
|
||||
import type { AuthInfo } from './parseAuthInfo.js'
|
||||
|
||||
export type UniversalOptions = Pick<Config, 'color' | 'dir' | 'rawConfig' | 'rawLocalConfig'>
|
||||
export type UniversalOptions = Pick<Config, 'color' | 'dir' | 'authConfig' | 'rawLocalConfig'>
|
||||
|
||||
|
||||
export type VerifyDepsBeforeRun = 'install' | 'warn' | 'error' | 'prompt' | false
|
||||
@@ -38,7 +38,9 @@ export interface Config extends AuthInfo, OptionsFromRootManifest {
|
||||
filter: string[]
|
||||
filterProd: string[]
|
||||
rawLocalConfig: Record<string, any>, // eslint-disable-line
|
||||
rawConfig: 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
|
||||
@@ -75,6 +77,7 @@ export interface Config extends AuthInfo, OptionsFromRootManifest {
|
||||
depth?: number
|
||||
engineStrict?: boolean
|
||||
nodeVersion?: string
|
||||
nodeDownloadMirrors?: Record<string, string>
|
||||
offline?: boolean
|
||||
registry?: string
|
||||
optional?: boolean
|
||||
|
||||
@@ -6,7 +6,7 @@ test('inheritAuthConfig copies only auth keys from source to target', () => {
|
||||
bin: 'foo',
|
||||
cacheDir: '/path/to/cache/dir',
|
||||
registry: 'https://npmjs.com/registry/',
|
||||
rawConfig: {
|
||||
authConfig: {
|
||||
'cache-dir': '/path/to/cache/dir',
|
||||
registry: 'https://npmjs.com/registry/',
|
||||
},
|
||||
@@ -21,7 +21,7 @@ test('inheritAuthConfig copies only auth keys from source to target', () => {
|
||||
cacheDir: '/path/to/another/cache/dir',
|
||||
storeDir: '/path/to/custom/store/dir',
|
||||
registry: 'https://example.com/local-registry/',
|
||||
rawConfig: {
|
||||
authConfig: {
|
||||
registry: 'https://example.com/global-registry/',
|
||||
'//example.com/global-registry/:_auth': 'MY_SECRET_GLOBAL_AUTH',
|
||||
},
|
||||
@@ -38,7 +38,7 @@ test('inheritAuthConfig copies only auth keys from source to target', () => {
|
||||
bin: 'foo',
|
||||
cacheDir: '/path/to/cache/dir',
|
||||
registry: 'https://example.com/local-registry/',
|
||||
rawConfig: {
|
||||
authConfig: {
|
||||
'cache-dir': '/path/to/cache/dir',
|
||||
registry: 'https://example.com/global-registry/',
|
||||
'//example.com/global-registry/:_auth': 'MY_SECRET_GLOBAL_AUTH',
|
||||
|
||||
@@ -22,7 +22,7 @@ import { omit } from 'ramda'
|
||||
import { realpathMissing } from 'realpath-missing'
|
||||
import semver from 'semver'
|
||||
|
||||
import { inheritAuthConfig, isIniConfigKey, pickIniConfig } from './auth.js'
|
||||
import { inheritAuthConfig, pickIniConfig } from './auth.js'
|
||||
import { checkGlobalBinDir } from './checkGlobalBinDir.js'
|
||||
import { getDefaultWorkspaceConcurrency, getWorkspaceConcurrency } from './concurrency.js'
|
||||
import type {
|
||||
@@ -85,9 +85,7 @@ export async function getConfig (opts: {
|
||||
name: string
|
||||
version: string
|
||||
}
|
||||
rcOptionsTypes?: Record<string, unknown>
|
||||
workspaceDir?: string | undefined
|
||||
checkUnknownSetting?: boolean
|
||||
env?: Record<string, string | undefined>
|
||||
ignoreNonAuthSettingsFromLocal?: boolean
|
||||
ignoreLocalSettings?: boolean
|
||||
@@ -124,7 +122,6 @@ export async function getConfig (opts: {
|
||||
if (cliOptions.dir) {
|
||||
cliOptions.dir = await realpathMissing(cliOptions.dir)
|
||||
}
|
||||
const rcOptionsTypes = { ...types, ...opts.rcOptionsTypes }
|
||||
const defaultOptions: Partial<KebabCaseConfig> = {
|
||||
'auto-install-peers': true,
|
||||
bail: true,
|
||||
@@ -235,25 +232,29 @@ export async function getConfig (opts: {
|
||||
})
|
||||
const warnings = npmrcResult.warnings
|
||||
|
||||
const rcOptions = Object.keys(rcOptionsTypes)
|
||||
|
||||
const configFromCliOpts = Object.fromEntries(Object.entries(cliOptions)
|
||||
.filter(([_, value]) => typeof value !== 'undefined')
|
||||
.map(([name, value]) => [camelcase(name, { locale: 'en-US' }), value])
|
||||
)
|
||||
|
||||
// Build initial config from defaults, then overlay auth/registry values from .npmrc
|
||||
const pnpmConfig = Object.fromEntries(
|
||||
rcOptions
|
||||
.map((configKey) => [
|
||||
camelcase(configKey, { locale: 'en-US' }),
|
||||
isIniConfigKey(configKey)
|
||||
? (npmrcResult.mergedConfig[configKey] ?? (defaultOptions as Record<string, unknown>)[configKey])
|
||||
: (defaultOptions as Record<string, unknown>)[configKey],
|
||||
])
|
||||
Object.entries(defaultOptions)
|
||||
.map(([key, value]) => [camelcase(key, { locale: 'en-US' }), value])
|
||||
) as unknown as ConfigWithDeprecatedSettings
|
||||
|
||||
for (const [key, value] of Object.entries(npmrcResult.mergedConfig)) {
|
||||
if (Object.hasOwn(types, key)) {
|
||||
;(pnpmConfig as unknown as Record<string, unknown>)[camelcase(key, { locale: 'en-US' })] = value
|
||||
}
|
||||
}
|
||||
|
||||
const globalDepsBuildConfig = extractAndRemoveDependencyBuildOptions(pnpmConfig)
|
||||
|
||||
// Track which keys are explicitly set (not defaults)
|
||||
const explicitlySetKeys = new Set<string>(Object.keys(configFromCliOpts))
|
||||
pnpmConfig.explicitlySetKeys = explicitlySetKeys
|
||||
|
||||
Object.assign(pnpmConfig, configFromCliOpts)
|
||||
// Resolving the current working directory to its actual location is crucial.
|
||||
// This prevents potential inconsistencies in the future, especially when processing or mapping subdirectories.
|
||||
@@ -280,14 +281,9 @@ export async function getConfig (opts: {
|
||||
npmrcResult.workspaceNpmrc,
|
||||
cliOptions
|
||||
)
|
||||
pnpmConfig.userAgent = pnpmConfig.rawLocalConfig['user-agent']
|
||||
? pnpmConfig.rawLocalConfig['user-agent']
|
||||
: `${packageManager.name}/${packageManager.version} npm/? node/${process.version} ${process.platform} ${process.arch}`
|
||||
pnpmConfig.rawConfig = Object.assign(
|
||||
{},
|
||||
pickIniConfig(npmrcResult.rawConfig),
|
||||
{ 'user-agent': pnpmConfig.userAgent }
|
||||
)
|
||||
pnpmConfig.userAgent = (cliOptions['user-agent'] as string | undefined)
|
||||
?? `${packageManager.name}/${packageManager.version} npm/? node/${process.version} ${process.platform} ${process.arch}`
|
||||
pnpmConfig.authConfig = pickIniConfig(npmrcResult.rawConfig)
|
||||
|
||||
// Reuse the global config.yaml already read for npmrcAuthFile
|
||||
const globalYamlConfig = globalYamlConfigForNpmrcAuthFile
|
||||
@@ -304,15 +300,15 @@ export async function getConfig (opts: {
|
||||
workspaceManifest: globalYamlConfig,
|
||||
})
|
||||
}
|
||||
const networkConfigs = getNetworkConfigs(pnpmConfig.rawConfig)
|
||||
const networkConfigs = getNetworkConfigs(pnpmConfig.authConfig)
|
||||
const registriesFromNpmrc = {
|
||||
default: normalizeRegistryUrl(pnpmConfig.rawConfig.registry),
|
||||
default: normalizeRegistryUrl(pnpmConfig.authConfig.registry),
|
||||
...networkConfigs.registries,
|
||||
}
|
||||
pnpmConfig.registries = { ...registriesFromNpmrc }
|
||||
pnpmConfig.authInfos = networkConfigs.authInfos ?? {} // TODO: remove `?? {}` (when possible)
|
||||
pnpmConfig.sslConfigs = networkConfigs.sslConfigs
|
||||
Object.assign(pnpmConfig, getDefaultAuthInfo(pnpmConfig.rawConfig))
|
||||
Object.assign(pnpmConfig, getDefaultAuthInfo(pnpmConfig.authConfig))
|
||||
pnpmConfig.pnpmHomeDir = getDataDir({ env, platform: process.platform })
|
||||
let globalDirRoot
|
||||
if (pnpmConfig.globalDir) {
|
||||
@@ -446,7 +442,6 @@ export async function getConfig (opts: {
|
||||
'init-version', // the type is a private function named 'semver'
|
||||
'node-version', // the type is a private function named 'semver'
|
||||
'umask', // the type is a private function named 'Umask'
|
||||
'logstream', // the custom parser doesn't have logic to handle 'Stream' yet
|
||||
], types)
|
||||
|
||||
for (const { key, value } of parseEnvVars(key => envPnpmTypes[key as keyof typeof envPnpmTypes], env)) {
|
||||
@@ -458,6 +453,7 @@ export async function getConfig (opts: {
|
||||
|
||||
// @ts-expect-error
|
||||
pnpmConfig[key] = value
|
||||
explicitlySetKeys.add(key)
|
||||
|
||||
if (key === 'registry') {
|
||||
if (typeof value !== 'string') {
|
||||
@@ -577,21 +573,6 @@ export async function getConfig (opts: {
|
||||
pnpmConfig.sideEffectsCacheRead = pnpmConfig.sideEffectsCache ?? pnpmConfig.sideEffectsCacheReadonly
|
||||
pnpmConfig.sideEffectsCacheWrite = pnpmConfig.sideEffectsCache
|
||||
|
||||
// TODO: consider removing checkUnknownSetting entirely
|
||||
if (opts.checkUnknownSetting) {
|
||||
const settingKeys = Object.keys(npmrcResult.workspaceNpmrc)
|
||||
.filter(key => key.trim() !== '')
|
||||
const unknownKeys = []
|
||||
for (const key of settingKeys) {
|
||||
if (!rcOptions.includes(key) && !key.startsWith('//') && !(key[0] === '@' && key.endsWith(':registry'))) {
|
||||
unknownKeys.push(key)
|
||||
}
|
||||
}
|
||||
if (unknownKeys.length > 0) {
|
||||
warnings.push(`Your .npmrc file contains unknown setting: ${unknownKeys.join(', ')}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (pnpmConfig.sharedWorkspaceLockfile && !pnpmConfig.lockfileDir && pnpmConfig.workspaceDir) {
|
||||
pnpmConfig.lockfileDir = pnpmConfig.workspaceDir
|
||||
}
|
||||
@@ -755,13 +736,7 @@ function addSettingsFromWorkspaceManifestToConfig (pnpmConfig: Config, {
|
||||
|
||||
// @ts-expect-error
|
||||
pnpmConfig[key] = value
|
||||
|
||||
const kebabKey = kebabCase(key)
|
||||
// Q: Why `types` instead of `rcOptionTypes`?
|
||||
// A: `rcOptionTypes` includes options that would matter to the `npm` cli which wouldn't care about `pnpm-workspace.yaml`.
|
||||
const isRc = kebabKey in types
|
||||
const targetKey = isRc ? kebabKey : key
|
||||
pnpmConfig.rawConfig[targetKey] = value
|
||||
pnpmConfig.explicitlySetKeys.add(key)
|
||||
}
|
||||
// All the pnpm_config_ env variables should override the settings from pnpm-workspace.yaml,
|
||||
// as it happens with .npmrc.
|
||||
@@ -770,7 +745,7 @@ function addSettingsFromWorkspaceManifestToConfig (pnpmConfig: Config, {
|
||||
// Related issue: https://github.com/pnpm/pnpm/issues/10060
|
||||
if (process.env.pnpm_config_verify_deps_before_run != null) {
|
||||
pnpmConfig.verifyDepsBeforeRun = process.env.pnpm_config_verify_deps_before_run as VerifyDepsBeforeRun
|
||||
pnpmConfig.rawConfig['verify-deps-before-run'] = pnpmConfig.verifyDepsBeforeRun
|
||||
}
|
||||
pnpmConfig.catalogs = getCatalogsFromWorkspaceManifest(workspaceManifest)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Config } from './Config.js'
|
||||
|
||||
export type InheritableConfig = Partial<Config> & Pick<Config, 'rawConfig' | 'rawLocalConfig'>
|
||||
export type InheritableConfig = Partial<Config> & Pick<Config, 'authConfig' | 'rawLocalConfig'>
|
||||
export type PickConfig = (cfg: Partial<Config>) => Partial<Config>
|
||||
export type PickRawConfig = (cfg: Record<string, unknown>) => Record<string, unknown>
|
||||
|
||||
@@ -12,6 +12,6 @@ export function inheritPickedConfig (
|
||||
pickRawLocalConfig: PickRawConfig = pickRawConfig
|
||||
): void {
|
||||
Object.assign(targetCfg, pickConfig(srcCfg))
|
||||
Object.assign(targetCfg.rawConfig, pickRawConfig(srcCfg.rawConfig))
|
||||
Object.assign(targetCfg.authConfig, pickRawConfig(srcCfg.authConfig))
|
||||
Object.assign(targetCfg.rawLocalConfig, pickRawLocalConfig(srcCfg.rawLocalConfig))
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ export interface NpmrcConfigResult {
|
||||
* Priority (lowest to highest): builtin < defaults < user < auth.ini < workspace < CLI
|
||||
*/
|
||||
mergedConfig: Record<string, unknown>
|
||||
/** Raw config suitable for pnpmConfig.rawConfig (filtered through pickIniConfig by consumer) */
|
||||
/** Raw config suitable for pnpmConfig.authConfig (filtered through pickIniConfig by consumer) */
|
||||
rawConfig: Record<string, unknown>
|
||||
/** Workspace .npmrc data (for rawLocalConfig and checkUnknownSetting) */
|
||||
/** Workspace .npmrc data (for rawLocalConfig) */
|
||||
workspaceNpmrc: Record<string, unknown>
|
||||
/** User ~/.npmrc data (for token helpers) */
|
||||
userConfig: Record<string, unknown>
|
||||
|
||||
@@ -1,55 +1,32 @@
|
||||
import path from 'node:path'
|
||||
import { Stream } from 'node:stream'
|
||||
import url from 'node:url'
|
||||
|
||||
// Inlined from @pnpm/npm-conf/lib/types.js
|
||||
// These are the npm config type definitions used for config key validation.
|
||||
// Subset of npm config type definitions that pnpm actually uses.
|
||||
// Originally inlined from @pnpm/npm-conf/lib/types.js, trimmed to only
|
||||
// keys referenced by pnpm commands or passed through to npm-compatible tooling.
|
||||
export const npmConfigTypes = {
|
||||
access: [null, 'restricted', 'public'],
|
||||
'allow-same-version': Boolean,
|
||||
'always-auth': Boolean,
|
||||
also: [null, 'dev', 'development'],
|
||||
audit: Boolean,
|
||||
'auth-type': ['legacy', 'sso', 'saml', 'oauth'],
|
||||
'bin-links': Boolean,
|
||||
browser: [null, String],
|
||||
ca: [null, String, Array],
|
||||
cafile: path,
|
||||
cache: path,
|
||||
'cache-lock-stale': Number,
|
||||
'cache-lock-retries': Number,
|
||||
'cache-lock-wait': Number,
|
||||
'cache-max': Number,
|
||||
'cache-min': Number,
|
||||
cert: [null, String],
|
||||
cidr: [null, String, Array],
|
||||
color: ['always', Boolean],
|
||||
'commit-hooks': Boolean,
|
||||
depth: Number,
|
||||
description: Boolean,
|
||||
dev: Boolean,
|
||||
'dry-run': Boolean,
|
||||
editor: String,
|
||||
'engine-strict': Boolean,
|
||||
force: Boolean,
|
||||
'fetch-retries': Number,
|
||||
'fetch-retry-factor': Number,
|
||||
'fetch-retry-mintimeout': Number,
|
||||
'fetch-retry-maxtimeout': Number,
|
||||
force: Boolean,
|
||||
git: String,
|
||||
'git-tag-version': Boolean,
|
||||
'commit-hooks': Boolean,
|
||||
global: Boolean,
|
||||
globalconfig: path,
|
||||
'global-style': Boolean,
|
||||
group: [Number, String],
|
||||
'https-proxy': [null, url],
|
||||
'user-agent': String,
|
||||
'ham-it-up': Boolean,
|
||||
heading: String,
|
||||
'if-present': Boolean,
|
||||
'ignore-prepublish': Boolean,
|
||||
'ignore-scripts': Boolean,
|
||||
'init-module': path,
|
||||
'init-author-name': String,
|
||||
'init-author-email': String,
|
||||
'init-author-url': ['', url],
|
||||
@@ -57,40 +34,27 @@ export const npmConfigTypes = {
|
||||
'init-version': String,
|
||||
json: Boolean,
|
||||
key: [null, String],
|
||||
'legacy-bundling': Boolean,
|
||||
link: Boolean,
|
||||
'local-address': String,
|
||||
loglevel: ['silent', 'error', 'warn', 'notice', 'http', 'timing', 'info', 'verbose', 'silly'],
|
||||
logstream: Stream,
|
||||
'logs-max': Number,
|
||||
long: Boolean,
|
||||
maxsockets: Number,
|
||||
message: String,
|
||||
'metrics-registry': [null, String],
|
||||
'node-options': [null, String],
|
||||
'node-version': [null, String],
|
||||
'no-proxy': [null, String, Array],
|
||||
offline: Boolean,
|
||||
'onload-script': [null, String],
|
||||
only: [null, 'dev', 'development', 'prod', 'production'],
|
||||
optional: Boolean,
|
||||
'package-lock': Boolean,
|
||||
otp: [null, String],
|
||||
'package-lock-only': Boolean,
|
||||
'package-lock': Boolean,
|
||||
parseable: Boolean,
|
||||
'prefer-offline': Boolean,
|
||||
'prefer-online': Boolean,
|
||||
prefix: path,
|
||||
production: Boolean,
|
||||
progress: Boolean,
|
||||
proxy: [null, false, url],
|
||||
provenance: Boolean,
|
||||
'read-only': Boolean,
|
||||
'rebuild-bundle': Boolean,
|
||||
proxy: [null, false, url],
|
||||
registry: [null, url],
|
||||
rollback: Boolean,
|
||||
save: Boolean,
|
||||
'save-bundle': Boolean,
|
||||
'save-dev': Boolean,
|
||||
'save-exact': Boolean,
|
||||
'save-optional': Boolean,
|
||||
@@ -99,29 +63,13 @@ export const npmConfigTypes = {
|
||||
scope: String,
|
||||
'script-shell': [null, String],
|
||||
'scripts-prepend-node-path': [false, true, 'auto', 'warn-only'],
|
||||
searchopts: String,
|
||||
searchexclude: [null, String],
|
||||
searchlimit: Number,
|
||||
searchstaleness: Number,
|
||||
'send-metrics': Boolean,
|
||||
shell: String,
|
||||
shrinkwrap: Boolean,
|
||||
'sign-git-tag': Boolean,
|
||||
'sso-poll-frequency': Number,
|
||||
'sso-type': [null, 'oauth', 'saml'],
|
||||
'strict-ssl': Boolean,
|
||||
tag: String,
|
||||
timing: Boolean,
|
||||
tmp: path,
|
||||
unicode: Boolean,
|
||||
'tag-version-prefix': String,
|
||||
'unsafe-perm': Boolean,
|
||||
usage: Boolean,
|
||||
user: [Number, String],
|
||||
'user-agent': String,
|
||||
userconfig: path,
|
||||
umask: Number,
|
||||
version: Boolean,
|
||||
'tag-version-prefix': String,
|
||||
versions: Boolean,
|
||||
viewer: String,
|
||||
_exit: Boolean,
|
||||
}
|
||||
|
||||
@@ -238,7 +238,7 @@ test('.npmrc does not load pnpm settings', async () => {
|
||||
})
|
||||
|
||||
// rc options appear as usual
|
||||
expect(config.rawConfig).toMatchObject({
|
||||
expect(config.authConfig).toMatchObject({
|
||||
'//my-org.registry.example.com:username': 'some-employee',
|
||||
'//my-org.registry.example.com:_authToken': 'some-employee-token',
|
||||
'@my-org:registry': 'https://my-org.registry.example.com',
|
||||
@@ -248,16 +248,16 @@ test('.npmrc does not load pnpm settings', async () => {
|
||||
})
|
||||
|
||||
// workspace-specific settings are omitted
|
||||
expect(config.rawConfig['dlx-cache-max-age']).toBeUndefined()
|
||||
expect(config.rawConfig['dlxCacheMaxAge']).toBeUndefined()
|
||||
expect(config.authConfig['dlx-cache-max-age']).toBeUndefined()
|
||||
expect(config.authConfig['dlxCacheMaxAge']).toBeUndefined()
|
||||
expect(config.dlxCacheMaxAge).toBe(24 * 60) // TODO: refactor to make defaultOptions importable
|
||||
expect(config.rawConfig['trust-policy-exclude']).toBeUndefined()
|
||||
expect(config.rawConfig['trustPolicyExclude']).toBeUndefined()
|
||||
expect(config.authConfig['trust-policy-exclude']).toBeUndefined()
|
||||
expect(config.authConfig['trustPolicyExclude']).toBeUndefined()
|
||||
expect(config.trustPolicyExclude).toBeUndefined()
|
||||
expect(config.rawConfig.packages).toBeUndefined()
|
||||
expect(config.authConfig.packages).toBeUndefined()
|
||||
})
|
||||
|
||||
test('rc options appear as kebab-case in rawConfig even if it was defined as camelCase by pnpm-workspace.yaml', async () => {
|
||||
test('camelCase settings from pnpm-workspace.yaml are read into typed Config properties', async () => {
|
||||
prepareEmpty()
|
||||
|
||||
writeYamlFileSync('pnpm-workspace.yaml', {
|
||||
@@ -283,21 +283,10 @@ test('rc options appear as kebab-case in rawConfig even if it was defined as cam
|
||||
linkWorkspacePackages: true,
|
||||
nodeLinker: 'hoisted',
|
||||
sharedWorkspaceLockfile: true,
|
||||
rawConfig: {
|
||||
'ignore-scripts': true,
|
||||
'link-workspace-packages': true,
|
||||
'node-linker': 'hoisted',
|
||||
'shared-workspace-lockfile': true,
|
||||
},
|
||||
})
|
||||
|
||||
expect(config.rawConfig.ignoreScripts).toBeUndefined()
|
||||
expect(config.rawConfig.linkWorkspacePackages).toBeUndefined()
|
||||
expect(config.rawConfig.nodeLinker).toBeUndefined()
|
||||
expect(config.rawConfig.sharedWorkspaceLockfile).toBeUndefined()
|
||||
})
|
||||
|
||||
test('workspace-specific settings preserve case in rawConfig', async () => {
|
||||
test('workspace-specific settings are read into typed Config properties', async () => {
|
||||
prepareEmpty()
|
||||
|
||||
writeYamlFileSync('pnpm-workspace.yaml', {
|
||||
@@ -327,20 +316,7 @@ test('workspace-specific settings preserve case in rawConfig', async () => {
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
expect(config.rawConfig.packages).toStrictEqual(['foo', 'bar'])
|
||||
expect(config.rawConfig.packageExtensions).toStrictEqual({
|
||||
'@babel/parser': {
|
||||
peerDependencies: {
|
||||
'@babel/types': '*',
|
||||
},
|
||||
},
|
||||
'jest-circus': {
|
||||
dependencies: {
|
||||
slash: '3',
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(config.rawConfig['package-extensions']).toBeUndefined()
|
||||
expect(config.workspacePackagePatterns).toStrictEqual(['foo', 'bar'])
|
||||
expect(config.packageExtensions).toStrictEqual({
|
||||
'@babel/parser': {
|
||||
peerDependencies: {
|
||||
@@ -475,7 +451,7 @@ test('auth tokens from pnpm auth file override ~/.npmrc', async () => {
|
||||
},
|
||||
})
|
||||
|
||||
expect(config.rawConfig['//registry.npmjs.org/:_authToken']).toBe('fresh-token')
|
||||
expect(config.authConfig['//registry.npmjs.org/:_authToken']).toBe('fresh-token')
|
||||
} finally {
|
||||
if (originalXdg != null) {
|
||||
process.env.XDG_CONFIG_HOME = originalXdg
|
||||
@@ -514,7 +490,7 @@ test('workspace .npmrc overrides pnpm auth file', async () => {
|
||||
},
|
||||
})
|
||||
|
||||
expect(config.rawConfig['//registry.npmjs.org/:_authToken']).toBe('workspace-token')
|
||||
expect(config.authConfig['//registry.npmjs.org/:_authToken']).toBe('workspace-token')
|
||||
} finally {
|
||||
if (originalXdg != null) {
|
||||
process.env.XDG_CONFIG_HOME = originalXdg
|
||||
@@ -812,7 +788,7 @@ test.skip('read only supported settings from config', async () => {
|
||||
expect(config.storeDir).toBe('__store__')
|
||||
// @ts-expect-error
|
||||
expect(config['foo']).toBeUndefined() // NOTE: This line current fails as there are yet a way to verify fields in pnpm-workspace.yaml
|
||||
expect(config.rawConfig['foo']).toBe('bar')
|
||||
expect(config.authConfig['foo']).toBe('bar')
|
||||
})
|
||||
|
||||
test('all CLI options are added to the config', async () => {
|
||||
@@ -984,7 +960,7 @@ test('dir is resolved to real path', async () => {
|
||||
expect(config.dir).toBe(realDir)
|
||||
})
|
||||
|
||||
test('warn user unknown settings in npmrc', async () => {
|
||||
test('non-auth settings in npmrc do not produce warnings', async () => {
|
||||
prepare()
|
||||
|
||||
const npmrc = [
|
||||
@@ -1004,20 +980,9 @@ test('warn user unknown settings in npmrc', async () => {
|
||||
name: 'pnpm',
|
||||
version: '1.0.0',
|
||||
},
|
||||
checkUnknownSetting: true,
|
||||
})
|
||||
|
||||
expect(warnings).toStrictEqual([])
|
||||
|
||||
const { warnings: noWarnings } = await getConfig({
|
||||
cliOptions: {},
|
||||
packageManager: {
|
||||
name: 'pnpm',
|
||||
version: '1.0.0',
|
||||
},
|
||||
})
|
||||
|
||||
expect(noWarnings).toStrictEqual([])
|
||||
})
|
||||
|
||||
test('getConfig() converts noproxy to noProxy', async () => {
|
||||
@@ -1332,7 +1297,6 @@ test('settings from pnpm-workspace.yaml are read', async () => {
|
||||
})
|
||||
|
||||
expect(config.trustPolicyExclude).toStrictEqual(['foo', 'bar'])
|
||||
expect(config.rawConfig['trust-policy-exclude']).toStrictEqual(['foo', 'bar'])
|
||||
})
|
||||
|
||||
test('settings sharedWorkspaceLockfile in pnpm-workspace.yaml should take effect', async () => {
|
||||
@@ -1365,7 +1329,6 @@ test('settings shamefullyHoist in pnpm-workspace.yaml should take effect', async
|
||||
})
|
||||
|
||||
expect(config.shamefullyHoist).toBe(true)
|
||||
expect(config.rawConfig['shamefully-hoist']).toBe(true)
|
||||
})
|
||||
|
||||
test('settings gitBranchLockfile in pnpm-workspace.yaml should take effect', async () => {
|
||||
@@ -1382,7 +1345,6 @@ test('settings gitBranchLockfile in pnpm-workspace.yaml should take effect', asy
|
||||
|
||||
expect(config.gitBranchLockfile).toBe(true)
|
||||
expect(config.useGitBranchLockfile).toBe(true)
|
||||
expect(config.rawConfig['git-branch-lockfile']).toBe(true)
|
||||
})
|
||||
|
||||
test('loads setting from environment variable pnpm_config_*', async () => {
|
||||
@@ -1557,9 +1519,7 @@ describe('global config.yaml', () => {
|
||||
expect(config.dangerouslyAllowAllBuilds).toBe(true)
|
||||
|
||||
// NOTE: the field may appear kebab-case here, but only internally,
|
||||
// `pnpm config list` would convert them to camelCase.
|
||||
// TODO: switch to camelCase entirely later.
|
||||
expect(config.rawConfig).toHaveProperty(['dangerously-allow-all-builds'])
|
||||
expect(config.dangerouslyAllowAllBuilds).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -187,6 +187,7 @@ export interface PnpmSettings {
|
||||
auditConfig?: AuditConfig
|
||||
requiredScripts?: string[]
|
||||
supportedArchitectures?: SupportedArchitectures
|
||||
nodeDownloadMirrors?: Record<string, string>
|
||||
}
|
||||
|
||||
export interface ProjectManifest extends BaseManifest {
|
||||
|
||||
4
deps/compliance/commands/src/audit/audit.ts
vendored
4
deps/compliance/commands/src/audit/audit.ts
vendored
@@ -165,7 +165,7 @@ export type AuditOptions = Pick<UniversalOptions, 'dir'> & {
|
||||
| 'overrides'
|
||||
| 'optional'
|
||||
| 'userConfig'
|
||||
| 'rawConfig'
|
||||
| 'authConfig'
|
||||
| 'rootProjectManifest'
|
||||
| 'rootProjectManifestDir'
|
||||
| 'virtualStoreDirMaxLength'
|
||||
@@ -187,7 +187,7 @@ export async function handler (opts: AuditOptions): Promise<{ exitCode: number,
|
||||
optionalDependencies: opts.optional !== false,
|
||||
}
|
||||
let auditReport!: AuditReport
|
||||
const getAuthHeader = createGetAuthHeaderByURI({ allSettings: opts.rawConfig, userSettings: opts.userConfig })
|
||||
const getAuthHeader = createGetAuthHeaderByURI({ allSettings: opts.authConfig, userSettings: opts.userConfig })
|
||||
try {
|
||||
auditReport = await audit(lockfile, getAuthHeader, {
|
||||
dispatcherOptions: {
|
||||
|
||||
2
deps/compliance/commands/test/audit/index.ts
vendored
2
deps/compliance/commands/test/audit/index.ts
vendored
@@ -201,7 +201,7 @@ describe('plugin-commands-audit', () => {
|
||||
...AUDIT_REGISTRY_OPTS,
|
||||
dir: hasVulnerabilitiesDir,
|
||||
rootProjectManifestDir: hasVulnerabilitiesDir,
|
||||
rawConfig: {
|
||||
authConfig: {
|
||||
registry: AUDIT_REGISTRY,
|
||||
[`${AUDIT_REGISTRY.replace(/^https?:/, '')}:_authToken`]: '123',
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
|
||||
const registries = {
|
||||
default: 'https://registry.npmjs.org/',
|
||||
}
|
||||
const rawConfig = {
|
||||
const authConfig = {
|
||||
registry: registries.default,
|
||||
}
|
||||
export const DEFAULT_OPTS = {
|
||||
@@ -42,7 +42,7 @@ export const DEFAULT_OPTS = {
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
rawConfig,
|
||||
authConfig,
|
||||
rawLocalConfig: {},
|
||||
registries,
|
||||
rootProjectManifestDir: '',
|
||||
@@ -66,7 +66,7 @@ export const AUDIT_REGISTRY_OPTS = {
|
||||
registries: {
|
||||
default: AUDIT_REGISTRY,
|
||||
},
|
||||
rawConfig: {
|
||||
authConfig: {
|
||||
registry: AUDIT_REGISTRY,
|
||||
},
|
||||
}
|
||||
@@ -78,7 +78,7 @@ export const MOCK_REGISTRY_OPTS = {
|
||||
registries: {
|
||||
default: MOCK_REGISTRY,
|
||||
},
|
||||
rawConfig: {
|
||||
authConfig: {
|
||||
registry: MOCK_REGISTRY,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export const DEFAULT_OPTS = {
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
authConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
registries: { default: REGISTRY },
|
||||
rootProjectManifestDir: '',
|
||||
|
||||
@@ -36,7 +36,7 @@ export const DEFAULT_OPTS = {
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
authConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
registries: { default: REGISTRY },
|
||||
rootProjectManifestDir: '',
|
||||
|
||||
@@ -165,7 +165,7 @@ export type OutdatedCommandOptions = {
|
||||
| 'offline'
|
||||
| 'optional'
|
||||
| 'production'
|
||||
| 'rawConfig'
|
||||
| 'authConfig'
|
||||
| 'registries'
|
||||
| 'selectedProjectsGraph'
|
||||
| 'strictSsl'
|
||||
|
||||
2
deps/inspection/commands/src/view/index.ts
vendored
2
deps/inspection/commands/src/view/index.ts
vendored
@@ -87,7 +87,7 @@ export async function handler (
|
||||
}
|
||||
const registry = pickRegistryForPackage(opts.registries, packageName)
|
||||
const fetchFromRegistry = createFetchFromRegistry(opts)
|
||||
const getAuthHeader = createGetAuthHeaderByURI({ allSettings: opts.rawConfig ?? {}, userSettings: opts.userConfig ?? {} })
|
||||
const getAuthHeader = createGetAuthHeaderByURI({ allSettings: opts.authConfig ?? {}, userSettings: opts.userConfig ?? {} })
|
||||
const fetchResult = await fetchMetadataFromFromRegistry(
|
||||
{
|
||||
fetch: fetchFromRegistry,
|
||||
|
||||
@@ -38,7 +38,7 @@ export const DEFAULT_OPTS = {
|
||||
pnpmHomeDir: '',
|
||||
proxy: undefined,
|
||||
preferWorkspacePackages: true,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
authConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
registries: { default: REGISTRY },
|
||||
registry: REGISTRY,
|
||||
|
||||
@@ -34,7 +34,7 @@ const OUTDATED_OPTIONS = {
|
||||
global: false,
|
||||
networkConcurrency: 16,
|
||||
offline: false,
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
authConfig: { registry: REGISTRY_URL },
|
||||
registries: { default: REGISTRY_URL },
|
||||
strictSsl: false,
|
||||
tag: 'latest',
|
||||
@@ -83,7 +83,7 @@ test('pnpm outdated: show details (using the public registry to verify that full
|
||||
...OUTDATED_OPTIONS,
|
||||
dir: process.cwd(),
|
||||
long: true,
|
||||
rawConfig: { registry: 'https://registry.npmjs.org/' },
|
||||
authConfig: { registry: 'https://registry.npmjs.org/' },
|
||||
registries: { default: 'https://registry.npmjs.org/' },
|
||||
})
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ export const DEFAULT_OPTS = {
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
authConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
registries: { default: REGISTRY },
|
||||
registry: REGISTRY,
|
||||
|
||||
@@ -9,14 +9,14 @@ import type { DependencyManifest, PackageVersionPolicy } from '@pnpm/types'
|
||||
interface GetManifestOpts {
|
||||
dir: string
|
||||
lockfileDir: string
|
||||
rawConfig: object
|
||||
authConfig: object
|
||||
minimumReleaseAge?: number
|
||||
minimumReleaseAgeExclude?: string[]
|
||||
}
|
||||
|
||||
export type ManifestGetterOptions = Omit<ClientOptions, 'authConfig' | 'minimumReleaseAgeExclude' | 'storeIndex'>
|
||||
& GetManifestOpts
|
||||
& { fullMetadata: boolean, rawConfig: Record<string, string> }
|
||||
& { fullMetadata: boolean, authConfig: Record<string, string> }
|
||||
|
||||
export function createManifestGetter (
|
||||
opts: ManifestGetterOptions
|
||||
@@ -27,7 +27,7 @@ export function createManifestGetter (
|
||||
|
||||
const { resolve } = createResolver({
|
||||
...opts,
|
||||
authConfig: opts.rawConfig,
|
||||
authConfig: opts.authConfig,
|
||||
filterMetadata: false, // We need all the data from metadata for "outdated --long" to work.
|
||||
strictPublishedByCheck: Boolean(opts.minimumReleaseAge),
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ test('getManifest()', async () => {
|
||||
const opts = {
|
||||
dir: '',
|
||||
lockfileDir: '',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}
|
||||
|
||||
const resolve: ResolveFunction = async function (_wantedPackage, _opts) {
|
||||
@@ -52,7 +52,7 @@ test('getManifest() with minimumReleaseAge filters latest when too new', async (
|
||||
const opts = {
|
||||
dir: '',
|
||||
lockfileDir: '',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
minimumReleaseAge: 10080,
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ test('getManifest() does not convert non-latest specifiers', async () => {
|
||||
const opts = {
|
||||
dir: '',
|
||||
lockfileDir: '',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}
|
||||
|
||||
const resolve = jest.fn<ResolveFunction>(async (wantedPackage) => {
|
||||
@@ -105,7 +105,7 @@ test('getManifest() returns null for NO_MATCHING_VERSION when publishedBy is set
|
||||
const opts = {
|
||||
dir: '',
|
||||
lockfileDir: '',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}
|
||||
|
||||
const publishedBy = new Date(Date.now() - 10080 * 60 * 1000)
|
||||
@@ -129,7 +129,7 @@ test('getManifest() throws NO_MATCHING_VERSION when publishedBy is not set', asy
|
||||
const opts = {
|
||||
dir: '',
|
||||
lockfileDir: '',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}
|
||||
|
||||
const resolve: ResolveFunction = jest.fn(async function () {
|
||||
@@ -145,7 +145,7 @@ test('getManifest() with minimumReleaseAgeExclude', async () => {
|
||||
const opts = {
|
||||
dir: '',
|
||||
lockfileDir: '',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}
|
||||
|
||||
const publishedBy = new Date(Date.now() - 10080 * 60 * 1000)
|
||||
|
||||
@@ -272,7 +272,7 @@ async function installFromLockfile (
|
||||
virtualStoreDirMaxLength: opts.virtualStoreDirMaxLength,
|
||||
sideEffectsCacheRead: false,
|
||||
sideEffectsCacheWrite: false,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
unsafePerm: false,
|
||||
userAgent: '',
|
||||
packageManager: opts.packageManager ?? { name: 'pnpm', version: '' },
|
||||
|
||||
@@ -63,7 +63,7 @@ export async function handler (
|
||||
if (isExecutedByCorepack()) {
|
||||
throw new PnpmError('CANT_SELF_UPDATE_IN_COREPACK', 'You should update pnpm with corepack')
|
||||
}
|
||||
const { resolve } = createResolver({ ...opts, authConfig: opts.rawConfig })
|
||||
const { resolve } = createResolver({ ...opts, authConfig: opts.authConfig })
|
||||
const pkgName = 'pnpm'
|
||||
const bareSpecifier = params[0] ?? 'latest'
|
||||
const resolution = await resolve({ alias: pkgName, bareSpecifier }, {
|
||||
|
||||
@@ -60,7 +60,7 @@ function prepareOptions (dir: string) {
|
||||
workspaceConcurrency: 1,
|
||||
extraEnv: {},
|
||||
pnpmfile: '',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
cacheDir: path.join(dir, '.cache'),
|
||||
virtualStoreDirMaxLength: process.platform === 'win32' ? 60 : 120,
|
||||
dir,
|
||||
|
||||
@@ -22,7 +22,6 @@ export interface BunRuntimeResolveResult extends ResolveResult {
|
||||
export async function resolveBunRuntime (
|
||||
ctx: {
|
||||
fetchFromRegistry: FetchFromRegistry
|
||||
rawConfig: Record<string, string>
|
||||
offline?: boolean
|
||||
resolveFromNpm: NpmResolver
|
||||
},
|
||||
|
||||
2
engine/runtime/commands/src/env/envList.ts
vendored
2
engine/runtime/commands/src/env/envList.ts
vendored
@@ -12,6 +12,6 @@ export async function envList (opts: NvmNodeCommandOptions, params: string[]): P
|
||||
async function listRemoteVersions (opts: NvmNodeCommandOptions, versionSpec?: string): Promise<string[]> {
|
||||
const fetch = createFetchFromRegistry(opts)
|
||||
const { releaseChannel, versionSpecifier } = versionSpec ? parseNodeSpecifier(versionSpec) : { releaseChannel: 'release', versionSpecifier: '' }
|
||||
const nodeMirrorBaseUrl = getNodeMirror(opts.rawConfig, releaseChannel)
|
||||
const nodeMirrorBaseUrl = getNodeMirror(opts.nodeDownloadMirrors, releaseChannel)
|
||||
return resolveNodeVersions(fetch, versionSpecifier, nodeMirrorBaseUrl)
|
||||
}
|
||||
|
||||
3
engine/runtime/commands/src/env/node.ts
vendored
3
engine/runtime/commands/src/env/node.ts
vendored
@@ -16,7 +16,8 @@ export type NvmNodeCommandOptions = Pick<Config,
|
||||
| 'key'
|
||||
| 'localAddress'
|
||||
| 'noProxy'
|
||||
| 'rawConfig'
|
||||
| 'nodeDownloadMirrors'
|
||||
| 'authConfig'
|
||||
| 'strictSsl'
|
||||
| 'storeDir'
|
||||
| 'pnpmHomeDir'
|
||||
|
||||
10
engine/runtime/commands/test/env/env.test.ts
vendored
10
engine/runtime/commands/test/env/env.test.ts
vendored
@@ -18,7 +18,7 @@ test('env use calls pnpm add with the correct arguments', async () => {
|
||||
cacheDir: '/tmp/cache',
|
||||
global: true,
|
||||
pnpmHomeDir: '/tmp/pnpm-home',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
storeDir: '/tmp/store',
|
||||
}, ['use', '18'])
|
||||
|
||||
@@ -33,7 +33,7 @@ test('env use passes lts specifier through unchanged', async () => {
|
||||
bin: '/usr/local/bin',
|
||||
global: true,
|
||||
pnpmHomeDir: '/tmp/pnpm-home',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
storeDir: '/tmp/store',
|
||||
}, ['use', 'lts'])
|
||||
|
||||
@@ -48,7 +48,7 @@ test('env use passes codename specifier through unchanged', async () => {
|
||||
bin: '/usr/local/bin',
|
||||
global: true,
|
||||
pnpmHomeDir: '/tmp/pnpm-home',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
storeDir: '/tmp/store',
|
||||
}, ['use', 'argon'])
|
||||
|
||||
@@ -64,7 +64,7 @@ test('fail if not run with --global', async () => {
|
||||
bin: '/usr/local/bin',
|
||||
global: false,
|
||||
pnpmHomeDir: '/tmp/pnpm-home',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['use', '18'])
|
||||
).rejects.toEqual(new PnpmError('NOT_IMPLEMENTED_YET', '"pnpm env use <version>" can only be used with the "--global" option currently'))
|
||||
|
||||
@@ -78,7 +78,7 @@ test('fail if there is no global bin directory', async () => {
|
||||
bin: undefined,
|
||||
global: true,
|
||||
pnpmHomeDir: '/tmp/pnpm-home',
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
}, ['use', 'lts'])
|
||||
).rejects.toEqual(new PnpmError('CANNOT_MANAGE_NODE', 'Unable to manage Node.js because pnpm was not installed using the standalone installation script'))
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ export interface DenoRuntimeResolveResult extends ResolveResult {
|
||||
export async function resolveDenoRuntime (
|
||||
ctx: {
|
||||
fetchFromRegistry: FetchFromRegistry
|
||||
rawConfig: Record<string, string>
|
||||
offline?: boolean
|
||||
resolveFromNpm: NpmResolver
|
||||
},
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import type { Config } from '@pnpm/config.reader'
|
||||
|
||||
export function getNodeMirror (rawConfig: Config['rawConfig'], releaseChannel: string): string {
|
||||
// This is a dynamic lookup since the 'use-node-version' option is allowed to be '<releaseChannel>/<version>'
|
||||
const configKey = `node-mirror:${releaseChannel}`
|
||||
const nodeMirror = rawConfig[configKey] ?? `https://nodejs.org/download/${releaseChannel}/`
|
||||
export function getNodeMirror (nodeDownloadMirrors: Record<string, string> | undefined, releaseChannel: string): string {
|
||||
const nodeMirror = nodeDownloadMirrors?.[releaseChannel] ?? `https://nodejs.org/download/${releaseChannel}/`
|
||||
return normalizeNodeMirror(nodeMirror)
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export interface NodeRuntimeResolveResult extends ResolveResult {
|
||||
export async function resolveNodeRuntime (
|
||||
ctx: {
|
||||
fetchFromRegistry: FetchFromRegistry
|
||||
rawConfig: Record<string, string>
|
||||
nodeDownloadMirrors?: Record<string, string>
|
||||
offline?: boolean
|
||||
},
|
||||
wantedDependency: WantedDependency,
|
||||
@@ -50,7 +50,7 @@ export async function resolveNodeRuntime (
|
||||
if (ctx.offline) throw new PnpmError('NO_OFFLINE_NODEJS_RESOLUTION', 'Offline Node.js resolution is not supported')
|
||||
const versionSpec = wantedDependency.bareSpecifier.substring('runtime:'.length)
|
||||
const { releaseChannel, versionSpecifier } = parseNodeSpecifier(versionSpec)
|
||||
const nodeMirrorBaseUrl = getNodeMirror(ctx.rawConfig, releaseChannel)
|
||||
const nodeMirrorBaseUrl = getNodeMirror(ctx.nodeDownloadMirrors, releaseChannel)
|
||||
const version = await resolveNodeVersion(ctx.fetchFromRegistry, versionSpecifier, nodeMirrorBaseUrl)
|
||||
if (!version) {
|
||||
throw new PnpmError('NODEJS_VERSION_NOT_FOUND', `Could not find a Node.js version that satisfies ${versionSpec}`)
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { getNodeMirror } from '../lib/getNodeMirror.js'
|
||||
|
||||
test.each([
|
||||
['release', { 'node-mirror:release': 'http://test.mirror.localhost/release' }, 'http://test.mirror.localhost/release/'],
|
||||
['nightly', { 'node-mirror:nightly': 'http://test.mirror.localhost/nightly' }, 'http://test.mirror.localhost/nightly/'],
|
||||
['rc', { 'node-mirror:rc': 'http://test.mirror.localhost/rc' }, 'http://test.mirror.localhost/rc/'],
|
||||
['test', { 'node-mirror:test': 'http://test.mirror.localhost/test' }, 'http://test.mirror.localhost/test/'],
|
||||
['v8-canary', { 'node-mirror:v8-canary': 'http://test.mirror.localhost/v8-canary' }, 'http://test.mirror.localhost/v8-canary/'],
|
||||
])('getNodeMirror(%s, %s)', (releaseDir, rawConfig, expected) => {
|
||||
expect(getNodeMirror(rawConfig, releaseDir)).toBe(expected)
|
||||
['release', { release: 'http://test.mirror.localhost/release' }, 'http://test.mirror.localhost/release/'],
|
||||
['nightly', { nightly: 'http://test.mirror.localhost/nightly' }, 'http://test.mirror.localhost/nightly/'],
|
||||
['rc', { rc: 'http://test.mirror.localhost/rc' }, 'http://test.mirror.localhost/rc/'],
|
||||
['test', { test: 'http://test.mirror.localhost/test' }, 'http://test.mirror.localhost/test/'],
|
||||
['v8-canary', { 'v8-canary': 'http://test.mirror.localhost/v8-canary' }, 'http://test.mirror.localhost/v8-canary/'],
|
||||
])('getNodeMirror(%s, %s)', (releaseDir, mirrors, expected) => {
|
||||
expect(getNodeMirror(mirrors, releaseDir)).toBe(expected)
|
||||
})
|
||||
|
||||
test('getNodeMirror uses defaults', () => {
|
||||
const rawConfig = {}
|
||||
expect(getNodeMirror(rawConfig, 'release')).toBe('https://nodejs.org/download/release/')
|
||||
expect(getNodeMirror({}, 'release')).toBe('https://nodejs.org/download/release/')
|
||||
})
|
||||
|
||||
test('getNodeMirror with undefined mirrors uses defaults', () => {
|
||||
expect(getNodeMirror(undefined, 'release')).toBe('https://nodejs.org/download/release/')
|
||||
})
|
||||
|
||||
test('getNodeMirror returns base url with trailing /', () => {
|
||||
const rawConfig = {
|
||||
'node-mirror:release': 'http://test.mirror.localhost',
|
||||
}
|
||||
expect(getNodeMirror(rawConfig, 'release')).toBe('http://test.mirror.localhost/')
|
||||
expect(getNodeMirror({ release: 'http://test.mirror.localhost' }, 'release')).toBe('http://test.mirror.localhost/')
|
||||
})
|
||||
|
||||
@@ -103,7 +103,7 @@ export async function handler (
|
||||
const catalogResolver = resolveFromCatalog.bind(null, opts.catalogs ?? {})
|
||||
const { resolve } = createResolver({
|
||||
...opts,
|
||||
authConfig: opts.rawConfig,
|
||||
authConfig: opts.authConfig,
|
||||
fullMetadata,
|
||||
filterMetadata: fullMetadata,
|
||||
retry: {
|
||||
|
||||
@@ -156,7 +156,6 @@ export type ExecOpts = Required<Pick<Config, 'selectedProjectsGraph'>> & {
|
||||
| 'modulesDir'
|
||||
| 'nodeOptions'
|
||||
| 'pnpmHomeDir'
|
||||
| 'rawConfig'
|
||||
| 'recursive'
|
||||
| 'reporterHidePrefix'
|
||||
| 'userAgent'
|
||||
|
||||
@@ -275,7 +275,6 @@ so you may run "pnpm -w run ${scriptName}"`,
|
||||
extraBinPaths: opts.extraBinPaths,
|
||||
extraEnv: opts.extraEnv,
|
||||
pkgRoot: dir,
|
||||
rawConfig: opts.rawConfig,
|
||||
rootModulesDir: await realpathMissing(path.join(dir, 'node_modules')),
|
||||
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
||||
scriptShell: opts.scriptShell,
|
||||
@@ -283,6 +282,7 @@ so you may run "pnpm -w run ${scriptName}"`,
|
||||
shellEmulator: opts.shellEmulator,
|
||||
stdio: (specifiedScripts.length > 1 && concurrency > 1) ? 'pipe' : 'inherit',
|
||||
unsafePerm: true, // when running scripts explicitly, assume that they're trusted.
|
||||
userAgent: opts.userAgent,
|
||||
}
|
||||
const existsPnp = existsInDir.bind(null, '.pnp.cjs')
|
||||
const pnpPath = (opts.workspaceDir && existsPnp(opts.workspaceDir)) ?? existsPnp(dir)
|
||||
|
||||
@@ -26,8 +26,8 @@ export type RecursiveRunOpts = Pick<Config,
|
||||
| 'enablePrePostScripts'
|
||||
| 'unsafePerm'
|
||||
| 'pnpmHomeDir'
|
||||
| 'rawConfig'
|
||||
| 'requiredScripts'
|
||||
| 'userAgent'
|
||||
| 'rootProjectManifest'
|
||||
| 'scriptsPrependNodePath'
|
||||
| 'scriptShell'
|
||||
@@ -123,7 +123,7 @@ export async function runRecursive (
|
||||
extraBinPaths: opts.extraBinPaths,
|
||||
extraEnv: opts.extraEnv,
|
||||
pkgRoot: prefix,
|
||||
rawConfig: opts.rawConfig,
|
||||
userAgent: opts.userAgent,
|
||||
rootModulesDir: await realpathMissing(path.join(prefix, 'node_modules')),
|
||||
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
||||
scriptShell: opts.scriptShell,
|
||||
|
||||
@@ -36,7 +36,6 @@ test('pnpm run: returns correct exit code', async () => {
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
}, ['exit0'])
|
||||
|
||||
let err!: Error & { errno: number }
|
||||
@@ -49,7 +48,6 @@ test('pnpm run: returns correct exit code', async () => {
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
}, ['exit1'])
|
||||
} catch (_err: any) { // eslint-disable-line
|
||||
err = _err
|
||||
@@ -74,7 +72,6 @@ test('pnpm run --no-bail never fails', async () => {
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
}, ['exit1'])
|
||||
|
||||
const { default: args } = await import(path.resolve('args.json'))
|
||||
@@ -101,7 +98,6 @@ test('run: pass the args to the command that is specified in the build script',
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
}, ['foo', 'arg', '--flag=true', '--help', '-h'])
|
||||
|
||||
const { default: args } = await import(path.resolve('args.json'))
|
||||
@@ -126,7 +122,6 @@ test('run: pass the args to the command that is specified in the build script of
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
}, ['foo', 'arg', '--flag=true', '--help', '-h'])
|
||||
|
||||
const { default: args } = await import(path.resolve('args.json'))
|
||||
@@ -151,7 +146,6 @@ test('test: pass the args to the command that is specified in the build script o
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
}, ['test', 'arg', '--flag=true', '--help', '-h'])
|
||||
|
||||
const { default: args } = await import(path.resolve('args.json'))
|
||||
@@ -176,7 +170,6 @@ test('run start: pass the args to the command that is specified in the build scr
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
}, ['start', 'arg', '--flag=true', '--help', '-h'])
|
||||
|
||||
const { default: args } = await import(path.resolve('args.json'))
|
||||
@@ -201,7 +194,6 @@ test('run stop: pass the args to the command that is specified in the build scri
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
}, ['stop', 'arg', '--flag=true', '--help', '-h'])
|
||||
|
||||
const { default: args } = await import(path.resolve('args.json'))
|
||||
@@ -234,7 +226,6 @@ test('restart: run stop, restart and start', async () => {
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
}, [])
|
||||
|
||||
expect(server.getLines()).toStrictEqual([
|
||||
@@ -271,7 +262,6 @@ test('restart: run stop, restart and start and all the pre/post scripts', async
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
}, [])
|
||||
|
||||
expect(server.getLines()).toStrictEqual([
|
||||
@@ -302,7 +292,6 @@ test('"pnpm run" prints the list of available commands', async () => {
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
}, [])
|
||||
|
||||
expect(output).toBe(`\
|
||||
@@ -351,7 +340,6 @@ test('"pnpm run" prints the list of available commands, including commands of th
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
selectedProjectsGraph,
|
||||
workspaceDir,
|
||||
}, [])
|
||||
@@ -381,7 +369,6 @@ Commands of the root workspace project (to run them, use "pnpm -w run"):
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
selectedProjectsGraph,
|
||||
workspaceDir,
|
||||
}, [])
|
||||
@@ -408,7 +395,6 @@ test('pnpm run does not fail with --if-present even if the wanted script is not
|
||||
extraEnv: {},
|
||||
ifPresent: true,
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
}, ['build'])
|
||||
})
|
||||
|
||||
@@ -477,7 +463,6 @@ test('scripts work with PnP', async () => {
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
}, ['foo'])
|
||||
|
||||
// https://github.com/pnpm/registry-mock/blob/ac2e129eb262009d2e7cd43ed869c31097793073/packages/hello-world-js-bin%401.0.0/index.js#L2
|
||||
@@ -514,7 +499,6 @@ skipOnWindows('pnpm run with custom shell', async () => {
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
scriptShell: path.resolve('node_modules/.bin/shell-mock'),
|
||||
}, ['build'])
|
||||
|
||||
@@ -547,7 +531,6 @@ onlyOnWindows('pnpm shows error if script-shell is .cmd', async () => {
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
scriptShell: path.resolve('node_modules/.bin/shell-mock.cmd'),
|
||||
}, ['build'])
|
||||
}
|
||||
@@ -578,7 +561,6 @@ test('pnpm run with RegExp script selector should work', async () => {
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
}, ['/^(lint|build):.*/'])
|
||||
|
||||
expect(fs.readFileSync('output-build-a.txt', { encoding: 'utf-8' })).toBe('a')
|
||||
@@ -605,7 +587,6 @@ test('pnpm run with RegExp script selector should work also for pre/post script'
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
enablePrePostScripts: true,
|
||||
}, ['/build:.*/'])
|
||||
|
||||
@@ -631,7 +612,6 @@ test('pnpm run with RegExp script selector should work parallel as a default beh
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
}, ['/build:.*/'])
|
||||
|
||||
const outputsA = serverA.getLines().map(x => Number.parseInt(x))
|
||||
@@ -658,7 +638,6 @@ test('pnpm run with RegExp script selector should work sequentially with --works
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
workspaceConcurrency: 1,
|
||||
}, ['/build:.*/'])
|
||||
|
||||
@@ -688,7 +667,6 @@ test.each(['d', 'g', 'i', 'm', 'u', 'v', 'y', 's'])('pnpm run with RegExp script
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
workspaceConcurrency: 1,
|
||||
}, [`/build:.*/${flag}`])
|
||||
} catch (_err: any) { // eslint-disable-line
|
||||
@@ -712,7 +690,6 @@ test('pnpm run with slightly incorrect command suggests correct one', async () =
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
workspaceConcurrency: 1,
|
||||
}, ['buil'])).rejects.toMatchObject({
|
||||
code: 'ERR_PNPM_NO_SCRIPT',
|
||||
@@ -734,7 +711,6 @@ test('pnpm run with custom node-options', async () => {
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
nodeOptions: '--max-old-space-size=1200',
|
||||
workspaceConcurrency: 1,
|
||||
}, ['build'])
|
||||
@@ -754,7 +730,6 @@ test('pnpm run without node version', async () => {
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: process.cwd(),
|
||||
rawConfig: {},
|
||||
workspaceConcurrency: 1,
|
||||
}, ['assert-node-version'])
|
||||
})
|
||||
|
||||
@@ -43,7 +43,7 @@ export const DEFAULT_OPTS = {
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
authConfig: { registry: REGISTRY_URL },
|
||||
rawLocalConfig: {},
|
||||
rootProjectManifestDir: '',
|
||||
registries: { default: REGISTRY_URL },
|
||||
@@ -85,7 +85,7 @@ export const DLX_DEFAULT_OPTS = {
|
||||
pnpmfile: ['.pnpmfile.cjs'],
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
authConfig: { registry: REGISTRY_URL },
|
||||
rawLocalConfig: { registry: REGISTRY_URL },
|
||||
registries: {
|
||||
default: REGISTRY_URL,
|
||||
|
||||
@@ -44,7 +44,6 @@ async function runTest (verifyDepsBeforeRun: VerifyDepsBeforeRun): Promise<void>
|
||||
extraBinPaths: [],
|
||||
extraEnv: {},
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {},
|
||||
verifyDepsBeforeRun,
|
||||
rootProjectManifest,
|
||||
rootProjectManifestDir: process.cwd(),
|
||||
|
||||
@@ -20,7 +20,6 @@ export interface RunLifecycleHookOptions {
|
||||
initCwd?: string
|
||||
optional?: boolean
|
||||
pkgRoot: string
|
||||
rawConfig: object
|
||||
rootModulesDir: string
|
||||
scriptShell?: string
|
||||
silent?: boolean
|
||||
@@ -28,6 +27,7 @@ export interface RunLifecycleHookOptions {
|
||||
shellEmulator?: boolean
|
||||
stdio?: string
|
||||
unsafePerm: boolean
|
||||
userAgent?: string
|
||||
}
|
||||
|
||||
export async function runLifecycleHook (
|
||||
@@ -120,7 +120,7 @@ Please unset the scriptShell option, or configure it to a .exe instead.
|
||||
...opts.extraEnv,
|
||||
INIT_CWD: opts.initCwd ?? process.cwd(),
|
||||
PNPM_SCRIPT_SRC_DIR: opts.pkgRoot,
|
||||
...('user-agent' in opts.rawConfig ? { npm_config_user_agent: (opts.rawConfig as Record<string, string>)['user-agent'] } : {}),
|
||||
...(opts.userAgent ? { npm_config_user_agent: opts.userAgent } : {}),
|
||||
},
|
||||
log: {
|
||||
clearProgress: noop,
|
||||
|
||||
@@ -24,7 +24,6 @@ test('runLifecycleHook()', async () => {
|
||||
depPath: '/simple/1.0.0',
|
||||
optional: false,
|
||||
pkgRoot,
|
||||
rawConfig: {},
|
||||
rootModulesDir,
|
||||
unsafePerm: true,
|
||||
})
|
||||
@@ -38,7 +37,6 @@ test('runLifecycleHook() escapes the args passed to the script', async () => {
|
||||
await runLifecycleHook('echo', pkg, {
|
||||
depPath: '/escape-args/1.0.0',
|
||||
pkgRoot,
|
||||
rawConfig: {},
|
||||
rootModulesDir,
|
||||
unsafePerm: true,
|
||||
args: ['Revert "feature (#1)"'],
|
||||
@@ -53,7 +51,6 @@ test('runLifecycleHook() passes newline correctly', async () => {
|
||||
await runLifecycleHook('echo', pkg, {
|
||||
depPath: 'escape-newline@1.0.0',
|
||||
pkgRoot,
|
||||
rawConfig: {},
|
||||
rootModulesDir,
|
||||
unsafePerm: true,
|
||||
args: ['a\nb != \'A\\nB\''],
|
||||
@@ -71,9 +68,6 @@ test('runLifecycleHook() does not set npm_config env vars', async () => {
|
||||
await runLifecycleHook('postinstall', pkg, {
|
||||
depPath: '/inspect-frozen-lockfile/1.0.0',
|
||||
pkgRoot,
|
||||
rawConfig: {
|
||||
'frozen-lockfile': true,
|
||||
},
|
||||
rootModulesDir,
|
||||
unsafePerm: true,
|
||||
})
|
||||
@@ -88,7 +82,6 @@ test('runPostinstallHooks()', async () => {
|
||||
depPath: '/with-many-scripts/1.0.0',
|
||||
optional: false,
|
||||
pkgRoot,
|
||||
rawConfig: {},
|
||||
rootModulesDir,
|
||||
unsafePerm: true,
|
||||
})
|
||||
@@ -104,7 +97,6 @@ test('runLifecycleHook() should throw an error while missing script start or fil
|
||||
depPath: '/without-script-start-serverjs/1.0.0',
|
||||
optional: false,
|
||||
pkgRoot,
|
||||
rawConfig: {},
|
||||
rootModulesDir,
|
||||
unsafePerm: true,
|
||||
})
|
||||
@@ -118,7 +110,6 @@ test('preinstall script does not trigger node-gyp rebuild', async () => {
|
||||
depPath: '/gyp-with-preinstall/1.0.0',
|
||||
optional: false,
|
||||
pkgRoot,
|
||||
rawConfig: {},
|
||||
rootModulesDir,
|
||||
unsafePerm: true,
|
||||
})
|
||||
@@ -148,7 +139,6 @@ skipOnWindows('runLifecycleHooksConcurrently() should check binding.gyp', async
|
||||
await runLifecycleHooksConcurrently(['install'], [{ buildIndex: 0, rootDir: projectDir as ProjectRootDir, modulesDir: '', manifest: {} }], 5, {
|
||||
storeController: {} as StoreController,
|
||||
optional: false,
|
||||
rawConfig: {},
|
||||
unsafePerm: true,
|
||||
})
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import { safeReadPackageJsonFromDir } from '@pnpm/pkg-manifest.reader'
|
||||
import type { AllowBuild, PackageManifest } from '@pnpm/types'
|
||||
import { rimraf } from '@zkochan/rimraf'
|
||||
import { preferredPM } from 'preferred-pm'
|
||||
import { omit } from 'ramda'
|
||||
|
||||
// We don't run prepublishOnly to prepare the dependency.
|
||||
// This might be counterintuitive as prepublishOnly is where a lot of packages put their build scripts.
|
||||
@@ -23,8 +22,8 @@ const PREPUBLISH_SCRIPTS = [
|
||||
export interface PreparePackageOptions {
|
||||
allowBuild?: AllowBuild
|
||||
ignoreScripts?: boolean
|
||||
rawConfig: Record<string, unknown>
|
||||
unsafePerm?: boolean
|
||||
userAgent?: string
|
||||
}
|
||||
|
||||
export async function preparePackage (opts: PreparePackageOptions, gitRootDir: string, subDir: string): Promise<{ shouldBeBuilt: boolean, pkgDir: string }> {
|
||||
@@ -49,11 +48,9 @@ allowBuilds:
|
||||
const execOpts: RunLifecycleHookOptions = {
|
||||
depPath: `${manifest.name}@${manifest.version}`,
|
||||
pkgRoot: pkgDir,
|
||||
// We can't prepare a package without running its lifecycle scripts.
|
||||
// An alternative solution could be to throw an exception.
|
||||
rawConfig: omit(['ignore-scripts'], opts.rawConfig),
|
||||
rootModulesDir: pkgDir, // We don't need this property but there is currently no way to not set it.
|
||||
unsafePerm: Boolean(opts.unsafePerm),
|
||||
userAgent: opts.userAgent,
|
||||
}
|
||||
try {
|
||||
const installScriptName = `${pm}-install`
|
||||
|
||||
@@ -12,7 +12,7 @@ test('prepare package runs the prepublish script', async () => {
|
||||
const tmp = tempDir()
|
||||
await using server = await createTestIpcServer(path.join(tmp, 'test.sock'))
|
||||
f.copy('has-prepublish-script', tmp)
|
||||
await preparePackage({ allowBuild, rawConfig: {} }, tmp, '')
|
||||
await preparePackage({ allowBuild }, tmp, '')
|
||||
expect(server.getLines()).toStrictEqual([
|
||||
'prepublish',
|
||||
])
|
||||
@@ -22,7 +22,7 @@ test('prepare package does not run the prepublish script if the main file is pre
|
||||
const tmp = tempDir()
|
||||
await using server = await createTestIpcServer(path.join(tmp, 'test.sock'))
|
||||
f.copy('has-prepublish-script-and-main-file', tmp)
|
||||
await preparePackage({ allowBuild, rawConfig: {} }, tmp, '')
|
||||
await preparePackage({ allowBuild }, tmp, '')
|
||||
expect(server.getLines()).toStrictEqual([
|
||||
'prepublish',
|
||||
])
|
||||
@@ -32,7 +32,7 @@ test('prepare package runs the prepublish script in the sub folder if pkgDir is
|
||||
const tmp = tempDir()
|
||||
await using server = await createTestIpcServer(path.join(tmp, 'test.sock'))
|
||||
f.copy('has-prepublish-script-in-workspace', tmp)
|
||||
await preparePackage({ allowBuild, rawConfig: {} }, tmp, 'packages/foo')
|
||||
await preparePackage({ allowBuild }, tmp, 'packages/foo')
|
||||
expect(server.getLines()).toStrictEqual([
|
||||
'prepublish',
|
||||
])
|
||||
|
||||
@@ -15,7 +15,6 @@ import { temporaryDirectory } from 'tempy'
|
||||
export function createBinaryFetcher (ctx: {
|
||||
fetch: FetchFromRegistry
|
||||
fetchFromRemoteTarball: FetchFunction
|
||||
rawConfig: Record<string, string>
|
||||
storeIndex: StoreIndex
|
||||
offline?: boolean
|
||||
}): { binary: BinaryFetcher } {
|
||||
|
||||
@@ -15,9 +15,9 @@ import { safeExeca as execa } from 'execa'
|
||||
|
||||
export interface CreateGitFetcherOptions {
|
||||
gitShallowHosts?: string[]
|
||||
rawConfig: Record<string, unknown>
|
||||
storeIndex: StoreIndex
|
||||
unsafePerm?: boolean
|
||||
userAgent?: string
|
||||
ignoreScripts?: boolean
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ export function createGitFetcher (createOpts: CreateGitFetcherOptions): { git: G
|
||||
const prepareResult = await preparePackage({
|
||||
allowBuild: opts.allowBuild,
|
||||
ignoreScripts: createOpts.ignoreScripts,
|
||||
rawConfig: createOpts.rawConfig,
|
||||
unsafePerm: createOpts.unsafePerm,
|
||||
userAgent: createOpts.userAgent,
|
||||
}, tempLocation, resolution.path ?? '')
|
||||
pkgDir = prepareResult.pkgDir
|
||||
if (ignoreScripts && prepareResult.shouldBeBuilt) {
|
||||
|
||||
@@ -49,7 +49,7 @@ beforeEach(() => {
|
||||
|
||||
test('fetch', async () => {
|
||||
const storeDir = temporaryDirectory()
|
||||
const fetch = createGitFetcher({ rawConfig: {}, storeIndex: createStoreIndex(storeDir) }).git
|
||||
const fetch = createGitFetcher({ storeIndex: createStoreIndex(storeDir) }).git
|
||||
const { filesMap, manifest } = await fetch(
|
||||
createCafsStore(storeDir),
|
||||
{
|
||||
@@ -68,7 +68,7 @@ test('fetch', async () => {
|
||||
|
||||
test('fetch a package from Git sub folder', async () => {
|
||||
const storeDir = temporaryDirectory()
|
||||
const fetch = createGitFetcher({ rawConfig: {}, storeIndex: createStoreIndex(storeDir) }).git
|
||||
const fetch = createGitFetcher({ storeIndex: createStoreIndex(storeDir) }).git
|
||||
const { filesMap } = await fetch(
|
||||
createCafsStore(storeDir),
|
||||
{
|
||||
@@ -86,7 +86,7 @@ test('fetch a package from Git sub folder', async () => {
|
||||
|
||||
test('prevent directory traversal attack when using Git sub folder', async () => {
|
||||
const storeDir = temporaryDirectory()
|
||||
const fetch = createGitFetcher({ rawConfig: {}, storeIndex: createStoreIndex(storeDir) }).git
|
||||
const fetch = createGitFetcher({ storeIndex: createStoreIndex(storeDir) }).git
|
||||
const repo = 'https://github.com/RexSkz/test-git-subfolder-fetch.git'
|
||||
const pkgDir = '../../etc'
|
||||
await expect(
|
||||
@@ -107,7 +107,7 @@ test('prevent directory traversal attack when using Git sub folder', async () =>
|
||||
|
||||
test('prevent directory traversal attack when using Git sub folder #2', async () => {
|
||||
const storeDir = temporaryDirectory()
|
||||
const fetch = createGitFetcher({ rawConfig: {}, storeIndex: createStoreIndex(storeDir) }).git
|
||||
const fetch = createGitFetcher({ storeIndex: createStoreIndex(storeDir) }).git
|
||||
const repo = 'https://github.com/RexSkz/test-git-subfolder-fetch.git'
|
||||
const pkgDir = 'not/exists'
|
||||
await expect(
|
||||
@@ -129,7 +129,6 @@ test('prevent directory traversal attack when using Git sub folder #2', async ()
|
||||
test('fetch a package from Git that has a prepare script', async () => {
|
||||
const storeDir = temporaryDirectory()
|
||||
const fetch = createGitFetcher({
|
||||
rawConfig: {},
|
||||
storeIndex: createStoreIndex(storeDir),
|
||||
}).git
|
||||
const { filesMap } = await fetch(
|
||||
@@ -150,7 +149,7 @@ test('fetch a package from Git that has a prepare script', async () => {
|
||||
// Test case for https://github.com/pnpm/pnpm/issues/1866
|
||||
test('fetch a package without a package.json', async () => {
|
||||
const storeDir = temporaryDirectory()
|
||||
const fetch = createGitFetcher({ rawConfig: {}, storeIndex: createStoreIndex(storeDir) }).git
|
||||
const fetch = createGitFetcher({ storeIndex: createStoreIndex(storeDir) }).git
|
||||
const { filesMap } = await fetch(
|
||||
createCafsStore(storeDir),
|
||||
{
|
||||
@@ -169,7 +168,7 @@ test('fetch a package without a package.json', async () => {
|
||||
// Covers the regression reported in https://github.com/pnpm/pnpm/issues/4064
|
||||
test('fetch a big repository', async () => {
|
||||
const storeDir = temporaryDirectory()
|
||||
const fetch = createGitFetcher({ rawConfig: {}, storeIndex: createStoreIndex(storeDir) }).git
|
||||
const fetch = createGitFetcher({ storeIndex: createStoreIndex(storeDir) }).git
|
||||
const { filesMap } = await fetch(createCafsStore(storeDir),
|
||||
{
|
||||
commit: 'a65fbf5a90f53c9d72fed4daaca59da50f074355',
|
||||
@@ -183,7 +182,7 @@ test('fetch a big repository', async () => {
|
||||
|
||||
test('still able to shallow fetch for allowed hosts', async () => {
|
||||
const storeDir = temporaryDirectory()
|
||||
const fetch = createGitFetcher({ gitShallowHosts: ['github.com'], rawConfig: {}, storeIndex: createStoreIndex(storeDir) }).git
|
||||
const fetch = createGitFetcher({ gitShallowHosts: ['github.com'], storeIndex: createStoreIndex(storeDir) }).git
|
||||
const resolution = {
|
||||
commit: 'c9b30e71d704cd30fa71f2edd1ecc7dcc4985493',
|
||||
repo: 'https://github.com/kevva/is-positive.git',
|
||||
@@ -213,7 +212,6 @@ test('still able to shallow fetch for allowed hosts', async () => {
|
||||
test('fail when preparing a git-hosted package', async () => {
|
||||
const storeDir = temporaryDirectory()
|
||||
const fetch = createGitFetcher({
|
||||
rawConfig: {},
|
||||
storeIndex: createStoreIndex(storeDir),
|
||||
}).git
|
||||
await expect(
|
||||
@@ -232,7 +230,6 @@ test('fail when preparing a git-hosted package', async () => {
|
||||
test('fail when preparing a git-hosted package with a partial commit', async () => {
|
||||
const storeDir = temporaryDirectory()
|
||||
const fetch = createGitFetcher({
|
||||
rawConfig: {},
|
||||
storeIndex: createStoreIndex(storeDir),
|
||||
}).git
|
||||
await expect(
|
||||
@@ -249,7 +246,7 @@ test('fail when preparing a git-hosted package with a partial commit', async ()
|
||||
|
||||
test('do not build the package when scripts are ignored', async () => {
|
||||
const storeDir = temporaryDirectory()
|
||||
const fetch = createGitFetcher({ ignoreScripts: true, rawConfig: {}, storeIndex: createStoreIndex(storeDir) }).git
|
||||
const fetch = createGitFetcher({ ignoreScripts: true, storeIndex: createStoreIndex(storeDir) }).git
|
||||
const { filesMap } = await fetch(createCafsStore(storeDir),
|
||||
{
|
||||
commit: '55416a9c468806a935636c0ad0371a14a64df8c9',
|
||||
@@ -265,7 +262,7 @@ test('do not build the package when scripts are ignored', async () => {
|
||||
|
||||
test('block git package with prepare script', async () => {
|
||||
const storeDir = temporaryDirectory()
|
||||
const fetch = createGitFetcher({ rawConfig: {}, storeIndex: createStoreIndex(storeDir) }).git
|
||||
const fetch = createGitFetcher({ storeIndex: createStoreIndex(storeDir) }).git
|
||||
const repo = 'https://github.com/pnpm-e2e/prepare-script-works.git'
|
||||
await expect(
|
||||
fetch(createCafsStore(storeDir),
|
||||
@@ -283,7 +280,6 @@ test('block git package with prepare script', async () => {
|
||||
test('allow git package with prepare script', async () => {
|
||||
const storeDir = temporaryDirectory()
|
||||
const fetch = createGitFetcher({
|
||||
rawConfig: {},
|
||||
storeIndex: createStoreIndex(storeDir),
|
||||
}).git
|
||||
// This should succeed without throwing because the package is in the allowlist
|
||||
@@ -307,7 +303,7 @@ function prefixGitArgs (): string[] {
|
||||
|
||||
test('fetch only the included files', async () => {
|
||||
const storeDir = temporaryDirectory()
|
||||
const fetch = createGitFetcher({ rawConfig: {}, storeIndex: createStoreIndex(storeDir) }).git
|
||||
const fetch = createGitFetcher({ storeIndex: createStoreIndex(storeDir) }).git
|
||||
const { filesMap } = await fetch(
|
||||
createCafsStore(storeDir),
|
||||
{
|
||||
|
||||
@@ -302,7 +302,7 @@ describe('custom fetcher implementation examples', () => {
|
||||
const tarballFetchers = createTarballFetcher(
|
||||
fetchFromRegistry,
|
||||
() => undefined,
|
||||
{ rawConfig: {}, storeIndex }
|
||||
{ storeIndex }
|
||||
)
|
||||
|
||||
// Custom fetcher that maps custom URLs to tarballs
|
||||
@@ -353,7 +353,7 @@ describe('custom fetcher implementation examples', () => {
|
||||
const tarballFetchers = createTarballFetcher(
|
||||
fetchFromRegistry,
|
||||
() => undefined,
|
||||
{ rawConfig: {}, storeIndex }
|
||||
{ storeIndex }
|
||||
)
|
||||
|
||||
// Custom fetcher that maps custom local paths to tarballs
|
||||
@@ -410,7 +410,7 @@ describe('custom fetcher implementation examples', () => {
|
||||
const tarballFetchers = createTarballFetcher(
|
||||
fetchFromRegistry,
|
||||
() => undefined,
|
||||
{ rawConfig: {}, storeIndex }
|
||||
{ storeIndex }
|
||||
)
|
||||
|
||||
// Custom fetcher that transforms custom resolution to tarball URL
|
||||
@@ -462,7 +462,7 @@ describe('custom fetcher implementation examples', () => {
|
||||
const tarballFetchers = createTarballFetcher(
|
||||
fetchFromRegistry,
|
||||
() => undefined,
|
||||
{ rawConfig: {}, storeIndex, ignoreScripts: true }
|
||||
{ storeIndex, ignoreScripts: true }
|
||||
)
|
||||
|
||||
// Custom fetcher that maps custom git resolution to git-hosted tarball
|
||||
|
||||
@@ -19,7 +19,6 @@ interface Resolution {
|
||||
|
||||
export interface CreateGitHostedTarballFetcher {
|
||||
ignoreScripts?: boolean
|
||||
rawConfig: Record<string, unknown>
|
||||
storeIndex: StoreIndex
|
||||
unsafePerm?: boolean
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ export function createTarballFetcher (
|
||||
fetchFromRegistry: FetchFromRegistry,
|
||||
getAuthHeader: GetAuthHeader,
|
||||
opts: {
|
||||
rawConfig: Record<string, unknown>
|
||||
unsafePerm?: boolean
|
||||
ignoreScripts?: boolean
|
||||
storeIndex: StoreIndex
|
||||
|
||||
@@ -69,7 +69,6 @@ const fetchFromRegistry = createFetchFromRegistry({})
|
||||
const getAuthHeader = () => undefined
|
||||
const fetch = createTarballFetcher(fetchFromRegistry, getAuthHeader, {
|
||||
storeIndex,
|
||||
rawConfig: {},
|
||||
retry: {
|
||||
maxTimeout: 100,
|
||||
minTimeout: 0,
|
||||
@@ -274,7 +273,6 @@ test("don't fail when fetching a local tarball in offline mode", async () => {
|
||||
const fetch = createTarballFetcher(fetchFromRegistry, getAuthHeader, {
|
||||
storeIndex,
|
||||
offline: true,
|
||||
rawConfig: {},
|
||||
retry: {
|
||||
maxTimeout: 100,
|
||||
minTimeout: 0,
|
||||
@@ -302,7 +300,6 @@ test('fail when trying to fetch a non-local tarball in offline mode', async () =
|
||||
const fetch = createTarballFetcher(fetchFromRegistry, getAuthHeader, {
|
||||
storeIndex,
|
||||
offline: true,
|
||||
rawConfig: {},
|
||||
retry: {
|
||||
maxTimeout: 100,
|
||||
minTimeout: 0,
|
||||
@@ -428,7 +425,6 @@ test('accessing private packages', async () => {
|
||||
const getAuthHeader = () => 'Bearer ofjergrg349gj3f2'
|
||||
const fetch = createTarballFetcher(fetchFromRegistry, getAuthHeader, {
|
||||
storeIndex,
|
||||
rawConfig: {},
|
||||
retry: {
|
||||
maxTimeout: 100,
|
||||
minTimeout: 0,
|
||||
@@ -547,7 +543,6 @@ test('do not build the package when scripts are ignored', async () => {
|
||||
const fetch = createTarballFetcher(fetchFromRegistry, getAuthHeader, {
|
||||
storeIndex,
|
||||
ignoreScripts: true,
|
||||
rawConfig: {},
|
||||
retry: {
|
||||
maxTimeout: 100,
|
||||
minTimeout: 0,
|
||||
@@ -596,7 +591,6 @@ test('use the subfolder when path is present', async () => {
|
||||
const fetch = createTarballFetcher(fetchFromRegistry, getAuthHeader, {
|
||||
storeIndex,
|
||||
ignoreScripts: true,
|
||||
rawConfig: {},
|
||||
retry: {
|
||||
maxTimeout: 100,
|
||||
minTimeout: 0,
|
||||
@@ -626,7 +620,6 @@ test('prevent directory traversal attack when path is present', async () => {
|
||||
const fetch = createTarballFetcher(fetchFromRegistry, getAuthHeader, {
|
||||
storeIndex,
|
||||
ignoreScripts: true,
|
||||
rawConfig: {},
|
||||
retry: {
|
||||
maxTimeout: 100,
|
||||
minTimeout: 0,
|
||||
@@ -654,7 +647,6 @@ test('fail when path is not exists', async () => {
|
||||
const fetch = createTarballFetcher(fetchFromRegistry, getAuthHeader, {
|
||||
storeIndex,
|
||||
ignoreScripts: true,
|
||||
rawConfig: {},
|
||||
retry: {
|
||||
maxTimeout: 100,
|
||||
minTimeout: 0,
|
||||
|
||||
@@ -22,11 +22,11 @@ export type ClientOptions = {
|
||||
customResolvers?: CustomResolver[]
|
||||
customFetchers?: CustomFetcher[]
|
||||
ignoreScripts?: boolean
|
||||
rawConfig: Record<string, string>
|
||||
sslConfigs?: Record<string, SslConfig>
|
||||
retry?: RetryTimeoutOptions
|
||||
storeIndex: StoreIndex
|
||||
timeout?: number
|
||||
nodeDownloadMirrors?: Record<string, string>
|
||||
unsafePerm?: boolean
|
||||
userAgent?: string
|
||||
userConfig?: Record<string, string>
|
||||
@@ -71,7 +71,7 @@ type Fetchers = {
|
||||
function createFetchers (
|
||||
fetchFromRegistry: FetchFromRegistry,
|
||||
getAuthHeader: GetAuthHeader,
|
||||
opts: Pick<ClientOptions, 'rawConfig' | 'retry' | 'gitShallowHosts' | 'resolveSymlinksInInjectedDirs' | 'unsafePerm' | 'includeOnlyPackageFiles' | 'offline' | 'fetchMinSpeedKiBps' | 'storeIndex'>
|
||||
opts: Pick<ClientOptions, 'retry' | 'gitShallowHosts' | 'resolveSymlinksInInjectedDirs' | 'unsafePerm' | 'userAgent' | 'includeOnlyPackageFiles' | 'offline' | 'fetchMinSpeedKiBps' | 'storeIndex'>
|
||||
): Fetchers {
|
||||
const tarballFetchers = createTarballFetcher(fetchFromRegistry, getAuthHeader, opts)
|
||||
return {
|
||||
@@ -82,7 +82,6 @@ function createFetchers (
|
||||
fetch: fetchFromRegistry,
|
||||
fetchFromRemoteTarball: tarballFetchers.remoteTarball,
|
||||
offline: opts.offline,
|
||||
rawConfig: opts.rawConfig,
|
||||
storeIndex: opts.storeIndex,
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -11,9 +11,8 @@ test('createClient()', () => {
|
||||
const storeIndex = new StoreIndex('.store')
|
||||
storeIndexes.push(storeIndex)
|
||||
const client = createClient({
|
||||
authConfig: { registry: 'https://registry.npmjs.org/' },
|
||||
authConfig: {},
|
||||
cacheDir: '',
|
||||
rawConfig: {},
|
||||
registries: {
|
||||
default: 'https://reigstry.npmjs.org/',
|
||||
},
|
||||
@@ -25,9 +24,8 @@ test('createClient()', () => {
|
||||
|
||||
test('createResolver()', () => {
|
||||
const { resolve } = createResolver({
|
||||
authConfig: { registry: 'https://registry.npmjs.org/' },
|
||||
authConfig: {},
|
||||
cacheDir: '',
|
||||
rawConfig: {},
|
||||
registries: {
|
||||
default: 'https://reigstry.npmjs.org/',
|
||||
},
|
||||
|
||||
@@ -413,8 +413,8 @@ export async function recursive (
|
||||
saveExact: typeof localConfig.saveExact === 'boolean' ? localConfig.saveExact : opts.saveExact,
|
||||
savePrefix: typeof localConfig.savePrefix === 'string' ? localConfig.savePrefix : opts.savePrefix,
|
||||
}),
|
||||
rawConfig: {
|
||||
...installOpts.rawConfig,
|
||||
authConfig: {
|
||||
...installOpts.authConfig,
|
||||
...localConfig,
|
||||
},
|
||||
storeController: store.ctrl,
|
||||
|
||||
@@ -161,6 +161,7 @@ dependencies is not found inside the workspace',
|
||||
}
|
||||
|
||||
export type UpdateCommandOptions = InstallCommandOptions & {
|
||||
include?: IncludedDependencies
|
||||
interactive?: boolean
|
||||
latest?: boolean
|
||||
packageVulnerabilityAudit?: PackageVulnerabilityAudit
|
||||
@@ -294,10 +295,15 @@ async function update (
|
||||
}
|
||||
}
|
||||
const includeDirect = makeIncludeDependenciesFromCLI(opts.cliOptions)
|
||||
// include is always all-true for updates: updates should not change which
|
||||
// dep types the modules directory supports. The filtering of which deps to
|
||||
// actually resolve/update is handled by includeDirect (from CLI flags).
|
||||
// This matches the original behavior where rawConfig didn't have derived
|
||||
// values like dev=false from --prod, so include defaulted to all-true.
|
||||
const include = {
|
||||
dependencies: opts.rawConfig.production !== false,
|
||||
devDependencies: opts.rawConfig.dev !== false,
|
||||
optionalDependencies: opts.rawConfig.optional !== false,
|
||||
dependencies: true,
|
||||
devDependencies: true,
|
||||
optionalDependencies: true,
|
||||
}
|
||||
const depth = opts.depth ?? Infinity
|
||||
let updateMatching: UpdateMatchingFunction | undefined
|
||||
@@ -315,8 +321,8 @@ async function update (
|
||||
allowNew: false,
|
||||
depth,
|
||||
ignoreCurrentSpecifiers: false,
|
||||
includeDirect,
|
||||
include,
|
||||
includeDirect,
|
||||
update: true,
|
||||
updateToLatest: opts.latest,
|
||||
updateMatching,
|
||||
|
||||
@@ -32,7 +32,7 @@ const DEFAULT_OPTIONS = {
|
||||
preferWorkspacePackages: true,
|
||||
pnpmfile: ['.pnpmfile.cjs'],
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
authConfig: { registry: REGISTRY_URL },
|
||||
rawLocalConfig: { registry: REGISTRY_URL },
|
||||
registries: {
|
||||
default: REGISTRY_URL,
|
||||
|
||||
@@ -11,8 +11,8 @@ import { DEFAULT_OPTS } from './utils/index.js'
|
||||
// This must be a function because some of its values depend on CWD
|
||||
const createOptions = (jsr: string = 'https://npm.jsr.io/') => ({
|
||||
...DEFAULT_OPTS,
|
||||
rawConfig: {
|
||||
...DEFAULT_OPTS.rawConfig,
|
||||
authConfig: {
|
||||
...DEFAULT_OPTS.authConfig,
|
||||
'@jsr:registry': jsr,
|
||||
},
|
||||
registries: {
|
||||
|
||||
@@ -31,7 +31,7 @@ const DEFAULT_OPTIONS = {
|
||||
preferWorkspacePackages: true,
|
||||
pnpmfile: ['.pnpmfile.cjs'],
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
authConfig: { registry: REGISTRY_URL },
|
||||
rawLocalConfig: { registry: REGISTRY_URL },
|
||||
registries: {
|
||||
default: REGISTRY_URL,
|
||||
|
||||
@@ -33,7 +33,7 @@ const DEFAULT_OPTS = {
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: { registry: REGISTRY },
|
||||
authConfig: { registry: REGISTRY },
|
||||
registries: { default: REGISTRY },
|
||||
registry: REGISTRY,
|
||||
rootProjectManifestDir: '',
|
||||
|
||||
@@ -31,7 +31,7 @@ const DEFAULT_OPTS = {
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: { registry: REGISTRY },
|
||||
authConfig: { registry: REGISTRY },
|
||||
registries: { default: REGISTRY },
|
||||
registry: REGISTRY,
|
||||
rootProjectManifestDir: '',
|
||||
|
||||
@@ -28,7 +28,7 @@ const DEFAULT_OPTIONS = {
|
||||
pnpmfile: ['.pnpmfile.cjs'],
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
authConfig: { registry: REGISTRY_URL },
|
||||
rawLocalConfig: { registry: REGISTRY_URL },
|
||||
registries: {
|
||||
default: REGISTRY_URL,
|
||||
|
||||
@@ -31,7 +31,7 @@ const DEFAULT_OPTIONS = {
|
||||
pnpmfile: ['.pnpmfile.cjs'],
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
authConfig: { registry: REGISTRY_URL },
|
||||
rawLocalConfig: { registry: REGISTRY_URL },
|
||||
registries: {
|
||||
default: REGISTRY_URL,
|
||||
|
||||
@@ -85,7 +85,7 @@ test('saveCatalogName works with different protocols', async () => {
|
||||
.reply(200)
|
||||
|
||||
const options = createOptions()
|
||||
options.registries['@jsr'] = options.rawConfig['@jsr:registry'] = 'https://npm.jsr.io/'
|
||||
options.registries['@jsr'] = options.authConfig['@jsr:registry'] = 'https://npm.jsr.io/'
|
||||
await add.handler(options, [
|
||||
'@pnpm.e2e/foo@100.1.0',
|
||||
'jsr:@rus/greet@0.0.3',
|
||||
|
||||
@@ -35,7 +35,7 @@ const DEFAULT_OPTIONS = {
|
||||
pnpmfile: ['.pnpmfile.cjs'],
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
authConfig: { registry: REGISTRY_URL },
|
||||
rawLocalConfig: { registry: REGISTRY_URL },
|
||||
registries: {
|
||||
default: REGISTRY_URL,
|
||||
|
||||
@@ -33,7 +33,7 @@ const DEFAULT_OPTIONS = {
|
||||
pnpmfile: ['.pnpmfile.cjs'],
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
rawConfig: { registry: REGISTRY_URL },
|
||||
authConfig: { registry: REGISTRY_URL },
|
||||
rawLocalConfig: { registry: REGISTRY_URL },
|
||||
registries: {
|
||||
default: REGISTRY_URL,
|
||||
|
||||
@@ -12,8 +12,8 @@ import { DEFAULT_OPTS } from '../utils/index.js'
|
||||
// This must be a function because some of its values depend on CWD
|
||||
const createOptions = (jsr: string = DEFAULT_OPTS.registry) => ({
|
||||
...DEFAULT_OPTS,
|
||||
rawConfig: {
|
||||
...DEFAULT_OPTS.rawConfig,
|
||||
authConfig: {
|
||||
...DEFAULT_OPTS.authConfig,
|
||||
'@jsr:registry': jsr,
|
||||
},
|
||||
registries: {
|
||||
|
||||
@@ -82,7 +82,6 @@ test('recursive update prod dependencies only', async () => {
|
||||
allProjects,
|
||||
dir: process.cwd(),
|
||||
lockfileDir: process.cwd(),
|
||||
optional: false,
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
@@ -95,16 +94,11 @@ test('recursive update prod dependencies only', async () => {
|
||||
...DEFAULT_OPTS,
|
||||
allProjects,
|
||||
cliOptions: {
|
||||
dev: false,
|
||||
optional: false,
|
||||
production: true,
|
||||
},
|
||||
dir: process.cwd(),
|
||||
lockfileDir: process.cwd(),
|
||||
rawConfig: {
|
||||
...DEFAULT_OPTS.rawConfig,
|
||||
optional: false,
|
||||
},
|
||||
recursive: true,
|
||||
selectedProjectsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
@@ -120,7 +114,7 @@ test('recursive update prod dependencies only', async () => {
|
||||
expect(modules?.included).toStrictEqual({
|
||||
dependencies: true,
|
||||
devDependencies: true,
|
||||
optionalDependencies: false,
|
||||
optionalDependencies: true,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ export const DEFAULT_OPTS = {
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
authConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
registries: { default: REGISTRY },
|
||||
registry: REGISTRY,
|
||||
|
||||
@@ -78,7 +78,7 @@ export interface StrictInstallOptions {
|
||||
depth: number
|
||||
lockfileDir: string
|
||||
modulesDir: string
|
||||
rawConfig: Record<string, any> // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
authConfig: Record<string, any> // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
verifyStoreIntegrity: boolean
|
||||
engineStrict: boolean
|
||||
allowBuilds?: Record<string, boolean | string>
|
||||
@@ -242,7 +242,7 @@ const defaults = (opts: InstallOptions): StrictInstallOptions => {
|
||||
preserveWorkspaceProtocol: true,
|
||||
pruneLockfileImporters: false,
|
||||
pruneStore: false,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
registries: DEFAULT_REGISTRIES,
|
||||
resolutionMode: 'highest',
|
||||
saveWorkspaceProtocol: 'rolling',
|
||||
@@ -338,7 +338,7 @@ export function extendOptions (
|
||||
extendedOpts.userAgent = `${extendedOpts.packageManager.name}/${extendedOpts.packageManager.version} ${extendedOpts.userAgent}`
|
||||
}
|
||||
extendedOpts.registries = normalizeRegistries(extendedOpts.registries)
|
||||
extendedOpts.rawConfig['registry'] = extendedOpts.registries.default
|
||||
extendedOpts.authConfig['registry'] = extendedOpts.registries.default
|
||||
if (extendedOpts.enableGlobalVirtualStore) {
|
||||
if (extendedOpts.virtualStoreDir == null) {
|
||||
extendedOpts.virtualStoreDir = path.join(extendedOpts.storeDir, 'links')
|
||||
|
||||
@@ -424,7 +424,7 @@ export async function mutateModules (
|
||||
extraNodePaths: ctx.extraNodePaths,
|
||||
extraEnv: opts.extraEnv,
|
||||
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
||||
rawConfig: opts.rawConfig,
|
||||
userAgent: opts.userAgent,
|
||||
resolveSymlinksInInjectedDirs: opts.resolveSymlinksInInjectedDirs,
|
||||
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
||||
scriptShell: opts.scriptShell,
|
||||
@@ -1461,7 +1461,6 @@ const _installInContext: InstallFunction = async (projects, ctx, opts) => {
|
||||
lockfileDir: ctx.lockfileDir,
|
||||
optional: opts.include.optionalDependencies,
|
||||
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
||||
rawConfig: opts.rawConfig,
|
||||
rootModulesDir: ctx.virtualStoreDir,
|
||||
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
||||
scriptShell: opts.scriptShell,
|
||||
|
||||
@@ -172,7 +172,7 @@ test('scoped package with custom registry', async () => {
|
||||
|
||||
await addDependenciesToPackage({}, ['@scoped/peer'], testDefaults({
|
||||
// setting an incorrect default registry URL
|
||||
rawConfig: {
|
||||
authConfig: {
|
||||
'@scoped:registry': `http://localhost:${REGISTRY_MOCK_PORT}/`,
|
||||
},
|
||||
registry: 'http://localhost:9999/',
|
||||
|
||||
@@ -349,7 +349,7 @@ test(`respects ${WANTED_LOCKFILE} for top dependencies`, async () => {
|
||||
// shouldn't care about what the registry in npmrc is
|
||||
// the one in lockfile should be used
|
||||
await install(manifest, testDefaults({
|
||||
rawConfig: {
|
||||
authConfig: {
|
||||
registry: 'https://registry.npmjs.org',
|
||||
},
|
||||
registry: 'https://registry.npmjs.org',
|
||||
|
||||
@@ -160,7 +160,7 @@ export interface HeadlessOptions {
|
||||
disableRelinkLocalDirDeps?: boolean
|
||||
force: boolean
|
||||
storeDir: string
|
||||
rawConfig: object
|
||||
authConfig: object
|
||||
unsafePerm: boolean
|
||||
userAgent: string
|
||||
registries: Registries
|
||||
@@ -234,7 +234,7 @@ export async function headlessInstall (opts: HeadlessOptions): Promise<Installat
|
||||
extraNodePaths: opts.extraNodePaths,
|
||||
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
||||
extraEnv: opts.extraEnv,
|
||||
rawConfig: opts.rawConfig,
|
||||
authConfig: opts.authConfig,
|
||||
resolveSymlinksInInjectedDirs: opts.resolveSymlinksInInjectedDirs,
|
||||
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
||||
scriptShell: opts.scriptShell,
|
||||
@@ -575,7 +575,6 @@ export async function headlessInstall (opts: HeadlessOptions): Promise<Installat
|
||||
lockfileDir,
|
||||
optional: opts.include.optionalDependencies,
|
||||
preferSymlinkedExecutables: opts.preferSymlinkedExecutables,
|
||||
rawConfig: opts.rawConfig,
|
||||
rootModulesDir: virtualStoreDir,
|
||||
scriptsPrependNodePath: opts.scriptsPrependNodePath,
|
||||
scriptShell: opts.scriptShell,
|
||||
|
||||
@@ -64,7 +64,7 @@ export async function testDefaults (
|
||||
allProjects: Object.fromEntries(
|
||||
await Promise.all(projects.map(async (project) => [project.rootDir, { ...project, manifest: await safeReadPackageJsonFromDir(project.rootDir) }]))
|
||||
),
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
registries: {
|
||||
default: registry,
|
||||
},
|
||||
|
||||
@@ -25,8 +25,6 @@ const IS_POSITIVE_TARBALL = f.find('is-positive-1.0.0.tgz')
|
||||
|
||||
const registries = { default: registry }
|
||||
|
||||
const authConfig = { registry }
|
||||
|
||||
const storeIndexes: StoreIndex[] = []
|
||||
afterAll(() => {
|
||||
for (const si of storeIndexes) si.close()
|
||||
@@ -36,10 +34,9 @@ const topStoreIndex = new StoreIndex('.store')
|
||||
storeIndexes.push(topStoreIndex)
|
||||
|
||||
const { resolve, fetchers } = createClient({
|
||||
authConfig,
|
||||
authConfig: {},
|
||||
cacheDir: '.store',
|
||||
storeDir: '.store',
|
||||
rawConfig: {},
|
||||
registries,
|
||||
storeIndex: topStoreIndex,
|
||||
})
|
||||
@@ -48,8 +45,7 @@ function createFetchersForStore (storeDir: string) {
|
||||
const si = new StoreIndex(storeDir)
|
||||
storeIndexes.push(si)
|
||||
return createClient({
|
||||
authConfig,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
cacheDir: storeDir,
|
||||
storeDir,
|
||||
registries,
|
||||
@@ -595,8 +591,7 @@ test('fetchPackageToStore() does not cache errors', async () => {
|
||||
const noRetryStoreIndex = new StoreIndex('.store')
|
||||
storeIndexes.push(noRetryStoreIndex)
|
||||
const noRetry = createClient({
|
||||
authConfig,
|
||||
rawConfig: {},
|
||||
authConfig: {},
|
||||
retry: { retries: 0 },
|
||||
cacheDir: '.pnpm',
|
||||
storeDir: '.store',
|
||||
|
||||
@@ -25,7 +25,7 @@ const f = fixtures(import.meta.dirname)
|
||||
|
||||
const basePatchOption = {
|
||||
pnpmHomeDir: '',
|
||||
rawConfig: {
|
||||
authConfig: {
|
||||
registry: `http://localhost:${REGISTRY_MOCK_PORT}/`,
|
||||
},
|
||||
registries: { default: `http://localhost:${REGISTRY_MOCK_PORT}/` },
|
||||
|
||||
@@ -38,7 +38,7 @@ export const DEFAULT_OPTS = {
|
||||
pnpmHomeDir: '',
|
||||
preferWorkspacePackages: true,
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
authConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
registries: { default: REGISTRY },
|
||||
registry: REGISTRY,
|
||||
|
||||
@@ -27,7 +27,7 @@ export async function checkForUpdates (config: Config): Promise<void> {
|
||||
|
||||
const { resolve } = createResolver({
|
||||
...config,
|
||||
authConfig: config.rawConfig,
|
||||
authConfig: config.authConfig,
|
||||
retry: {
|
||||
retries: 0,
|
||||
},
|
||||
|
||||
@@ -183,7 +183,6 @@ const cliOptionsTypesByCommandName: Record<string, () => Record<string, unknown>
|
||||
const aliasToFullName = new Map<string, string>()
|
||||
const completionByCommandName: Record<string, CompletionFunc> = {}
|
||||
const shorthandsByCommandName: Record<string, Record<string, string | string[]>> = {}
|
||||
const rcOptionsTypes: Record<string, unknown> = {}
|
||||
const skipPackageManagerCheckForCommandArray = ['completion-server']
|
||||
const recursiveByDefaultCommandArray: string[] = []
|
||||
const overridableByScriptCommandArray: string[] = []
|
||||
@@ -195,7 +194,6 @@ for (let i = 0; i < commands.length; i++) {
|
||||
completion,
|
||||
handler,
|
||||
help,
|
||||
rcOptionsTypes,
|
||||
shorthands,
|
||||
skipPackageManagerCheck,
|
||||
recursiveByDefault,
|
||||
@@ -212,7 +210,6 @@ for (let i = 0; i < commands.length; i++) {
|
||||
if (completion != null) {
|
||||
completionByCommandName[commandName] = completion
|
||||
}
|
||||
Object.assign(rcOptionsTypes, rcOptionsTypes())
|
||||
}
|
||||
if (skipPackageManagerCheck) {
|
||||
skipPackageManagerCheckForCommandArray.push(...commandNames)
|
||||
@@ -262,4 +259,4 @@ export const recursiveByDefaultCommands = new Set(recursiveByDefaultCommandArray
|
||||
|
||||
export const overridableByScriptCommands = new Set(overridableByScriptCommandArray)
|
||||
|
||||
export { NOT_IMPLEMENTED_COMMAND_SET, rcOptionsTypes, shorthandsByCommandName }
|
||||
export { NOT_IMPLEMENTED_COMMAND_SET, shorthandsByCommandName }
|
||||
|
||||
@@ -15,9 +15,7 @@ export async function getConfig (
|
||||
opts: {
|
||||
excludeReporter: boolean
|
||||
globalDirShouldAllowWrite?: boolean
|
||||
rcOptionsTypes: Record<string, unknown>
|
||||
workspaceDir: string | undefined
|
||||
checkUnknownSetting?: boolean
|
||||
ignoreNonAuthSettingsFromLocal?: boolean
|
||||
}
|
||||
): Promise<Config> {
|
||||
@@ -25,9 +23,7 @@ export async function getConfig (
|
||||
cliOptions,
|
||||
globalDirShouldAllowWrite: opts.globalDirShouldAllowWrite,
|
||||
packageManager,
|
||||
rcOptionsTypes: opts.rcOptionsTypes,
|
||||
workspaceDir: opts.workspaceDir,
|
||||
checkUnknownSetting: opts.checkUnknownSetting,
|
||||
ignoreNonAuthSettingsFromLocal: opts.ignoreNonAuthSettingsFromLocal,
|
||||
})
|
||||
config.cliOptions = cliOptions
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user