chore: Command Configs
Command-Specific Flags Implementation Checklist
This PR enables commands to define their own distinct flags separate from global configuration, eliminating the need for verbose prefixed flags like --init-name and allowing cleaner command-specific options.
Key Features
- New
flagsconfiguration source - Adds a dedicated layer in the config hierarchy for command-specific flags without modifying existing sources - Command definition merging - Implements
loadCommand()method to merge command-specific definitions with global definitions - Warning deduplication - Captures and deduplicates warnings during initial config load to prevent duplicate messages across sources
- Synchronous design - No new async calls or config instances, integrates seamlessly with existing load flow
- Clean flag naming - Enables commands to use simple flags (e.g.,
--name) instead of verbose prefixed versions (e.g.,--init-name)
Implementation Tasks
- [x] Add
flagsto config source hierarchy (betweenenvandcli) - [x] Implement
loadCommand(definitions)method for dynamic definition merging - [x] Add warning capture system (
#warningsarray) to delay logging until after command load - [x] Implement
removeWarnings(types)to clear superseded warnings - [x] Implement
#deduplicateWarnings()to prevent duplicate warnings - [x] Add
logWarnings()method to flush captured warnings - [x] Update type/default merging to support command-specific definitions
- [x] Handle
exclusiveflag validation across merged definitions - [x] Update
checkUnknown()to recognize bothcliandflagssources - [x] Test command-specific flag parsing and precedence
Now just running npm run prepack will make sure all commands have a .md file and add it to nav.yml and generate a md with flags populated example below:
---
title: npm-tset
section: 1
description: A test command with custom flags
---
### Synopsis
```bash
npm tset [options]
```
Note: This command is unaware of workspaces.
### Description
A test command with custom flags
### Configuration
#### `say`
* Default: "hello"
* Type: String
What to say when running the test command
### See Also
* [npm help config](/commands/npm-config)
yay! (seems more like a feat than a chore tho :-p)
so does this mean i'll be able to set a top-level config value, but then override it for a specific command?
@ljharb
yay! (seems more like a feat than a chore tho :-p)
so does this mean i'll be able to set a top-level config value, but then override it for a specific command?
I put it as a chore because It's not really a customer facing thing. It just allows commands to not need to have prefix flags like what init did with --init-name and we can just do --name...
It has to be at least a fix since it affects the published code. chore is reserved for things that shouldn't trigger a new published version.
so does this mean i'll be able to set a top-level config value, but then override it for a specific command?
No it means we can define a config that only is for a specific command. Obviously these couldn't go into config files (yet?). This will allow commands to have overlapping names in config that have wholly separate configs (i.e. --name in npm init could have different rules than --name in npm token create)
ah ok, gotcha. i'm not sure how name in npmrc would work tho in that example, if there's two distinct sets of possible values for that config key?
meaning, merely adding a command line config whose acceptable values differ across commands would make that config name poisoned (in npmrc or env vars) until the capability exists for users to override a config value more granularly down to a specific command
@ljharb this PR adds command-level configs just for flags (no .npmrc support, and that's intentional) for new commands. If / when we want that we could go with ini "sections" syntax like this:
[command-foo]
name=hello world
[command-bar]
name=true
right - but as soon as you add that example without npmrc/env var support, the string "name" in npmrc becomes broken. Meaning, it's not just about new commands, it's that adding any new config key becomes a breaking change, because someone could have it in their env or npmrc.