fix: handle scoped registry keys in config get (#10082)

* fix: handle scoped registry keys in config get

* Update config/plugin-commands-config/test/configGet.test.ts

Co-authored-by: Khải <hvksmr1996@gmail.com>

* Update config/plugin-commands-config/test/configGet.test.ts

Co-authored-by: Khải <hvksmr1996@gmail.com>

* Update config/plugin-commands-config/test/configGet.test.ts

Co-authored-by: Khải <hvksmr1996@gmail.com>

* chore: add a comment

---------

Co-authored-by: Khải <hvksmr1996@gmail.com>

close #9362
This commit is contained in:
Ryo Matsukawa
2025-10-14 07:23:08 +09:00
committed by GitHub
parent 6089939e15
commit 47295e2d25
3 changed files with 62 additions and 4 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/plugin-commands-config": patch
"pnpm": patch
---
Fixed scoped registry keys (e.g., `@scope:registry`) being parsed as property paths in `pnpm config get` when `--location=project` is used [#9362](https://github.com/pnpm/pnpm/issues/9362).

View File

@@ -9,13 +9,25 @@ import { parseConfigPropertyPath } from './parseConfigPropertyPath.js'
import { settingShouldFallBackToNpm } from './settingShouldFallBackToNpm.js'
export function configGet (opts: ConfigCommandOptions, key: string): { output: string, exitCode: number } {
if (opts.global && settingShouldFallBackToNpm(key)) {
const isScopedKey = key.startsWith('@')
// Exclude scoped keys from npm fallback because they are pnpm-native config
// that can be read directly from rawConfig (e.g., '@scope:registry')
if (opts.global && settingShouldFallBackToNpm(key) && !isScopedKey) {
const { status: exitCode } = runNpm(opts.npmPath, ['config', 'get', key])
return { output: '', exitCode: exitCode ?? 0 }
}
const config = isStrictlyKebabCase(key)
? opts.rawConfig[kebabCase(key)] // we don't parse kebab-case keys as property paths because it's not a valid JS syntax
: getConfigByPropertyPath(opts.rawConfig, key)
let config: unknown
if (isStrictlyKebabCase(key)) {
// we don't parse kebab-case keys as property paths because it's not a valid JS syntax
config = opts.rawConfig[kebabCase(key)]
} else if (isScopedKey) {
// scoped registry keys like '@scope:registry' are used as-is
config = opts.rawConfig[key]
} else {
config = getConfigByPropertyPath(opts.rawConfig, key)
}
const output = displayConfig(config, opts)
return { output, exitCode: 0 }
}

View File

@@ -179,3 +179,43 @@ describe('config get with a property path', () => {
})
})
})
test('config get with scoped registry key (global: false)', async () => {
const getResult = await config.handler({
dir: process.cwd(),
cliOptions: {},
configDir: process.cwd(),
global: false,
rawConfig: {
'@scope:registry': 'https://custom-registry.example.com/',
},
}, ['get', '@scope:registry'])
expect(getOutputString(getResult)).toBe('https://custom-registry.example.com/')
})
test('config get with scoped registry key (global: true)', async () => {
const getResult = await config.handler({
dir: process.cwd(),
cliOptions: {},
configDir: process.cwd(),
global: true,
rawConfig: {
'@scope:registry': 'https://custom-registry.example.com/',
},
}, ['get', '@scope:registry'])
expect(getOutputString(getResult)).toBe('https://custom-registry.example.com/')
})
test('config get with scoped registry key that does not exist', async () => {
const getResult = await config.handler({
dir: process.cwd(),
cliOptions: {},
configDir: process.cwd(),
global: false,
rawConfig: {},
}, ['get', '@scope:registry'])
expect(getOutputString(getResult)).toBe('undefined')
})