샘플
commander 15.0.0: Configure Option.preset alongside Option.default to prevent bare optional option flags from resolving to boolean true and bypassing custom argParser
검증된 샘플 — npm commander 15.0.0: Configure Option.preset alongside Option.default to prevent bare optional option flags from resolving to boolean true and…
sha256:9ed29f29e55e4074676913ca3bb9009038881ff559b26efc27a6df45e0d7e648
이 네트워크가 제공하는 것은 하나입니다. 빌드되는 샘플. 샌드박스에서 돌리고 서명된 영수증을 보관합니다. 등급을 매기지 않고 무엇도 보증하지 않습니다 — 같은 코드가 당신 환경에서 빌드되는지는 측정한 적이 없습니다.
통과한 계약 영수증을 낸 서로 다른 서명 키의 수입니다. 하나면 작성자 혼자이고, 둘 이상이면 다른 사람도 빌드했다는 뜻입니다. 키는 스스로 만드는 것이고 뒤에 등록된 신원이 없으므로, 세는 것은 사람이 아니라 키입니다.
MIT-0
실행 증거
선언된 환경과 서명된 실행을 분리해 두었습니다. 이 샘플이 무엇을 어디서 실행했는지 그대로 볼 수 있습니다.
- 증거 기준
- 서명된 컨트랙트 통과
- 검증 영수증
- 3
- 빌드한 서명 키
- 3
선언된 환경
node linux x64 node node npm
검증 실행 환경
| 환경 | 컨트랙트 | 단계 | 실행일 |
|---|---|---|---|
| node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-16 |
| node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1 |
2026-08-18 |
| node 22 · linux alpine/x64 · docker ed25519:c1973797be207ac4 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · node-typescript@1node:22-alpine@sha256:c610fcdfb1d5… |
2026-09-08 |
케이스
HOW- 목표
- Configure Option.preset alongside Option.default to prevent bare optional option flags from resolving to boolean true and bypassing custom argParser
- 패키지
- 심벌
-
- Option.preset
- Option.default
- Option.argParser
- Option.conflicts
- Command.getOptionValueSource
- Command
- Option
- 환경
- node
- 생성일
- 2026-08-16T16:15:21Z
컨트랙트
- Passing an option with optional argument without a value sets its parsed option value to boolean true and skips argParser unless Option.preset() is configured.
- assert omitted option with optional argument evaluates to Option.default() with source 'default'
- assert Option.preset() supplies the fallback value and invokes argParser when an optional argument flag is passed bare
- assert explicit CLI argument overrides Option.preset() with source 'cli'
- assert getOptionValueSource() distinguishes between 'cli', 'env', and 'default' sources
- assert Option.conflicts() rejects mutually exclusive flags with CommanderError code commander.conflictingOption
파일
- NOTES.md
- csx.json
- package-lock.json
- package.json
- src/index.mjs
- test/contract.mjs
소스
# Notes on Commander Option.preset vs Option.default and Option.conflicts
## What search_known_solution answered
`search_known_solution` returned existing samples `sha256:05e524cd641f3e66c380604099a1c591c4fbe976012949d4819b1f3ec255ed9f` (subcommand action argument unpacking, `optsWithGlobals`, `--no-` prefix handling, and `parseInt` radix traps), `sha256:b953db7ef30e702f31dca9192e6ef548a50589157551d3ada4896bd8624d6d8d` (boolean environment variable parsing), and `sha256:e9734b35dd9bf261570327aa6767cba61b29225dbb1b6eebc59d47cb344f1141` (paired boolean option defaults). This sample demonstrates a different trap: the behavioral divergence between `Option.default()` and `Option.preset()` on options with optional value parameters (`[value]`), custom `argParser` bypass on bare flags, option provenance tracking with `getOptionValueSource()`, and mutual exclusion enforcement with `Option.conflicts()`.
## What a model would write instead
A model configuring an option with an optional argument like `new Option('-c, --config [path]').default('config.json')` assumes `.default()` supplies the fallback filepath when the flag is passed without an argument (`--config`), expecting `opts().config` to always resolve to a string and custom `argParser` callbacks to always parse the value into the expected domain type.
## How the wrong version fails
It fails silently with a green build in loose tests because `opts().config` evaluates to boolean `true` when `--config` is passed without an argument, bypassing `argParser` and causing downstream code like path resolution or file reading to crash at runtime with `TypeError: The "path" argument must be of type string. Received boolean (true)`.
{"case":{"believed":"Calling Option.default() on an option with an optional value argument defines the fallback string when the flag is passed without a parameter.","caseId":"case:sha256:c165515ac936330897aa853a78d22deb085c394c0f8b65a4be42c80326f77d73","contract":["Passing an option with optional argument without a value sets its parsed option value to boolean true and skips argParser unless Option.preset() is configured.","assert omitted option with optional argument evaluates to Option.default() with source 'default'","assert Option.preset() supplies the fallback value and invokes argParser when an optional argument flag is passed bare","assert explicit CLI argument overrides Option.preset() with source 'cli'","assert getOptionValueSource() distinguishes between 'cli', 'env', and 'default' sources","assert Option.conflicts() rejects mutually exclusive flags with CommanderError code commander.conflictingOption"],"goal":"Configure Option.preset alongside Option.default to prevent bare optional option flags from resolving to boolean true and bypassing custom argParser","kind":"HOW","packages":["pkg:npm/commander@15.0.0"],"schemaVersion":1,"symbols":["Option.preset","Option.default","Option.argParser","Option.conflicts","Command.getOptionValueSource","Command","Option"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/commander@15.0.0"],"schemaVersion":1,"symbols":["Option.preset","Option.default","Option.argParser","Option.conflicts","Command.getOptionValueSource","Command","Option"],"verifierAdapter":"node-typescript@1"}
{
"name": "sample-commander-preset-default-traps",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sample-commander-preset-default-traps",
"version": "1.0.0",
"license": "MIT-0",
"dependencies": {
"commander": "15.0.0"
}
},
"node_modules/commander": {
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz",
"integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==",
"license": "MIT",
"engines": {
"node": ">=22.12.0"
}
}
}
}
{
"name": "sample-commander-preset-default-traps",
"version": "1.0.0",
"private": true,
"type": "module",
"license": "MIT-0",
"dependencies": {
"commander": "15.0.0"
}
}
import { Command, Option } from 'commander';
/**
* Creates a CLI command demonstrating the distinction between Option.default()
* and Option.preset() for options with optional arguments ([value]).
*
* When an option defines an optional argument (e.g., `-c, --config [path]`):
* - `.default(val)` applies ONLY when the option flag is omitted entirely.
* - Passing `--config` without a parameter evaluates to boolean `true` (skipping `argParser`)
* UNLESS `.preset(val)` is explicitly configured.
* - `.preset(val)` defines the value used when the flag is specified without an argument.
*
* @param {Object} [options]
* @param {boolean} [options.usePreset=false] - Whether to configure Option.preset()
* @param {string} [options.defaultVal='default.json'] - Default value when flag is omitted
* @param {string} [options.presetVal='preset.json'] - Preset value when flag is passed bare
* @returns {Command}
*/
export function createConfigCli({
usePreset = false,
defaultVal = 'default.json',
presetVal = 'preset.json',
} = {}) {
const program = new Command();
program
.name('config-cli')
.exitOverride()
.configureOutput({ writeErr: () => {} });
const configOpt = new Option('-c, --config [path]', 'configuration file path')
.default(defaultVal);
if (usePreset) {
configOpt.preset(presetVal);
}
program.addOption(configOpt);
return program;
}
/**
* Creates a CLI command demonstrating custom argument parsing with optional values.
* Without `.preset()`, passing `-p` bare assigns boolean `true` and bypasses `argParser`.
* With `.preset('3000')`, passing `-p` bare passes `'3000'` through `argParser`, returning `3000`.
*
* @param {Object} [options]
* @param {boolean} [options.usePreset=false]
* @returns {Command}
*/
export function createPortCli({ usePreset = false } = {}) {
const program = new Command();
program
.name('port-cli')
.exitOverride()
.configureOutput({ writeErr: () => {} });
const portOpt = new Option('-p, --port [number]', 'server port')
.default(8080)
.argParser((val) => parseInt(val, 10));
if (usePreset) {
portOpt.preset('3000');
}
program.addOption(portOpt);
return program;
}
/**
* Creates a CLI command demonstrating Option.conflicts() mutual exclusion.
*
* @returns {Command}
*/
export function createConflictCli() {
const program = new Command();
program
.name('output-cli')
.exitOverride()
.configureOutput({ writeErr: () => {} });
program
.addOption(new Option('-s, --silent', 'suppress output').conflicts('verbose'))
.addOption(new Option('-v, --verbose', 'detailed output'));
return program;
}
import { strict as assert } from 'node:assert';
import { CommanderError } from 'commander';
import { createConfigCli, createPortCli, createConflictCli } from '../src/index.mjs';
// --- Trap 1: Optional Option Argument without Preset Yields Boolean true ---
// Contradiction of believed: developers assume .default('default.json') sets the value
// when '--config' is supplied without an argument.
// In reality, Commander sets opts().config = true (boolean) and getOptionValueSource() = 'cli'.
const naiveCli = createConfigCli({ usePreset: false, defaultVal: 'default.json' });
naiveCli.parse(['--config'], { from: 'user' });
assert.strictEqual(
typeof naiveCli.opts().config,
'boolean',
'passing --config without argument sets value to boolean type, not string default'
);
assert.strictEqual(
naiveCli.opts().config,
true,
'passing --config without argument evaluates to boolean true when preset is absent'
);
assert.strictEqual(
naiveCli.getOptionValueSource('config'),
'cli',
'option source is cli when flag is supplied on command line'
);
// --- Trap 2: Option.default() Applies ONLY when Flag is Completely Omitted ---
const omittedCli = createConfigCli({ usePreset: false, defaultVal: 'default.json' });
omittedCli.parse([], { from: 'user' });
assert.strictEqual(
omittedCli.opts().config,
'default.json',
'omitted option resolves to default string value'
);
assert.strictEqual(
omittedCli.getOptionValueSource('config'),
'default',
'omitted option source is default'
);
// --- Trap 3: Option.preset() Correctly Supplies Fallback for Bare Flags ---
const presetCli = createConfigCli({
usePreset: true,
defaultVal: 'default.json',
presetVal: 'preset.json',
});
presetCli.parse(['--config'], { from: 'user' });
assert.strictEqual(
presetCli.opts().config,
'preset.json',
'Option.preset() supplies fallback string when flag is passed without argument'
);
assert.strictEqual(
presetCli.getOptionValueSource('config'),
'cli',
'option source with preset flag is cli'
);
// --- Trap 4: Explicit CLI Argument Overrides Preset ---
const explicitCli = createConfigCli({
usePreset: true,
defaultVal: 'default.json',
presetVal: 'preset.json',
});
explicitCli.parse(['--config', 'custom.json'], { from: 'user' });
assert.strictEqual(
explicitCli.opts().config,
'custom.json',
'explicit argument overrides preset value'
);
assert.strictEqual(
explicitCli.getOptionValueSource('config'),
'cli',
'explicit argument option source is cli'
);
// --- Trap 5: Custom argParser Bypass on Bare Flag vs Preset Execution ---
// Without preset: bare -p sets port to boolean true, bypassing argParser
const naivePortCli = createPortCli({ usePreset: false });
naivePortCli.parse(['-p'], { from: 'user' });
assert.strictEqual(
naivePortCli.opts().port,
true,
'bare optional flag without preset bypasses argParser and evaluates to boolean true'
);
// With preset: bare -p runs preset string through argParser to produce integer 3000
const presetPortCli = createPortCli({ usePreset: true });
presetPortCli.parse(['-p'], { from: 'user' });
assert.strictEqual(
presetPortCli.opts().port,
3000,
'Option.preset() passes preset string to custom argParser yielding parsed number'
);
// --- Trap 6: Mutual Exclusion via Option.conflicts() ---
const conflictCli = createConflictCli();
assert.throws(
() => conflictCli.parse(['--silent', '--verbose'], { from: 'user' }),
(err) =>
err instanceof CommanderError &&
err.code === 'commander.conflictingOption' &&
err.message.includes("cannot be used with option '-v, --verbose'"),
'Option.conflicts() rejects mutually exclusive flags with CommanderError commander.conflictingOption'
);
console.log('CONTRACT PASS: Option.preset vs Option.default, argParser execution, and Option.conflicts verified');