mirror of
https://github.com/pnpm/pnpm.git
synced 2026-04-27 18:46:18 -04:00
feat(cli/config)!: config command outputs changed from INI to JSON with camelCase keys (#10142)
This commit is contained in:
9
.changeset/crazy-swans-retire.md
Normal file
9
.changeset/crazy-swans-retire.md
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-config": major
|
||||
"pnpm": major
|
||||
---
|
||||
|
||||
`pnpm config get` (without `--json`) no longer print INI formatted text.
|
||||
Instead, it would print JSON for both objects and arrays and raw string for
|
||||
strings, numbers, booleans, and nulls.
|
||||
`pnpm config get --json` would still print all types of values as JSON like before.
|
||||
6
.changeset/sixty-results-end.md
Normal file
6
.changeset/sixty-results-end.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-config": major
|
||||
"pnpm": major
|
||||
---
|
||||
|
||||
`pnpm config list` now prints a JSON object instead of INI formatted text.
|
||||
@@ -58,7 +58,7 @@ export function help (): string {
|
||||
name: '--location <project|global>',
|
||||
},
|
||||
{
|
||||
description: 'Show all the config settings in JSON format',
|
||||
description: 'Show all types of values in JSON format (not just objects and arrays)',
|
||||
name: '--json',
|
||||
},
|
||||
],
|
||||
@@ -68,9 +68,9 @@ export function help (): string {
|
||||
usages: [
|
||||
'pnpm config set <key> <value>',
|
||||
'pnpm config get <key>',
|
||||
'pnpm config get --json <key>',
|
||||
'pnpm config delete <key>',
|
||||
'pnpm config list',
|
||||
'pnpm config list --json',
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import kebabCase from 'lodash.kebabcase'
|
||||
import { encode } from 'ini'
|
||||
import { types } from '@pnpm/config'
|
||||
import { isCamelCase, isStrictlyKebabCase } from '@pnpm/naming-cases'
|
||||
import { getObjectValueByPropertyPath } from '@pnpm/object.property-path'
|
||||
@@ -42,13 +41,11 @@ function getRcConfig (rawConfig: Record<string, unknown>, key: string, isScopedK
|
||||
return undefined
|
||||
}
|
||||
|
||||
type GetConfigByPropertyPathOptions = Pick<ConfigCommandOptions, 'json'>
|
||||
|
||||
function getConfigByPropertyPath (rawConfig: Record<string, unknown>, propertyPath: string, opts?: GetConfigByPropertyPathOptions): Found<unknown> {
|
||||
function getConfigByPropertyPath (rawConfig: Record<string, unknown>, propertyPath: string): Found<unknown> {
|
||||
const parsedPropertyPath = Array.from(parseConfigPropertyPath(propertyPath))
|
||||
if (parsedPropertyPath.length === 0) {
|
||||
return {
|
||||
value: processConfig(rawConfig, opts),
|
||||
value: processConfig(rawConfig),
|
||||
}
|
||||
}
|
||||
return {
|
||||
@@ -63,7 +60,7 @@ function displayConfig (config: unknown, opts: DisplayConfigOptions): string {
|
||||
return JSON.stringify(config, undefined, 2)
|
||||
}
|
||||
if (typeof config === 'object' && config != null) {
|
||||
return encode(config)
|
||||
return JSON.stringify(config, undefined, 2)
|
||||
}
|
||||
return String(config)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { encode } from 'ini'
|
||||
import { processConfig } from './processConfig.js'
|
||||
import { type ConfigCommandOptions } from './ConfigCommandOptions.js'
|
||||
|
||||
export async function configList (opts: ConfigCommandOptions): Promise<string> {
|
||||
const processedConfig = processConfig(opts.rawConfig, opts)
|
||||
if (opts.json) {
|
||||
return JSON.stringify(processedConfig, null, 2)
|
||||
}
|
||||
return encode(processedConfig)
|
||||
export type ConfigListOptions = Pick<ConfigCommandOptions, 'rawConfig'>
|
||||
|
||||
export async function configList (opts: ConfigListOptions): Promise<string> {
|
||||
const processedConfig = processConfig(opts.rawConfig)
|
||||
return JSON.stringify(processedConfig, undefined, 2)
|
||||
}
|
||||
|
||||
@@ -17,10 +17,6 @@ export interface ProcessConfigOptions {
|
||||
json?: boolean
|
||||
}
|
||||
|
||||
function normalizeConfigKeyCases (rawConfig: Record<string, unknown>, opts?: ProcessConfigOptions): Record<string, unknown> {
|
||||
return opts?.json ? camelCaseConfig(rawConfig) : rawConfig
|
||||
}
|
||||
|
||||
export function processConfig (rawConfig: Record<string, unknown>, opts?: ProcessConfigOptions): Record<string, unknown> {
|
||||
return normalizeConfigKeyCases(censorProtectedSettings(sortDirectKeys(rawConfig)), opts)
|
||||
export function processConfig (rawConfig: Record<string, unknown>): Record<string, unknown> {
|
||||
return camelCaseConfig(censorProtectedSettings(sortDirectKeys(rawConfig)))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as ini from 'ini'
|
||||
import { config } from '@pnpm/plugin-commands-config'
|
||||
import { getOutputString } from './utils/index.js'
|
||||
|
||||
@@ -64,7 +63,7 @@ test('config get on array should return a comma-separated list', async () => {
|
||||
])
|
||||
})
|
||||
|
||||
test('config get on object should return an ini string', async () => {
|
||||
test('config get on object should return a JSON string', async () => {
|
||||
const getResult = await config.handler({
|
||||
dir: process.cwd(),
|
||||
cliOptions: {},
|
||||
@@ -77,7 +76,7 @@ test('config get on object should return an ini string', async () => {
|
||||
},
|
||||
}, ['get', 'catalog'])
|
||||
|
||||
expect(ini.decode(getOutputString(getResult))).toEqual({ react: '^19.0.0' })
|
||||
expect(JSON.parse(getOutputString(getResult))).toStrictEqual({ react: '^19.0.0' })
|
||||
})
|
||||
|
||||
test('config get without key show list all settings', async () => {
|
||||
@@ -100,10 +99,11 @@ test('config get without key show list all settings', async () => {
|
||||
rawConfig,
|
||||
}, ['list'])
|
||||
|
||||
expect(getOutput).toEqual(listOutput)
|
||||
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',
|
||||
'only-built-dependencies': ['foo', 'bar'],
|
||||
@@ -169,7 +169,13 @@ describe('config get with a property path', () => {
|
||||
|
||||
describe('object without --json', () => {
|
||||
test.each([
|
||||
['', rawConfig],
|
||||
// TODO: change `rawConfig` into camelCase and replace this object with just `rawConfig`.
|
||||
['', {
|
||||
dlxCacheMaxAge: rawConfig['dlx-cache-max-age'],
|
||||
onlyBuiltDependencies: rawConfig['only-built-dependencies'],
|
||||
packageExtensions: rawConfig.packageExtensions,
|
||||
}],
|
||||
|
||||
['packageExtensions', rawConfig.packageExtensions],
|
||||
['packageExtensions["@babel/parser"]', rawConfig.packageExtensions['@babel/parser']],
|
||||
['packageExtensions["@babel/parser"].peerDependencies', rawConfig.packageExtensions['@babel/parser'].peerDependencies],
|
||||
@@ -184,7 +190,7 @@ describe('config get with a property path', () => {
|
||||
rawConfig,
|
||||
}, ['get', propertyPath])
|
||||
|
||||
expect(ini.decode(getOutputString(getResult))).toEqual(expected)
|
||||
expect(JSON.parse(getOutputString(getResult))).toStrictEqual(expected)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import * as ini from 'ini'
|
||||
import { config } from '@pnpm/plugin-commands-config'
|
||||
import { getOutputString } from './utils/index.js'
|
||||
|
||||
@@ -13,9 +12,9 @@ test('config list', async () => {
|
||||
},
|
||||
}, ['list'])
|
||||
|
||||
expect(ini.decode(getOutputString(output))).toEqual({
|
||||
'fetch-retries': '2',
|
||||
'store-dir': '~/store',
|
||||
expect(JSON.parse(getOutputString(output))).toStrictEqual({
|
||||
fetchRetries: '2',
|
||||
storeDir: '~/store',
|
||||
})
|
||||
})
|
||||
|
||||
@@ -53,8 +52,10 @@ test('config list censors protected settings', async () => {
|
||||
rawConfig,
|
||||
}, ['list'])
|
||||
|
||||
expect(ini.decode(getOutputString(output))).toEqual({
|
||||
...rawConfig,
|
||||
expect(JSON.parse(getOutputString(output))).toStrictEqual({
|
||||
storeDir: '~/store',
|
||||
fetchRetries: '2',
|
||||
'@my-org:registry': 'https://my-org.example.com/registry',
|
||||
'//my-org.example.com:username': '(protected)',
|
||||
username: '(protected)',
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import fs from 'fs'
|
||||
import * as ini from 'ini'
|
||||
import { sync as writeYamlFile } from 'write-yaml-file'
|
||||
import { type Config } from '@pnpm/config'
|
||||
import { prepare } from '@pnpm/prepare'
|
||||
@@ -109,12 +108,6 @@ test('pnpm config list still reads unknown camelCase keys from pnpm-workspace.ya
|
||||
|
||||
{
|
||||
const { stdout } = execPnpmSync(['config', 'list'], { expectSuccess: true })
|
||||
expect(ini.decode(stdout.toString())).toMatchObject(workspaceManifest)
|
||||
expect(ini.decode(stdout.toString())).not.toHaveProperty(['this-option-is-not-defined-by-pnpm'])
|
||||
}
|
||||
|
||||
{
|
||||
const { stdout } = execPnpmSync(['config', 'list', '--json'], { expectSuccess: true })
|
||||
expect(JSON.parse(stdout.toString())).toMatchObject(workspaceManifest)
|
||||
expect(JSON.parse(stdout.toString())).not.toHaveProperty(['this-option-is-not-defined-by-pnpm'])
|
||||
}
|
||||
@@ -142,43 +135,9 @@ test('pnpm config list --json shows all keys in camelCase', () => {
|
||||
prepare()
|
||||
writeYamlFile('pnpm-workspace.yaml', workspaceManifest)
|
||||
|
||||
const { stdout } = execPnpmSync(['config', 'list', '--json'], { expectSuccess: true })
|
||||
const { stdout } = execPnpmSync(['config', 'list'], { expectSuccess: true })
|
||||
expect(JSON.parse(stdout.toString())).toStrictEqual(expect.objectContaining(workspaceManifest))
|
||||
expect(JSON.parse(stdout.toString())).not.toHaveProperty(['dlx-cache-max-age'])
|
||||
expect(JSON.parse(stdout.toString())).not.toHaveProperty(['only-built-dependencies'])
|
||||
expect(JSON.parse(stdout.toString())).not.toHaveProperty(['package-extensions'])
|
||||
})
|
||||
|
||||
test('pnpm config list without --json shows rc options in kebab-case and workspace-specific settings in camelCase', () => {
|
||||
const workspaceManifest = {
|
||||
dlxCacheMaxAge: 1234,
|
||||
onlyBuiltDependencies: ['foo', 'bar'],
|
||||
packages: ['baz', 'qux'],
|
||||
packageExtensions: {
|
||||
'@babel/parser': {
|
||||
peerDependencies: {
|
||||
'@babel/types': '*',
|
||||
},
|
||||
},
|
||||
'jest-circus': {
|
||||
dependencies: {
|
||||
slash: '3',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
prepare()
|
||||
writeYamlFile('pnpm-workspace.yaml', workspaceManifest)
|
||||
|
||||
const { stdout } = execPnpmSync(['config', 'list'], { expectSuccess: true })
|
||||
expect(ini.decode(stdout.toString())).toEqual(expect.objectContaining({
|
||||
'dlx-cache-max-age': String(workspaceManifest.dlxCacheMaxAge), // must be a string because ini doesn't decode to numbers
|
||||
'only-built-dependencies': workspaceManifest.onlyBuiltDependencies,
|
||||
packages: workspaceManifest.packages,
|
||||
packageExtensions: workspaceManifest.packageExtensions,
|
||||
}))
|
||||
expect(ini.decode(stdout.toString())).not.toHaveProperty(['dlxCacheMaxAge'])
|
||||
expect(ini.decode(stdout.toString())).not.toHaveProperty(['onlyBuiltDependencies'])
|
||||
expect(ini.decode(stdout.toString())).not.toHaveProperty(['package-extensions'])
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user