From 96704a1c5862b28388cba0d18dfcd02f29b04196 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Sat, 4 Apr 2026 20:33:43 +0200 Subject: [PATCH] refactor(config): rename rawConfig to authConfig, add nodeDownloadMirrors, simplify config reader (#11194) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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` on `PnpmSettings` and `Config` - Replaces the `node-mirror:` 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 --- .changeset/refactor-config-auth-config.md | 29 +++ .husky/pre-push | 2 +- auth/commands/src/login.ts | 2 +- auth/commands/test/login.test.ts | 26 +-- .../after-install/src/extendBuildOptions.ts | 4 +- building/after-install/src/index.ts | 4 +- building/commands/src/build/recursive.ts | 4 - building/commands/test/build/utils/index.ts | 2 +- building/during-install/src/index.ts | 5 +- cli/default-reporter/src/reportError.ts | 2 +- cli/default-reporter/test/reportingErrors.ts | 6 +- config/commands/src/ConfigCommandOptions.ts | 5 +- config/commands/src/configGet.ts | 60 ++++-- config/commands/src/configList.ts | 11 +- config/commands/src/configToRecord.ts | 50 +++++ .../commands/src/parseConfigPropertyPath.ts | 22 +- config/commands/src/processConfig.ts | 23 -- config/commands/test/configDelete.test.ts | 12 +- config/commands/test/configGet.test.ts | 196 ++++++++---------- config/commands/test/configList.test.ts | 47 ++--- config/commands/test/configSet.test.ts | 64 +++--- .../test/managingAuthSettings.test.ts | 18 +- config/reader/src/Config.ts | 7 +- config/reader/src/auth.test.ts | 6 +- config/reader/src/index.ts | 71 ++----- config/reader/src/inheritPickedConfig.ts | 4 +- config/reader/src/loadNpmrcFiles.ts | 4 +- config/reader/src/npmConfigTypes.ts | 70 +------ config/reader/test/index.ts | 68 ++---- core/types/src/package.ts | 1 + deps/compliance/commands/src/audit/audit.ts | 4 +- deps/compliance/commands/test/audit/index.ts | 2 +- .../commands/test/audit/utils/options.ts | 8 +- .../commands/test/licenses/utils/index.ts | 2 +- .../commands/test/sbom/utils/index.ts | 2 +- .../commands/src/outdated/outdated.ts | 2 +- deps/inspection/commands/src/view/index.ts | 2 +- .../commands/test/listing/utils/index.ts | 2 +- .../commands/test/outdated/index.ts | 4 +- .../commands/test/outdated/utils/index.ts | 2 +- .../outdated/src/createManifestGetter.ts | 6 +- .../outdated/test/getManifest.spec.ts | 12 +- .../commands/src/self-updater/installPnpm.ts | 2 +- .../commands/src/self-updater/selfUpdate.ts | 2 +- .../test/self-updater/selfUpdate.test.ts | 2 +- engine/runtime/bun-resolver/src/index.ts | 1 - engine/runtime/commands/src/env/envList.ts | 2 +- engine/runtime/commands/src/env/node.ts | 3 +- engine/runtime/commands/test/env/env.test.ts | 10 +- engine/runtime/deno-resolver/src/index.ts | 1 - .../node-resolver/src/getNodeMirror.ts | 8 +- engine/runtime/node-resolver/src/index.ts | 4 +- .../node-resolver/test/getNodeMirror.test.ts | 26 +-- exec/commands/src/dlx.ts | 2 +- exec/commands/src/exec.ts | 1 - exec/commands/src/run.ts | 2 +- exec/commands/src/runRecursive.ts | 4 +- exec/commands/test/index.ts | 25 --- exec/commands/test/utils/index.ts | 4 +- exec/commands/test/verifyDepsBeforeRun.ts | 1 - exec/lifecycle/src/runLifecycleHook.ts | 4 +- exec/lifecycle/test/index.ts | 10 - exec/prepare-package/src/index.ts | 7 +- exec/prepare-package/test/index.ts | 6 +- fetching/binary-fetcher/src/index.ts | 1 - fetching/git-fetcher/src/index.ts | 4 +- fetching/git-fetcher/test/index.ts | 24 +-- fetching/pick-fetcher/test/customFetch.ts | 8 +- .../src/gitHostedTarballFetcher.ts | 1 - fetching/tarball-fetcher/src/index.ts | 1 - fetching/tarball-fetcher/test/fetch.ts | 8 - installing/client/src/index.ts | 5 +- installing/client/test/index.ts | 6 +- installing/commands/src/recursive.ts | 4 +- installing/commands/src/update/index.ts | 14 +- installing/commands/test/add.ts | 2 +- installing/commands/test/addJsr.ts | 4 +- installing/commands/test/fetch.ts | 2 +- installing/commands/test/import.ts | 2 +- installing/commands/test/importRecursive.ts | 2 +- installing/commands/test/peerDependencies.ts | 2 +- installing/commands/test/prune.ts | 2 +- installing/commands/test/saveCatalog.ts | 2 +- .../commands/test/update/interactive.ts | 2 +- installing/commands/test/update/issue-7415.ts | 2 +- installing/commands/test/update/jsr.ts | 4 +- installing/commands/test/update/recursive.ts | 8 +- installing/commands/test/utils/index.ts | 2 +- .../src/install/extendInstallOptions.ts | 6 +- .../deps-installer/src/install/index.ts | 3 +- .../deps-installer/test/install/misc.ts | 2 +- installing/deps-installer/test/lockfile.ts | 2 +- installing/deps-restorer/src/index.ts | 5 +- .../deps-restorer/test/utils/testDefaults.ts | 2 +- installing/package-requester/test/index.ts | 11 +- patching/commands/test/patch.test.ts | 2 +- patching/commands/test/utils/index.ts | 2 +- pnpm/src/checkForUpdates.ts | 2 +- pnpm/src/cmd/index.ts | 5 +- pnpm/src/getConfig.ts | 4 - pnpm/src/main.ts | 4 +- pnpm/test/getConfig.test.ts | 2 - releasing/commands/src/publish/pack.ts | 5 +- releasing/commands/src/publish/publish.ts | 2 +- .../commands/src/publish/recursivePublish.ts | 4 +- releasing/commands/test/deploy/utils/index.ts | 2 +- .../commands/test/publish/recursivePublish.ts | 4 +- .../commands/test/publish/utils/index.ts | 2 +- resolving/default-resolver/src/index.ts | 8 +- .../default-resolver/test/customResolver.ts | 12 -- resolving/default-resolver/test/index.ts | 1 - store/commands/src/inspecting/catIndex.ts | 4 +- store/commands/test/store/storeAdd.ts | 6 +- store/commands/test/store/storePath.ts | 4 +- store/commands/test/store/storePrune.ts | 32 +-- store/commands/test/store/storeStatus.ts | 6 +- .../src/createNewStoreController.ts | 7 +- store/controller/test/index.ts | 8 +- testing/temp-store/src/index.ts | 1 - workspace/commands/src/init.ts | 14 +- workspace/commands/src/utils.ts | 57 ++--- workspace/commands/test/init.test.ts | 42 ++-- workspace/commands/test/utils.test.ts | 21 +- 123 files changed, 595 insertions(+), 811 deletions(-) create mode 100644 .changeset/refactor-config-auth-config.md create mode 100644 config/commands/src/configToRecord.ts delete mode 100644 config/commands/src/processConfig.ts diff --git a/.changeset/refactor-config-auth-config.md b/.changeset/refactor-config-auth-config.md new file mode 100644 index 0000000000..f41313467b --- /dev/null +++ b/.changeset/refactor-config-auth-config.md @@ -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`. diff --git a/.husky/pre-push b/.husky/pre-push index 701e0fbce4..8f85b36dca 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1 +1 @@ -pnpm run lint --quiet +pnpm run compile-only && pnpm run lint --quiet diff --git a/auth/commands/src/login.ts b/auth/commands/src/login.ts index 0fba330e9f..aa2a29e252 100644 --- a/auth/commands/src/login.ts +++ b/auth/commands/src/login.ts @@ -63,7 +63,7 @@ export type LoginCommandOptions = Pick & { registry?: string } diff --git a/auth/commands/test/login.test.ts b/auth/commands/test/login.test.ts index 8fb983f9df..41958ef48e 100644 --- a/auth/commands/test/login.test.ts +++ b/auth/commands/test/login.test.ts @@ -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') diff --git a/building/after-install/src/extendBuildOptions.ts b/building/after-install/src/extendBuildOptions.ts index 5e06b3a44e..2f693a2337 100644 --- a/building/after-install/src/extendBuildOptions.ts +++ b/building/after-install/src/extendBuildOptions.ts @@ -35,7 +35,7 @@ export type StrictBuildOptions = { production: boolean development: boolean optional: boolean - rawConfig: object + authConfig: object userConfig: Record userAgent: string packageManager: { @@ -73,7 +73,7 @@ const defaults = async (opts: BuildOptions): Promise => { packageManager, pending: false, production: true, - rawConfig: {}, + authConfig: {}, registries: DEFAULT_REGISTRIES, scriptsPrependNodePath: false, shamefullyHoist: false, diff --git a/building/after-install/src/index.ts b/building/after-install/src/index.ts index e2366f406d..0d4d4b871d 100644 --- a/building/after-install/src/index.ts +++ b/building/after-install/src/index.ts @@ -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) diff --git a/building/commands/src/build/recursive.ts b/building/commands/src/build/recursive.ts index bd5816cc34..60ee363487 100755 --- a/building/commands/src/build/recursive.ts +++ b/building/commands/src/build/recursive.ts @@ -137,10 +137,6 @@ export async function recursiveRebuild ( ...localConfig, dir: rootDir, pending: opts.pending === true, - rawConfig: { - ...rebuildOpts.rawConfig, - ...localConfig, - }, } ) result[rootDir].status = 'passed' diff --git a/building/commands/test/build/utils/index.ts b/building/commands/test/build/utils/index.ts index e1ec04d168..55f133f052 100644 --- a/building/commands/test/build/utils/index.ts +++ b/building/commands/test/build/utils/index.ts @@ -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, diff --git a/building/during-install/src/index.ts b/building/during-install/src/index.ts index b1c870f412..8cbd4b1cc9 100644 --- a/building/during-install/src/index.ts +++ b/building/during-install/src/index.ts @@ -43,7 +43,6 @@ export async function buildModules ( lockfileDir: string optional: boolean preferSymlinkedExecutables?: boolean - rawConfig: object unsafePerm: boolean userAgent: string scriptsPrependNodePath?: boolean | 'warn-only' @@ -141,7 +140,6 @@ async function buildDependency ( lockfileDir: string optional: boolean preferSymlinkedExecutables?: boolean - rawConfig: object rootModulesDir: string scriptsPrependNodePath?: boolean | 'warn-only' scriptShell?: string @@ -149,6 +147,7 @@ async function buildDependency ( sideEffectsCacheWrite: boolean storeController: StoreController unsafePerm: boolean + userAgent?: string hoistedLocations?: Record builtHoistedDeps?: Record> enableGlobalVirtualStore?: boolean @@ -184,12 +183,12 @@ async function buildDependency ( 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. diff --git a/cli/default-reporter/src/reportError.ts b/cli/default-reporter/src/reportError.ts index 3dd1c86013..cb3a1aa74e 100644 --- a/cli/default-reporter/src/reportError.ts +++ b/cli/default-reporter/src/reportError.ts @@ -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 diff --git a/cli/default-reporter/test/reportingErrors.ts b/cli/default-reporter/test/reportingErrors.ts index 03522fb5f9..efbe1af363 100644 --- a/cli/default-reporter/test/reportingErrors.ts +++ b/cli/default-reporter/test/reportingErrors.ts @@ -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(), }) diff --git a/config/commands/src/ConfigCommandOptions.ts b/config/commands/src/ConfigCommandOptions.ts index 725caba879..3b69bafc29 100644 --- a/config/commands/src/ConfigCommandOptions.ts +++ b/config/commands/src/ConfigCommandOptions.ts @@ -5,9 +5,12 @@ export type ConfigCommandOptions = Pick & { 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 } diff --git a/config/commands/src/configGet.ts b/config/commands/src/configGet.ts index 32c6ea3b7c..f9bc929270 100644 --- a/config/commands/src/configGet.ts +++ b/config/commands/src/configGet.ts @@ -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 } -function getRcConfig (rawConfig: Record, key: string, isScopedKey: boolean): Found | undefined { +function lookupConfig (opts: ConfigCommandOptions, key: string, isScopedKey: boolean): Found | 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)[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, propertyPath: string): Found { +function lookupByPropertyPath (opts: ConfigCommandOptions, propertyPath: string): Found { 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 function displayConfig (config: unknown, opts: DisplayConfigOptions): string { diff --git a/config/commands/src/configList.ts b/config/commands/src/configList.ts index 07f0749a67..d2989253a6 100644 --- a/config/commands/src/configList.ts +++ b/config/commands/src/configList.ts @@ -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 - -export async function configList (opts: ConfigListOptions): Promise { - const processedConfig = processConfig(opts.rawConfig) - return JSON.stringify(processedConfig, undefined, 2) +export async function configList (opts: ConfigCommandOptions): Promise { + return JSON.stringify(configToRecord(opts as unknown as Config), undefined, 2) } diff --git a/config/commands/src/configToRecord.ts b/config/commands/src/configToRecord.ts new file mode 100644 index 0000000000..35b8819bdb --- /dev/null +++ b/config/commands/src/configToRecord.ts @@ -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 { + const result: Record = {} + 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)[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)) +} diff --git a/config/commands/src/parseConfigPropertyPath.ts b/config/commands/src/parseConfigPropertyPath.ts index f7f84a11f5..2fbba39ee3 100644 --- a/config/commands/src/parseConfigPropertyPath.ts +++ b/config/commands/src/parseConfigPropertyPath.ts @@ -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 { 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 -} diff --git a/config/commands/src/processConfig.ts b/config/commands/src/processConfig.ts deleted file mode 100644 index 96085057c4..0000000000 --- a/config/commands/src/processConfig.ts +++ /dev/null @@ -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): Record { - const result: Record = {} - 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): Record { - return camelCaseConfig(censorProtectedSettings(sortDirectKeys(rawConfig))) -} diff --git a/config/commands/test/configDelete.test.ts b/config/commands/test/configDelete.test.ts index a8d801269b..d526fe4c24 100644 --- a/config/commands/test/configDelete.test.ts +++ b/config/commands/test/configDelete.test.ts @@ -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') diff --git a/config/commands/test/configGet.test.ts b/config/commands/test/configGet.test.ts index 93c5c1ebb6..76fa91f364 100644 --- a/config/commands/test/configGet.test.ts +++ b/config/commands/test/configGet.test.ts @@ -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') diff --git a/config/commands/test/configList.test.ts b/config/commands/test/configList.test.ts index dc68b33892..dc4b86a2fb 100644 --- a/config/commands/test/configList.test.ts +++ b/config/commands/test/configList.test.ts @@ -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)', }) }) diff --git a/config/commands/test/configSet.test.ts b/config/commands/test/configSet.test.ts index 5e761a0173..4889bff026 100644 --- a/config/commands/test/configSet.test.ts +++ b/config/commands/test/configSet.test.ts @@ -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({ diff --git a/config/commands/test/managingAuthSettings.test.ts b/config/commands/test/managingAuthSettings.test.ts index ab593fd37c..b5088ca51a 100644 --- a/config/commands/test/managingAuthSettings.test.ts +++ b/config/commands/test/managingAuthSettings.test.ts @@ -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({ diff --git a/config/reader/src/Config.ts b/config/reader/src/Config.ts index 12c36f5e0b..e1f9a09704 100644 --- a/config/reader/src/Config.ts +++ b/config/reader/src/Config.ts @@ -14,7 +14,7 @@ import type { import type { OptionsFromRootManifest } from './getOptionsFromRootManifest.js' import type { AuthInfo } from './parseAuthInfo.js' -export type UniversalOptions = Pick +export type UniversalOptions = Pick export type VerifyDepsBeforeRun = 'install' | 'warn' | 'error' | 'prompt' | false @@ -38,7 +38,9 @@ export interface Config extends AuthInfo, OptionsFromRootManifest { filter: string[] filterProd: string[] rawLocalConfig: Record, // eslint-disable-line - rawConfig: Record, // eslint-disable-line + authConfig: Record, // eslint-disable-line + /** Keys explicitly set from workspace yaml, CLI, or env vars (not defaults). */ + explicitlySetKeys: Set 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 offline?: boolean registry?: string optional?: boolean diff --git a/config/reader/src/auth.test.ts b/config/reader/src/auth.test.ts index a8fa87236f..e4297b07a8 100644 --- a/config/reader/src/auth.test.ts +++ b/config/reader/src/auth.test.ts @@ -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', diff --git a/config/reader/src/index.ts b/config/reader/src/index.ts index 7ec8e0e29f..19d9478592 100644 --- a/config/reader/src/index.ts +++ b/config/reader/src/index.ts @@ -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 workspaceDir?: string | undefined - checkUnknownSetting?: boolean env?: Record 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 = { '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)[configKey]) - : (defaultOptions as Record)[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)[camelcase(key, { locale: 'en-US' })] = value + } + } + const globalDepsBuildConfig = extractAndRemoveDependencyBuildOptions(pnpmConfig) + // Track which keys are explicitly set (not defaults) + const explicitlySetKeys = new Set(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) } + diff --git a/config/reader/src/inheritPickedConfig.ts b/config/reader/src/inheritPickedConfig.ts index 46d48ba823..a37a89f416 100644 --- a/config/reader/src/inheritPickedConfig.ts +++ b/config/reader/src/inheritPickedConfig.ts @@ -1,6 +1,6 @@ import type { Config } from './Config.js' -export type InheritableConfig = Partial & Pick +export type InheritableConfig = Partial & Pick export type PickConfig = (cfg: Partial) => Partial export type PickRawConfig = (cfg: Record) => Record @@ -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)) } diff --git a/config/reader/src/loadNpmrcFiles.ts b/config/reader/src/loadNpmrcFiles.ts index 29a4844db0..f35715f380 100644 --- a/config/reader/src/loadNpmrcFiles.ts +++ b/config/reader/src/loadNpmrcFiles.ts @@ -13,9 +13,9 @@ export interface NpmrcConfigResult { * Priority (lowest to highest): builtin < defaults < user < auth.ini < workspace < CLI */ mergedConfig: Record - /** Raw config suitable for pnpmConfig.rawConfig (filtered through pickIniConfig by consumer) */ + /** Raw config suitable for pnpmConfig.authConfig (filtered through pickIniConfig by consumer) */ rawConfig: Record - /** Workspace .npmrc data (for rawLocalConfig and checkUnknownSetting) */ + /** Workspace .npmrc data (for rawLocalConfig) */ workspaceNpmrc: Record /** User ~/.npmrc data (for token helpers) */ userConfig: Record diff --git a/config/reader/src/npmConfigTypes.ts b/config/reader/src/npmConfigTypes.ts index 8fa699729d..d52dab4c46 100644 --- a/config/reader/src/npmConfigTypes.ts +++ b/config/reader/src/npmConfigTypes.ts @@ -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, } diff --git a/config/reader/test/index.ts b/config/reader/test/index.ts index 794e81cce4..c4b93ee6e1 100644 --- a/config/reader/test/index.ts +++ b/config/reader/test/index.ts @@ -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() }) }) diff --git a/core/types/src/package.ts b/core/types/src/package.ts index 646796600d..4247d4cdc9 100644 --- a/core/types/src/package.ts +++ b/core/types/src/package.ts @@ -187,6 +187,7 @@ export interface PnpmSettings { auditConfig?: AuditConfig requiredScripts?: string[] supportedArchitectures?: SupportedArchitectures + nodeDownloadMirrors?: Record } export interface ProjectManifest extends BaseManifest { diff --git a/deps/compliance/commands/src/audit/audit.ts b/deps/compliance/commands/src/audit/audit.ts index 5785d313ba..41af89fcbc 100644 --- a/deps/compliance/commands/src/audit/audit.ts +++ b/deps/compliance/commands/src/audit/audit.ts @@ -165,7 +165,7 @@ export type AuditOptions = Pick & { | '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: { diff --git a/deps/compliance/commands/test/audit/index.ts b/deps/compliance/commands/test/audit/index.ts index 8f4532f635..38bc95e075 100644 --- a/deps/compliance/commands/test/audit/index.ts +++ b/deps/compliance/commands/test/audit/index.ts @@ -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', }, diff --git a/deps/compliance/commands/test/audit/utils/options.ts b/deps/compliance/commands/test/audit/utils/options.ts index 49437a3e28..c19d37d8a9 100644 --- a/deps/compliance/commands/test/audit/utils/options.ts +++ b/deps/compliance/commands/test/audit/utils/options.ts @@ -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, }, } diff --git a/deps/compliance/commands/test/licenses/utils/index.ts b/deps/compliance/commands/test/licenses/utils/index.ts index 83b9e7cc35..f510158919 100644 --- a/deps/compliance/commands/test/licenses/utils/index.ts +++ b/deps/compliance/commands/test/licenses/utils/index.ts @@ -36,7 +36,7 @@ export const DEFAULT_OPTS = { pnpmHomeDir: '', preferWorkspacePackages: true, proxy: undefined, - rawConfig: { registry: REGISTRY }, + authConfig: { registry: REGISTRY }, rawLocalConfig: {}, registries: { default: REGISTRY }, rootProjectManifestDir: '', diff --git a/deps/compliance/commands/test/sbom/utils/index.ts b/deps/compliance/commands/test/sbom/utils/index.ts index 26df1a0b1f..c06f0a39ad 100644 --- a/deps/compliance/commands/test/sbom/utils/index.ts +++ b/deps/compliance/commands/test/sbom/utils/index.ts @@ -36,7 +36,7 @@ export const DEFAULT_OPTS = { pnpmHomeDir: '', preferWorkspacePackages: true, proxy: undefined, - rawConfig: { registry: REGISTRY }, + authConfig: { registry: REGISTRY }, rawLocalConfig: {}, registries: { default: REGISTRY }, rootProjectManifestDir: '', diff --git a/deps/inspection/commands/src/outdated/outdated.ts b/deps/inspection/commands/src/outdated/outdated.ts index 32c5f01b13..19a948ec41 100644 --- a/deps/inspection/commands/src/outdated/outdated.ts +++ b/deps/inspection/commands/src/outdated/outdated.ts @@ -165,7 +165,7 @@ export type OutdatedCommandOptions = { | 'offline' | 'optional' | 'production' -| 'rawConfig' +| 'authConfig' | 'registries' | 'selectedProjectsGraph' | 'strictSsl' diff --git a/deps/inspection/commands/src/view/index.ts b/deps/inspection/commands/src/view/index.ts index 2859cf2133..0b5f7662b6 100644 --- a/deps/inspection/commands/src/view/index.ts +++ b/deps/inspection/commands/src/view/index.ts @@ -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, diff --git a/deps/inspection/commands/test/listing/utils/index.ts b/deps/inspection/commands/test/listing/utils/index.ts index a9fe033b9e..8591dd645d 100644 --- a/deps/inspection/commands/test/listing/utils/index.ts +++ b/deps/inspection/commands/test/listing/utils/index.ts @@ -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, diff --git a/deps/inspection/commands/test/outdated/index.ts b/deps/inspection/commands/test/outdated/index.ts index da375e1881..5499cc4e52 100644 --- a/deps/inspection/commands/test/outdated/index.ts +++ b/deps/inspection/commands/test/outdated/index.ts @@ -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/' }, }) diff --git a/deps/inspection/commands/test/outdated/utils/index.ts b/deps/inspection/commands/test/outdated/utils/index.ts index 439cfa1331..9122ca898e 100644 --- a/deps/inspection/commands/test/outdated/utils/index.ts +++ b/deps/inspection/commands/test/outdated/utils/index.ts @@ -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, diff --git a/deps/inspection/outdated/src/createManifestGetter.ts b/deps/inspection/outdated/src/createManifestGetter.ts index 3b9f00e18f..c1f7473aa4 100644 --- a/deps/inspection/outdated/src/createManifestGetter.ts +++ b/deps/inspection/outdated/src/createManifestGetter.ts @@ -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 & GetManifestOpts -& { fullMetadata: boolean, rawConfig: Record } +& { fullMetadata: boolean, authConfig: Record } 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), }) diff --git a/deps/inspection/outdated/test/getManifest.spec.ts b/deps/inspection/outdated/test/getManifest.spec.ts index 5d02fc5a1d..770d22b027 100644 --- a/deps/inspection/outdated/test/getManifest.spec.ts +++ b/deps/inspection/outdated/test/getManifest.spec.ts @@ -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(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) diff --git a/engine/pm/commands/src/self-updater/installPnpm.ts b/engine/pm/commands/src/self-updater/installPnpm.ts index 03df581c3b..d2157d3113 100644 --- a/engine/pm/commands/src/self-updater/installPnpm.ts +++ b/engine/pm/commands/src/self-updater/installPnpm.ts @@ -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: '' }, diff --git a/engine/pm/commands/src/self-updater/selfUpdate.ts b/engine/pm/commands/src/self-updater/selfUpdate.ts index fdad55da13..e1b28d6547 100644 --- a/engine/pm/commands/src/self-updater/selfUpdate.ts +++ b/engine/pm/commands/src/self-updater/selfUpdate.ts @@ -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 }, { diff --git a/engine/pm/commands/test/self-updater/selfUpdate.test.ts b/engine/pm/commands/test/self-updater/selfUpdate.test.ts index cc132ea86b..5426c15d6b 100644 --- a/engine/pm/commands/test/self-updater/selfUpdate.test.ts +++ b/engine/pm/commands/test/self-updater/selfUpdate.test.ts @@ -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, diff --git a/engine/runtime/bun-resolver/src/index.ts b/engine/runtime/bun-resolver/src/index.ts index 3de38f979b..f60d1b4738 100644 --- a/engine/runtime/bun-resolver/src/index.ts +++ b/engine/runtime/bun-resolver/src/index.ts @@ -22,7 +22,6 @@ export interface BunRuntimeResolveResult extends ResolveResult { export async function resolveBunRuntime ( ctx: { fetchFromRegistry: FetchFromRegistry - rawConfig: Record offline?: boolean resolveFromNpm: NpmResolver }, diff --git a/engine/runtime/commands/src/env/envList.ts b/engine/runtime/commands/src/env/envList.ts index cc92d4e034..737502597a 100644 --- a/engine/runtime/commands/src/env/envList.ts +++ b/engine/runtime/commands/src/env/envList.ts @@ -12,6 +12,6 @@ export async function envList (opts: NvmNodeCommandOptions, params: string[]): P async function listRemoteVersions (opts: NvmNodeCommandOptions, versionSpec?: string): Promise { 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) } diff --git a/engine/runtime/commands/src/env/node.ts b/engine/runtime/commands/src/env/node.ts index 6a8dfab6f3..e2b81e05f3 100644 --- a/engine/runtime/commands/src/env/node.ts +++ b/engine/runtime/commands/src/env/node.ts @@ -16,7 +16,8 @@ export type NvmNodeCommandOptions = Pick { 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 " 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')) diff --git a/engine/runtime/deno-resolver/src/index.ts b/engine/runtime/deno-resolver/src/index.ts index bdf7818b9d..c9ef4c51c9 100644 --- a/engine/runtime/deno-resolver/src/index.ts +++ b/engine/runtime/deno-resolver/src/index.ts @@ -32,7 +32,6 @@ export interface DenoRuntimeResolveResult extends ResolveResult { export async function resolveDenoRuntime ( ctx: { fetchFromRegistry: FetchFromRegistry - rawConfig: Record offline?: boolean resolveFromNpm: NpmResolver }, diff --git a/engine/runtime/node-resolver/src/getNodeMirror.ts b/engine/runtime/node-resolver/src/getNodeMirror.ts index 26f8d3b034..aa9d1c6d3b 100644 --- a/engine/runtime/node-resolver/src/getNodeMirror.ts +++ b/engine/runtime/node-resolver/src/getNodeMirror.ts @@ -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 '/' - const configKey = `node-mirror:${releaseChannel}` - const nodeMirror = rawConfig[configKey] ?? `https://nodejs.org/download/${releaseChannel}/` +export function getNodeMirror (nodeDownloadMirrors: Record | undefined, releaseChannel: string): string { + const nodeMirror = nodeDownloadMirrors?.[releaseChannel] ?? `https://nodejs.org/download/${releaseChannel}/` return normalizeNodeMirror(nodeMirror) } diff --git a/engine/runtime/node-resolver/src/index.ts b/engine/runtime/node-resolver/src/index.ts index 818515d663..65f5417286 100644 --- a/engine/runtime/node-resolver/src/index.ts +++ b/engine/runtime/node-resolver/src/index.ts @@ -31,7 +31,7 @@ export interface NodeRuntimeResolveResult extends ResolveResult { export async function resolveNodeRuntime ( ctx: { fetchFromRegistry: FetchFromRegistry - rawConfig: Record + nodeDownloadMirrors?: Record 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}`) diff --git a/engine/runtime/node-resolver/test/getNodeMirror.test.ts b/engine/runtime/node-resolver/test/getNodeMirror.test.ts index 07b588e62e..f110a96e45 100644 --- a/engine/runtime/node-resolver/test/getNodeMirror.test.ts +++ b/engine/runtime/node-resolver/test/getNodeMirror.test.ts @@ -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/') }) diff --git a/exec/commands/src/dlx.ts b/exec/commands/src/dlx.ts index f79e16873b..2688e4c035 100644 --- a/exec/commands/src/dlx.ts +++ b/exec/commands/src/dlx.ts @@ -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: { diff --git a/exec/commands/src/exec.ts b/exec/commands/src/exec.ts index e021a0b35b..50dba1df27 100644 --- a/exec/commands/src/exec.ts +++ b/exec/commands/src/exec.ts @@ -156,7 +156,6 @@ export type ExecOpts = Required> & { | 'modulesDir' | 'nodeOptions' | 'pnpmHomeDir' -| 'rawConfig' | 'recursive' | 'reporterHidePrefix' | 'userAgent' diff --git a/exec/commands/src/run.ts b/exec/commands/src/run.ts index 4524dbfa78..33925f19c1 100644 --- a/exec/commands/src/run.ts +++ b/exec/commands/src/run.ts @@ -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) diff --git a/exec/commands/src/runRecursive.ts b/exec/commands/src/runRecursive.ts index 4510a879e9..9572fef3e8 100644 --- a/exec/commands/src/runRecursive.ts +++ b/exec/commands/src/runRecursive.ts @@ -26,8 +26,8 @@ export type RecursiveRunOpts = Pick { 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']) }) diff --git a/exec/commands/test/utils/index.ts b/exec/commands/test/utils/index.ts index 9314ba6b46..57eec08832 100644 --- a/exec/commands/test/utils/index.ts +++ b/exec/commands/test/utils/index.ts @@ -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, diff --git a/exec/commands/test/verifyDepsBeforeRun.ts b/exec/commands/test/verifyDepsBeforeRun.ts index e3b3d7e610..30da313b04 100644 --- a/exec/commands/test/verifyDepsBeforeRun.ts +++ b/exec/commands/test/verifyDepsBeforeRun.ts @@ -44,7 +44,6 @@ async function runTest (verifyDepsBeforeRun: VerifyDepsBeforeRun): Promise extraBinPaths: [], extraEnv: {}, pnpmHomeDir: '', - rawConfig: {}, verifyDepsBeforeRun, rootProjectManifest, rootProjectManifestDir: process.cwd(), diff --git a/exec/lifecycle/src/runLifecycleHook.ts b/exec/lifecycle/src/runLifecycleHook.ts index 39c6f9a969..5a43a88cee 100644 --- a/exec/lifecycle/src/runLifecycleHook.ts +++ b/exec/lifecycle/src/runLifecycleHook.ts @@ -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)['user-agent'] } : {}), + ...(opts.userAgent ? { npm_config_user_agent: opts.userAgent } : {}), }, log: { clearProgress: noop, diff --git a/exec/lifecycle/test/index.ts b/exec/lifecycle/test/index.ts index 187dff3167..08905e23e8 100644 --- a/exec/lifecycle/test/index.ts +++ b/exec/lifecycle/test/index.ts @@ -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, }) diff --git a/exec/prepare-package/src/index.ts b/exec/prepare-package/src/index.ts index 6bf591d0c5..e98b1ab82d 100644 --- a/exec/prepare-package/src/index.ts +++ b/exec/prepare-package/src/index.ts @@ -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 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` diff --git a/exec/prepare-package/test/index.ts b/exec/prepare-package/test/index.ts index ecfce41521..451f62df96 100644 --- a/exec/prepare-package/test/index.ts +++ b/exec/prepare-package/test/index.ts @@ -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', ]) diff --git a/fetching/binary-fetcher/src/index.ts b/fetching/binary-fetcher/src/index.ts index b12bd789df..f25149ad84 100644 --- a/fetching/binary-fetcher/src/index.ts +++ b/fetching/binary-fetcher/src/index.ts @@ -15,7 +15,6 @@ import { temporaryDirectory } from 'tempy' export function createBinaryFetcher (ctx: { fetch: FetchFromRegistry fetchFromRemoteTarball: FetchFunction - rawConfig: Record storeIndex: StoreIndex offline?: boolean }): { binary: BinaryFetcher } { diff --git a/fetching/git-fetcher/src/index.ts b/fetching/git-fetcher/src/index.ts index a399eca319..2a402e07bf 100644 --- a/fetching/git-fetcher/src/index.ts +++ b/fetching/git-fetcher/src/index.ts @@ -15,9 +15,9 @@ import { safeExeca as execa } from 'execa' export interface CreateGitFetcherOptions { gitShallowHosts?: string[] - rawConfig: Record 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) { diff --git a/fetching/git-fetcher/test/index.ts b/fetching/git-fetcher/test/index.ts index c24f2af1b6..6d84f4a4db 100644 --- a/fetching/git-fetcher/test/index.ts +++ b/fetching/git-fetcher/test/index.ts @@ -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), { diff --git a/fetching/pick-fetcher/test/customFetch.ts b/fetching/pick-fetcher/test/customFetch.ts index 678716661c..94e800b3db 100644 --- a/fetching/pick-fetcher/test/customFetch.ts +++ b/fetching/pick-fetcher/test/customFetch.ts @@ -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 diff --git a/fetching/tarball-fetcher/src/gitHostedTarballFetcher.ts b/fetching/tarball-fetcher/src/gitHostedTarballFetcher.ts index 64d5b1d7d6..1be2c0f200 100644 --- a/fetching/tarball-fetcher/src/gitHostedTarballFetcher.ts +++ b/fetching/tarball-fetcher/src/gitHostedTarballFetcher.ts @@ -19,7 +19,6 @@ interface Resolution { export interface CreateGitHostedTarballFetcher { ignoreScripts?: boolean - rawConfig: Record storeIndex: StoreIndex unsafePerm?: boolean } diff --git a/fetching/tarball-fetcher/src/index.ts b/fetching/tarball-fetcher/src/index.ts index d4f023cbe8..04d421d0bf 100644 --- a/fetching/tarball-fetcher/src/index.ts +++ b/fetching/tarball-fetcher/src/index.ts @@ -40,7 +40,6 @@ export function createTarballFetcher ( fetchFromRegistry: FetchFromRegistry, getAuthHeader: GetAuthHeader, opts: { - rawConfig: Record unsafePerm?: boolean ignoreScripts?: boolean storeIndex: StoreIndex diff --git a/fetching/tarball-fetcher/test/fetch.ts b/fetching/tarball-fetcher/test/fetch.ts index 91d73696f4..71d52be57d 100644 --- a/fetching/tarball-fetcher/test/fetch.ts +++ b/fetching/tarball-fetcher/test/fetch.ts @@ -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, diff --git a/installing/client/src/index.ts b/installing/client/src/index.ts index d7ffd8e277..fe9ce21e62 100644 --- a/installing/client/src/index.ts +++ b/installing/client/src/index.ts @@ -22,11 +22,11 @@ export type ClientOptions = { customResolvers?: CustomResolver[] customFetchers?: CustomFetcher[] ignoreScripts?: boolean - rawConfig: Record sslConfigs?: Record retry?: RetryTimeoutOptions storeIndex: StoreIndex timeout?: number + nodeDownloadMirrors?: Record unsafePerm?: boolean userAgent?: string userConfig?: Record @@ -71,7 +71,7 @@ type Fetchers = { function createFetchers ( fetchFromRegistry: FetchFromRegistry, getAuthHeader: GetAuthHeader, - opts: Pick + opts: Pick ): 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, }), } diff --git a/installing/client/test/index.ts b/installing/client/test/index.ts index ba596dfec6..28392c6bbd 100644 --- a/installing/client/test/index.ts +++ b/installing/client/test/index.ts @@ -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/', }, diff --git a/installing/commands/src/recursive.ts b/installing/commands/src/recursive.ts index 4095a95adc..a48fb45da6 100755 --- a/installing/commands/src/recursive.ts +++ b/installing/commands/src/recursive.ts @@ -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, diff --git a/installing/commands/src/update/index.ts b/installing/commands/src/update/index.ts index cf2bfa0d43..ebb71599c1 100644 --- a/installing/commands/src/update/index.ts +++ b/installing/commands/src/update/index.ts @@ -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, diff --git a/installing/commands/test/add.ts b/installing/commands/test/add.ts index 0948d06e6f..475a9abac6 100644 --- a/installing/commands/test/add.ts +++ b/installing/commands/test/add.ts @@ -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, diff --git a/installing/commands/test/addJsr.ts b/installing/commands/test/addJsr.ts index d98fa623d5..0acbc2042f 100644 --- a/installing/commands/test/addJsr.ts +++ b/installing/commands/test/addJsr.ts @@ -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: { diff --git a/installing/commands/test/fetch.ts b/installing/commands/test/fetch.ts index d4643bb4e1..e70bbeb859 100644 --- a/installing/commands/test/fetch.ts +++ b/installing/commands/test/fetch.ts @@ -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, diff --git a/installing/commands/test/import.ts b/installing/commands/test/import.ts index ca7af8f993..6cf635d2c2 100644 --- a/installing/commands/test/import.ts +++ b/installing/commands/test/import.ts @@ -33,7 +33,7 @@ const DEFAULT_OPTS = { preferWorkspacePackages: true, proxy: undefined, pnpmHomeDir: '', - rawConfig: { registry: REGISTRY }, + authConfig: { registry: REGISTRY }, registries: { default: REGISTRY }, registry: REGISTRY, rootProjectManifestDir: '', diff --git a/installing/commands/test/importRecursive.ts b/installing/commands/test/importRecursive.ts index 4d01913d80..a8936263d2 100644 --- a/installing/commands/test/importRecursive.ts +++ b/installing/commands/test/importRecursive.ts @@ -31,7 +31,7 @@ const DEFAULT_OPTS = { preferWorkspacePackages: true, proxy: undefined, pnpmHomeDir: '', - rawConfig: { registry: REGISTRY }, + authConfig: { registry: REGISTRY }, registries: { default: REGISTRY }, registry: REGISTRY, rootProjectManifestDir: '', diff --git a/installing/commands/test/peerDependencies.ts b/installing/commands/test/peerDependencies.ts index ed9ad7b31a..678f90d960 100644 --- a/installing/commands/test/peerDependencies.ts +++ b/installing/commands/test/peerDependencies.ts @@ -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, diff --git a/installing/commands/test/prune.ts b/installing/commands/test/prune.ts index 0c0b15b174..e7e37187a2 100644 --- a/installing/commands/test/prune.ts +++ b/installing/commands/test/prune.ts @@ -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, diff --git a/installing/commands/test/saveCatalog.ts b/installing/commands/test/saveCatalog.ts index 0b404133a2..171a0aa2e6 100644 --- a/installing/commands/test/saveCatalog.ts +++ b/installing/commands/test/saveCatalog.ts @@ -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', diff --git a/installing/commands/test/update/interactive.ts b/installing/commands/test/update/interactive.ts index cdb2dec8da..2dc4108e10 100644 --- a/installing/commands/test/update/interactive.ts +++ b/installing/commands/test/update/interactive.ts @@ -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, diff --git a/installing/commands/test/update/issue-7415.ts b/installing/commands/test/update/issue-7415.ts index a9f173fe2a..5ee522ee5b 100644 --- a/installing/commands/test/update/issue-7415.ts +++ b/installing/commands/test/update/issue-7415.ts @@ -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, diff --git a/installing/commands/test/update/jsr.ts b/installing/commands/test/update/jsr.ts index 3cda2b203b..9ca7d87d46 100644 --- a/installing/commands/test/update/jsr.ts +++ b/installing/commands/test/update/jsr.ts @@ -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: { diff --git a/installing/commands/test/update/recursive.ts b/installing/commands/test/update/recursive.ts index 11acff73e6..5ca085fd5b 100644 --- a/installing/commands/test/update/recursive.ts +++ b/installing/commands/test/update/recursive.ts @@ -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, }) }) diff --git a/installing/commands/test/utils/index.ts b/installing/commands/test/utils/index.ts index c8587e3452..a4cc99739d 100644 --- a/installing/commands/test/utils/index.ts +++ b/installing/commands/test/utils/index.ts @@ -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, diff --git a/installing/deps-installer/src/install/extendInstallOptions.ts b/installing/deps-installer/src/install/extendInstallOptions.ts index 7476d89be1..23278b6568 100644 --- a/installing/deps-installer/src/install/extendInstallOptions.ts +++ b/installing/deps-installer/src/install/extendInstallOptions.ts @@ -78,7 +78,7 @@ export interface StrictInstallOptions { depth: number lockfileDir: string modulesDir: string - rawConfig: Record // eslint-disable-line @typescript-eslint/no-explicit-any + authConfig: Record // eslint-disable-line @typescript-eslint/no-explicit-any verifyStoreIntegrity: boolean engineStrict: boolean allowBuilds?: Record @@ -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') diff --git a/installing/deps-installer/src/install/index.ts b/installing/deps-installer/src/install/index.ts index b9940cb220..1f98bb9ecd 100644 --- a/installing/deps-installer/src/install/index.ts +++ b/installing/deps-installer/src/install/index.ts @@ -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, diff --git a/installing/deps-installer/test/install/misc.ts b/installing/deps-installer/test/install/misc.ts index b603e90a82..1519c7fa81 100644 --- a/installing/deps-installer/test/install/misc.ts +++ b/installing/deps-installer/test/install/misc.ts @@ -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/', diff --git a/installing/deps-installer/test/lockfile.ts b/installing/deps-installer/test/lockfile.ts index 7394a4434b..0e349b0c8b 100644 --- a/installing/deps-installer/test/lockfile.ts +++ b/installing/deps-installer/test/lockfile.ts @@ -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', diff --git a/installing/deps-restorer/src/index.ts b/installing/deps-restorer/src/index.ts index 832789a290..9722798bd8 100644 --- a/installing/deps-restorer/src/index.ts +++ b/installing/deps-restorer/src/index.ts @@ -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 [project.rootDir, { ...project, manifest: await safeReadPackageJsonFromDir(project.rootDir) }])) ), - rawConfig: {}, + authConfig: {}, registries: { default: registry, }, diff --git a/installing/package-requester/test/index.ts b/installing/package-requester/test/index.ts index cf491332f8..e51a01d824 100644 --- a/installing/package-requester/test/index.ts +++ b/installing/package-requester/test/index.ts @@ -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', diff --git a/patching/commands/test/patch.test.ts b/patching/commands/test/patch.test.ts index 38e2e08e6e..53814381ca 100644 --- a/patching/commands/test/patch.test.ts +++ b/patching/commands/test/patch.test.ts @@ -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}/` }, diff --git a/patching/commands/test/utils/index.ts b/patching/commands/test/utils/index.ts index 41c0b8fdb6..f82a526034 100644 --- a/patching/commands/test/utils/index.ts +++ b/patching/commands/test/utils/index.ts @@ -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, diff --git a/pnpm/src/checkForUpdates.ts b/pnpm/src/checkForUpdates.ts index 42566f84b9..564d999b14 100644 --- a/pnpm/src/checkForUpdates.ts +++ b/pnpm/src/checkForUpdates.ts @@ -27,7 +27,7 @@ export async function checkForUpdates (config: Config): Promise { const { resolve } = createResolver({ ...config, - authConfig: config.rawConfig, + authConfig: config.authConfig, retry: { retries: 0, }, diff --git a/pnpm/src/cmd/index.ts b/pnpm/src/cmd/index.ts index 99aafae264..71b85e4ee1 100644 --- a/pnpm/src/cmd/index.ts +++ b/pnpm/src/cmd/index.ts @@ -183,7 +183,6 @@ const cliOptionsTypesByCommandName: Record Record const aliasToFullName = new Map() const completionByCommandName: Record = {} const shorthandsByCommandName: Record> = {} -const rcOptionsTypes: Record = {} 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 } diff --git a/pnpm/src/getConfig.ts b/pnpm/src/getConfig.ts index a6f4caa30a..21a7c94de5 100644 --- a/pnpm/src/getConfig.ts +++ b/pnpm/src/getConfig.ts @@ -15,9 +15,7 @@ export async function getConfig ( opts: { excludeReporter: boolean globalDirShouldAllowWrite?: boolean - rcOptionsTypes: Record workspaceDir: string | undefined - checkUnknownSetting?: boolean ignoreNonAuthSettingsFromLocal?: boolean } ): Promise { @@ -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 diff --git a/pnpm/src/main.ts b/pnpm/src/main.ts index d2f7efd6a0..928374b8e1 100644 --- a/pnpm/src/main.ts +++ b/pnpm/src/main.ts @@ -24,7 +24,7 @@ import { isEmpty } from 'ramda' import semver from 'semver' import { checkForUpdates } from './checkForUpdates.js' -import { NOT_IMPLEMENTED_COMMAND_SET, overridableByScriptCommands, pnpmCmds, rcOptionsTypes, recursiveByDefaultCommands, skipPackageManagerCheckForCommand } from './cmd/index.js' +import { NOT_IMPLEMENTED_COMMAND_SET, overridableByScriptCommands, pnpmCmds, recursiveByDefaultCommands, skipPackageManagerCheckForCommand } from './cmd/index.js' import { formatUnknownOptionsError } from './formatError.js' import { getConfig, installConfigDepsAndLoadHooks } from './getConfig.js' import type { ParsedCliArgsWithBuiltIn } from './parseCliArgs.js' @@ -98,9 +98,7 @@ export async function main (inputArgv: string[]): Promise { config = await getConfig(cliOptions, { excludeReporter: false, globalDirShouldAllowWrite, - rcOptionsTypes, workspaceDir, - checkUnknownSetting: false, ignoreNonAuthSettingsFromLocal: isDlxOrCreateCommand, }) as typeof config if (!isExecutedByCorepack() && cmd !== 'setup' && config.wantedPackageManager != null) { diff --git a/pnpm/test/getConfig.test.ts b/pnpm/test/getConfig.test.ts index 4236572c0a..04905e1276 100644 --- a/pnpm/test/getConfig.test.ts +++ b/pnpm/test/getConfig.test.ts @@ -25,7 +25,6 @@ test('console a warning when the .npmrc has an env variable that does not exist' }, { workspaceDir: '.', excludeReporter: false, - rcOptionsTypes: {}, }) expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('Failed to replace env in config: ${ENV_VAR_123}')) @@ -70,7 +69,6 @@ test('hoist: false removes hoistPattern', async () => { }, { workspaceDir: '.', excludeReporter: false, - rcOptionsTypes: {}, }) expect(config.hoist).toBe(false) diff --git a/releasing/commands/src/publish/pack.ts b/releasing/commands/src/publish/pack.ts index cefc518a3d..89c52cfa46 100644 --- a/releasing/commands/src/publish/pack.ts +++ b/releasing/commands/src/publish/pack.ts @@ -95,10 +95,11 @@ export function help (): string { export type PackOptions = Pick & Pick & Partial { extraBinPaths: opts.extraBinPaths, extraEnv: opts.extraEnv, pkgRoot: opts.dir, - rawConfig: opts.rawConfig, rootModulesDir: await realpathMissing(path.join(opts.dir, 'node_modules')), stdio: 'inherit', unsafePerm: true, // when running scripts explicitly, assume that they're trusted. + userAgent: opts.userAgent, }) if (!opts.ignoreScripts) { await _runScriptsIfPresent([ diff --git a/releasing/commands/src/publish/publish.ts b/releasing/commands/src/publish/publish.ts index 58da5d2590..47c44875e3 100644 --- a/releasing/commands/src/publish/publish.ts +++ b/releasing/commands/src/publish/publish.ts @@ -213,10 +213,10 @@ Do you want to continue?`, extraBinPaths: opts.extraBinPaths, extraEnv: opts.extraEnv, pkgRoot: dir, - rawConfig: opts.rawConfig, rootModulesDir: await realpathMissing(path.join(dir, 'node_modules')), stdio: 'inherit', unsafePerm: true, // when running scripts explicitly, assume that they're trusted. + userAgent: opts.userAgent, }) const { manifest } = await readProjectManifest(dir, opts) // Unfortunately, we cannot support postpack at the moment diff --git a/releasing/commands/src/publish/recursivePublish.ts b/releasing/commands/src/publish/recursivePublish.ts index ed4b2e1ce7..2682a8c18f 100644 --- a/releasing/commands/src/publish/recursivePublish.ts +++ b/releasing/commands/src/publish/recursivePublish.ts @@ -20,7 +20,7 @@ export type PublishRecursiveOpts = Required> & @@ -66,7 +66,7 @@ export async function recursivePublish ( const pkgs = Object.values(opts.selectedProjectsGraph).map((wsPkg) => wsPkg.package) const { resolve } = createResolver({ ...opts, - authConfig: opts.rawConfig, + authConfig: opts.authConfig, userConfig: opts.userConfig, retry: { factor: opts.fetchRetryFactor, diff --git a/releasing/commands/test/deploy/utils/index.ts b/releasing/commands/test/deploy/utils/index.ts index 146fa166ff..114eb6d748 100644 --- a/releasing/commands/test/deploy/utils/index.ts +++ b/releasing/commands/test/deploy/utils/index.ts @@ -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, diff --git a/releasing/commands/test/publish/recursivePublish.ts b/releasing/commands/test/publish/recursivePublish.ts index 59be7001af..eca235f57d 100644 --- a/releasing/commands/test/publish/recursivePublish.ts +++ b/releasing/commands/test/publish/recursivePublish.ts @@ -299,8 +299,8 @@ test('errors on fake registry', async () => { const promise = publish.handler({ ...DEFAULT_OPTS, ...await filterProjectsBySelectorObjectsFromDir(process.cwd(), []), - rawConfig: { - ...DEFAULT_OPTS.rawConfig, + authConfig: { + ...DEFAULT_OPTS.authConfig, registry: fakeRegistry, }, registries: { diff --git a/releasing/commands/test/publish/utils/index.ts b/releasing/commands/test/publish/utils/index.ts index cd786adb16..41c3616ca4 100644 --- a/releasing/commands/test/publish/utils/index.ts +++ b/releasing/commands/test/publish/utils/index.ts @@ -36,7 +36,7 @@ export const DEFAULT_OPTS = { pnpmfile: ['./.pnpmfile.cjs'], pnpmHomeDir: '', proxy: undefined, - rawConfig: { registry: REGISTRY }, + authConfig: { registry: REGISTRY }, rawLocalConfig: {}, registries: { default: REGISTRY }, registry: REGISTRY, diff --git a/resolving/default-resolver/src/index.ts b/resolving/default-resolver/src/index.ts index e0f04c3965..1aa1db870f 100644 --- a/resolving/default-resolver/src/index.ts +++ b/resolving/default-resolver/src/index.ts @@ -87,7 +87,7 @@ export function createResolver ( fetchFromRegistry: FetchFromRegistry, getAuthHeader: GetAuthHeader, pnpmOpts: ResolverFactoryOptions & { - rawConfig: Record + nodeDownloadMirrors?: Record customResolvers?: CustomResolver[] } ): { resolve: DefaultResolver, clearCache: () => void } { @@ -96,9 +96,9 @@ export function createResolver ( const _resolveFromLocal = resolveFromLocal.bind(null, { preserveAbsolutePaths: pnpmOpts.preserveAbsolutePaths, }) - const _resolveNodeRuntime = resolveNodeRuntime.bind(null, { fetchFromRegistry, offline: pnpmOpts.offline, rawConfig: pnpmOpts.rawConfig }) - const _resolveDenoRuntime = resolveDenoRuntime.bind(null, { fetchFromRegistry, offline: pnpmOpts.offline, rawConfig: pnpmOpts.rawConfig, resolveFromNpm }) - const _resolveBunRuntime = resolveBunRuntime.bind(null, { fetchFromRegistry, offline: pnpmOpts.offline, rawConfig: pnpmOpts.rawConfig, resolveFromNpm }) + const _resolveNodeRuntime = resolveNodeRuntime.bind(null, { fetchFromRegistry, offline: pnpmOpts.offline, nodeDownloadMirrors: pnpmOpts.nodeDownloadMirrors }) + const _resolveDenoRuntime = resolveDenoRuntime.bind(null, { fetchFromRegistry, offline: pnpmOpts.offline, resolveFromNpm }) + const _resolveBunRuntime = resolveBunRuntime.bind(null, { fetchFromRegistry, offline: pnpmOpts.offline, resolveFromNpm }) const _resolveFromCustomResolvers = pnpmOpts.customResolvers ? resolveFromCustomResolvers.bind(null, pnpmOpts.customResolvers) : null diff --git a/resolving/default-resolver/test/customResolver.ts b/resolving/default-resolver/test/customResolver.ts index dbb65e7c3e..6844466324 100644 --- a/resolving/default-resolver/test/customResolver.ts +++ b/resolving/default-resolver/test/customResolver.ts @@ -25,7 +25,6 @@ test('custom resolver intercepts matching packages', async () => { const { resolve } = createResolver(fetchFromRegistry, getAuthHeader, { customResolvers: [customResolver], - rawConfig: {}, cacheDir: '/tmp/test-cache', offline: false, preferOffline: false, @@ -71,7 +70,6 @@ test('custom resolver with synchronous methods', async () => { const { resolve } = createResolver(fetchFromRegistry, getAuthHeader, { customResolvers: [customResolver], - rawConfig: {}, cacheDir: '/tmp/test-cache', offline: false, preferOffline: false, @@ -116,7 +114,6 @@ test('multiple custom resolvers - first matching wins', async () => { const { resolve } = createResolver(fetchFromRegistry, getAuthHeader, { customResolvers: [resolver1, resolver2], // Order matters - rawConfig: {}, cacheDir: '/tmp/test-cache', offline: false, preferOffline: false, @@ -150,7 +147,6 @@ test('custom resolver error handling', async () => { const { resolve } = createResolver(async () => new Response(''), () => undefined, { customResolvers: [customResolver], - rawConfig: {}, cacheDir: '/tmp/test-cache', offline: false, preferOffline: false, @@ -175,7 +171,6 @@ test('preferredVersions are passed to custom resolver', async () => { const { resolve: resolvePackage } = createResolver(async () => new Response(''), () => undefined, { customResolvers: [customResolver], - rawConfig: {}, cacheDir: '/tmp/test-cache', offline: false, preferOffline: false, @@ -209,7 +204,6 @@ test('custom resolver can intercept any protocol', async () => { const { resolve } = createResolver(async () => new Response(''), () => undefined, { customResolvers: [customResolver], - rawConfig: {}, cacheDir: '/tmp/test-cache', offline: false, preferOffline: false, @@ -241,7 +235,6 @@ test('custom resolver falls through when not supported', async () => { const { resolve } = createResolver(async () => new Response(''), () => undefined, { customResolvers: [customResolver], - rawConfig: {}, cacheDir: '/tmp/test-cache', offline: false, preferOffline: false, @@ -275,7 +268,6 @@ test('custom resolver can override npm registry resolution', async () => { const { resolve } = createResolver(async () => new Response(''), () => undefined, { customResolvers: [npmStyleResolver], - rawConfig: {}, cacheDir: '/tmp/test-cache', offline: false, preferOffline: false, @@ -315,7 +307,6 @@ test('custom custom fetcher: reuse local tarball fetcher', async () => { const { resolve } = createResolver(async () => new Response(''), () => undefined, { customResolvers: [localTarballResolver], - rawConfig: {}, cacheDir: '/tmp/test-cache', offline: false, preferOffline: false, @@ -353,7 +344,6 @@ test('custom custom fetcher: reuse remote tarball downloader', async () => { const { resolve } = createResolver(async () => new Response(''), () => undefined, { customResolvers: [cdnResolver], - rawConfig: {}, cacheDir: '/tmp/test-cache', offline: false, preferOffline: false, @@ -395,7 +385,6 @@ test('custom custom fetcher: wrap npm registry with custom logic', async () => { const { resolve } = createResolver(async () => new Response(''), () => undefined, { customResolvers: [privateNpmResolver], - rawConfig: {}, cacheDir: '/tmp/test-cache', offline: false, preferOffline: false, @@ -445,7 +434,6 @@ test('custom resolver receives currentPkg when provided', async () => { const { resolve } = createResolver(fetchFromRegistry, getAuthHeader, { customResolvers: [customResolver], - rawConfig: {}, cacheDir: '/tmp/test-cache', offline: false, preferOffline: false, diff --git a/resolving/default-resolver/test/index.ts b/resolving/default-resolver/test/index.ts index e447d0a8ec..1bd20adf75 100644 --- a/resolving/default-resolver/test/index.ts +++ b/resolving/default-resolver/test/index.ts @@ -9,7 +9,6 @@ test('createResolver()', () => { registries: { default: 'https://registry.npmjs.org/', }, - rawConfig: {}, storeDir: '.store', }) expect(typeof resolve).toBe('function') diff --git a/store/commands/src/inspecting/catIndex.ts b/store/commands/src/inspecting/catIndex.ts index 4b0424e054..566b9f6620 100644 --- a/store/commands/src/inspecting/catIndex.ts +++ b/store/commands/src/inspecting/catIndex.ts @@ -32,7 +32,7 @@ export function help (): string { export type CatIndexCommandOptions = Pick< Config, -| 'rawConfig' +| 'authConfig' | 'pnpmHomeDir' | 'storeDir' | 'lockfileDir' @@ -70,7 +70,7 @@ export async function handler (opts: CatIndexCommandOptions, params: string[]): }) const { resolve } = createResolver({ ...opts, - authConfig: opts.rawConfig, + authConfig: opts.authConfig, }) const pkgSnapshot = await resolve( { alias, bareSpecifier }, diff --git a/store/commands/test/store/storeAdd.ts b/store/commands/test/store/storeAdd.ts index 79bb0f5799..0365324463 100644 --- a/store/commands/test/store/storeAdd.ts +++ b/store/commands/test/store/storeAdd.ts @@ -17,7 +17,7 @@ test('pnpm store add express@4.16.3', async () => { cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: `http://localhost:${REGISTRY_MOCK_PORT}/`, }, registries: { default: `http://localhost:${REGISTRY_MOCK_PORT}/` }, @@ -41,7 +41,7 @@ test('pnpm store add scoped package that uses not the standard registry', async cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: 'https://registry.npmjs.org/', }, registries: { @@ -71,7 +71,7 @@ test('should fail if some packages can not be added', async () => { cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: 'https://registry.npmjs.org/', }, registries: { diff --git a/store/commands/test/store/storePath.ts b/store/commands/test/store/storePath.ts index 541f596671..ac732e261e 100644 --- a/store/commands/test/store/storePath.ts +++ b/store/commands/test/store/storePath.ts @@ -15,7 +15,7 @@ test('CLI prints the current store path', async () => { cacheDir: path.resolve('cache'), dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -44,7 +44,7 @@ test('CLI prints the current store path when storeDir is relative', async () => dir: subpackageDir, workspaceDir, pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, diff --git a/store/commands/test/store/storePrune.ts b/store/commands/test/store/storePrune.ts index c74164238b..5c4417eb87 100644 --- a/store/commands/test/store/storePrune.ts +++ b/store/commands/test/store/storePrune.ts @@ -47,7 +47,7 @@ test('remove unreferenced packages', async () => { cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -72,7 +72,7 @@ test('remove unreferenced packages', async () => { cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -120,7 +120,7 @@ test('prune outputs total size of removed files', async () => { cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -158,7 +158,7 @@ test('remove packages that are used by project that no longer exist', async () = cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -206,7 +206,7 @@ test('keep dependencies used by others', async () => { cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -232,7 +232,7 @@ test('keep dependency used by package', async () => { cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -256,7 +256,7 @@ test('prune will skip scanning non-directory in storeDir', async () => { cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -283,7 +283,7 @@ test('prune does not fail if the store contains an unexpected directory', async cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -321,7 +321,7 @@ test('prune removes alien files from the store if the --force flag is used', asy cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -352,7 +352,7 @@ describe('prune when store directory is not properly configured', () => { cacheDir: path.resolve('cache'), dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -388,7 +388,7 @@ describe('prune when store directory is not properly configured', () => { cacheDir: path.resolve('cache'), dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -449,7 +449,7 @@ test('prune removes cache directories that outlives dlx-cache-max-age', async () cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -514,7 +514,7 @@ describe('global virtual store prune', () => { cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -582,7 +582,7 @@ describe('global virtual store prune', () => { cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -653,7 +653,7 @@ describe('global virtual store prune', () => { cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, @@ -737,7 +737,7 @@ describe('global virtual store prune', () => { cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: { default: REGISTRY }, diff --git a/store/commands/test/store/storeStatus.ts b/store/commands/test/store/storeStatus.ts index 2987fa2bd5..b0db4e3b01 100644 --- a/store/commands/test/store/storeStatus.ts +++ b/store/commands/test/store/storeStatus.ts @@ -41,7 +41,7 @@ test('CLI fails when store status finds modified packages', async () => { cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: modulesState!.registries!, @@ -95,7 +95,7 @@ test('CLI does not fail when store status does not find modified packages', asyn cacheDir, dir: process.cwd(), pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: modulesState!.registries!, @@ -141,7 +141,7 @@ storeDir: "${relativeStoreDir}" dir: subpackageDir, workspaceDir, pnpmHomeDir: '', - rawConfig: { + authConfig: { registry: REGISTRY, }, registries: modulesState!.registries!, diff --git a/store/connection-manager/src/createNewStoreController.ts b/store/connection-manager/src/createNewStoreController.ts index 717f166ebb..b57e6ef81c 100644 --- a/store/connection-manager/src/createNewStoreController.ts +++ b/store/connection-manager/src/createNewStoreController.ts @@ -12,7 +12,7 @@ type CreateResolverOptions = Pick & Required> @@ -21,6 +21,7 @@ export type CreateNewStoreControllerOptions = CreateResolverOptions & Pick { const storeDir = path.join(tmp, 'store') const cacheDir = path.join(tmp, 'cache') const registry = 'https://registry.npmjs.org/' - const authConfig = { registry } const storeIndex = new StoreIndex(storeDir) const { resolve, fetchers, clearResolutionCache } = createClient({ - authConfig, + authConfig: {}, cacheDir: path.join(tmp, 'cache'), storeDir: path.join(tmp, 'store'), - rawConfig: {}, storeIndex, registries: { default: registry, @@ -59,13 +57,11 @@ describe('store.importPackage()', () => { const storeDir = path.join(tmp, 'store') const cacheDir = path.join(tmp, 'cache') const registry = 'https://registry.npmjs.org/' - const authConfig = { registry } const storeIndex = new StoreIndex(storeDir) const { resolve, fetchers, clearResolutionCache } = createClient({ - authConfig, + authConfig: {}, cacheDir: path.join(tmp, 'cache'), storeDir: path.join(tmp, 'store'), - rawConfig: {}, storeIndex, registries: { default: registry, diff --git a/testing/temp-store/src/index.ts b/testing/temp-store/src/index.ts index e53c1eac58..f76bc24574 100644 --- a/testing/temp-store/src/index.ts +++ b/testing/temp-store/src/index.ts @@ -26,7 +26,6 @@ export function createTempStore (opts?: { const storeIndex = new StoreIndex(storeDir) const { resolve, fetchers, clearResolutionCache } = createClient({ authConfig, - rawConfig: {}, retry: { retries: 4, factor: 10, diff --git a/workspace/commands/src/init.ts b/workspace/commands/src/init.ts index b39645ccf6..dc328d8528 100644 --- a/workspace/commands/src/init.ts +++ b/workspace/commands/src/init.ts @@ -3,7 +3,7 @@ import path from 'node:path' import { packageManager } from '@pnpm/cli.meta' import { docsUrl } from '@pnpm/cli.utils' -import { type Config, types as allTypes, type UniversalOptions } from '@pnpm/config.reader' +import { type Config, types as allTypes } from '@pnpm/config.reader' import { PnpmError } from '@pnpm/error' import { sortKeysByPriority } from '@pnpm/object.key-sorting' import type { ProjectManifest } from '@pnpm/types' @@ -11,7 +11,7 @@ import { writeProjectManifest } from '@pnpm/workspace.project-manifest-writer' import { pick } from 'ramda' import { renderHelp } from 'render-help' -import { parseRawConfig } from './utils.js' +import { getInitConfig } from './utils.js' export const rcOptionsTypes = cliOptionsTypes @@ -55,13 +55,17 @@ export function help (): string { } export type InitOptions = - & Pick & Pick & Partial> & { bare?: boolean + initAuthorName?: string + initAuthorEmail?: string + initAuthorUrl?: string + initLicense?: string + initVersion?: string } export async function handler (opts: InitOptions, params?: string[]): Promise { @@ -96,8 +100,8 @@ export async function handler (opts: InitOptions, params?: string[]): Promise): Record { - const { initModule, ...restConfig } = localConfig - if (initModule) { - const filePath = path.resolve(localConfig.initModule) - const isFileExist = fs.existsSync(filePath) - if (['.js', '.cjs'].includes(path.extname(filePath)) && isFileExist) { - spawnSync('node', [filePath], { - stdio: 'inherit', - }) - } - } - return restConfig +export interface InitProperties { + initAuthorName?: string + initAuthorEmail?: string + initAuthorUrl?: string + initLicense?: string + initVersion?: string } -export function workWithInitConfig (localConfig: Record): Record { +export function getInitConfig (opts: InitProperties): Record { const packageJson: Record = {} - const authorInfo: Record = {} - for (const localConfigKey in localConfig) { - if (localConfigKey.startsWith('init') && localConfigKey !== 'initPackageManager') { - const pureKey = localConfigKey.replace('init', '') - const value = localConfig[localConfigKey] - if (pureKey.startsWith('Author')) { - authorInfo[pureKey.replace('Author', '')] = value - } else { - packageJson[pureKey] = value - } - } + if (opts.initVersion) { + packageJson.version = opts.initVersion } - - const author = personToString(camelcaseKeys(authorInfo)) + if (opts.initLicense) { + packageJson.license = opts.initLicense + } + const author = personToString({ + name: opts.initAuthorName, + email: opts.initAuthorEmail, + url: opts.initAuthorUrl, + }) if (author) { packageJson.author = author } - return camelcaseKeys(packageJson) -} - -export async function parseRawConfig (rawConfig: Record): Promise> { - return workWithInitConfig( - workWithInitModule(camelcaseKeys(rawConfig)) - ) + return packageJson } diff --git a/workspace/commands/test/init.test.ts b/workspace/commands/test/init.test.ts index 9eee1a95e1..68b0640a95 100644 --- a/workspace/commands/test/init.test.ts +++ b/workspace/commands/test/init.test.ts @@ -8,7 +8,7 @@ import { loadJsonFileSync } from 'load-json-file' test('init a new package.json', async () => { prepareEmpty() - await init.handler({ rawConfig: {}, cliOptions: {} }) + await init.handler({ cliOptions: {} }) const manifest = loadJsonFileSync(path.resolve('package.json')) expect(manifest).toBeTruthy() }) @@ -17,32 +17,31 @@ test('throws an error if a package.json exists in the current directory', async prepare({}) await expect( - init.handler({ rawConfig: {}, cliOptions: {} }) + init.handler({ cliOptions: {} }) ).rejects.toThrow('package.json already exists') }) -test('init a new package.json with npmrc', async () => { - const rawConfig = { - 'init-author-email': 'xxxxxx@pnpm.com', - 'init-author-name': 'pnpm', - 'init-author-url': 'https://www.github.com/pnpm', - 'init-license': 'MIT', - 'init-version': '2.0.0', - } +test('init a new package.json with author and license settings', async () => { prepareEmpty() - await init.handler({ rawConfig, cliOptions: {} }) + await init.handler({ + cliOptions: {}, + initAuthorEmail: 'xxxxxx@pnpm.com', + initAuthorName: 'pnpm', + initAuthorUrl: 'https://www.github.com/pnpm', + initLicense: 'MIT', + initVersion: '2.0.0', + }) const manifest: Record = loadJsonFileSync(path.resolve('package.json')) - const expectAuthor = `${rawConfig['init-author-name']} <${rawConfig['init-author-email']}> (${rawConfig['init-author-url']})` - expect(manifest.version).toBe(rawConfig['init-version']) - expect(manifest.author).toBe(expectAuthor) - expect(manifest.license).toBe(rawConfig['init-license']) + expect(manifest.version).toBe('2.0.0') + expect(manifest.author).toBe('pnpm (https://www.github.com/pnpm)') + expect(manifest.license).toBe('MIT') }) test('throw an error if params are passed to the init command', async () => { prepare({}) await expect( - init.handler({ rawConfig: {}, cliOptions: {} }, ['react-app']) + init.handler({ cliOptions: {} }, ['react-app']) ).rejects.toThrow('init command does not accept any arguments') }) @@ -51,7 +50,7 @@ test('init a new package.json if a package.json exists in the parent directory', fs.mkdirSync('empty-dir1') process.chdir('./empty-dir1') - await init.handler({ rawConfig: {}, cliOptions: {} }) + await init.handler({ cliOptions: {} }) const manifest = loadJsonFileSync(path.resolve('package.json')) expect(manifest).toBeTruthy() }) @@ -61,7 +60,6 @@ test('init a new package.json if a package.json exists in the current directory fs.mkdirSync('empty-dir2') await init.handler({ - rawConfig: {}, cliOptions: { dir: './empty-dir2', }, @@ -72,7 +70,7 @@ test('init a new package.json if a package.json exists in the current directory test('init a new package.json with init-package-manager=true', async () => { prepareEmpty() - await init.handler({ rawConfig: { 'init-package-manager': true }, cliOptions: {}, initPackageManager: true }) + await init.handler({ cliOptions: {}, initPackageManager: true }) const manifest = loadJsonFileSync(path.resolve('package.json')) expect(manifest).toBeTruthy() expect(manifest.packageManager).toBeTruthy() @@ -80,7 +78,7 @@ test('init a new package.json with init-package-manager=true', async () => { test('init a new package.json with init-package-manager=false', async () => { prepareEmpty() - await init.handler({ rawConfig: { 'init-package-manager': false }, cliOptions: {}, initPackageManager: false }) + await init.handler({ cliOptions: {}, initPackageManager: false }) const manifest = loadJsonFileSync(path.resolve('package.json')) expect(manifest).toBeTruthy() expect(manifest).not.toHaveProperty('packageManager') @@ -88,14 +86,14 @@ test('init a new package.json with init-package-manager=false', async () => { test('init a new package.json with init-type=module', async () => { prepareEmpty() - await init.handler({ rawConfig: { 'init-type': 'module' }, cliOptions: {}, initType: 'module' }) + await init.handler({ cliOptions: {}, initType: 'module' }) const manifest = loadJsonFileSync(path.resolve('package.json')) expect(manifest.type).toBe('module') }) test('init a new package.json with --bare', async () => { prepareEmpty() - await init.handler({ rawConfig: {}, cliOptions: {}, bare: true }) + await init.handler({ cliOptions: {}, bare: true }) const manifest = loadJsonFileSync(path.resolve('package.json')) expect(manifest).not.toHaveProperty(['name']) expect(manifest).not.toHaveProperty(['version']) diff --git a/workspace/commands/test/utils.test.ts b/workspace/commands/test/utils.test.ts index 212c53f706..704ab66532 100644 --- a/workspace/commands/test/utils.test.ts +++ b/workspace/commands/test/utils.test.ts @@ -1,23 +1,4 @@ -import fs from 'node:fs' -import path from 'node:path' - -import { fixtures } from '@pnpm/test-fixtures' - -import { personToString, workWithInitModule } from '../lib/utils.js' - -const f = fixtures(path.join(import.meta.dirname, '../fixtures')) - -test('run the workWithInitModule function', async () => { - const dir = f.prepare('init-module') - const rawConfig = { - initVersion: '2.0.0', - initModule: '.pnpm-init.js', - } - expect(workWithInitModule(rawConfig)).toEqual({ - initVersion: '2.0.0', - }) - expect(fs.existsSync(path.resolve(dir, 'test.txt'))).toBeTruthy() -}) +import { personToString } from '../lib/utils.js' test('run the personToString function', () => { const expectAuthor = 'pnpm (https://www.github.com/pnpm)'