---
# System prepended metadata

title: Untitled

---

### Problem Statement(s)

1. There's a difference between an option/flag being declared & it's definition
2. "options" nomenclature was overloaded
3. Inconsistant API (ex. arguments accepted, & values returned, could be a String|Array)
4. Throw if arguments passed don't pass validation/parsing (ie. exception handling)

### Success Criteria

- npm version 9 will adopt this for parsing CLI arguments
- npm version 8 can warn/polyfill to deprecate CLI arg parsing that would differ from this
- jackspeak is reduced to merely a set of argument requirements, types, and validation

Key requirement: easier than `for (const arg of process.mainArgs) { switch (arg) { ... } }`



### Add `process.mainArgs`

This can be done even if the rest of it changes completely or is abandoned, it is unanimously desired by all process.argv parsers right now.  Please add it asap.

Implementation:

```javascript
process.mainArgs = process.argv.slice(process._exec ? 1 : 2)
```

```javascript
#!/usr/bin/env node
// foo.js
console.log(process.mainArgs)
```

```
# process.argv.slice(2)
node foo.js a b c
// [ 'a', 'b', 'c' ]

# process.argv.slice(1)
node -p 'process.mainArgs' a b c
// ['a', 'b', 'c']
```

Done, do it, ship it yesterday.

### Isaac's List Of ~~Annoying~~ Important Questions

- Is `cmd --foo=bar baz` the same as `cmd baz --foo=bar`?
    - Yes, if `withValue: ['foo']`, otherwise no
- Does the parser execute a function?
    - no
- Does the parser execute one of several functions, depending on input?
    - no
- Can subcommands take options that are distinct from the main command?
    - no (this might be a problem? at least it's a more definitive "opinion")
- Does it output generated help when no options match?
    - no
- Does it generated short usage?  Like: `usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1] [file ...]`
    - no (no usage/help at all)
- Does the user provide the long usage text?  For each option?  For the whole command?
    - no
- Do subcommands (if implemented) have their own usage output?
    - no
- Does usage print if the user runs `cmd --help`?
    - no
- Does it set `process.exitCode`?
    - no
- Does usage print to stderr or stdout?
    - N/A
- Does it check types?  (Say, specify that an option is a boolean, number, etc.)
    - no
- Can an option have more than one type?  (string or false, for example)
    - no
- Can the user define a type?  (Say, `type: path` to call `path.resolve()` on the argument.)
    - no
- Does a `--foo=0o22` mean 0, 22, 18, or "0o22"?
    - `"0o22"`
- Does it coerce types?
    - no
- Does `--no-foo` coerce to `--foo=false`?  For all flags?  Only boolean flags?
    - no, it sets `{args:{'no-foo': true}}`
- Is `--foo` the same as `--foo=true`?  Only for known booleans?  Only at the end?
    - no, `--foo` is the same as `--foo=`
- Does it read environment variables?  Ie, is `FOO=1 cmd` the same as `cmd --foo=1`?
    - no
- Do unknown arguments raise an error?  Are they parsed?  Are they treated as positional arguments?
    - no, they are parsed, not treated as positionals
- Does `--` signal the end of flags/options?
    - **open question**
    - If `--` signals the end, is `--` included as a positional?  is `program -- foo` the same as `program foo`?  Are both `{positionals:['foo']}`, or is the first one `{positionals:['--', 'foo']}`?
- Does the API specify whether a `--` was present/relevant?
    - no
- Is `-foo` the same as `--foo`?
    - yes <-- ! kind of a blocker for shortopts !
    - Recommend: "No, -foo is shortopts form of --f --o --o" (assuming none are defined, or withValues)
- Is `---foo` the same as `--foo`?
    - yes (?!)
    - other options:
        1. `---foo` is a key of `-foo`
        2. this is a positional
        3. throw (we can't parse it)
    - This gets ridiculous:   `node prog.js --------------------------------foo`?
- Is `-` a positional?  ie, `bash some-test.sh | tap -`
    - ??
- Any support for posix-style shortopts?  How are they defined?
    - **open question** 
    - Is `-F` the same as `--F`?
        - yes
    - Is `-AB` the same as `-A -B`?  (Ever?  Sometimes?  Always?)
        - no, never
    - Is `-AB` the same as `-BA`?  (Ever?  Sometimes?  Always?)
        - no, never
    - Is `-Bvalue` the same as `-B value`?  (Ever?  Sometimes?  Always?)
        - `rm -rf`
        - `ls -laF`
        - `tap -bcRspec` -> `tap --bail --color --reporter=spec`
        - `tap -bxc` === `tap -xbc`, order is irrelevant
        - `tap -bxc` -> `tap --bail --x --color`
            - in other words:
                ```javascript
                parseArgs(['-bxc'], {
                  short: { // this should throw if a key has length >1?
                    b: 'bail',
                    c: 'color',
                  },
                })
                {
                  args: {
                    'bail': true,
                    'x': true,
                    'color': true,
                  },
                  values: {
                    bail: [undefined],
                    x: [undefined],
                    color: [undefined],
                  },
                }
                ```
        - pseudocode:
            - position = 0
            - while position < string.length
                - if (string[position] is a shortopt)
                    - add expanded form
                    - if it takes a value
                        - value is string.slice(position), break
                        - break
                    - else
                        - position++
                        - continue
                - else
                    - treat it as if it was `{short: { x: 'x' }}`, ie, expand as if it was `--bail --x`
                    - value is "", key is the single char
                    - `args.x = true`
        - 
    - Is `-Bvalue` the same as `-B=value`?  (Ever?  Sometimes?  Always?)
- Any support for "set this multiple times for more thingness"?  Eg, `foo -v` for some verbosity, `foo -vvvv` for lots of verbosity?
    - **open question** - yes, if shortopts
    - `{ args, values } = parseArgs(['-vvvv'], { short: { v: 'verbose' }, multiples: ['verbose']})`
        `const { values: { verbose: { length: verbosity }}} = { values: { 'verbose': [undefined, undefined, undefined, undefined ] }}`, I would look at `values.verbose.length` for level of verbosity.
        `args.verbose = true`


Suggestion:  Add a strict mode to throw if unknown args are encountered

```javascript
const result = parseArgs(args, { strict: true })
```


### Potential Solution(s)

1. Split up the existence of an argument from it's value
2. Change names & corresponding documentation
3. Standardize on Arrays
4. Don't coerce values so that there is nothing to validate

```js

const argv = ['--foo=a', '--', '--foo=b', '--bar=baz', '--', 'xyz']
const { args, values, positionals } = parseArgs(argv)
expect({
  args: {
    foo: true,
  },
  values: {
    foo: ['a'],
  },
  positionals: [
    '--foo=b',
    '--bar=baz',
    '--',
    'xyz'
  ]
})

npm test --loglevel=silly -- --snapshot=true



const argv = ['--foo=a', 'b', '--foo', 'c']
const { args, values, positionals } = parseArgs(argv)
args // { foo: true }
values // { foo: [''] }

const argv = ['a', '--foo']
const { args, values, positionals } = parseArgs(argv, { withValue: ['foo'] })

// '--foo' === '--foo=' unless followed by positional and withValue:['foo']

const argv = ['--foo', 'a', 'b', '--foo', 'c']
const { args, values, positionals } = parseArgs(argv, { withValue: ['foo'] })
args // { foo: true }
values // { foo: ['c'] } 
positionals // []


const argv = ['--foo', 'a', '--foo', 'b']
const { args, values, positionals } = parseArgs(argv, { withValue: ['foo'], multiples: ['foo'] })
args // { foo: true }
values // { foo: ['a', 'b'] } 
positionals // []

const argv = ['--foo', 'a', 'b', '--foo', 'c']
const { args, values, positionals } = parseArgs(argv, { withValue: ['foo'], multiples: ['foo'] })
args // { foo: true, bar: true }
values // { foo: ['a', 'c'] } 
positionals // ['b']

// node.js --a --b --c
const { args, values, positionals } = util.parseArgs()
args 
{
  a: true,
  b: true,
  c: true
}
values 
{
  a: undefined,
  b: undefined,
  c: undefined
}
positionals
[]

// if process._exec, default is process.argv.slice(1)
node -p 'require("util").parseArgs()' foo --bar=baz
print:
{
  args: {
    bar: true,
  },
  values: {
    bar: ['baz']
  },
  positionals: [
    'foo'
  ]
}

node program.js <top-level opts> subcommand <subcommand opts>



```

```js
// script.js
// called via `node script.js --foo`
const argv = util.parseArgs()
argv.foo // true <- because it's the defaut value

// called via `node script.js --foo=true`
argv.foo // true <- Boolean: because we defined it

// called via `node script.js --foo=hello`
argv.foo // 'hello' <- String: because we defined it

// called via `node script.js`
argv.foo // ... undefined?

// current 
const { options, positionals } = util.parseArgs()

// new idea...
const { args, values, positionals } = util.parseArgs()

args // declarations
values // definitions

// --------------------------------------------------
//const { parseArgs } = require('util')
// --------------------------------------------------


const argv = ['--foo=false', '-foo=true']

// new
const { args, values, positionals } = parseArgs(argv, { multiples: ['foo']})
args.foo // true // return boolean
values.foo // ['false', 'true'] // return Array
positionals // Array

// old
const { options, positionals } = parseArgs(argv)
options.foo // false <- do logic...



// --------------------------------------------------
const argv = ['--foo=true']
const { args, values } = parseArgs(argv)
args.foo // true
values.foo // 'true'
// --------------------------------------------------
const argv = ['--foo=bar', '--foo', 'baz']
const { args, values, positionals } = parseArgs(argv)
args.foo // true
values.foo // 'bar'
positonals // ['baz']
// --------------------------------------------------
const argv = ['--foo=bar', '--foo', 'baz']
const { args, values, positionals } = parseArgs(argv)
args.foo // true
values.foo // 'bar'
positionals // ['baz']
// --------------------------------------------------
const argv = ['--foo=bar', '--foo', 'baz']
const { values } = parseArgs(argv, { withValue: ['foo'] })
values.foo // 'baz'
// --------------------------------------------------
const argv = ['--foo=bar', '--foo', 'baz']
const { values } = parseArgs(argv, { multiples: ['foo'] })
values.foo // ['bar', undefined]
// --------------------------------------------------
const argv = ['--file=bar', '--foo', 'baz']
const { values } = parseArgs(argv, { withValue: ['foo'], multiples: ['foo'] })
values.foo // ['bar', 'baz']
// --------------------------------------------------
const argv = ['--foo=bar', '--foo', 'baz']
const { values } = parseArgs(argv, { withValue: ['foo'], multiples: ['foo'] })
values.foo // ['bar', 'baz']
// --------------------------------------------------
// Example of the 99%...
const argv = ['--foo']
const { args } = parseArgs(argv)
args.foo // true
args.bar // undefined
// --------------------------------------------------


npm install express graphql

/// 
```
