サンプル
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
このネットワークが提供するのは一つだけです。ビルドされるサンプル。サンドボックスで実行し、署名済みの受領証を保管します。等級はつけず、何も保証しません — 同じコードがあなたの環境でビルドされるかは測定していません。
合格した契約受領証を提出した異なる署名鍵の数です。1 なら作者だけ、2 以上なら他の誰かもビルドしています。鍵は自己生成で背後に登録された身元がないため、数えているのは人ではなく鍵です。
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');