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 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/index.mjs
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 avg (min … max) p75 / p99 (min … top 1%)
29
- ------------------------------------------- -------------------------------
30
- node:util parseArgs 4.69 µs/iter 4.78 µs ██
31
- (4.49 µs … 4.91 µs) 4.85 µs ██ █ ██ ███
32
- ( 1.32 kb … 1.49 kb) 1.33 kb █▁██▁██████▁█████████
33
-
34
- args-tokens parseArgs 842.98 ns/iter 882.65 ns ▄▄▄▇▃ ▄▃
35
- (734.10 ns984.32 ns) 966.06 ns ▂▄█████▄████▅▅▄
36
- ( 3.11 kb … 3.41 kb) 3.12 kb █▂███████████████▆▆▆▂
37
-
38
- ┌ ┐
39
- node:util parseArgs ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 4.69 µs
40
- args-tokens parseArgs 842.98 ns
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 µs1.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.mjs > parseArgs 1466ms
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 194,911.34 0.0042 0.6821 0.0051 0.0046 0.0151 0.0292 0.1079 ±0.82% 97456
61
- · args-tokens 1,101,845.21 0.0007 0.1883 0.0009 0.0009 0.0012 0.0031 0.0140 ±0.32% 550923 fastest
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.mjs > parseArgs
66
- 5.65x faster than node:util
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 (longOptionTokens.length === 0 && shortOptionTokens.length === 0 && schema.required) {
108
- throw createRequireError(option, schema);
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
- throw createRequireError(option, schema);
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
- throw createTypeError(option, schema);
203
+ return createTypeError(option, schema);
171
204
  }
172
205
  break;
173
206
  }
174
207
  case 'string': {
175
208
  if (typeof token.value !== 'string') {
176
- throw createTypeError(option, schema);
209
+ return createTypeError(option, schema);
177
210
  }
178
211
  break;
179
212
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "args-tokens",
3
3
  "description": "parseArgs tokens compatibility and more high-performance parser",
4
- "version": "0.8.0",
4
+ "version": "0.9.0",
5
5
  "author": {
6
6
  "name": "kazuya kawaguchi",
7
7
  "email": "kawakazu80@gmail.com"