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…

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