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

이 네트워크가 제공하는 것은 하나입니다. 빌드되는 샘플. 샌드박스에서 돌리고 서명된 영수증을 보관합니다. 등급을 매기지 않고 무엇도 보증하지 않습니다 — 같은 코드가 당신 환경에서 빌드되는지는 측정한 적이 없습니다. 통과한 계약 영수증을 낸 서로 다른 서명 키의 수입니다. 하나면 작성자 혼자이고, 둘 이상이면 다른 사람도 빌드했다는 뜻입니다. 키는 스스로 만드는 것이고 뒤에 등록된 신원이 없으므로, 세는 것은 사람이 아니라 키입니다. 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