args-tokens 0.7.1 → 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
@@ -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>;
@@ -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 = {}) {
@@ -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[];
@@ -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.7.1",
4
+ "version": "0.9.0",
5
5
  "author": {
6
6
  "name": "kazuya kawaguchi",
7
7
  "email": "kawakazu80@gmail.com"
@@ -26,69 +26,68 @@
26
26
  "access": "public"
27
27
  },
28
28
  "engines": {
29
- "node": ">= 18.18"
29
+ "node": ">= 20"
30
30
  },
31
+ "type": "module",
31
32
  "files": [
32
33
  "lib"
33
34
  ],
34
- "main": "lib/cjs/index.js",
35
- "module": "lib/esm/index.js",
35
+ "module": "lib/index.js",
36
36
  "exports": {
37
37
  ".": {
38
- "types": "./lib/cjs/index.d.ts",
39
- "import": "./lib/esm/index.js",
40
- "require": "./lib/cjs/index.js",
41
- "default": "./lib/cjs/index.js"
38
+ "types": "./lib/index.d.ts",
39
+ "import": "./lib/index.js",
40
+ "require": "./lib/index.js",
41
+ "default": "./lib/index.js"
42
42
  },
43
43
  "./parser": {
44
- "types": "./lib/cjs/parser.d.ts",
45
- "import": "./lib/esm/parser.js",
46
- "require": "./lib/cjs/parser.js",
47
- "default": "./lib/cjs/parser.js"
44
+ "types": "./lib/parser.d.ts",
45
+ "import": "./lib/parser.js",
46
+ "require": "./lib/parser.js",
47
+ "default": "./lib/parser.js"
48
48
  },
49
49
  "./resolver": {
50
- "types": "./lib/cjs/resolver.d.ts",
51
- "import": "./lib/esm/resolver.js",
52
- "require": "./lib/cjs/resolver.js",
53
- "default": "./lib/cjs/resolver.js"
50
+ "types": "./lib/resolver.d.ts",
51
+ "import": "./lib/resolver.js",
52
+ "require": "./lib/resolver.js",
53
+ "default": "./lib/resolver.js"
54
54
  },
55
55
  "./package.json": "./package.json",
56
56
  "./*": "./*"
57
57
  },
58
- "types": "lib/cjs/index.d.ts",
58
+ "types": "lib/index.d.ts",
59
59
  "typesVersions": {
60
60
  "*": {
61
61
  "*": [
62
- "./lib/cjs/*",
63
- "./lib/esm/*",
62
+ "./lib/*",
64
63
  "./*"
65
64
  ]
66
65
  }
67
66
  },
68
67
  "devDependencies": {
69
68
  "@eslint/markdown": "^6.2.2",
70
- "@kazupon/eslint-config": "^0.19.0",
69
+ "@kazupon/eslint-config": "^0.22.0",
71
70
  "@kazupon/prettier-config": "^0.1.1",
72
- "@types/node": "^22.13.4",
71
+ "@types/node": "^22.13.5",
73
72
  "@vitest/eslint-plugin": "^1.1.31",
74
73
  "bumpp": "^10.0.3",
75
- "eslint": "^9.20.1",
74
+ "eslint": "^9.21.0",
76
75
  "eslint-config-prettier": "^10.0.1",
77
76
  "eslint-plugin-jsonc": "^2.19.1",
78
77
  "eslint-plugin-promise": "^7.2.1",
79
78
  "eslint-plugin-regexp": "^2.7.0",
80
- "eslint-plugin-unicorn": "^56.0.1",
81
- "eslint-plugin-yml": "^1.16.0",
79
+ "eslint-plugin-unicorn": "^57.0.0",
80
+ "eslint-plugin-yml": "^1.17.0",
82
81
  "gh-changelogen": "^0.2.8",
83
82
  "jsr": "^0.13.3",
84
- "knip": "^5.44.1",
83
+ "knip": "^5.44.4",
85
84
  "lint-staged": "^15.4.3",
86
85
  "mitata": "^1.0.34",
87
86
  "pkg-pr-new": "^0.0.39",
88
- "prettier": "^3.5.1",
87
+ "prettier": "^3.5.2",
89
88
  "typescript": "^5.7.3",
90
- "typescript-eslint": "^8.24.0",
91
- "vitest": "^3.0.5"
89
+ "typescript-eslint": "^8.24.1",
90
+ "vitest": "^3.0.6"
92
91
  },
93
92
  "prettier": "@kazupon/prettier-config",
94
93
  "lint-staged": {
@@ -107,9 +106,7 @@
107
106
  "scripts": {
108
107
  "bench:mitata": "node --expose-gc bench/mitata.js",
109
108
  "bench:vitest": "vitest bench --run",
110
- "build": "pnpm run --parallel --color \"/^build:/\"",
111
- "build:cjs": "tsc -p ./tsconfig.cjs.json",
112
- "build:esm": "tsc -p ./tsconfig.esm.json",
109
+ "build": "tsc -p ./tsconfig.build.json",
113
110
  "changelog": "gh-changelogen --repo=kazupon/eslint-config",
114
111
  "dev": "pnpx @eslint/config-inspector --config eslint.config.ts",
115
112
  "dev:eslint": "pnpx @eslint/config-inspector --config eslint.config.ts",
package/lib/cjs/index.js DELETED
@@ -1,11 +0,0 @@
1
- "use strict";
2
- // SPDX-License-Identifier: MIT
3
- // Modifier: kazuya kawaguchi (a.k.a. kazupon)
4
- Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.resolveArgs = exports.parseArgs = exports.parse = void 0;
6
- var parse_js_1 = require("./parse.js");
7
- Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return parse_js_1.parse; } });
8
- var parser_js_1 = require("./parser.js");
9
- Object.defineProperty(exports, "parseArgs", { enumerable: true, get: function () { return parser_js_1.parseArgs; } });
10
- var resolver_js_1 = require("./resolver.js");
11
- Object.defineProperty(exports, "resolveArgs", { enumerable: true, get: function () { return resolver_js_1.resolveArgs; } });
@@ -1,40 +0,0 @@
1
- import type { ParserOptions } from './parser';
2
- import type { ArgOptions, ArgValues } from './resolver';
3
- /**
4
- * Parse options for {@link parse} function
5
- */
6
- export interface ParseOptions<O extends ArgOptions> extends ParserOptions {
7
- /**
8
- * Command line options, about details see {@link ArgOptions}
9
- */
10
- options?: O;
11
- }
12
- /**
13
- * Parsed command line arguments
14
- */
15
- export type ParsedArgs<T extends ArgOptions> = {
16
- /**
17
- * Parsed values, same as `values` in {@link resolveArgs}
18
- */
19
- values: ArgValues<T>;
20
- /**
21
- * Positional arguments, same as `positionals` in {@link resolveArgs}
22
- */
23
- positionals: string[];
24
- };
25
- /**
26
- * Parse command line arguments
27
- * @description This function is a convenient API, that is used {@link parseArgs} and {@link resolveArgs} in internal.
28
- * @example
29
- * ```js
30
- * import { parse } from 'args-tokens'
31
- *
32
- * const { values, positionals } = parse(process.argv.slice(2))
33
- * console.log('values', values)
34
- * console.log('positionals', positionals)
35
- * ```
36
- * @param args command line arguments
37
- * @param options parse options, about details see {@link ParseOptions}
38
- * @returns parsed values
39
- */
40
- export declare function parse<O extends ArgOptions>(args: string[], options?: ParseOptions<O>): ParsedArgs<O>;
package/lib/cjs/parse.js DELETED
@@ -1,37 +0,0 @@
1
- "use strict";
2
- // SPDX-License-Identifier: MIT
3
- // Modifier: kazuya kawaguchi (a.k.a. kazupon)
4
- Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.parse = parse;
6
- const parser_js_1 = require("./parser.js");
7
- const resolver_js_1 = require("./resolver.js");
8
- const DEFAULT_OPTIONS = {
9
- help: {
10
- type: 'boolean',
11
- short: 'h'
12
- },
13
- version: {
14
- type: 'boolean',
15
- short: 'v'
16
- }
17
- };
18
- /**
19
- * Parse command line arguments
20
- * @description This function is a convenient API, that is used {@link parseArgs} and {@link resolveArgs} in internal.
21
- * @example
22
- * ```js
23
- * import { parse } from 'args-tokens'
24
- *
25
- * const { values, positionals } = parse(process.argv.slice(2))
26
- * console.log('values', values)
27
- * console.log('positionals', positionals)
28
- * ```
29
- * @param args command line arguments
30
- * @param options parse options, about details see {@link ParseOptions}
31
- * @returns parsed values
32
- */
33
- function parse(args, options = {}) {
34
- const { options: argOptions, allowCompatible = false } = options;
35
- const tokens = (0, parser_js_1.parseArgs)(args, { allowCompatible });
36
- return (0, resolver_js_1.resolveArgs)(argOptions || DEFAULT_OPTIONS, tokens);
37
- }
package/lib/cjs/parser.js DELETED
@@ -1,231 +0,0 @@
1
- "use strict";
2
- // SPDX-License-Identifier: MIT
3
- // Modifier: kazuya kawaguchi (a.k.a. kazupon)
4
- // Forked from `nodejs/node` (`pkgjs/parseargs`)
5
- // Repository url: https://github.com/nodejs/node (https://github.com/pkgjs/parseargs)
6
- // Author: Node.js contributors
7
- // Code url: https://github.com/nodejs/node/blob/main/lib/internal/util/parse_args/parse_args.js
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.parseArgs = parseArgs;
10
- exports.isShortOption = isShortOption;
11
- exports.hasLongOptionPrefix = hasLongOptionPrefix;
12
- const HYPHEN_CHAR = '-';
13
- const HYPHEN_CODE = HYPHEN_CHAR.codePointAt(0);
14
- const EQUAL_CHAR = '=';
15
- const EQUAL_CODE = EQUAL_CHAR.codePointAt(0);
16
- const TERMINATOR = '--';
17
- const SHORT_OPTION_PREFIX = HYPHEN_CHAR;
18
- const LONG_OPTION_PREFIX = '--';
19
- /**
20
- * Parse command line arguments
21
- * @example
22
- * ```js
23
- * import { parseArgs } from 'args-tokens' // for Node.js and Bun
24
- * // import { parseArgs } from 'jsr:@kazupon/args-tokens' // for Deno
25
- *
26
- * const tokens = parseArgs(['--foo', 'bar', '-x', '--bar=baz'])
27
- * // do something with using tokens
28
- * // ...
29
- * console.log('tokens:', tokens)
30
- * ```
31
- * @param args command line arguments
32
- * @param options parse options
33
- * @returns argument tokens
34
- */
35
- function parseArgs(args, options = {}) {
36
- const { allowCompatible = false } = options;
37
- const tokens = [];
38
- const remainings = [...args];
39
- let index = -1;
40
- let groupCount = 0;
41
- let hasShortValueSeparator = false;
42
- while (remainings.length > 0) {
43
- const arg = remainings.shift();
44
- if (arg == undefined) {
45
- break;
46
- }
47
- const nextArg = remainings[0];
48
- if (groupCount > 0) {
49
- groupCount--;
50
- }
51
- else {
52
- index++;
53
- }
54
- // check if `arg` is an options terminator.
55
- // guideline 10 in https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html
56
- if (arg === TERMINATOR) {
57
- tokens.push({
58
- kind: 'option-terminator',
59
- index
60
- });
61
- const mapped = remainings.map(arg => {
62
- return { kind: 'positional', index: ++index, value: arg };
63
- });
64
- tokens.push(...mapped);
65
- break;
66
- }
67
- if (isShortOption(arg)) {
68
- const shortOption = arg.charAt(1);
69
- let value;
70
- let inlineValue;
71
- if (groupCount) {
72
- // e.g. `-abc`
73
- tokens.push({
74
- kind: 'option',
75
- name: shortOption,
76
- rawName: arg,
77
- index,
78
- value,
79
- inlineValue
80
- });
81
- if (groupCount === 1 && hasOptionValue(nextArg)) {
82
- value = remainings.shift();
83
- if (hasShortValueSeparator) {
84
- inlineValue = true;
85
- hasShortValueSeparator = false;
86
- }
87
- tokens.push({
88
- kind: 'option',
89
- index,
90
- value,
91
- inlineValue
92
- });
93
- }
94
- }
95
- else {
96
- // e.g. `-a`
97
- tokens.push({
98
- kind: 'option',
99
- name: shortOption,
100
- rawName: arg,
101
- index,
102
- value,
103
- inlineValue
104
- });
105
- }
106
- // eslint-disable-next-line unicorn/no-null
107
- if (value != null) {
108
- ++index;
109
- }
110
- continue;
111
- }
112
- if (isShortOptionGroup(arg)) {
113
- // expend short option group (e.g. `-abc` => `-a -b -c`, `-f=bar` => `-f bar`)
114
- const expanded = [];
115
- let shortValue = '';
116
- for (let i = 1; i < arg.length; i++) {
117
- const shortableOption = arg.charAt(i);
118
- if (hasShortValueSeparator) {
119
- shortValue += shortableOption;
120
- }
121
- else {
122
- if (!allowCompatible && shortableOption.codePointAt(0) === EQUAL_CODE) {
123
- hasShortValueSeparator = true;
124
- }
125
- else {
126
- expanded.push(`${SHORT_OPTION_PREFIX}${shortableOption}`);
127
- }
128
- }
129
- }
130
- if (shortValue) {
131
- expanded.push(shortValue);
132
- }
133
- remainings.unshift(...expanded);
134
- groupCount = expanded.length;
135
- continue;
136
- }
137
- if (isLongOption(arg)) {
138
- // e.g. `--foo`
139
- const longOption = arg.slice(2);
140
- tokens.push({
141
- kind: 'option',
142
- name: longOption,
143
- rawName: arg,
144
- index,
145
- value: undefined,
146
- inlineValue: undefined
147
- });
148
- continue;
149
- }
150
- if (isLongOptionAndValue(arg)) {
151
- // e.g. `--foo=bar`
152
- const equalIndex = arg.indexOf(EQUAL_CHAR);
153
- const longOption = arg.slice(2, equalIndex);
154
- const value = arg.slice(equalIndex + 1);
155
- tokens.push({
156
- kind: 'option',
157
- name: longOption,
158
- rawName: `${LONG_OPTION_PREFIX}${longOption}`,
159
- index,
160
- value,
161
- inlineValue: true
162
- });
163
- continue;
164
- }
165
- tokens.push({
166
- kind: 'positional',
167
- index,
168
- value: arg
169
- });
170
- }
171
- return tokens;
172
- }
173
- /**
174
- * Check if `arg` is a short option (e.g. `-f`)
175
- * @param arg the argument to check
176
- * @returns whether `arg` is a short option
177
- */
178
- function isShortOption(arg) {
179
- return (arg.length === 2 && arg.codePointAt(0) === HYPHEN_CODE && arg.codePointAt(1) !== HYPHEN_CODE);
180
- }
181
- /**
182
- * Check if `arg` is a short option group (e.g. `-abc`)
183
- * @param arg the argument to check
184
- * @returns whether `arg` is a short option group
185
- */
186
- function isShortOptionGroup(arg) {
187
- if (arg.length <= 2) {
188
- return false;
189
- }
190
- if (arg.codePointAt(0) !== HYPHEN_CODE) {
191
- return false;
192
- }
193
- if (arg.codePointAt(1) === HYPHEN_CODE) {
194
- return false;
195
- }
196
- return true;
197
- }
198
- /**
199
- * Check if `arg` is a long option (e.g. `--foo`)
200
- * @param arg the argument to check
201
- * @returns whether `arg` is a long option
202
- */
203
- function isLongOption(arg) {
204
- return hasLongOptionPrefix(arg) && !arg.includes(EQUAL_CHAR, 3);
205
- }
206
- /**
207
- * Check if `arg` is a long option with value (e.g. `--foo=bar`)
208
- * @param arg the argument to check
209
- * @returns whether `arg` is a long option
210
- */
211
- function isLongOptionAndValue(arg) {
212
- return hasLongOptionPrefix(arg) && arg.includes(EQUAL_CHAR, 3);
213
- }
214
- /**
215
- * Check if `arg` is a long option prefix (e.g. `--`)
216
- * @param arg the argument to check
217
- * @returns whether `arg` is a long option prefix
218
- */
219
- function hasLongOptionPrefix(arg) {
220
- // @ts-ignore -- NOTE: `~` is a bitwise NOT operator
221
- return arg.length > 2 && ~arg.indexOf(LONG_OPTION_PREFIX);
222
- }
223
- /**
224
- * Check if a `value` is an option value
225
- * @param value a value to check
226
- * @returns whether a `value` is an option value
227
- */
228
- function hasOptionValue(value) {
229
- // eslint-disable-next-line unicorn/no-null
230
- return !(value == null) && value.codePointAt(0) !== HYPHEN_CODE;
231
- }
@@ -1,50 +0,0 @@
1
- import type { ArgToken } from './parser';
2
- /**
3
- * An option schema for an argument.
4
- */
5
- export interface ArgOptionSchema {
6
- /**
7
- * Type of argument.
8
- */
9
- type: 'string' | 'boolean' | 'number';
10
- /**
11
- * A single character alias for the option.
12
- */
13
- short?: string;
14
- /**
15
- * Whether the argument is required or not.
16
- */
17
- required?: true;
18
- /**
19
- * The default value of the argument.
20
- */
21
- default?: string | boolean | number;
22
- }
23
- /**
24
- * An object that contains {@link ArgOptionSchema | options schema}.
25
- */
26
- export interface ArgOptions {
27
- [option: string]: ArgOptionSchema;
28
- }
29
- /**
30
- * An object that contains the values of the arguments.
31
- */
32
- export type ArgValues<T> = T extends ArgOptions ? ResolveArgValues<T, {
33
- [Option in keyof T]: ExtractOptionValue<T[Option]>;
34
- }> : {
35
- [option: string]: string | boolean | number | undefined;
36
- };
37
- type ExtractOptionValue<O extends ArgOptionSchema> = O['type'] extends 'string' ? string : O['type'] extends 'boolean' ? boolean : O['type'] extends 'number' ? number : string | boolean | number;
38
- type ResolveArgValues<O extends ArgOptions, V extends Record<keyof O, unknown>> = {
39
- -readonly [Option in keyof O]?: V[Option];
40
- } & FilterArgs<O, V, 'default'> & FilterArgs<O, V, 'required'> extends infer P ? {
41
- [K in keyof P]: P[K];
42
- } : never;
43
- type FilterArgs<O extends ArgOptions, V extends Record<keyof O, unknown>, K extends keyof ArgOptionSchema> = {
44
- [Option in keyof O as O[Option][K] extends {} ? Option : never]: V[Option];
45
- };
46
- export declare function resolveArgs<T extends ArgOptions>(options: T, tokens: ArgToken[]): {
47
- values: ArgValues<T>;
48
- positionals: string[];
49
- };
50
- export {};
@@ -1,201 +0,0 @@
1
- "use strict";
2
- // SPDX-License-Identifier: MIT
3
- // Modifier: kazuya kawaguchi (a.k.a. kazupon)
4
- Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.resolveArgs = resolveArgs;
6
- const parser_js_1 = require("./parser.js");
7
- function resolveArgs(options, tokens) {
8
- const values = {};
9
- const positionals = [];
10
- const longOptionTokens = [];
11
- const shortOptionTokens = [];
12
- let currentLongOption;
13
- let currentShortOption;
14
- const expandableShortOptions = [];
15
- function toShortValue() {
16
- if (expandableShortOptions.length === 0) {
17
- return undefined;
18
- }
19
- else {
20
- const value = expandableShortOptions.map(token => token.name).join('');
21
- expandableShortOptions.length = 0;
22
- return value;
23
- }
24
- }
25
- function applyLongOptionValue(value = undefined) {
26
- if (currentLongOption) {
27
- currentLongOption.value = value;
28
- longOptionTokens.push({ ...currentLongOption });
29
- currentLongOption = undefined;
30
- }
31
- }
32
- function applyShortOptionValue(value = undefined) {
33
- if (currentShortOption) {
34
- currentShortOption.value = value || toShortValue();
35
- shortOptionTokens.push({ ...currentShortOption });
36
- currentShortOption = undefined;
37
- }
38
- }
39
- /**
40
- * analyze phase to resolve value
41
- * separate tokens into positionals, long and short options, after that resolve values
42
- */
43
- // eslint-disable-next-line unicorn/no-for-loop
44
- for (let i = 0; i < tokens.length; i++) {
45
- const token = tokens[i];
46
- if (token.kind === 'positional') {
47
- positionals.push(token.value);
48
- // check if previous option is not resolved
49
- applyLongOptionValue(token.value);
50
- applyShortOptionValue(token.value);
51
- }
52
- else if (token.kind === 'option') {
53
- if (token.rawName) {
54
- if ((0, parser_js_1.hasLongOptionPrefix)(token.rawName)) {
55
- if (token.inlineValue) {
56
- longOptionTokens.push({ ...token });
57
- }
58
- else {
59
- currentLongOption = { ...token };
60
- }
61
- // check if previous short option is not resolved
62
- applyShortOptionValue();
63
- }
64
- else if ((0, parser_js_1.isShortOption)(token.rawName)) {
65
- if (currentShortOption) {
66
- if (currentShortOption.index === token.index) {
67
- expandableShortOptions.push({ ...token });
68
- }
69
- else {
70
- currentShortOption.value = toShortValue();
71
- shortOptionTokens.push({ ...currentShortOption });
72
- currentShortOption = { ...token };
73
- }
74
- // check if previous long option is not resolved
75
- applyLongOptionValue();
76
- }
77
- else {
78
- currentShortOption = { ...token };
79
- // check if previous long option is not resolved
80
- applyLongOptionValue();
81
- }
82
- }
83
- }
84
- else {
85
- // short option value
86
- if (currentShortOption && currentShortOption.index == token.index && token.inlineValue) {
87
- currentShortOption.value = token.value;
88
- shortOptionTokens.push({ ...currentShortOption });
89
- currentShortOption = undefined;
90
- }
91
- // check if previous long option is not resolved
92
- applyLongOptionValue();
93
- }
94
- }
95
- else {
96
- // check if previous option is not resolved
97
- applyLongOptionValue();
98
- applyShortOptionValue();
99
- }
100
- }
101
- /**
102
- * check if the last long or short option is not resolved
103
- */
104
- applyLongOptionValue();
105
- applyShortOptionValue();
106
- /**
107
- * resolve values
108
- */
109
- for (const [option, schema] of Object.entries(options)) {
110
- if (longOptionTokens.length === 0 && shortOptionTokens.length === 0 && schema.required) {
111
- throw createRequireError(option, schema);
112
- }
113
- // eslint-disable-next-line unicorn/no-for-loop
114
- for (let i = 0; i < longOptionTokens.length; i++) {
115
- const token = longOptionTokens[i];
116
- // eslint-disable-next-line unicorn/no-null
117
- if (option === token.name && token.rawName != null && (0, parser_js_1.hasLongOptionPrefix)(token.rawName)) {
118
- validateRequire(token, option, schema);
119
- if (schema.type === 'boolean') {
120
- // NOTE: re-set value to undefined, because long boolean type option is set on analyze phase
121
- token.value = undefined;
122
- }
123
- else {
124
- validateValue(token, option, schema);
125
- }
126
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
127
- ;
128
- values[option] = resolveOptionValue(token, schema);
129
- continue;
130
- }
131
- }
132
- // eslint-disable-next-line unicorn/no-for-loop
133
- for (let i = 0; i < shortOptionTokens.length; i++) {
134
- const token = shortOptionTokens[i];
135
- // eslint-disable-next-line unicorn/no-null
136
- if (schema.short === token.name && token.rawName != null && (0, parser_js_1.isShortOption)(token.rawName)) {
137
- validateRequire(token, option, schema);
138
- if (schema.type === 'boolean') {
139
- // NOTE: re-set value to undefined, because short boolean type option is set on analyze phase
140
- token.value = undefined;
141
- }
142
- else {
143
- validateValue(token, option, schema);
144
- }
145
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
146
- ;
147
- values[option] = resolveOptionValue(token, schema);
148
- continue;
149
- }
150
- }
151
- // eslint-disable-next-line unicorn/no-null
152
- if (values[option] == null && schema.default != null) {
153
- // check if the default value is in values
154
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
155
- ;
156
- values[option] = schema.default;
157
- }
158
- }
159
- return { values, positionals };
160
- }
161
- function createRequireError(option, schema) {
162
- return new Error(`Option '--${option}' ${schema.short ? `or '-${schema.short}'` : ''} is required`);
163
- }
164
- function validateRequire(token, option, schema) {
165
- if (schema.required && schema.type !== 'boolean' && !token.value) {
166
- throw createRequireError(option, schema);
167
- }
168
- }
169
- function validateValue(token, option, schema) {
170
- switch (schema.type) {
171
- case 'number': {
172
- if (!isNumeric(token.value)) {
173
- throw createTypeError(option, schema);
174
- }
175
- break;
176
- }
177
- case 'string': {
178
- if (typeof token.value !== 'string') {
179
- throw createTypeError(option, schema);
180
- }
181
- break;
182
- }
183
- }
184
- }
185
- function isNumeric(str) {
186
- // @ts-ignore
187
- // eslint-disable-next-line unicorn/prefer-number-properties
188
- return str.trim() !== '' && !isNaN(str);
189
- }
190
- function createTypeError(option, schema) {
191
- return new TypeError(`Option '--${option}' ${schema.short ? `or '-${schema.short}'` : ''} should be '${schema.type}'`);
192
- }
193
- function resolveOptionValue(token, schema) {
194
- if (token.value) {
195
- return schema.type === 'number' ? +token.value : token.value;
196
- }
197
- if (schema.type === 'boolean') {
198
- return true;
199
- }
200
- return schema.type === 'number' ? +(schema.default || '') : schema.default;
201
- }
@@ -1,6 +0,0 @@
1
- export { parse } from './parse.js';
2
- export { parseArgs } from './parser.js';
3
- export { resolveArgs } from './resolver.js';
4
- export type { ParsedArgs, ParseOptions } from './parse';
5
- export type { ArgToken, ParserOptions } from './parser';
6
- export type { ArgOptions, ArgOptionSchema, ArgValues } from './resolver';
@@ -1,77 +0,0 @@
1
- /**
2
- * Argument token Kind
3
- * - `option`: option token, support short option (e.g. `-x`) and long option (e.g. `--foo`)
4
- * - `option-terminator`: option terminator (`--`) token, see guideline 10 in https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html
5
- * - `positional`: positional token
6
- */
7
- type ArgTokenKind = 'option' | 'option-terminator' | 'positional';
8
- /**
9
- * Argument token
10
- */
11
- export interface ArgToken {
12
- /**
13
- * Argument token kind
14
- */
15
- kind: ArgTokenKind;
16
- /**
17
- * Argument token index, e.g `--foo bar` => `--foo` index is 0, `bar` index is 1
18
- */
19
- index: number;
20
- /**
21
- * Option name, e.g. `--foo` => `foo`, `-x` => `x`
22
- */
23
- name?: string;
24
- /**
25
- * Raw option name, e.g. `--foo` => `--foo`, `-x` => `-x`
26
- */
27
- rawName?: string;
28
- /**
29
- * Option value, e.g. `--foo=bar` => `bar`, `-x=bar` => `bar`.
30
- * If the `allowCompatible` option is `true`, short option value will be same as Node.js `parseArgs` behavior.
31
- */
32
- value?: string;
33
- /**
34
- * Inline value, e.g. `--foo=bar` => `true`, `-x=bar` => `true`.
35
- */
36
- inlineValue?: boolean;
37
- }
38
- /**
39
- * Parser Options
40
- */
41
- export interface ParserOptions {
42
- /**
43
- * [Node.js parseArgs](https://nodejs.org/api/util.html#parseargs-tokens) tokens compatible mode
44
- * @default false
45
- */
46
- allowCompatible?: boolean;
47
- }
48
- /**
49
- * Parse command line arguments
50
- * @example
51
- * ```js
52
- * import { parseArgs } from 'args-tokens' // for Node.js and Bun
53
- * // import { parseArgs } from 'jsr:@kazupon/args-tokens' // for Deno
54
- *
55
- * const tokens = parseArgs(['--foo', 'bar', '-x', '--bar=baz'])
56
- * // do something with using tokens
57
- * // ...
58
- * console.log('tokens:', tokens)
59
- * ```
60
- * @param args command line arguments
61
- * @param options parse options
62
- * @returns argument tokens
63
- */
64
- export declare function parseArgs(args: string[], options?: ParserOptions): ArgToken[];
65
- /**
66
- * Check if `arg` is a short option (e.g. `-f`)
67
- * @param arg the argument to check
68
- * @returns whether `arg` is a short option
69
- */
70
- export declare function isShortOption(arg: string): boolean;
71
- /**
72
- * Check if `arg` is a long option prefix (e.g. `--`)
73
- * @param arg the argument to check
74
- * @returns whether `arg` is a long option prefix
75
- */
76
- export declare function hasLongOptionPrefix(arg: string): boolean;
77
- export {};
File without changes
File without changes
File without changes
File without changes