Skip to content

minimal argument parsing - bring your own validation™️

License

Notifications You must be signed in to change notification settings

darcyclarke/minargs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

minargs

minargs is an argument parser with minimal configuration & assumptions. Argument parsing can take many shapes but the explicit goals of this library are as follows:

Goals

  • no usage
  • no validation
  • no types or type cohersion
  • no regular expressions
  • no strictness
  • no dependencies
  • no vulnerabilities
  • minimal assumptions
  • minimal configuration
  • 100% test coverage

Mantras

  • Bring Your Own Usage™️
  • Bring Your Own Validation™️

Installation

npm install minargs

miargs([argv][, options])

  • argv (Array)
    • Default: process.argv
    • The argument strings to parse

Options

  • known (Array)
    • Default: none
    • ...
  • aliases (Object)
    • Default: none
    • ...
  • multiples (Array)
    • Default: none
    • ...
  • positionalValues (Array)
    • Default: none
    • ...

Returned Values

{
  args: {},
  values: {},
  positionals: [],
  remainder: [],
  process: []
}

args

values

  • Returned values are a string by default
  • Returned values are an array of strings if the corresponding arg was defined in multiples
  • Examples:
    • --foo=bar will return undefined with no configuration
    • --foo=bar will return "bar" when 'foo'
    • --foo bar will return "bar" when 'foo' & positionalValues is true
      • Notably, bar is treated as a positional & returned in positionals if positionalValues is false

positionals

remainder

  • Returned value is the remaining array of results from process.argv after the first bare --
  • Notably, this is useful for recursively parsing arguments or passing along args to other processes

process

  • Returned value is an array associated with process args split from process.argv at the beginning of parsing if the default process.argv is being parsed
    • Notably, mainArgs() is used to determine these values
  • Returned value will be an empty array if an explicit array to parse was passed to minargs()

Examples Usage

Basic

$ basic.js - --foo=bar -- --baz
#!/usr/bin/env node

// basic.js
const { minargs } = require('minargs')
const { args, values, positionals, remainder, process } = minargs()

args          // { "foo": true }
values        // { "foo": "bar" }
positionals   // ["-"]
remainder     // ["--baz"]
process       // [ "/path/to/node", "/path/to/program/cli.js" ]

Handling existence

$ exists.js --foo
#!/usr/bin/env node

// exists.js
const { minargs } = require('minargs')
const { args } = minargs()
if (args.foo) {
  // ...
}

Handling unknown args

$ unknown.js --baz
#!/usr/bin/env node

// unknown.js
const { minargs } = require('minargs')
const { args } = minargs()
const known = ['foo', 'bar']
const unknown = Object.keys(args).filter(arg => !known.includes(arg))
if (unknown.length > 0) {
  console.error('unknown flags passed:', unknown)
  // stop the process & set an `exitCode` appropriately
  process.exit(1)
}

// ...

Handling validation

$ validate.js --num=1337
#!/usr/bin/env node

// validate.js
const { minargs } = require('minargs')
const { args, values } = minargs()
const usage = {
  num: {
    validate: (value) => {
      if (!isNaN(value)) {
        return Number(value)
      }
      throw Error('Validation error!')
    }
  },
  force: {
    validate: (value) => {
      if (~['true','false'].indexOf(value.toLowerCase())) {
        return Boolean(value)
      }
      throw Error('Validation error!')
    }
  }
}

Object.keys(args).filter(name => args[name]).map(name => {
  usage[name].validate(values[name])
})

// ...

Handling recursive parsing

$ recursive-parse.js
#!/usr/bin/env node

// recursive-parse.js
const { minargs } = require('minargs')

function minargsRecursiveSyncArray(argv, arr) {
  arr = arr || []
  const result = minargs(argv)
  arr.push(result)
  if (result.remainder.length > 0) {
    minargsRecursiveSyncArray(result.remainder, arr)
  }
  return arr
}

minargsRecursiveSyncArray(process.argv) // array of results

function minargsRecursiveSyncFlat(argv, obj) {
  const result = minargs(argv)
  obj = obj || { args: {}, values: {}, positionals: [] }
  obj.args = { ...obj.args, ...result.args }
  obj.values = { ...obj.values, ...result.values }
  obj.positionals = obj.positionals.concat(result.positionals)
  if (result.remainder.length > 0) {
    minargsRecursiveSyncFlat(result.remainder, obj)
  }
  return obj
}

minargsRecursiveSyncFlat(process.argv) // flattened results object

// ...

Handling sub process

$ mkdir.js ./path/to/new/dir/ --force --verbose --parents
#!/usr/bin/env node

// mkdir.js
const knownOpts = ['force']
const { flags, positionals } = parseArgs({ withValue: knownOpts })
const args = Object.keys(flags).filter(f => knownOpts[f])
const cmd = (flags.force) ? 'sudo mkdir' : 'mkdir'

process('child_process').spawnSync(cmd, [...args, ...positionals])

Handling robust options & usage

$ usage.js -h
#!/usr/bin/env node

// usage.js
const { minargs } = require('minargs')
const usage = {
  help: {
    short: 'h',
    usage: 'cli --help',
    description: 'Print usage information'
  }
  force: {
    short: 'f',
    usage: 'cli --force',
    description: 'Run this cli tool with no restrictions'
  }
}
const opts = {
  known: Object.keys(usage),
  multiple: Object.keys(usage).filter(arg => usage[arg].multiple),
  alias: Object.keys(usage).filter(arg => usage[arg].short).reduce((o, k) => {
    o[usage[k].short] = k
    return o
  }, {})
}
const { args } = minargs(opts)

if (args.help) {
  Object.keys(usage).map(name => {
    let short = usage[name].short ? `-${usage[name].short}, ` : ''
    let row = [`  ${short}--${name}`, usage[name].usage, usage[name].description]
    console.log.apply(this, fill(columns, row))
  })
}

/// ...

F.A.Q.

Why isn't strictness supported?

  • Strictness is a function of usage. By default, minargs does not assume that any known or "unknown" arguments should or shouldn't be allowed. Usage examples above show how you can quickly & easily utilize minargs as the backbone for an application which does enforce strictness though.

Are shorts supported?

  • Yes.
  • -a & -aCdeFg are supported
  • -a=b will capture & return "b" as a value
  • -a b will capture & return "b" as a value if positionalValues is true

What is an alias?

  • An alias can be any other string that maps to the canonical option; this includes single characters which will map shorts to a long-form (ex. alias: { f: foo } will parse -f as { args: { 'foo': true } })

Is cmd --foo=bar baz the same as cmd baz --foo=bar?

  • Yes.

Is value validation or type cohersion supported?

  • No.

Are usage errors supported?

  • No.

Does --no-foo coerce to --foo=false?

  • No.
  • It would set { args: { 'no-foo': true } }

Is --foo the same as --foo=true?

  • No.

Are environment variables supported?

  • No.

Does -- signal the end of flags/options?

  • Yes.
  • Any arguments following a bare -- definition will be returned in remainder.

Is a value stored to represent the existence of --?

  • No.
  • The only way to determine if -- was present & there were arguments passed afterward is to check the value of remainder

Is - a positional?

  • Yes.
  • A bare - is treated as & returned in positionals

Is -bar the same as --bar?

Is ---foo the same as --foo?

  • No.
  • ---foo returns { args: '-foo': true }
  • --foo returns { args: { 'foo': true }

Is foo=bar a positional?

  • Yes.

Are negative numbers supported as positional values?

  • No.
  • --number -2 will be parsed as { args: { 'number': true, '2': true } }
  • You will have to use explicit value setting to make this association (ex. --number=-2)

CLI

minargs has a companion CLI library: @minargs/cli

Installation

# install package globally & call bin...
npm install minargs -g && minargs

# or, use `npx` to install & call bin...
npx minargs

Usage

minargs "<args>" [<options>]

Options & more....

To learn more, check out the @minargs/cli GitHub repository or package page

About

minimal argument parsing - bring your own validation™️

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published