Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions docs/lib/content/configuring-npm/npmrc.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,44 @@ export ELECTRON_CUSTOM_DIR="{{ version }}"
Environment variables are the most portable approach and work regardless
of `.npmrc` format.

### Command-specific configuration

You can scope configuration to a specific npm command using INI section
headers. For example, to use a different registry only when publishing:

```ini
registry=http://localhost:1337/npm

[publish]
registry=https://registry.npmjs.org/
```

Any npm configuration option can be placed inside a command section. The
section name must match an npm command name (e.g., `[install]`, `[publish]`,
`[audit]`).

Command-specific configs follow the standard priority cascade
(builtin < global < user < project), but **command sections always override
non-sectioned values from any file layer**. CLI flags (`--registry=...`)
and environment variables (`npm_config_registry=...`) still take the
highest precedence.

You can manage command-scoped configs using the `--command` flag:

```bash
# Set a registry only for publish
npm config set registry=https://registry.npmjs.org/ --command=publish

# Get the publish-scoped registry value
npm config get registry --command=publish

# Delete a command-scoped value
npm config delete registry --command=publish
```

Command sections work in all `.npmrc` files: per-project, per-user,
global, and builtin.

### See also

* [npm folders](/configuring-npm/folders)
Expand Down
36 changes: 31 additions & 5 deletions lib/commands/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class Config extends BaseCommand {
'editor',
'location',
'long',
'command',
]

static ignoreImplicitWorkspace = false
Expand Down Expand Up @@ -163,6 +164,8 @@ class Config extends BaseCommand {
}

const where = this.npm.flatOptions.location
const commandScope = this.npm.config.get('command')

for (const [key, val] of Object.entries(keyValues(args))) {
log.info('config', 'set %j %j', key, val)
const baseKey = key.split(':').pop()
Expand All @@ -176,10 +179,18 @@ class Config extends BaseCommand {
)
}

if (val === '') {
this.npm.config.delete(key, where)
if (commandScope) {
if (val === '') {
this.npm.config.deleteCommandConfig(commandScope, key, where)
} else {
this.npm.config.setCommandConfig(commandScope, key, val, where)
}
} else {
this.npm.config.set(key, val, where)
if (val === '') {
this.npm.config.delete(key, where)
} else {
this.npm.config.set(key, val, where)
}
}

if (!this.npm.config.validate(where)) {
Expand All @@ -195,9 +206,18 @@ class Config extends BaseCommand {
return this.list()
}

const commandScope = this.npm.config.get('command')
const out = []
for (const key of keys) {
const val = this.npm.config.get(key)
let val
if (commandScope) {
val = this.npm.config.getCommandConfig(commandScope, key)
if (val === undefined) {
val = this.npm.config.get(key)
}
} else {
val = this.npm.config.get(key)
}
if (isPrivate(key, val)) {
throw new Error(`The ${key} option is protected, and cannot be retrieved in this way`)
}
Expand All @@ -214,8 +234,14 @@ class Config extends BaseCommand {
}

const where = this.npm.flatOptions.location
const commandScope = this.npm.config.get('command')

for (const key of keys) {
this.npm.config.delete(key, where)
if (commandScope) {
this.npm.config.deleteCommandConfig(commandScope, key, where)
} else {
this.npm.config.delete(key, where)
}
}
await this.npm.config.save(where)
}
Expand Down
3 changes: 3 additions & 0 deletions lib/npm.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ class Npm {
// This is the actual name of the command that will be run or undefined if deref could not find a match
const command = deref(commandArg)

// Apply command-specific configs from [command] sections in .npmrc files
this.config.applyCommand(command)

await this.#display.load({
command,
loglevel: this.config.get('loglevel'),
Expand Down
2 changes: 2 additions & 0 deletions tap-snapshots/test/lib/commands/config.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ exports[`test/lib/commands/config.js TAP config list --json > output matches sna
"call": "",
"cert": null,
"cidr": null,
"command": null,
"commit-hooks": true,
"cpu": null,
"depth": null,
Expand Down Expand Up @@ -211,6 +212,7 @@ call = ""
cert = null
cidr = null
; color = {COLOR}
command = null
commit-hooks = true
cpu = null
depth = null
Expand Down
12 changes: 6 additions & 6 deletions tap-snapshots/test/lib/commands/install.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,8 @@ verbose stack Error: The developer of this package has specified the following t
verbose stack Invalid devEngines.runtime
verbose stack Invalid name "nondescript" does not match "node" for "runtime"
verbose stack at Install.checkDevEngines ({CWD}/lib/base-cmd.js:247:27)
verbose stack at MockNpm.execCommandClass ({CWD}/lib/npm.js:292:7)
verbose stack at MockNpm.exec ({CWD}/lib/npm.js:193:9)
verbose stack at MockNpm.execCommandClass ({CWD}/lib/npm.js:295:7)
verbose stack at MockNpm.exec ({CWD}/lib/npm.js:196:9)
error code EBADDEVENGINES
error EBADDEVENGINES The developer of this package has specified the following through devEngines
error EBADDEVENGINES Invalid devEngines.runtime
Expand Down Expand Up @@ -200,8 +200,8 @@ verbose stack Error: The developer of this package has specified the following t
verbose stack Invalid devEngines.runtime
verbose stack Invalid name "nondescript" does not match "node" for "runtime"
verbose stack at Install.checkDevEngines ({CWD}/lib/base-cmd.js:247:27)
verbose stack at MockNpm.execCommandClass ({CWD}/lib/npm.js:292:7)
verbose stack at MockNpm.exec ({CWD}/lib/npm.js:193:9)
verbose stack at MockNpm.execCommandClass ({CWD}/lib/npm.js:295:7)
verbose stack at MockNpm.exec ({CWD}/lib/npm.js:196:9)
error code EBADDEVENGINES
error EBADDEVENGINES The developer of this package has specified the following through devEngines
error EBADDEVENGINES Invalid devEngines.runtime
Expand All @@ -226,8 +226,8 @@ verbose stack Error: The developer of this package has specified the following t
verbose stack Invalid devEngines.runtime
verbose stack Invalid name "nondescript" does not match "node" for "runtime"
verbose stack at Install.checkDevEngines ({CWD}/lib/base-cmd.js:247:27)
verbose stack at MockNpm.execCommandClass ({CWD}/lib/npm.js:292:7)
verbose stack at MockNpm.exec ({CWD}/lib/npm.js:193:9)
verbose stack at MockNpm.execCommandClass ({CWD}/lib/npm.js:295:7)
verbose stack at MockNpm.exec ({CWD}/lib/npm.js:196:9)
error code EBADDEVENGINES
error EBADDEVENGINES The developer of this package has specified the following through devEngines
error EBADDEVENGINES Invalid devEngines.runtime
Expand Down
21 changes: 20 additions & 1 deletion tap-snapshots/test/lib/docs.js.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,19 @@ true, then only prints color codes for tty file descriptors.



#### \`command\`

* Default: null
* Type: null or String

When used with \`npm config set\`, \`npm config get\`, or \`npm config delete\`,
specifies the command section to target. For example, \`npm config set
registry=https://registry.npmjs.org/ --command=publish\` will set the
registry only for the \`publish\` command by writing it under a \`[publish]\`
section in the .npmrc file.



#### \`commit-hooks\`

* Default: true
Expand Down Expand Up @@ -2276,6 +2289,7 @@ Array [
"cert",
"cidr",
"color",
"command",
"commit-hooks",
"cpu",
"depth",
Expand Down Expand Up @@ -2578,6 +2592,7 @@ Array [

exports[`test/lib/docs.js TAP config > keys that are not flattened 1`] = `
Array [
"command",
"expect-result-count",
"expect-results",
"init-author-email",
Expand Down Expand Up @@ -3169,7 +3184,7 @@ npm config fix

Options:
[--json] [-g|--global] [--editor <editor>] [-L|--location <global|user|project>]
[-l|--long]
[-l|--long] [--command <command>]

--json
Whether or not to output JSON data, rather than the normal output.
Expand All @@ -3186,6 +3201,9 @@ Options:
-l|--long
Show extended information in \`ls\`, \`search\`, and \`help-search\`.

--command
When used with \`npm config set\`, \`npm config get\`, or


alias: c

Expand All @@ -3209,6 +3227,7 @@ Note: This command is unaware of workspaces.
#### \`editor\`
#### \`location\`
#### \`long\`
#### \`command\`
`

exports[`test/lib/docs.js TAP usage dedupe > must match snapshot 1`] = `
Expand Down
11 changes: 11 additions & 0 deletions workspaces/config/lib/definitions/definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,17 @@ const definitions = {
: !!process.stderr.isTTY
},
}),
command: new Definition('command', {
default: null,
type: [null, String],
description: `
When used with \`npm config set\`, \`npm config get\`, or
\`npm config delete\`, specifies the command section to target.
For example, \`npm config set registry=https://registry.npmjs.org/
--command=publish\` will set the registry only for the \`publish\`
command by writing it under a \`[publish]\` section in the .npmrc file.
`,
}),
'commit-hooks': new Definition('commit-hooks', {
default: true,
type: Boolean,
Expand Down
Loading
Loading