parseArgs tokens compatibility and more high-performance parser
parseArgstokens compatibility and more high-performance parser
util.parseArgs token compatibilityparseArgs can return tokens, that the short options are not in the format I expect. Of course, I recoginize the background of this issue.parseArgs gives the command line args parser a useful util, so the resolution of the options values and the parsing of the tokens are tightly coupled. As a result, Performance is sacrificed. Of course, I recoginize that’s the trade-off.With mitata:
pnpm bench:mitata
> args-tokens@0.0.0 bench:mitata /path/to/projects/args-tokens
> node --expose-gc bench/mitata.js
clk: ~2.87 GHz
cpu: Apple M1 Max
runtime: node 18.19.1 (arm64-darwin)
benchmark avg (min … max) p75 / p99 (min … top 1%)
--------------------------------------------------------------- -------------------------------
util.parseArgs 4.16 µs/iter 4.20 µs █
(4.09 µs … 4.29 µs) 4.28 µs ██ ▅▅▅ ▅
( 1.36 kb … 1.52 kb) 1.37 kb ██▁████▅▅█▅▁██▁▁▅▁█▅█
args-tokens parse (equivalent to util.parseArgs) 1.65 µs/iter 1.66 µs █
(1.61 µs … 1.80 µs) 1.79 µs ▅▃ █▂ ▄
( 1.95 kb … 2.66 kb) 1.97 kb █████▆█▄▃▃▅▃▁▃▃▁▄▁▁▁▂
args-tokens parseArgs 729.56 ns/iter 734.11 ns █
(697.43 ns … 797.08 ns) 774.93 ns ▂█▅▂
( 2.87 kb … 3.54 kb) 3.11 kb ▂▂▃▇▆▅▆████▃▃▄▂▂▂▂▂▁▂
args-tokens resolveArgs 886.78 ns/iter 887.70 ns █
(853.96 ns … 978.89 ns) 957.24 ns █
( 2.51 kb … 2.87 kb) 2.79 kb ▂▃█▃▄▅█▄▃▂▂▃▃▂▂▂▂▂▁▁▁
┌ ┐
util.parseArgs ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 4.16 µs
args-tokens parse (equivalent to util.parseArgs) ┤■■■■■■■■■ 1.65 µs
args-tokens parseArgs ┤ 729.56 ns
args-tokens resolveArgs ┤■■ 886.78 ns
└ ┘
With vitest:
pnpm bench:vitest
> args-tokens@0.0.0 bench:vitest /path/to/projects/args-tokens
> vitest bench --run
Benchmarking is an experimental feature.
Breaking changes might not follow SemVer, please pin Vitest's version when using it.
RUN v3.0.5 /path/to/projects/args-tokens
✓ bench/vitest.bench.js > parse and resolve 1350ms
name hz min max mean p75 p99 p995 p999 rme samples
· util.parseArgs 221,285.36 0.0041 0.2700 0.0045 0.0044 0.0054 0.0063 0.0629 ±0.38% 110643
· args-tokens parse 527,127.11 0.0017 0.2153 0.0019 0.0019 0.0023 0.0027 0.0055 ±0.38% 263564 fastest
✓ bench/vitest.bench.js > parseArgs 1434ms
name hz min max mean p75 p99 p995 p999 rme samples
· node:util 235,217.05 0.0039 0.2665 0.0043 0.0042 0.0048 0.0058 0.0139 ±0.43% 117609
· args-tokens 1,307,135.24 0.0006 0.1737 0.0008 0.0008 0.0009 0.0010 0.0016 ±0.43% 653568 fastest
BENCH Summary
args-tokens parse - bench/vitest.bench.js > parse and resolve
2.38x faster than util.parseArgs
args-tokens - bench/vitest.bench.js > parseArgs
5.56x faster than node:util
parseArgs tokens?The token output for the short option -x=v is different:
import { parseArgs as parseArgsNode } from 'node:util'
import { parseArgs } from 'args-tokens'
// Node.js parseArgs tokens
const { tokens: tokensNode } = parseArgsNode({
allowPositionals: true,
strict: false,
args: ['-a=1'],
tokens: true
})
console.log(tokensNode)
// ({
// kind: 'option',
// name: 'a',
// rawName: '-a',
// index: 0,
// value: undefined,
// inlineValue: undefined
// },
// {
// kind: 'option',
// name: '=',
// rawName: '-=',
// index: 0,
// value: undefined,
// inlineValue: undefined
// },
// {
// kind: 'option',
// name: '1',
// rawName: '-1',
// index: 0,
// value: undefined,
// inlineValue: undefined
// })
// ]
// args-tokens parseArgs tokens
const tokens = parseArgs(['-a=1'])
console.log(tokens)
// [
// {
// kind: 'option',
// name: 'a',
// rawName: '-a',
// index: 0,
// value: undefined,
// inlineValue: undefined
// },
// { kind: 'option', index: 0, value: '1', inlineValue: true }
// ]
# npm
npm install --save args-tokens
## yarn
yarn add args-tokens
## pnpm
pnpm add args-tokens
deno add jsr:@kazupon/args-tokens
bun add args-tokens
parseArgs will transform arguments into tokens. This function is useful if you want to analyze arguments yourself based on the tokens. It’s faster than parseArgs of node:util because it only focuses on token transformation.
import { parseArgs } from 'args-tokens' // for Node.js and Bun
// import { parseArgs } from 'jsr:@kazupon/args-tokens' // for Deno
const tokens = parseArgs(['--foo', 'bar', '-x', '--bar=baz'])
// do something with using tokens
// ...
console.log('tokens:', tokens)
resolveArgs is a useful function when you want to resolve values from the tokens obtained by parseArgs.
import { parseArgs, resolveArgs } from 'args-tokens' // for Node.js and Bun
// import { parseArgs, resolveArgs } from 'jsr:@kazupon/args-tokens' // for Deno
const args = ['dev', '-p=9131', '--host=example.com', '--mode=production']
const tokens = parseArgs(args)
const { values, positionals } = resolveArgs(
{
help: {
type: 'boolean',
short: 'h'
},
version: {
type: 'boolean',
short: 'v'
},
port: {
type: 'number',
short: 'p',
default: 8080
},
mode: {
type: 'string',
short: 'm'
},
host: {
type: 'string',
short: 'o',
required: true
}
},
tokens
)
console.log('values:', values)
console.log('positionals:', positionals)
Using the parse you can transform the arguments into tokens and resolve the argument values once:
import { parse } from 'args-tokens' // for Node.js and Bun
// import { parse } from 'jsr:@kazupon/args-tokens' // for Deno
const args = ['dev', '-p=9131', '--host=example.com', '--mode=production']
const { values, positionals } = parse(args, {
options: {
help: {
type: 'boolean',
short: 'h'
},
version: {
type: 'boolean',
short: 'v'
},
port: {
type: 'number',
short: 'p',
default: 8080
},
mode: {
type: 'string',
short: 'm'
},
host: {
type: 'string',
short: 'o',
required: true
}
}
})
console.log('values:', values)
console.log('positionals:', positionals)
parseArgs tokens compatibleIf you want to use the same short options tokens as returned Node.js parseArgs, you can use allowCompatible parse option on parseArgs:
import { parseArgs as parseArgsNode } from 'node:util'
import { parseArgs } from 'args-tokens'
import { deepStrictEqual } from 'node:assert'
const args = ['-a=1', '2']
// Node.js parseArgs tokens
const { tokens: tokensNode } = parseArgsNode({
allowPositionals: true,
strict: false,
args,
tokens: true
})
// args-tokens parseArgs tokens
const tokens = parseArgs(['-a=1'], { allowCompatible: true }) // add `allowCompatible` option
// validate
deepStrictEqual(tokensNode, tokens)
ArgSchema ReferenceThe ArgSchema interface defines the configuration for command-line arguments. This schema is similar to Node.js util.parseArgs but with extended features.
type (required)Type of the argument value:
'string': Text value (default if not specified)'boolean': True/false flag (can be negatable with --no- prefix)'number': Numeric value (parsed as integer or float)'enum': One of predefined string values (requires choices property)'positional': Non-option argument by position'custom': Custom parsing with user-defined parse function{
name: { type: 'string' }, // --name value
verbose: { type: 'boolean' }, // --verbose or --no-verbose
port: { type: 'number' }, // --port 3000
level: { type: 'enum', choices: ['debug', 'info'] },
file: { type: 'positional' }, // first positional arg
config: { type: 'custom', parse: JSON.parse }
}
short (optional)Single character alias for the long option name. Allows users to use -x instead of --extended-option.
{
verbose: {
type: 'boolean',
short: 'v' // Enables both --verbose and -v
},
port: {
type: 'number',
short: 'p' // Enables both --port 3000 and -p 3000
}
}
description (optional)Human-readable description used for help text generation and documentation.
{
config: {
type: 'string',
description: 'Path to configuration file'
},
timeout: {
type: 'number',
description: 'Request timeout in milliseconds'
}
}
required (optional)Marks the argument as required. When true, the argument must be provided or an ArgResolveError will be thrown.
{
input: {
type: 'string',
required: true, // Must be provided: --input file.txt
description: 'Input file path'
},
source: {
type: 'positional',
required: true // First positional argument must exist
}
}
multiple (optional)Allows the argument to accept multiple values. The resolved value becomes an array.
--tag foo --tag bar){
tags: {
type: 'string',
multiple: true, // --tags foo --tags bar → ['foo', 'bar']
description: 'Tags to apply'
},
files: {
type: 'positional',
multiple: true // Collects all remaining positional args
}
}
negatable (optional)Enables negation for boolean arguments using --no- prefix. Only applicable to type: 'boolean'.
{
color: {
type: 'boolean',
negatable: true,
default: true,
description: 'Enable colorized output'
}
// Usage: --color (true), --no-color (false)
}
choices (optional)Array of allowed string values for enum-type arguments. Required when type: 'enum'.
{
logLevel: {
type: 'enum',
choices: ['debug', 'info', 'warn', 'error'],
default: 'info',
description: 'Logging verbosity level'
},
format: {
type: 'enum',
choices: ['json', 'yaml', 'toml'],
description: 'Output format'
}
}
default (optional)Default value used when the argument is not provided. The type must match the argument’s type property.
{
host: {
type: 'string',
default: 'localhost' // string default
},
verbose: {
type: 'boolean',
default: false // boolean default
},
port: {
type: 'number',
default: 8080 // number default
},
level: {
type: 'enum',
choices: ['low', 'high'],
default: 'low' // must be in choices
}
}
toKebab (optional)Converts the argument name from camelCase to kebab-case for CLI usage. A property like maxCount becomes available as --max-count.
{
maxRetries: {
type: 'number',
toKebab: true, // Accessible as --max-retries
description: 'Maximum retry attempts'
},
enableLogging: {
type: 'boolean',
toKebab: true // Accessible as --enable-logging
}
}
parse (optional)Custom parsing function for type: 'custom' arguments. Required when type: 'custom'. Should throw an Error if parsing fails.
{
config: {
type: 'custom',
parse: (value) => {
try {
return JSON.parse(value) // Parse JSON config
} catch {
throw new Error('Invalid JSON configuration')
}
},
description: 'JSON configuration object'
},
date: {
type: 'custom',
parse: (value) => {
const date = new Date(value)
if (isNaN(date.getTime())) {
throw new Error('Invalid date format')
}
return date
}
}
}
conflicts (optional)Specifies other options that cannot be used together with this option. When conflicting options are provided together, an ArgResolveError will be thrown.
Conflicts only need to be defined on one side - if option A defines a conflict with option B, the conflict is automatically detected when both are used.
{
// Single conflict
port: {
type: 'number',
conflicts: 'socket' // Cannot use --port with --socket
},
socket: {
type: 'string'
// No need to define conflicts: 'port' here
}
}
// Multiple conflicts (mutually exclusive options)
{
tcp: {
type: 'number',
conflicts: ['udp', 'unix'] // Cannot use with --udp or --unix
},
udp: {
type: 'number',
conflicts: ['tcp', 'unix']
},
unix: {
type: 'string',
conflicts: ['tcp', 'udp']
}
}
If you are interested in contributing to args-tokens, I highly recommend checking out the contributing guidelines here. You’ll find all the relevant information such as how to make a PR, how to setup development) etc., there.
This project is inspired by:
util.parseArgs, created by Node.js contributors and OpenJS Foundationpkgjs/parseargs, created by Node.js CLI package maintainers and Node.js community.The development of Gunish is supported by my OSS sponsors!
We use cookies
We use cookies to analyze traffic and improve your experience. You can accept or reject analytics cookies.