mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-28 02:53:15 -04:00
fix: match read/write priority when both pnpm-workspace.yaml and .npmrc exist (#10073)
* fix: match read/write priority when both pnpm-workspace.yaml and .npmrc exist * chore: update help message close #10072
This commit is contained in:
6
.changeset/shy-brooms-feel.md
Normal file
6
.changeset/shy-brooms-feel.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@pnpm/plugin-commands-config": patch
|
||||||
|
pnpm: patch
|
||||||
|
---
|
||||||
|
|
||||||
|
When both `pnpm-workspace.yaml` and `.npmrc` exist, `pnpm config set --location=project` now writes to `pnpm-workspace.yaml` (matching read priority) [#10072](https://github.com/pnpm/pnpm/issues/10072).
|
||||||
@@ -54,7 +54,7 @@ export function help (): string {
|
|||||||
shortAlias: '-g',
|
shortAlias: '-g',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'When set to "project", the .npmrc file at the nearest package.json will be used. If no .npmrc file is present in the directory, the setting will be written to a pnpm-workspace.yaml file.',
|
description: 'When set to "project", the pnpm-workspace.yaml file will be used if it exists. If only .npmrc exists, it will be used. If neither exists, a pnpm-workspace.yaml file will be created.',
|
||||||
name: '--location <project|global>',
|
name: '--location <project|global>',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import fs from 'fs'
|
|
||||||
import path from 'path'
|
|
||||||
import util from 'util'
|
import util from 'util'
|
||||||
import { types } from '@pnpm/config'
|
import { types } from '@pnpm/config'
|
||||||
import { PnpmError } from '@pnpm/error'
|
import { PnpmError } from '@pnpm/error'
|
||||||
@@ -11,6 +9,7 @@ import kebabCase from 'lodash.kebabcase'
|
|||||||
import { readIniFile } from 'read-ini-file'
|
import { readIniFile } from 'read-ini-file'
|
||||||
import { writeIniFile } from 'write-ini-file'
|
import { writeIniFile } from 'write-ini-file'
|
||||||
import { type ConfigCommandOptions } from './ConfigCommandOptions.js'
|
import { type ConfigCommandOptions } from './ConfigCommandOptions.js'
|
||||||
|
import { getConfigFilePath } from './getConfigFilePath.js'
|
||||||
import { isStrictlyKebabCase } from './isStrictlyKebabCase.js'
|
import { isStrictlyKebabCase } from './isStrictlyKebabCase.js'
|
||||||
import { settingShouldFallBackToNpm } from './settingShouldFallBackToNpm.js'
|
import { settingShouldFallBackToNpm } from './settingShouldFallBackToNpm.js'
|
||||||
|
|
||||||
@@ -36,8 +35,17 @@ export async function configSet (opts: ConfigCommandOptions, key: string, valueP
|
|||||||
}
|
}
|
||||||
throw new PnpmError('CONFIG_SET_AUTH_NON_STRING', `Cannot set ${key} to a non-string value (${JSON.stringify(value)})`)
|
throw new PnpmError('CONFIG_SET_AUTH_NON_STRING', `Cannot set ${key} to a non-string value (${JSON.stringify(value)})`)
|
||||||
}
|
}
|
||||||
if (opts.global === true || fs.existsSync(path.join(opts.dir, '.npmrc'))) {
|
|
||||||
const configPath = opts.global ? path.join(opts.configDir, 'rc') : path.join(opts.dir, '.npmrc')
|
const { configPath, isWorkspaceYaml } = getConfigFilePath(opts)
|
||||||
|
|
||||||
|
if (isWorkspaceYaml) {
|
||||||
|
key = camelCase(key)
|
||||||
|
await updateWorkspaceManifest(opts.workspaceDir ?? opts.dir, {
|
||||||
|
updatedFields: ({
|
||||||
|
[key]: castField(value, kebabCase(key)),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
const settings = await safeReadIniFile(configPath)
|
const settings = await safeReadIniFile(configPath)
|
||||||
key = kebabCase(key)
|
key = kebabCase(key)
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
@@ -47,14 +55,7 @@ export async function configSet (opts: ConfigCommandOptions, key: string, valueP
|
|||||||
settings[key] = value
|
settings[key] = value
|
||||||
}
|
}
|
||||||
await writeIniFile(configPath, settings)
|
await writeIniFile(configPath, settings)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
key = camelCase(key)
|
|
||||||
await updateWorkspaceManifest(opts.workspaceDir ?? opts.dir, {
|
|
||||||
updatedFields: ({
|
|
||||||
[key]: castField(value, kebabCase(key)),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function castField (value: unknown, key: string) {
|
function castField (value: unknown, key: string) {
|
||||||
|
|||||||
42
config/plugin-commands-config/src/getConfigFilePath.ts
Normal file
42
config/plugin-commands-config/src/getConfigFilePath.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import { type ConfigCommandOptions } from './ConfigCommandOptions.js'
|
||||||
|
|
||||||
|
interface ConfigFilePathInfo {
|
||||||
|
configPath: string
|
||||||
|
isWorkspaceYaml: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Priority: pnpm-workspace.yaml > .npmrc > default to pnpm-workspace.yaml
|
||||||
|
*/
|
||||||
|
export function getConfigFilePath (opts: Pick<ConfigCommandOptions, 'global' | 'configDir' | 'dir'>): ConfigFilePathInfo {
|
||||||
|
if (opts.global) {
|
||||||
|
return {
|
||||||
|
configPath: path.join(opts.configDir, 'rc'),
|
||||||
|
isWorkspaceYaml: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const workspaceYamlPath = path.join(opts.dir, 'pnpm-workspace.yaml')
|
||||||
|
if (fs.existsSync(workspaceYamlPath)) {
|
||||||
|
return {
|
||||||
|
configPath: workspaceYamlPath,
|
||||||
|
isWorkspaceYaml: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const npmrcPath = path.join(opts.dir, '.npmrc')
|
||||||
|
if (fs.existsSync(npmrcPath)) {
|
||||||
|
return {
|
||||||
|
configPath: npmrcPath,
|
||||||
|
isWorkspaceYaml: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If neither exists, return pnpm-workspace.yaml
|
||||||
|
return {
|
||||||
|
configPath: workspaceYamlPath,
|
||||||
|
isWorkspaceYaml: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -290,3 +290,48 @@ test('config set with location=project and json=true', async () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('config set when both pnpm-workspace.yaml and .npmrc exist, pnpm-workspace.yaml has priority', async () => {
|
||||||
|
const tmp = tempDir()
|
||||||
|
const configDir = path.join(tmp, 'global-config')
|
||||||
|
fs.mkdirSync(configDir, { recursive: true })
|
||||||
|
fs.writeFileSync(path.join(tmp, '.npmrc'), 'store-dir=~/store')
|
||||||
|
fs.writeFileSync(path.join(tmp, 'pnpm-workspace.yaml'), 'fetchRetries: 5')
|
||||||
|
|
||||||
|
await config.handler({
|
||||||
|
dir: process.cwd(),
|
||||||
|
cliOptions: {},
|
||||||
|
configDir,
|
||||||
|
location: 'project',
|
||||||
|
rawConfig: {},
|
||||||
|
}, ['set', 'fetch-timeout', '2000'])
|
||||||
|
|
||||||
|
expect(readYamlFile(path.join(tmp, 'pnpm-workspace.yaml'))).toEqual({
|
||||||
|
fetchRetries: 5,
|
||||||
|
fetchTimeout: 2000,
|
||||||
|
})
|
||||||
|
expect(readIniFileSync(path.join(tmp, '.npmrc'))).toEqual({
|
||||||
|
'store-dir': '~/store',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('config set when only pnpm-workspace.yaml exists, writes to it', async () => {
|
||||||
|
const tmp = tempDir()
|
||||||
|
const configDir = path.join(tmp, 'global-config')
|
||||||
|
fs.mkdirSync(configDir, { recursive: true })
|
||||||
|
fs.writeFileSync(path.join(tmp, 'pnpm-workspace.yaml'), 'fetchRetries: 5')
|
||||||
|
|
||||||
|
await config.handler({
|
||||||
|
dir: process.cwd(),
|
||||||
|
cliOptions: {},
|
||||||
|
configDir,
|
||||||
|
location: 'project',
|
||||||
|
rawConfig: {},
|
||||||
|
}, ['set', 'fetch-timeout', '3000'])
|
||||||
|
|
||||||
|
expect(readYamlFile(path.join(tmp, 'pnpm-workspace.yaml'))).toEqual({
|
||||||
|
fetchRetries: 5,
|
||||||
|
fetchTimeout: 3000,
|
||||||
|
})
|
||||||
|
expect(fs.existsSync(path.join(tmp, '.npmrc'))).toBeFalsy()
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user