npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@davidwells/dx-args

v0.2.2

Published

A forgiving argv parser for CLIs that accepts loose key/value syntax, globs, and typo-tolerant long flags.

Readme

@davidwells/dx-args

A forgiving argv parser for CLIs that should keep working when someone types a slightly imperfect command.

@davidwells/dx-args keeps oparser as the value parser. It adds argv normalization, file/glob grouping, single-dash long option recovery, and a predictable result shape around it.

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

const result = dxParse(['-files', 'README.md', '-dry'])

console.log(result.mergedOptions)
// { files: 'README.md', dry: true }

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

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

| Input | mergedOptions | | --- | --- | | --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' }

Result Shape

interface GlobGroup {
  key: string
  rawKey: string
  values: Array<string | RegExp>
}

interface ParseResult {
  rawArgv: string
  leadingCommands: string[]
  globGroups: GlobGroup[]
  extraParse: Record<string, unknown>
  mriOptionsOriginal: Record<string, unknown>
  mriOptionsClean: Record<string, unknown>
  mriDiff: boolean
  yargsParsed: string
  mergedOptions: Record<string, unknown>
}

Most callers should use:

  • mergedOptions for parsed options.
  • globGroups for file and glob-aware workflows.
  • leadingCommands when a CLI has command-like leading words before options.

The remaining fields are diagnostic:

  • extraParse is the oparser-derived parse result after argv normalization.
  • mriOptionsOriginal is the raw mri result.
  • mriOptionsClean is mri after cleanup of artifacts such as letters from -stage.
  • mriDiff is true when cleanup changed the mri result.
  • yargsParsed is not active; it is currently a compatibility/debug field.

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.

Duplicate Values

Duplicate flags are last-write-wins by default.

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

Accumulation is explicit:

dxParse(['--tag', 'one', '--tag', 'two'], {
  accumulate: ['tag']
}).mergedOptions
// { tag: ['one', 'two'] }

The parser currently accepts three option names for accumulation:

  • accumulate
  • accumulateFlags
  • arrayKeys

Prefer accumulate in new code.

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 }

Markdown Magic Integration

Markdown Magic uses dxParse as its CLI parse boundary.

Markdown Magic maps parser output like this:

  • file, files, path, and bare file groups become config.files.
  • ignore groups become config.ignore.
  • Other mergedOptions become Markdown Magic config overrides.
  • output and outputDir normalization happens in Markdown Magic, not @davidwells/dx-args.

Example Markdown Magic commands:

md-magic -files README.md -config md.config.js -dry
md-magic --path "docs/**/*.md" --ignore "dist/**/*.md"

Debugging

The package includes a small CLI that prints the full parse result:

dx-args -files README.md -config md.config.js -dry

When debugging, check:

  • mergedOptions
  • globGroups
  • mriOptionsOriginal
  • mriOptionsClean
  • mriDiff

Known Gaps

  • Space-separated list capture like --numbers 1 2 3 --strings a b c is not supported yet.
  • Real shell argv with quotes should be tested as arrays, not only string-split fixtures.
  • Diagnostic fields may move under a debug or trace field before a stable 1.0.0.
  • node_modules/ filtering is currently built into glob grouping.
  • -- rest passthrough should be specified and tested before it is documented as public API.