CodeSampleX

サンプル

commander 13.1.0: Retrieve positional argument default values in Commander via processedArgs or action callbacks rather than program.args when arguments are unset

検証済みサンプル — npm commander 13.1.0: Retrieve positional argument default values in Commander via processedArgs or action callbacks rather than program.args when…

sha256:3fc59d6ae2df342949bb6f6c09dcdc027cda2183237046c0189f6cabfae569e9

このネットワークが提供するのは一つだけです。ビルドされるサンプル。サンドボックスで実行し、署名済みの受領証を保管します。等級はつけず、何も保証しません — 同じコードがあなたの環境でビルドされるかは測定していません。 合格した契約受領証を提出した異なる署名鍵の数です。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
ゴール
Retrieve positional argument default values in Commander via processedArgs or action callbacks rather than program.args when arguments are unset
パッケージ
シンボル
  • Command.argument
  • Command.args
  • Command.processedArgs
  • Command.opts
  • Option.default
  • Argument.default
環境
node
作成日
2026-08-16T16:14:22Z

コントラクト

  1. program.args remains empty when an optional positional argument with a default value is omitted, because default argument values are populated into program.processedArgs and action parameters rather than program.args.
  2. omitted defaulted argument is absent from program.args when partial arguments are supplied
  3. variadic omitted argument without an explicit default defaults to an empty Array rather than undefined
  4. custom coerce parser is bypassed for default values when options or arguments are omitted
  5. program.opts populates defaults for omitted options while program.args does not populate defaults for omitted arguments

ファイル

  • NOTES.md
  • csx.json
  • package-lock.json
  • package.json
  • test/contract.mjs

ソースアーティファクトをダウンロード (tar.gz)

ソース

NOTES.md
# Commander Positional Argument and Option Defaults

## Prior Findings
`search_known_solution` returned existing samples for boolean flag handling (`sha256:e9734b35dd9bf261570327aa6767cba61b29225dbb1b6eebc59d47cb344f1141` regarding dual positive/negative flags in Commander 15, and `sha256:b953db7ef30e702f31dca9192e6ef548a50589157551d3ada4896bd8624d6d8d` regarding boolean environment variables) and subcommand action unpacking (`sha256:05e524cd641f3e66c380604099a1c591c4fbe976012949d4819b1f3ec255ed9f`), but did not cover the omission of positional argument defaults from `program.args` or the bypass of custom coerce parsers on default values.

## How the Wrong Assumption Fails
Fails silently with a green build: reading `program.args[0]` after omitting an argument configured with `.argument('[env]', 'desc', 'dev')` returns `undefined` instead of `'dev'`, causing downstream configurations to silently receive `undefined`, while `program.opts()` simultaneously populates option defaults.
csx.json
{"case":{"believed":"Configuring an optional argument with a default value populates program.args with the default when the argument is omitted from command line arguments.","caseId":"case:sha256:ceb6c66935c73ad3b8e65ab752af91690ee9f28e300a70a4a3b15723e89d5e22","contract":["program.args remains empty when an optional positional argument with a default value is omitted, because default argument values are populated into program.processedArgs and action parameters rather than program.args.","omitted defaulted argument is absent from program.args when partial arguments are supplied","variadic omitted argument without an explicit default defaults to an empty Array rather than undefined","custom coerce parser is bypassed for default values when options or arguments are omitted","program.opts populates defaults for omitted options while program.args does not populate defaults for omitted arguments"],"goal":"Retrieve positional argument default values in Commander via processedArgs or action callbacks rather than program.args when arguments are unset","kind":"HOW","packages":["pkg:npm/commander@13.1.0"],"schemaVersion":1,"symbols":["Command.argument","Command.args","Command.processedArgs","Command.opts","Option.default","Argument.default"]},"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@13.1.0"],"schemaVersion":1,"symbols":["Command.argument","Command.args","Command.processedArgs","Command.opts","Option.default","Argument.default"],"verifierAdapter":"node-typescript@1"}
package-lock.json
{
  "name": "npm-commander-defaults",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "npm-commander-defaults",
      "version": "1.0.0",
      "dependencies": {
        "commander": "13.1.0"
      }
    },
    "node_modules/commander": {
      "version": "13.1.0",
      "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
      "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
      "license": "MIT",
      "engines": {
        "node": ">=18"
      }
    }
  }
}
package.json
{
  "name": "npm-commander-defaults",
  "version": "1.0.0",
  "type": "module",
  "dependencies": {
    "commander": "13.1.0"
  }
}
test/contract.mjs
import assert from 'node:assert/strict';
import { Command, Argument, Option } from 'commander';

// ---------------------------------------------------------------------------
// 1. Positional argument defaults are NOT populated in program.args
// ---------------------------------------------------------------------------
// When an optional argument is configured with a default value, callers often
// expect program.args to contain that default value when left unset, mirroring
// how program.opts() populates option defaults.
// In reality, program.args contains ONLY the raw CLI tokens passed by the user.
// Default argument values are populated into program.processedArgs and passed
// to .action() handlers, but program.args remains empty.
{
  const program = new Command();
  let actionReceivedEnv;

  program
    .name('deploy')
    .argument('[env]', 'target deployment environment', 'development')
    .action((env) => {
      actionReceivedEnv = env;
    });

  program.parse([], { from: 'user' });

  // Naive expectation: program.args[0] === 'development'
  // Actual behavior: program.args is empty []
  assert.equal(
    program.args.length,
    0,
    'program.args must remain empty when an optional argument with a default value is omitted',
  );
  assert.equal(
    program.args[0],
    undefined,
    'program.args[0] is undefined rather than the default value',
  );

  // Default is correctly available in processedArgs and action parameters
  assert.deepEqual(
    program.processedArgs,
    ['development'],
    'program.processedArgs contains the resolved default value',
  );
  assert.equal(
    actionReceivedEnv,
    'development',
    'action handler receives the resolved default value',
  );
}

// ---------------------------------------------------------------------------
// 2. Partial arguments: program.args reflects only supplied tokens
// ---------------------------------------------------------------------------
// When a required argument is supplied but a subsequent defaulted argument is
// omitted, program.args has length 1 (only the supplied token), while
// program.processedArgs contains both the supplied value and the default.
{
  const program = new Command();
  let actionArgs;

  program
    .argument('<target>', 'deployment target')
    .argument('[cluster]', 'target cluster', 'primary-cluster')
    .action((target, cluster) => {
      actionArgs = { target, cluster };
    });

  program.parse(['frontend'], { from: 'user' });

  assert.deepEqual(
    program.args,
    ['frontend'],
    'program.args contains only the explicitly passed CLI argument',
  );
  assert.equal(
    program.args[1],
    undefined,
    'omitted defaulted argument is absent from program.args',
  );
  assert.deepEqual(
    program.processedArgs,
    ['frontend', 'primary-cluster'],
    'program.processedArgs contains both explicit and default argument values',
  );
  assert.deepEqual(actionArgs, { target: 'frontend', cluster: 'primary-cluster' });
}

// ---------------------------------------------------------------------------
// 3. Variadic argument defaults to empty Array [] rather than undefined
// ---------------------------------------------------------------------------
// A single optional argument [file] evaluates to undefined when unset.
// A variadic argument [files...] without an explicit default evaluates to []
// (an empty Array) rather than undefined, causing nullish coalescing (files ?? default)
// to retain the empty array.
{
  const program = new Command();
  let actionFiles;

  program
    .argument('[single]')
    .argument('[files...]')
    .action((single, files) => {
      actionFiles = { single, files };
    });

  program.parse([], { from: 'user' });

  assert.equal(
    actionFiles.single,
    undefined,
    'single omitted argument without default is undefined',
  );
  assert.deepEqual(
    actionFiles.files,
    [],
    'variadic omitted argument without default defaults to empty Array [], not undefined',
  );
  assert.deepEqual(
    program.processedArgs,
    [undefined, []],
    'processedArgs holds undefined for single arg and [] for variadic arg',
  );
}

// ---------------------------------------------------------------------------
// 4. Custom coerce parser is bypassed for default values when unset
// ---------------------------------------------------------------------------
// When an option or argument has a custom parse/coerce function and a default value,
// Commander does NOT execute the parse function on the default value.
// When left unset, the raw default value is returned directly without transformation.
{
  const program = new Command();
  program
    .addOption(new Option('-p, --port <number>', 'port number').argParser((v) => parseInt(v, 10)).default('8080'))
    .addArgument(new Argument('[retries]', 'retry count').argParser((v) => parseInt(v, 10)).default('3'));

  program.parse([], { from: 'user' });

  // Unset: raw string defaults are returned, NOT numbers
  assert.strictEqual(
    typeof program.opts().port,
    'string',
    'option default value is not passed through custom coerce parser when omitted',
  );
  assert.strictEqual(program.opts().port, '8080');

  assert.strictEqual(
    typeof program.processedArgs[0],
    'string',
    'argument default value is not passed through custom coerce parser when omitted',
  );
  assert.strictEqual(program.processedArgs[0], '3');

  // Explicit CLI value: custom coerce parser is executed
  const explicitProgram = new Command();
  explicitProgram
    .addOption(new Option('-p, --port <number>', 'port number').argParser((v) => parseInt(v, 10)).default('8080'))
    .addArgument(new Argument('[retries]', 'retry count').argParser((v) => parseInt(v, 10)).default('3'));

  explicitProgram.parse(['-p', '9000', '5'], { from: 'user' });
  assert.strictEqual(typeof explicitProgram.opts().port, 'number');
  assert.strictEqual(explicitProgram.opts().port, 9000);
  assert.strictEqual(typeof explicitProgram.processedArgs[0], 'number');
  assert.strictEqual(explicitProgram.processedArgs[0], 5);
}

// ---------------------------------------------------------------------------
// 5. Contrast: program.opts() populates defaults, while program.args does not
// ---------------------------------------------------------------------------
// This fundamental asymmetry is what misleads developers: program.opts()
// returns defaults for omitted options, but program.args stays empty for
// omitted arguments.
{
  const program = new Command();
  program
    .argument('[env]', 'target environment', 'development')
    .option('-t, --timeout <ms>', 'timeout in ms', '5000');

  program.parse([], { from: 'user' });

  assert.equal(program.opts().timeout, '5000', 'opts() contains option default');
  assert.equal(program.args[0], undefined, 'args[0] does NOT contain argument default');
}

オリジンシーダー

csx-seed