args-tokens 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -20
- package/lib/parse.d.ts +3 -2
- package/lib/parse.js +3 -2
- package/lib/resolver.d.ts +7 -0
- package/lib/resolver.js +42 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,10 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
[![Version][npm-version-src]][npm-version-href]
|
|
4
4
|
[![JSR][jsr-src]][jsr-href]
|
|
5
|
+
[![InstallSize][install-size-src]][install-size-src]
|
|
5
6
|
[![CI][ci-src]][ci-href]
|
|
6
7
|
|
|
7
8
|
> [`parseArgs` tokens](https://nodejs.org/api/util.html#parseargs-tokens) compatibility and more high-performance parser
|
|
8
9
|
|
|
10
|
+
## ✨ Features
|
|
11
|
+
|
|
12
|
+
- ✅ High performance parser and resolver
|
|
13
|
+
- ✅ `util.parseArgs` token compatibility
|
|
14
|
+
- ✅ Type safe argument values
|
|
15
|
+
- ✅ ES Modules distribution library
|
|
16
|
+
- ✅ Support multi runtime: Browser, Node.js, Deno, Bun
|
|
17
|
+
|
|
9
18
|
## 🐱 Motivation
|
|
10
19
|
|
|
11
20
|
- Although Node.js [`parseArgs`](https://nodejs.org/api/util.html#utilparseargsconfig) can return tokens, that the short options are not in the format I expect. Of course, I recoginize the background of [this issue](https://github.com/pkgjs/parseargs/issues/78).
|
|
@@ -19,26 +28,36 @@ With [mitata](https://github.com/evanwashere/mitata):
|
|
|
19
28
|
pnpm bench:mitata
|
|
20
29
|
|
|
21
30
|
> args-tokens@0.0.0 bench:mitata /path/to/projects/args-tokens
|
|
22
|
-
> node --expose-gc bench/
|
|
31
|
+
> node --expose-gc bench/mitata.js
|
|
23
32
|
|
|
24
33
|
clk: ~2.87 GHz
|
|
25
34
|
cpu: Apple M1 Max
|
|
26
35
|
runtime: node 18.19.1 (arm64-darwin)
|
|
27
36
|
|
|
28
|
-
benchmark
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
args-tokens parseArgs
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
benchmark avg (min … max) p75 / p99 (min … top 1%)
|
|
38
|
+
--------------------------------------------------------------- -------------------------------
|
|
39
|
+
util.parseArgs 4.16 µs/iter 4.20 µs █
|
|
40
|
+
(4.09 µs … 4.29 µs) 4.28 µs ██ ▅▅▅ ▅
|
|
41
|
+
( 1.36 kb … 1.52 kb) 1.37 kb ██▁████▅▅█▅▁██▁▁▅▁█▅█
|
|
42
|
+
|
|
43
|
+
args-tokens parse (equivalent to util.parseArgs) 1.65 µs/iter 1.66 µs █
|
|
44
|
+
(1.61 µs … 1.80 µs) 1.79 µs ▅▃ █▂ ▄
|
|
45
|
+
( 1.95 kb … 2.66 kb) 1.97 kb █████▆█▄▃▃▅▃▁▃▃▁▄▁▁▁▂
|
|
46
|
+
|
|
47
|
+
args-tokens parseArgs 729.56 ns/iter 734.11 ns █
|
|
48
|
+
(697.43 ns … 797.08 ns) 774.93 ns ▂█▅▂
|
|
49
|
+
( 2.87 kb … 3.54 kb) 3.11 kb ▂▂▃▇▆▅▆████▃▃▄▂▂▂▂▂▁▂
|
|
50
|
+
|
|
51
|
+
args-tokens resolveArgs 886.78 ns/iter 887.70 ns █
|
|
52
|
+
(853.96 ns … 978.89 ns) 957.24 ns █
|
|
53
|
+
( 2.51 kb … 2.87 kb) 2.79 kb ▂▃█▃▄▅█▄▃▂▂▃▃▂▂▂▂▂▁▁▁
|
|
54
|
+
|
|
55
|
+
┌ ┐
|
|
56
|
+
util.parseArgs ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 4.16 µs
|
|
57
|
+
args-tokens parse (equivalent to util.parseArgs) ┤■■■■■■■■■ 1.65 µs
|
|
58
|
+
args-tokens parseArgs ┤ 729.56 ns
|
|
59
|
+
args-tokens resolveArgs ┤■■ 886.78 ns
|
|
60
|
+
└ ┘
|
|
42
61
|
```
|
|
43
62
|
|
|
44
63
|
With [vitest](https://vitest.dev/guide/features.html#benchmarking):
|
|
@@ -55,15 +74,24 @@ Breaking changes might not follow SemVer, please pin Vitest's version when using
|
|
|
55
74
|
RUN v3.0.5 /path/to/projects/args-tokens
|
|
56
75
|
|
|
57
76
|
|
|
58
|
-
✓ bench/vitest.bench.
|
|
77
|
+
✓ bench/vitest.bench.js > parse and resolve 1350ms
|
|
78
|
+
name hz min max mean p75 p99 p995 p999 rme samples
|
|
79
|
+
· util.parseArgs 221,285.36 0.0041 0.2700 0.0045 0.0044 0.0054 0.0063 0.0629 ±0.38% 110643
|
|
80
|
+
· args-tokens parse 527,127.11 0.0017 0.2153 0.0019 0.0019 0.0023 0.0027 0.0055 ±0.38% 263564 fastest
|
|
81
|
+
|
|
82
|
+
✓ bench/vitest.bench.js > parseArgs 1434ms
|
|
59
83
|
name hz min max mean p75 p99 p995 p999 rme samples
|
|
60
|
-
· node:util
|
|
61
|
-
· args-tokens 1,
|
|
84
|
+
· node:util 235,217.05 0.0039 0.2665 0.0043 0.0042 0.0048 0.0058 0.0139 ±0.43% 117609
|
|
85
|
+
· args-tokens 1,307,135.24 0.0006 0.1737 0.0008 0.0008 0.0009 0.0010 0.0016 ±0.43% 653568 fastest
|
|
62
86
|
|
|
63
87
|
BENCH Summary
|
|
64
88
|
|
|
65
|
-
args-tokens - bench/vitest.bench.
|
|
66
|
-
|
|
89
|
+
args-tokens parse - bench/vitest.bench.js > parse and resolve
|
|
90
|
+
2.38x faster than util.parseArgs
|
|
91
|
+
|
|
92
|
+
args-tokens - bench/vitest.bench.js > parseArgs
|
|
93
|
+
5.56x faster than node:util
|
|
94
|
+
|
|
67
95
|
```
|
|
68
96
|
|
|
69
97
|
## ❓ What's different about parseArgs tokens?
|
|
@@ -273,6 +301,10 @@ const tokens = parseArgs(['-a=1'], { allowCompatible: true }) // add `allowCompa
|
|
|
273
301
|
deepStrictEqual(tokensNode, tokens)
|
|
274
302
|
```
|
|
275
303
|
|
|
304
|
+
## 💁♀️ Showcases
|
|
305
|
+
|
|
306
|
+
- [pnpmc](https://github.com/kazupon/pnpmc): PNPM Catalogs Tooling
|
|
307
|
+
|
|
276
308
|
## 🙌 Contributing guidelines
|
|
277
309
|
|
|
278
310
|
If you are interested in contributing to `args-tokens`, I highly recommend checking out [the contributing guidelines](/CONTRIBUTING.md) here. You'll find all the relevant information such as [how to make a PR](/CONTRIBUTING.md#pull-request-guidelines), [how to setup development](/CONTRIBUTING.md#development-setup)) etc., there.
|
|
@@ -294,5 +326,7 @@ This project is inspired by:
|
|
|
294
326
|
[npm-version-href]: https://npmjs.com/package/args-tokens
|
|
295
327
|
[jsr-src]: https://jsr.io/badges/@kazupon/args-tokens
|
|
296
328
|
[jsr-href]: https://jsr.io/@kazupon/args-tokens
|
|
329
|
+
[install-size-src]: https://pkg-size.dev/badge/install/35082
|
|
330
|
+
[install-size-href]: https://pkg-size.dev/args-tokens
|
|
297
331
|
[ci-src]: https://github.com/kazupon/args-tokens/actions/workflows/ci.yml/badge.svg
|
|
298
332
|
[ci-href]: https://github.com/kazupon/args-tokens/actions/workflows/ci.yml
|
package/lib/parse.d.ts
CHANGED
|
@@ -33,8 +33,9 @@ export type ParsedArgs<T extends ArgOptions> = {
|
|
|
33
33
|
* console.log('values', values)
|
|
34
34
|
* console.log('positionals', positionals)
|
|
35
35
|
* ```
|
|
36
|
-
* @param args command line arguments
|
|
37
|
-
* @param options parse options, about details see {@link ParseOptions}
|
|
36
|
+
* @param args - command line arguments
|
|
37
|
+
* @param options - parse options, about details see {@link ParseOptions}
|
|
38
|
+
* @throws if command line arguments are invalid, this function will cause {@link AggregateError | validation errors}.
|
|
38
39
|
* @returns parsed values
|
|
39
40
|
*/
|
|
40
41
|
export declare function parse<O extends ArgOptions>(args: string[], options?: ParseOptions<O>): ParsedArgs<O>;
|
package/lib/parse.js
CHANGED
|
@@ -23,8 +23,9 @@ const DEFAULT_OPTIONS = {
|
|
|
23
23
|
* console.log('values', values)
|
|
24
24
|
* console.log('positionals', positionals)
|
|
25
25
|
* ```
|
|
26
|
-
* @param args command line arguments
|
|
27
|
-
* @param options parse options, about details see {@link ParseOptions}
|
|
26
|
+
* @param args - command line arguments
|
|
27
|
+
* @param options - parse options, about details see {@link ParseOptions}
|
|
28
|
+
* @throws if command line arguments are invalid, this function will cause {@link AggregateError | validation errors}.
|
|
28
29
|
* @returns parsed values
|
|
29
30
|
*/
|
|
30
31
|
export function parse(args, options = {}) {
|
package/lib/resolver.d.ts
CHANGED
|
@@ -43,6 +43,13 @@ type ResolveArgValues<O extends ArgOptions, V extends Record<keyof O, unknown>>
|
|
|
43
43
|
type FilterArgs<O extends ArgOptions, V extends Record<keyof O, unknown>, K extends keyof ArgOptionSchema> = {
|
|
44
44
|
[Option in keyof O as O[Option][K] extends {} ? Option : never]: V[Option];
|
|
45
45
|
};
|
|
46
|
+
/**
|
|
47
|
+
* Resolve command line arguments
|
|
48
|
+
* @param options - An options that contains {@link ArgOptionSchema | options schema}.
|
|
49
|
+
* @param tokens - An array of {@link ArgToken | tokens}.
|
|
50
|
+
* @throws if command line arguments are invalid, this function will cause {@link AggregateError | validation errors}.
|
|
51
|
+
* @returns An object that contains the values of the arguments and positional arguments.
|
|
52
|
+
*/
|
|
46
53
|
export declare function resolveArgs<T extends ArgOptions>(options: T, tokens: ArgToken[]): {
|
|
47
54
|
values: ArgValues<T>;
|
|
48
55
|
positionals: string[];
|
package/lib/resolver.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
// Modifier: kazuya kawaguchi (a.k.a. kazupon)
|
|
3
3
|
import { hasLongOptionPrefix, isShortOption } from './parser.js';
|
|
4
|
+
/**
|
|
5
|
+
* Resolve command line arguments
|
|
6
|
+
* @param options - An options that contains {@link ArgOptionSchema | options schema}.
|
|
7
|
+
* @param tokens - An array of {@link ArgToken | tokens}.
|
|
8
|
+
* @throws if command line arguments are invalid, this function will cause {@link AggregateError | validation errors}.
|
|
9
|
+
* @returns An object that contains the values of the arguments and positional arguments.
|
|
10
|
+
*/
|
|
4
11
|
export function resolveArgs(options, tokens) {
|
|
5
12
|
const values = {};
|
|
6
13
|
const positionals = [];
|
|
@@ -103,22 +110,36 @@ export function resolveArgs(options, tokens) {
|
|
|
103
110
|
/**
|
|
104
111
|
* resolve values
|
|
105
112
|
*/
|
|
113
|
+
const errors = [];
|
|
106
114
|
for (const [option, schema] of Object.entries(options)) {
|
|
107
|
-
if (
|
|
108
|
-
|
|
115
|
+
if (schema.required) {
|
|
116
|
+
const found = longOptionTokens.find(token => token.name === option) ||
|
|
117
|
+
(schema.short && shortOptionTokens.find(token => token.name === schema.short));
|
|
118
|
+
if (!found) {
|
|
119
|
+
errors.push(createRequireError(option, schema));
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
109
122
|
}
|
|
110
123
|
// eslint-disable-next-line unicorn/no-for-loop
|
|
111
124
|
for (let i = 0; i < longOptionTokens.length; i++) {
|
|
112
125
|
const token = longOptionTokens[i];
|
|
113
126
|
// eslint-disable-next-line unicorn/no-null
|
|
114
127
|
if (option === token.name && token.rawName != null && hasLongOptionPrefix(token.rawName)) {
|
|
115
|
-
validateRequire(token, option, schema);
|
|
128
|
+
const invalid = validateRequire(token, option, schema);
|
|
129
|
+
if (invalid) {
|
|
130
|
+
errors.push(invalid);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
116
133
|
if (schema.type === 'boolean') {
|
|
117
134
|
// NOTE: re-set value to undefined, because long boolean type option is set on analyze phase
|
|
118
135
|
token.value = undefined;
|
|
119
136
|
}
|
|
120
137
|
else {
|
|
121
|
-
validateValue(token, option, schema);
|
|
138
|
+
const invalid = validateValue(token, option, schema);
|
|
139
|
+
if (invalid) {
|
|
140
|
+
errors.push(invalid);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
122
143
|
}
|
|
123
144
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
124
145
|
;
|
|
@@ -131,13 +152,21 @@ export function resolveArgs(options, tokens) {
|
|
|
131
152
|
const token = shortOptionTokens[i];
|
|
132
153
|
// eslint-disable-next-line unicorn/no-null
|
|
133
154
|
if (schema.short === token.name && token.rawName != null && isShortOption(token.rawName)) {
|
|
134
|
-
validateRequire(token, option, schema);
|
|
155
|
+
const invalid = validateRequire(token, option, schema);
|
|
156
|
+
if (invalid) {
|
|
157
|
+
errors.push(invalid);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
135
160
|
if (schema.type === 'boolean') {
|
|
136
161
|
// NOTE: re-set value to undefined, because short boolean type option is set on analyze phase
|
|
137
162
|
token.value = undefined;
|
|
138
163
|
}
|
|
139
164
|
else {
|
|
140
|
-
validateValue(token, option, schema);
|
|
165
|
+
const invalid = validateValue(token, option, schema);
|
|
166
|
+
if (invalid) {
|
|
167
|
+
errors.push(invalid);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
141
170
|
}
|
|
142
171
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
143
172
|
;
|
|
@@ -153,6 +182,10 @@ export function resolveArgs(options, tokens) {
|
|
|
153
182
|
values[option] = schema.default;
|
|
154
183
|
}
|
|
155
184
|
}
|
|
185
|
+
if (errors.length > 0) {
|
|
186
|
+
// eslint-disable-next-line unicorn/error-message
|
|
187
|
+
throw new AggregateError(errors);
|
|
188
|
+
}
|
|
156
189
|
return { values, positionals };
|
|
157
190
|
}
|
|
158
191
|
function createRequireError(option, schema) {
|
|
@@ -160,20 +193,20 @@ function createRequireError(option, schema) {
|
|
|
160
193
|
}
|
|
161
194
|
function validateRequire(token, option, schema) {
|
|
162
195
|
if (schema.required && schema.type !== 'boolean' && !token.value) {
|
|
163
|
-
|
|
196
|
+
return createRequireError(option, schema);
|
|
164
197
|
}
|
|
165
198
|
}
|
|
166
199
|
function validateValue(token, option, schema) {
|
|
167
200
|
switch (schema.type) {
|
|
168
201
|
case 'number': {
|
|
169
202
|
if (!isNumeric(token.value)) {
|
|
170
|
-
|
|
203
|
+
return createTypeError(option, schema);
|
|
171
204
|
}
|
|
172
205
|
break;
|
|
173
206
|
}
|
|
174
207
|
case 'string': {
|
|
175
208
|
if (typeof token.value !== 'string') {
|
|
176
|
-
|
|
209
|
+
return createTypeError(option, schema);
|
|
177
210
|
}
|
|
178
211
|
break;
|
|
179
212
|
}
|