Dismiss
  • Scroll up
  • Toggle Theme
  • View as Mobile

@davidwells/dx-args

@davidwells/dx-args is a forgiving argument parser for CLIs that should keep working when people type imperfect commands.

It wraps oparser with argv normalization, glob grouping, single-dash long-option recovery, and a predictable result shape for CLI tooling.

Why it matters

CLI tools are more useful when they accept the way people actually type. Forgiving parsing reduces brittle command syntax and makes automation tools easier to use by humans and agents.

README excerpt

Install

npm install @davidwells/dx-args

API

const {
  dxParse,
  getGlobGroupsFromArgs,
  splitOutsideQuotes,
} = require('@davidwells/dx-args')

dxParse(argv, opts)

Parses an argv array and returns a ParseResult.

const result = dxParse(['--stage', 'prod', 'cool', '=', 'true'])

console.log(result.mergedOptions)
// { stage: 'prod', cool: true }

getGlobGroupsFromArgs(argv, opts)

Extracts file and glob-like values into grouped collections.

const result = getGlobGroupsFromArgs(
  ['--files', 'README.md', 'docs/**/*.md'],
  { globKeys: ['files'] }
)

console.log(result.globGroups)
// [{ key: 'files', rawKey: '--files', values: ['README.md', 'docs/**/*.md'] }]

splitOutsideQuotes(input)

Splits a normalized option string while preserving quoted strings, objects, arrays, and key = value forms.

splitOutsideQuotes('name = "David Wells" config={ enabled: true }')
// ['name="David Wells"', 'config={ enabled: true }']

Accepted Input Forms

InputmergedOptions
--stage prod{ stage: 'prod' }
--stage=prod{ stage: 'prod' }
stage = prod{ stage: 'prod' }
stage=prod{ stage: 'prod' }
-stage prod{ stage: 'prod' }
-f README.md{ f: 'README.md' }
--no-cache{ cache: false }
--count 3{ count: 3 }
--items=[1,2,3]{ items: [1, 2, 3] }

File and glob-like values are also exposed through globGroups.

const result = dxParse(['README.md', 'docs/**/*.md', '--stage', 'dev'])

console.log(result.globGroups)
// [{ key: '', rawKey: '', values: ['README.md', 'docs/**/*.md'] }]

console.log(result.mergedOptions)
// { stage: 'dev' }

Glob Groups

@davidwells/dx-args groups file-looking and glob-looking arguments so CLIs can handle files separately from ordinary options.

dxParse(['--files', 'README.md', 'docs/**/*.md']).globGroups
// [{ key: 'files', rawKey: '--files', values: ['README.md', 'docs/**/*.md'] }]

Bare file and glob values use an empty key:

dxParse(['README.md', 'docs/**/*.md']).globGroups
// [{ key: '', rawKey: '', values: ['README.md', 'docs/**/*.md'] }]

Custom glob keys are configurable:

dxParse(['--ignore', 'dist/**/*.md'], {
  globKeys: ['files', 'file', 'path', 'ignore']
}).globGroups
// [{ key: 'ignore', rawKey: '--ignore', values: ['dist/**/*.md'] }]

Shell-expanded file lists are intentionally not treated as leading commands:

const result = dxParse(['README.md', 'NOTES.md', 'build', '=', 'false'])

console.log(result.globGroups)
// [{ key: '', rawKey: '', values: ['README.md', 'NOTES.md'] }]

console.log(result.mergedOptions)
// { build: false }

Values containing node_modules/ are currently filtered from glob groups.

Single Dash Policy

Multi-character single-dash options are treated as forgiving long options:

dxParse(['-stage', 'prod']).mergedOptions
// { stage: 'prod' }

That also works when the value looks like a file:

dxParse(['-config', 'md.config.js']).mergedOptions
// { config: 'md.config.js' }

Separate short flags still work:

dxParse(['-l', '-a', '-h']).mergedOptions
// { l: true, a: true, h: true }

Short clusters are opt-in because they conflict with forgiving long option recovery:

dxParse(['-abc']).mergedOptions
// { abc: true }

dxParse(['-abc'], {
  allowShortClusters: true,
  shortFlags: ['a', 'b', 'c']
}).mergedOptions
// { a: true, b: true, c: true }