parseArgs
tokens compatibility and more high-performance parser
- β High performance
- β
util.parseArgs
token compatibility - β ES Modules and modern JavaScript
- β Type safe
- β Zero dependencies
- β Universal runtime
- Although Node.js
parseArgs
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
> [email protected] 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
> [email protected] 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
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)
If 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)
The ArgSchema
interface defines the configuration for command-line arguments. This schema is similar to Node.js util.parseArgs
but with extended features.
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 (requireschoices
property)'positional'
: Non-option argument by position'custom'
: Custom parsing with user-definedparse
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 }
}
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
}
}
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'
}
}
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
}
}
Allows the argument to accept multiple values. The resolved value becomes an array.
- For options: can be specified multiple times (
--tag foo --tag bar
) - For positional: collects remaining positional arguments
{
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
}
}
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)
}
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 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
}
}
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
}
}
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
}
}
}
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!