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 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/index.mjs
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 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
- └ ┘
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 µs1.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.mjs > parseArgs 1466ms
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 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
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.mjs > parseArgs
66
- 5.65x faster than node:util
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 parsed values
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 parsed values
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 (longOptionTokens.length === 0 && shortOptionTokens.length === 0 && schema.required) {
108
- throw createRequireError(option, schema);
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
- return { values, positionals };
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
- throw createRequireError(option, schema);
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
- throw createTypeError(option, schema);
199
+ return createTypeError(option, schema);
171
200
  }
172
201
  break;
173
202
  }
174
203
  case 'string': {
175
204
  if (typeof token.value !== 'string') {
176
- throw createTypeError(option, schema);
205
+ return createTypeError(option, schema);
177
206
  }
178
207
  break;
179
208
  }
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.10.0",
5
5
  "author": {
6
6
  "name": "kazuya kawaguchi",
7
7
  "email": "kawakazu80@gmail.com"