args-tokens 0.8.0 → 0.10.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 +55 -20
- package/lib/parse.d.ts +7 -3
- package/lib/parse.js +3 -3
- package/lib/resolver.d.ts +7 -0
- package/lib/resolver.js +39 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,10 +2,20 @@
|
|
|
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
|
|
13
|
+
- ✅ `util.parseArgs` token compatibility
|
|
14
|
+
- ✅ ES Modules and modern JavaScript
|
|
15
|
+
- ✅ Type safe
|
|
16
|
+
- ✅ Zero dependencies
|
|
17
|
+
- ✅ Universal runtime
|
|
18
|
+
|
|
9
19
|
## 🐱 Motivation
|
|
10
20
|
|
|
11
21
|
- 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 +29,36 @@ With [mitata](https://github.com/evanwashere/mitata):
|
|
|
19
29
|
pnpm bench:mitata
|
|
20
30
|
|
|
21
31
|
> args-tokens@0.0.0 bench:mitata /path/to/projects/args-tokens
|
|
22
|
-
> node --expose-gc bench/
|
|
32
|
+
> node --expose-gc bench/mitata.js
|
|
23
33
|
|
|
24
34
|
clk: ~2.87 GHz
|
|
25
35
|
cpu: Apple M1 Max
|
|
26
36
|
runtime: node 18.19.1 (arm64-darwin)
|
|
27
37
|
|
|
28
|
-
benchmark
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
args-tokens parseArgs
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
benchmark avg (min … max) p75 / p99 (min … top 1%)
|
|
39
|
+
--------------------------------------------------------------- -------------------------------
|
|
40
|
+
util.parseArgs 4.16 µs/iter 4.20 µs █
|
|
41
|
+
(4.09 µs … 4.29 µs) 4.28 µs ██ ▅▅▅ ▅
|
|
42
|
+
( 1.36 kb … 1.52 kb) 1.37 kb ██▁████▅▅█▅▁██▁▁▅▁█▅█
|
|
43
|
+
|
|
44
|
+
args-tokens parse (equivalent to util.parseArgs) 1.65 µs/iter 1.66 µs █
|
|
45
|
+
(1.61 µs … 1.80 µs) 1.79 µs ▅▃ █▂ ▄
|
|
46
|
+
( 1.95 kb … 2.66 kb) 1.97 kb █████▆█▄▃▃▅▃▁▃▃▁▄▁▁▁▂
|
|
47
|
+
|
|
48
|
+
args-tokens parseArgs 729.56 ns/iter 734.11 ns █
|
|
49
|
+
(697.43 ns … 797.08 ns) 774.93 ns ▂█▅▂
|
|
50
|
+
( 2.87 kb … 3.54 kb) 3.11 kb ▂▂▃▇▆▅▆████▃▃▄▂▂▂▂▂▁▂
|
|
51
|
+
|
|
52
|
+
args-tokens resolveArgs 886.78 ns/iter 887.70 ns █
|
|
53
|
+
(853.96 ns … 978.89 ns) 957.24 ns █
|
|
54
|
+
( 2.51 kb … 2.87 kb) 2.79 kb ▂▃█▃▄▅█▄▃▂▂▃▃▂▂▂▂▂▁▁▁
|
|
55
|
+
|
|
56
|
+
┌ ┐
|
|
57
|
+
util.parseArgs ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 4.16 µs
|
|
58
|
+
args-tokens parse (equivalent to util.parseArgs) ┤■■■■■■■■■ 1.65 µs
|
|
59
|
+
args-tokens parseArgs ┤ 729.56 ns
|
|
60
|
+
args-tokens resolveArgs ┤■■ 886.78 ns
|
|
61
|
+
└ ┘
|
|
42
62
|
```
|
|
43
63
|
|
|
44
64
|
With [vitest](https://vitest.dev/guide/features.html#benchmarking):
|
|
@@ -55,15 +75,24 @@ Breaking changes might not follow SemVer, please pin Vitest's version when using
|
|
|
55
75
|
RUN v3.0.5 /path/to/projects/args-tokens
|
|
56
76
|
|
|
57
77
|
|
|
58
|
-
✓ bench/vitest.bench.
|
|
78
|
+
✓ bench/vitest.bench.js > parse and resolve 1350ms
|
|
79
|
+
name hz min max mean p75 p99 p995 p999 rme samples
|
|
80
|
+
· util.parseArgs 221,285.36 0.0041 0.2700 0.0045 0.0044 0.0054 0.0063 0.0629 ±0.38% 110643
|
|
81
|
+
· args-tokens parse 527,127.11 0.0017 0.2153 0.0019 0.0019 0.0023 0.0027 0.0055 ±0.38% 263564 fastest
|
|
82
|
+
|
|
83
|
+
✓ bench/vitest.bench.js > parseArgs 1434ms
|
|
59
84
|
name hz min max mean p75 p99 p995 p999 rme samples
|
|
60
|
-
· node:util
|
|
61
|
-
· args-tokens 1,
|
|
85
|
+
· node:util 235,217.05 0.0039 0.2665 0.0043 0.0042 0.0048 0.0058 0.0139 ±0.43% 117609
|
|
86
|
+
· 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
87
|
|
|
63
88
|
BENCH Summary
|
|
64
89
|
|
|
65
|
-
args-tokens - bench/vitest.bench.
|
|
66
|
-
|
|
90
|
+
args-tokens parse - bench/vitest.bench.js > parse and resolve
|
|
91
|
+
2.38x faster than util.parseArgs
|
|
92
|
+
|
|
93
|
+
args-tokens - bench/vitest.bench.js > parseArgs
|
|
94
|
+
5.56x faster than node:util
|
|
95
|
+
|
|
67
96
|
```
|
|
68
97
|
|
|
69
98
|
## ❓ What's different about parseArgs tokens?
|
|
@@ -273,6 +302,10 @@ const tokens = parseArgs(['-a=1'], { allowCompatible: true }) // add `allowCompa
|
|
|
273
302
|
deepStrictEqual(tokensNode, tokens)
|
|
274
303
|
```
|
|
275
304
|
|
|
305
|
+
## 💁♀️ Showcases
|
|
306
|
+
|
|
307
|
+
- [pnpmc](https://github.com/kazupon/pnpmc): PNPM Catalogs Tooling
|
|
308
|
+
|
|
276
309
|
## 🙌 Contributing guidelines
|
|
277
310
|
|
|
278
311
|
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 +327,7 @@ This project is inspired by:
|
|
|
294
327
|
[npm-version-href]: https://npmjs.com/package/args-tokens
|
|
295
328
|
[jsr-src]: https://jsr.io/badges/@kazupon/args-tokens
|
|
296
329
|
[jsr-href]: https://jsr.io/@kazupon/args-tokens
|
|
330
|
+
[install-size-src]: https://pkg-size.dev/badge/install/35082
|
|
331
|
+
[install-size-href]: https://pkg-size.dev/args-tokens
|
|
297
332
|
[ci-src]: https://github.com/kazupon/args-tokens/actions/workflows/ci.yml/badge.svg
|
|
298
333
|
[ci-href]: https://github.com/kazupon/args-tokens/actions/workflows/ci.yml
|
package/lib/parse.d.ts
CHANGED
|
@@ -21,6 +21,10 @@ export type ParsedArgs<T extends ArgOptions> = {
|
|
|
21
21
|
* Positional arguments, same as `positionals` in {@link resolveArgs}
|
|
22
22
|
*/
|
|
23
23
|
positionals: string[];
|
|
24
|
+
/**
|
|
25
|
+
* Validation errors, same as `errors` in {@link resolveArgs}
|
|
26
|
+
*/
|
|
27
|
+
error: AggregateError | undefined;
|
|
24
28
|
};
|
|
25
29
|
/**
|
|
26
30
|
* Parse command line arguments
|
|
@@ -33,8 +37,8 @@ export type ParsedArgs<T extends ArgOptions> = {
|
|
|
33
37
|
* console.log('values', values)
|
|
34
38
|
* console.log('positionals', positionals)
|
|
35
39
|
* ```
|
|
36
|
-
* @param args command line arguments
|
|
37
|
-
* @param options parse options, about details see {@link ParseOptions}
|
|
38
|
-
* @returns
|
|
40
|
+
* @param args - command line arguments
|
|
41
|
+
* @param options - parse options, about details see {@link ParseOptions}
|
|
42
|
+
* @returns An object that contains the values of the arguments, positional arguments, and {@link AggregateError | validation errors}.
|
|
39
43
|
*/
|
|
40
44
|
export declare function parse<O extends ArgOptions>(args: string[], options?: ParseOptions<O>): ParsedArgs<O>;
|
package/lib/parse.js
CHANGED
|
@@ -23,9 +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}
|
|
28
|
-
* @returns
|
|
26
|
+
* @param args - command line arguments
|
|
27
|
+
* @param options - parse options, about details see {@link ParseOptions}
|
|
28
|
+
* @returns An object that contains the values of the arguments, positional arguments, and {@link AggregateError | validation errors}.
|
|
29
29
|
*/
|
|
30
30
|
export function parse(args, options = {}) {
|
|
31
31
|
const { options: argOptions, allowCompatible = false } = options;
|
package/lib/resolver.d.ts
CHANGED
|
@@ -43,8 +43,15 @@ 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
|
+
* @returns An object that contains the values of the arguments, positional arguments, and {@link AggregateError | validation errors}.
|
|
51
|
+
*/
|
|
46
52
|
export declare function resolveArgs<T extends ArgOptions>(options: T, tokens: ArgToken[]): {
|
|
47
53
|
values: ArgValues<T>;
|
|
48
54
|
positionals: string[];
|
|
55
|
+
error: AggregateError | undefined;
|
|
49
56
|
};
|
|
50
57
|
export {};
|
package/lib/resolver.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
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
|
+
* @returns An object that contains the values of the arguments, positional arguments, and {@link AggregateError | validation errors}.
|
|
9
|
+
*/
|
|
4
10
|
export function resolveArgs(options, tokens) {
|
|
5
11
|
const values = {};
|
|
6
12
|
const positionals = [];
|
|
@@ -103,22 +109,36 @@ export function resolveArgs(options, tokens) {
|
|
|
103
109
|
/**
|
|
104
110
|
* resolve values
|
|
105
111
|
*/
|
|
112
|
+
const errors = [];
|
|
106
113
|
for (const [option, schema] of Object.entries(options)) {
|
|
107
|
-
if (
|
|
108
|
-
|
|
114
|
+
if (schema.required) {
|
|
115
|
+
const found = longOptionTokens.find(token => token.name === option) ||
|
|
116
|
+
(schema.short && shortOptionTokens.find(token => token.name === schema.short));
|
|
117
|
+
if (!found) {
|
|
118
|
+
errors.push(createRequireError(option, schema));
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
109
121
|
}
|
|
110
122
|
// eslint-disable-next-line unicorn/no-for-loop
|
|
111
123
|
for (let i = 0; i < longOptionTokens.length; i++) {
|
|
112
124
|
const token = longOptionTokens[i];
|
|
113
125
|
// eslint-disable-next-line unicorn/no-null
|
|
114
126
|
if (option === token.name && token.rawName != null && hasLongOptionPrefix(token.rawName)) {
|
|
115
|
-
validateRequire(token, option, schema);
|
|
127
|
+
const invalid = validateRequire(token, option, schema);
|
|
128
|
+
if (invalid) {
|
|
129
|
+
errors.push(invalid);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
116
132
|
if (schema.type === 'boolean') {
|
|
117
133
|
// NOTE: re-set value to undefined, because long boolean type option is set on analyze phase
|
|
118
134
|
token.value = undefined;
|
|
119
135
|
}
|
|
120
136
|
else {
|
|
121
|
-
validateValue(token, option, schema);
|
|
137
|
+
const invalid = validateValue(token, option, schema);
|
|
138
|
+
if (invalid) {
|
|
139
|
+
errors.push(invalid);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
122
142
|
}
|
|
123
143
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
124
144
|
;
|
|
@@ -131,13 +151,21 @@ export function resolveArgs(options, tokens) {
|
|
|
131
151
|
const token = shortOptionTokens[i];
|
|
132
152
|
// eslint-disable-next-line unicorn/no-null
|
|
133
153
|
if (schema.short === token.name && token.rawName != null && isShortOption(token.rawName)) {
|
|
134
|
-
validateRequire(token, option, schema);
|
|
154
|
+
const invalid = validateRequire(token, option, schema);
|
|
155
|
+
if (invalid) {
|
|
156
|
+
errors.push(invalid);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
135
159
|
if (schema.type === 'boolean') {
|
|
136
160
|
// NOTE: re-set value to undefined, because short boolean type option is set on analyze phase
|
|
137
161
|
token.value = undefined;
|
|
138
162
|
}
|
|
139
163
|
else {
|
|
140
|
-
validateValue(token, option, schema);
|
|
164
|
+
const invalid = validateValue(token, option, schema);
|
|
165
|
+
if (invalid) {
|
|
166
|
+
errors.push(invalid);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
141
169
|
}
|
|
142
170
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
|
|
143
171
|
;
|
|
@@ -153,27 +181,28 @@ export function resolveArgs(options, tokens) {
|
|
|
153
181
|
values[option] = schema.default;
|
|
154
182
|
}
|
|
155
183
|
}
|
|
156
|
-
|
|
184
|
+
// eslint-disable-next-line unicorn/error-message
|
|
185
|
+
return { values, positionals, error: errors.length > 0 ? new AggregateError(errors) : undefined };
|
|
157
186
|
}
|
|
158
187
|
function createRequireError(option, schema) {
|
|
159
188
|
return new Error(`Option '--${option}' ${schema.short ? `or '-${schema.short}'` : ''} is required`);
|
|
160
189
|
}
|
|
161
190
|
function validateRequire(token, option, schema) {
|
|
162
191
|
if (schema.required && schema.type !== 'boolean' && !token.value) {
|
|
163
|
-
|
|
192
|
+
return createRequireError(option, schema);
|
|
164
193
|
}
|
|
165
194
|
}
|
|
166
195
|
function validateValue(token, option, schema) {
|
|
167
196
|
switch (schema.type) {
|
|
168
197
|
case 'number': {
|
|
169
198
|
if (!isNumeric(token.value)) {
|
|
170
|
-
|
|
199
|
+
return createTypeError(option, schema);
|
|
171
200
|
}
|
|
172
201
|
break;
|
|
173
202
|
}
|
|
174
203
|
case 'string': {
|
|
175
204
|
if (typeof token.value !== 'string') {
|
|
176
|
-
|
|
205
|
+
return createTypeError(option, schema);
|
|
177
206
|
}
|
|
178
207
|
break;
|
|
179
208
|
}
|