args-tokens 0.0.0 โ 0.1.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 +200 -1
- package/lib/cjs/index.d.ts +27 -1
- package/lib/cjs/index.js +215 -3
- package/lib/esm/index.d.ts +27 -1
- package/lib/esm/index.js +214 -2
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1 +1,200 @@
|
|
|
1
|
-
# args
|
|
1
|
+
# args-tokens
|
|
2
|
+
|
|
3
|
+
[![Version][npm-version-src]][npm-version-href]
|
|
4
|
+
[![CI][ci-src]][ci-href]
|
|
5
|
+
|
|
6
|
+
> [`parseArgs` tokens](https://nodejs.org/api/util.html#parseargs-tokens) compatibility and more high-performance parser
|
|
7
|
+
|
|
8
|
+
## ๐ฑ Motivation
|
|
9
|
+
|
|
10
|
+
- Although Node.js's [`parseArgs`](https://nodejs.org/api/util.html#utilparseargsconfig) can return tokens, tokens that the shot options are not in the format I expect. Of course, I know the background of [this issue](https://github.com/pkgjs/parseargs/issues/78).
|
|
11
|
+
- `parseArgs` gives the command line args parser a useful util, so the resolution of the options values and the parsing of the tokens are tightly coupled. As a result, Performance is sacrificed. Of course, I know that's a trade-off.
|
|
12
|
+
|
|
13
|
+
## โฑ๏ธ Benchmark
|
|
14
|
+
|
|
15
|
+
With [mitata](https://github.com/evanwashere/mitata):
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
pnpm bench:mitata
|
|
19
|
+
|
|
20
|
+
> args-tokens@0.0.0 bench:mitata /path/to/projects/args-tokens
|
|
21
|
+
> node --expose-gc bench/index.mjs
|
|
22
|
+
|
|
23
|
+
clk: ~2.87 GHz
|
|
24
|
+
cpu: Apple M1 Max
|
|
25
|
+
runtime: node 18.19.1 (arm64-darwin)
|
|
26
|
+
|
|
27
|
+
benchmark avg (min โฆ max) p75 / p99 (min โฆ top 1%)
|
|
28
|
+
------------------------------------------- -------------------------------
|
|
29
|
+
node:util parseArgs 4.69 ยตs/iter 4.78 ยตs โ โ โโ
|
|
30
|
+
(4.49 ยตs โฆ 4.91 ยตs) 4.85 ยตs โ โ โโ โ โโ โโโ
|
|
31
|
+
( 1.32 kb โฆ 1.49 kb) 1.33 kb โโโโโโโโโโโโโโโโโโโโโ
|
|
32
|
+
|
|
33
|
+
args-tokens parseArgs 842.98 ns/iter 882.65 ns โโโโโ โโ โ
|
|
34
|
+
(734.10 ns โฆ 984.32 ns) 966.06 ns โโโโโโโโโโโโโ
โ
โ
|
|
35
|
+
( 3.11 kb โฆ 3.41 kb) 3.12 kb โโโโโโโโโโโโโโโโโโโโโ
|
|
36
|
+
|
|
37
|
+
โ โ
|
|
38
|
+
node:util parseArgs โคโ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ โ 4.69 ยตs
|
|
39
|
+
args-tokens parseArgs โค 842.98 ns
|
|
40
|
+
โ โ
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
With [vitest](https://vitest.dev/guide/features.html#benchmarking):
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
pnpm bench:vitest
|
|
47
|
+
|
|
48
|
+
> args-tokens@0.0.0 bench:vitest /path/to/projects/args-tokens
|
|
49
|
+
> vitest bench --run
|
|
50
|
+
|
|
51
|
+
Benchmarking is an experimental feature.
|
|
52
|
+
Breaking changes might not follow SemVer, please pin Vitest's version when using it.
|
|
53
|
+
|
|
54
|
+
RUN v3.0.5 /path/to/projects/args-tokens
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
โ bench/vitest.bench.mjs > parseArgs 1466ms
|
|
58
|
+
name hz min max mean p75 p99 p995 p999 rme samples
|
|
59
|
+
ยท node:util 194,911.34 0.0042 0.6821 0.0051 0.0046 0.0151 0.0292 0.1079 ยฑ0.82% 97456
|
|
60
|
+
ยท args-tokens 1,101,845.21 0.0007 0.1883 0.0009 0.0009 0.0012 0.0031 0.0140 ยฑ0.32% 550923 fastest
|
|
61
|
+
|
|
62
|
+
BENCH Summary
|
|
63
|
+
|
|
64
|
+
args-tokens - bench/vitest.bench.mjs > parseArgs
|
|
65
|
+
5.65x faster than node:util
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## โ What's different about parseArgs tokens?
|
|
69
|
+
|
|
70
|
+
The token output for the short option `-x=v` is different:
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
import { parseArgs as parseArgsNode } from 'node:util'
|
|
74
|
+
import { parseArgs } from 'args-tokens'
|
|
75
|
+
|
|
76
|
+
// Node.js parseArgs tokens
|
|
77
|
+
const { tokens: tokensNode } = parseArgsNode({
|
|
78
|
+
allowPositionals: true,
|
|
79
|
+
strict: false,
|
|
80
|
+
args: ['-a=1'],
|
|
81
|
+
tokens: true
|
|
82
|
+
})
|
|
83
|
+
console.log(tokensNode)
|
|
84
|
+
|
|
85
|
+
// ({
|
|
86
|
+
// kind: 'option',
|
|
87
|
+
// name: 'a',
|
|
88
|
+
// rawName: '-a',
|
|
89
|
+
// index: 0,
|
|
90
|
+
// value: undefined,
|
|
91
|
+
// inlineValue: undefined
|
|
92
|
+
// },
|
|
93
|
+
// {
|
|
94
|
+
// kind: 'option',
|
|
95
|
+
// name: '=',
|
|
96
|
+
// rawName: '-=',
|
|
97
|
+
// index: 0,
|
|
98
|
+
// value: undefined,
|
|
99
|
+
// inlineValue: undefined
|
|
100
|
+
// },
|
|
101
|
+
// {
|
|
102
|
+
// kind: 'option',
|
|
103
|
+
// name: '1',
|
|
104
|
+
// rawName: '-1',
|
|
105
|
+
// index: 0,
|
|
106
|
+
// value: undefined,
|
|
107
|
+
// inlineValue: undefined
|
|
108
|
+
// })
|
|
109
|
+
// ]
|
|
110
|
+
|
|
111
|
+
// args-tokens parseArgs tokens
|
|
112
|
+
const tokens = parseArgs(['-a=1'])
|
|
113
|
+
console.log(tokens)
|
|
114
|
+
|
|
115
|
+
// [
|
|
116
|
+
// {
|
|
117
|
+
// kind: 'option',
|
|
118
|
+
// name: 'a',
|
|
119
|
+
// rawName: '-a',
|
|
120
|
+
// index: 0,
|
|
121
|
+
// value: undefined,
|
|
122
|
+
// inlineValue: undefined
|
|
123
|
+
// },
|
|
124
|
+
// { kind: 'option', index: 0, value: '1', inlineValue: true }
|
|
125
|
+
// ]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## ๐ Usage
|
|
129
|
+
|
|
130
|
+
### ๐ฟ Installation
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
# npm
|
|
134
|
+
npm install --save args-tokens
|
|
135
|
+
|
|
136
|
+
## yarn
|
|
137
|
+
yarn add args-tokens
|
|
138
|
+
|
|
139
|
+
## pnpm
|
|
140
|
+
pnpm add args-tokens
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### ๐ญ Codes
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
import { parseArgs } from 'args-tokens'
|
|
147
|
+
|
|
148
|
+
const tokens = parseArgs(['--foo', 'bar', '-x', '--bar=baz'])
|
|
149
|
+
// do something with using tokens
|
|
150
|
+
// ...
|
|
151
|
+
console.log('tokens:', tokens)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Node.js `parseArgs` tokens compatible
|
|
155
|
+
|
|
156
|
+
If you want to use the same short options tokens as returned Node.js `parseArgs`, you can use `allowCompatible` parse option on `parseArgs`:
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
import { parseArgs as parseArgsNode } from 'node:util'
|
|
160
|
+
import { parseArgs } from 'args-tokens'
|
|
161
|
+
import { deepStrictEqual } from 'node:assert'
|
|
162
|
+
|
|
163
|
+
const args = ['-a=1', '2']
|
|
164
|
+
|
|
165
|
+
// Node.js parseArgs tokens
|
|
166
|
+
const { tokens: tokensNode } = parseArgsNode({
|
|
167
|
+
allowPositionals: true,
|
|
168
|
+
strict: false,
|
|
169
|
+
args,
|
|
170
|
+
tokens: true
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
// args-tokens parseArgs tokens
|
|
174
|
+
const tokens = parseArgs(['-a=1'])
|
|
175
|
+
|
|
176
|
+
// validate
|
|
177
|
+
deepStrictEqual(tokensNode, tokens)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## ๐ Contributing guidelines
|
|
181
|
+
|
|
182
|
+
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.
|
|
183
|
+
|
|
184
|
+
## ๐ Credits
|
|
185
|
+
|
|
186
|
+
This project is inspired by:
|
|
187
|
+
|
|
188
|
+
- [`utils.parseArgs`](https://nodejs.org/api/util.html#utilparseargsconfig), created by Node.js contributors and [OpenJS Foundation](https://openjsf.org/)
|
|
189
|
+
- [`pkgjs/parseargs`](https://github.com/pkgjs/parseargs), created by Node.js CLI package maintainers and Node.js community.
|
|
190
|
+
|
|
191
|
+
## ยฉ๏ธ License
|
|
192
|
+
|
|
193
|
+
[MIT](http://opensource.org/licenses/MIT)
|
|
194
|
+
|
|
195
|
+
<!-- Badges -->
|
|
196
|
+
|
|
197
|
+
[npm-version-src]: https://img.shields.io/npm/v/args-tokens?style=flat
|
|
198
|
+
[npm-version-href]: https://npmjs.com/package/args-tokens
|
|
199
|
+
[ci-src]: https://github.com/kazupon/args-tokens/actions/workflows/ci.yml/badge.svg
|
|
200
|
+
[ci-href]: https://github.com/kazupon/args-tokens/actions/workflows/ci.yml
|
package/lib/cjs/index.d.ts
CHANGED
|
@@ -1 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
type ArgTokenKind = 'option' | 'option-terminator' | 'positional';
|
|
2
|
+
export interface ArgToken {
|
|
3
|
+
kind: ArgTokenKind;
|
|
4
|
+
index: number;
|
|
5
|
+
name?: string;
|
|
6
|
+
rawName?: string;
|
|
7
|
+
value?: string;
|
|
8
|
+
inlineValue?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parse Options
|
|
12
|
+
*/
|
|
13
|
+
export interface ParseOptions {
|
|
14
|
+
/**
|
|
15
|
+
* [Node.js parseArgs](https://nodejs.org/api/util.html#parseargs-tokens) tokens compatible mode
|
|
16
|
+
* @default false
|
|
17
|
+
*/
|
|
18
|
+
allowCompatible?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parse command line arguments
|
|
22
|
+
* @param args command line arguments
|
|
23
|
+
* @param options parse options
|
|
24
|
+
* @returns argument tokens
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseArgs(args: string[], options?: ParseOptions): ArgToken[];
|
|
27
|
+
export {};
|
package/lib/cjs/index.js
CHANGED
|
@@ -1,6 +1,218 @@
|
|
|
1
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
|
|
2
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
5
|
-
|
|
9
|
+
exports.parseArgs = parseArgs;
|
|
10
|
+
const HYPHEN_CHAR = '-';
|
|
11
|
+
const HYPHEN_CODE = HYPHEN_CHAR.codePointAt(0);
|
|
12
|
+
const EQUAL_CHAR = '=';
|
|
13
|
+
const EQUAL_CODE = EQUAL_CHAR.codePointAt(0);
|
|
14
|
+
const TERMINATOR = '--';
|
|
15
|
+
const SHORT_OPTION_PREFIX = HYPHEN_CHAR;
|
|
16
|
+
const LONG_OPTION_PREFIX = '--';
|
|
17
|
+
/**
|
|
18
|
+
* Parse command line arguments
|
|
19
|
+
* @param args command line arguments
|
|
20
|
+
* @param options parse options
|
|
21
|
+
* @returns argument tokens
|
|
22
|
+
*/
|
|
23
|
+
function parseArgs(args, options = {}) {
|
|
24
|
+
const { allowCompatible = false } = options;
|
|
25
|
+
const tokens = [];
|
|
26
|
+
const remainings = [...args];
|
|
27
|
+
let index = -1;
|
|
28
|
+
let groupCount = 0;
|
|
29
|
+
let hasShortValueSeparator = false;
|
|
30
|
+
while (remainings.length > 0) {
|
|
31
|
+
const arg = remainings.shift();
|
|
32
|
+
if (arg == undefined) {
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
const nextArg = remainings[0];
|
|
36
|
+
if (groupCount > 0) {
|
|
37
|
+
groupCount--;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
index++;
|
|
41
|
+
}
|
|
42
|
+
// check if `arg` is an options terminator.
|
|
43
|
+
// guideline 10 in https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html
|
|
44
|
+
if (arg === TERMINATOR) {
|
|
45
|
+
tokens.push({
|
|
46
|
+
kind: 'option-terminator',
|
|
47
|
+
index
|
|
48
|
+
});
|
|
49
|
+
const mapped = remainings.map(arg => {
|
|
50
|
+
return { kind: 'positional', index: ++index, value: arg };
|
|
51
|
+
});
|
|
52
|
+
tokens.push(...mapped);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
if (isShortOption(arg)) {
|
|
56
|
+
const shortOption = arg.charAt(1);
|
|
57
|
+
let value;
|
|
58
|
+
let inlineValue;
|
|
59
|
+
if (groupCount) {
|
|
60
|
+
// e.g. `-abc`
|
|
61
|
+
tokens.push({
|
|
62
|
+
kind: 'option',
|
|
63
|
+
name: shortOption,
|
|
64
|
+
rawName: arg,
|
|
65
|
+
index,
|
|
66
|
+
value,
|
|
67
|
+
inlineValue
|
|
68
|
+
});
|
|
69
|
+
if (groupCount === 1 && hasOptionValue(nextArg)) {
|
|
70
|
+
value = remainings.shift();
|
|
71
|
+
if (hasShortValueSeparator) {
|
|
72
|
+
inlineValue = true;
|
|
73
|
+
hasShortValueSeparator = false;
|
|
74
|
+
}
|
|
75
|
+
tokens.push({
|
|
76
|
+
kind: 'option',
|
|
77
|
+
index,
|
|
78
|
+
value,
|
|
79
|
+
inlineValue
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// e.g. `-a`
|
|
85
|
+
tokens.push({
|
|
86
|
+
kind: 'option',
|
|
87
|
+
name: shortOption,
|
|
88
|
+
rawName: arg,
|
|
89
|
+
index,
|
|
90
|
+
value,
|
|
91
|
+
inlineValue
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// eslint-disable-next-line unicorn/no-null
|
|
95
|
+
if (value != null) {
|
|
96
|
+
++index;
|
|
97
|
+
}
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (isShortOptionGroup(arg)) {
|
|
101
|
+
// expend short option group (e.g. `-abc` => `-a -b -c`, `-f=bar` => `-f bar`)
|
|
102
|
+
const expanded = [];
|
|
103
|
+
let shortValue = '';
|
|
104
|
+
for (let i = 1; i < arg.length; i++) {
|
|
105
|
+
const shortableOption = arg.charAt(i);
|
|
106
|
+
if (hasShortValueSeparator) {
|
|
107
|
+
shortValue += shortableOption;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
if (!allowCompatible && shortableOption.codePointAt(0) === EQUAL_CODE) {
|
|
111
|
+
hasShortValueSeparator = true;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
expanded.push(`${SHORT_OPTION_PREFIX}${shortableOption}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (shortValue) {
|
|
119
|
+
expanded.push(shortValue);
|
|
120
|
+
}
|
|
121
|
+
remainings.unshift(...expanded);
|
|
122
|
+
groupCount = expanded.length;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (isLongOption(arg)) {
|
|
126
|
+
// e.g. `--foo`
|
|
127
|
+
const longOption = arg.slice(2);
|
|
128
|
+
tokens.push({
|
|
129
|
+
kind: 'option',
|
|
130
|
+
name: longOption,
|
|
131
|
+
rawName: arg,
|
|
132
|
+
index,
|
|
133
|
+
value: undefined,
|
|
134
|
+
inlineValue: undefined
|
|
135
|
+
});
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (isLongOptionAndValue(arg)) {
|
|
139
|
+
// e.g. `--foo=bar`
|
|
140
|
+
const equalIndex = arg.indexOf(EQUAL_CHAR);
|
|
141
|
+
const longOption = arg.slice(2, equalIndex);
|
|
142
|
+
const value = arg.slice(equalIndex + 1);
|
|
143
|
+
tokens.push({
|
|
144
|
+
kind: 'option',
|
|
145
|
+
name: longOption,
|
|
146
|
+
rawName: `${LONG_OPTION_PREFIX}${longOption}`,
|
|
147
|
+
index,
|
|
148
|
+
value,
|
|
149
|
+
inlineValue: true
|
|
150
|
+
});
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
tokens.push({
|
|
154
|
+
kind: 'positional',
|
|
155
|
+
index,
|
|
156
|
+
value: arg
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
return tokens;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Check if `arg` is a short option (e.g. `-f`)
|
|
163
|
+
* @param arg the argument to check
|
|
164
|
+
* @returns whether `arg` is a short option
|
|
165
|
+
*/
|
|
166
|
+
function isShortOption(arg) {
|
|
167
|
+
return (arg.length === 2 && arg.codePointAt(0) === HYPHEN_CODE && arg.codePointAt(1) !== HYPHEN_CODE);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Check if `arg` is a short option group (e.g. `-abc`)
|
|
171
|
+
* @param arg the argument to check
|
|
172
|
+
* @returns whether `arg` is a short option group
|
|
173
|
+
*/
|
|
174
|
+
function isShortOptionGroup(arg) {
|
|
175
|
+
if (arg.length <= 2) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
if (arg.codePointAt(0) !== HYPHEN_CODE) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
if (arg.codePointAt(1) === HYPHEN_CODE) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Check if `arg` is a long option (e.g. `--foo`)
|
|
188
|
+
* @param arg the argument to check
|
|
189
|
+
* @returns whether `arg` is a long option
|
|
190
|
+
*/
|
|
191
|
+
function isLongOption(arg) {
|
|
192
|
+
return hasLongOptionPrefix(arg) && !arg.includes(EQUAL_CHAR, 3);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Check if `arg` is a long option with value (e.g. `--foo=bar`)
|
|
196
|
+
* @param arg the argument to check
|
|
197
|
+
* @returns whether `arg` is a long option
|
|
198
|
+
*/
|
|
199
|
+
function isLongOptionAndValue(arg) {
|
|
200
|
+
return hasLongOptionPrefix(arg) && arg.includes(EQUAL_CHAR, 3);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Check if `arg` is a long option prefix (e.g. `--`)
|
|
204
|
+
* @param arg the argument to check
|
|
205
|
+
* @returns whether `arg` is a long option prefix
|
|
206
|
+
*/
|
|
207
|
+
function hasLongOptionPrefix(arg) {
|
|
208
|
+
return arg.length > 2 && ~arg.indexOf(LONG_OPTION_PREFIX);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Check if a `value` is an option value
|
|
212
|
+
* @param value a value to check
|
|
213
|
+
* @returns whether a `value` is an option value
|
|
214
|
+
*/
|
|
215
|
+
function hasOptionValue(value) {
|
|
216
|
+
// eslint-disable-next-line unicorn/no-null
|
|
217
|
+
return !(value == null) && value.codePointAt(0) !== HYPHEN_CODE;
|
|
6
218
|
}
|
package/lib/esm/index.d.ts
CHANGED
|
@@ -1 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
type ArgTokenKind = 'option' | 'option-terminator' | 'positional';
|
|
2
|
+
export interface ArgToken {
|
|
3
|
+
kind: ArgTokenKind;
|
|
4
|
+
index: number;
|
|
5
|
+
name?: string;
|
|
6
|
+
rawName?: string;
|
|
7
|
+
value?: string;
|
|
8
|
+
inlineValue?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Parse Options
|
|
12
|
+
*/
|
|
13
|
+
export interface ParseOptions {
|
|
14
|
+
/**
|
|
15
|
+
* [Node.js parseArgs](https://nodejs.org/api/util.html#parseargs-tokens) tokens compatible mode
|
|
16
|
+
* @default false
|
|
17
|
+
*/
|
|
18
|
+
allowCompatible?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parse command line arguments
|
|
22
|
+
* @param args command line arguments
|
|
23
|
+
* @param options parse options
|
|
24
|
+
* @returns argument tokens
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseArgs(args: string[], options?: ParseOptions): ArgToken[];
|
|
27
|
+
export {};
|
package/lib/esm/index.js
CHANGED
|
@@ -1,3 +1,215 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Modifier: kazuya kawaguchi (a.k.a. kazupon)
|
|
3
|
+
// Forked from `nodejs/node` (`pkgjs/parseargs`)
|
|
4
|
+
// Repository url: https://github.com/nodejs/node (https://github.com/pkgjs/parseargs)
|
|
5
|
+
// Author: Node.js contributors
|
|
6
|
+
// Code url: https://github.com/nodejs/node/blob/main/lib/internal/util/parse_args/parse_args.js
|
|
7
|
+
const HYPHEN_CHAR = '-';
|
|
8
|
+
const HYPHEN_CODE = HYPHEN_CHAR.codePointAt(0);
|
|
9
|
+
const EQUAL_CHAR = '=';
|
|
10
|
+
const EQUAL_CODE = EQUAL_CHAR.codePointAt(0);
|
|
11
|
+
const TERMINATOR = '--';
|
|
12
|
+
const SHORT_OPTION_PREFIX = HYPHEN_CHAR;
|
|
13
|
+
const LONG_OPTION_PREFIX = '--';
|
|
14
|
+
/**
|
|
15
|
+
* Parse command line arguments
|
|
16
|
+
* @param args command line arguments
|
|
17
|
+
* @param options parse options
|
|
18
|
+
* @returns argument tokens
|
|
19
|
+
*/
|
|
20
|
+
export function parseArgs(args, options = {}) {
|
|
21
|
+
const { allowCompatible = false } = options;
|
|
22
|
+
const tokens = [];
|
|
23
|
+
const remainings = [...args];
|
|
24
|
+
let index = -1;
|
|
25
|
+
let groupCount = 0;
|
|
26
|
+
let hasShortValueSeparator = false;
|
|
27
|
+
while (remainings.length > 0) {
|
|
28
|
+
const arg = remainings.shift();
|
|
29
|
+
if (arg == undefined) {
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
const nextArg = remainings[0];
|
|
33
|
+
if (groupCount > 0) {
|
|
34
|
+
groupCount--;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
index++;
|
|
38
|
+
}
|
|
39
|
+
// check if `arg` is an options terminator.
|
|
40
|
+
// guideline 10 in https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html
|
|
41
|
+
if (arg === TERMINATOR) {
|
|
42
|
+
tokens.push({
|
|
43
|
+
kind: 'option-terminator',
|
|
44
|
+
index
|
|
45
|
+
});
|
|
46
|
+
const mapped = remainings.map(arg => {
|
|
47
|
+
return { kind: 'positional', index: ++index, value: arg };
|
|
48
|
+
});
|
|
49
|
+
tokens.push(...mapped);
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
if (isShortOption(arg)) {
|
|
53
|
+
const shortOption = arg.charAt(1);
|
|
54
|
+
let value;
|
|
55
|
+
let inlineValue;
|
|
56
|
+
if (groupCount) {
|
|
57
|
+
// e.g. `-abc`
|
|
58
|
+
tokens.push({
|
|
59
|
+
kind: 'option',
|
|
60
|
+
name: shortOption,
|
|
61
|
+
rawName: arg,
|
|
62
|
+
index,
|
|
63
|
+
value,
|
|
64
|
+
inlineValue
|
|
65
|
+
});
|
|
66
|
+
if (groupCount === 1 && hasOptionValue(nextArg)) {
|
|
67
|
+
value = remainings.shift();
|
|
68
|
+
if (hasShortValueSeparator) {
|
|
69
|
+
inlineValue = true;
|
|
70
|
+
hasShortValueSeparator = false;
|
|
71
|
+
}
|
|
72
|
+
tokens.push({
|
|
73
|
+
kind: 'option',
|
|
74
|
+
index,
|
|
75
|
+
value,
|
|
76
|
+
inlineValue
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// e.g. `-a`
|
|
82
|
+
tokens.push({
|
|
83
|
+
kind: 'option',
|
|
84
|
+
name: shortOption,
|
|
85
|
+
rawName: arg,
|
|
86
|
+
index,
|
|
87
|
+
value,
|
|
88
|
+
inlineValue
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
// eslint-disable-next-line unicorn/no-null
|
|
92
|
+
if (value != null) {
|
|
93
|
+
++index;
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (isShortOptionGroup(arg)) {
|
|
98
|
+
// expend short option group (e.g. `-abc` => `-a -b -c`, `-f=bar` => `-f bar`)
|
|
99
|
+
const expanded = [];
|
|
100
|
+
let shortValue = '';
|
|
101
|
+
for (let i = 1; i < arg.length; i++) {
|
|
102
|
+
const shortableOption = arg.charAt(i);
|
|
103
|
+
if (hasShortValueSeparator) {
|
|
104
|
+
shortValue += shortableOption;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
if (!allowCompatible && shortableOption.codePointAt(0) === EQUAL_CODE) {
|
|
108
|
+
hasShortValueSeparator = true;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
expanded.push(`${SHORT_OPTION_PREFIX}${shortableOption}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (shortValue) {
|
|
116
|
+
expanded.push(shortValue);
|
|
117
|
+
}
|
|
118
|
+
remainings.unshift(...expanded);
|
|
119
|
+
groupCount = expanded.length;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (isLongOption(arg)) {
|
|
123
|
+
// e.g. `--foo`
|
|
124
|
+
const longOption = arg.slice(2);
|
|
125
|
+
tokens.push({
|
|
126
|
+
kind: 'option',
|
|
127
|
+
name: longOption,
|
|
128
|
+
rawName: arg,
|
|
129
|
+
index,
|
|
130
|
+
value: undefined,
|
|
131
|
+
inlineValue: undefined
|
|
132
|
+
});
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (isLongOptionAndValue(arg)) {
|
|
136
|
+
// e.g. `--foo=bar`
|
|
137
|
+
const equalIndex = arg.indexOf(EQUAL_CHAR);
|
|
138
|
+
const longOption = arg.slice(2, equalIndex);
|
|
139
|
+
const value = arg.slice(equalIndex + 1);
|
|
140
|
+
tokens.push({
|
|
141
|
+
kind: 'option',
|
|
142
|
+
name: longOption,
|
|
143
|
+
rawName: `${LONG_OPTION_PREFIX}${longOption}`,
|
|
144
|
+
index,
|
|
145
|
+
value,
|
|
146
|
+
inlineValue: true
|
|
147
|
+
});
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
tokens.push({
|
|
151
|
+
kind: 'positional',
|
|
152
|
+
index,
|
|
153
|
+
value: arg
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return tokens;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Check if `arg` is a short option (e.g. `-f`)
|
|
160
|
+
* @param arg the argument to check
|
|
161
|
+
* @returns whether `arg` is a short option
|
|
162
|
+
*/
|
|
163
|
+
function isShortOption(arg) {
|
|
164
|
+
return (arg.length === 2 && arg.codePointAt(0) === HYPHEN_CODE && arg.codePointAt(1) !== HYPHEN_CODE);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Check if `arg` is a short option group (e.g. `-abc`)
|
|
168
|
+
* @param arg the argument to check
|
|
169
|
+
* @returns whether `arg` is a short option group
|
|
170
|
+
*/
|
|
171
|
+
function isShortOptionGroup(arg) {
|
|
172
|
+
if (arg.length <= 2) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
if (arg.codePointAt(0) !== HYPHEN_CODE) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
if (arg.codePointAt(1) === HYPHEN_CODE) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Check if `arg` is a long option (e.g. `--foo`)
|
|
185
|
+
* @param arg the argument to check
|
|
186
|
+
* @returns whether `arg` is a long option
|
|
187
|
+
*/
|
|
188
|
+
function isLongOption(arg) {
|
|
189
|
+
return hasLongOptionPrefix(arg) && !arg.includes(EQUAL_CHAR, 3);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Check if `arg` is a long option with value (e.g. `--foo=bar`)
|
|
193
|
+
* @param arg the argument to check
|
|
194
|
+
* @returns whether `arg` is a long option
|
|
195
|
+
*/
|
|
196
|
+
function isLongOptionAndValue(arg) {
|
|
197
|
+
return hasLongOptionPrefix(arg) && arg.includes(EQUAL_CHAR, 3);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Check if `arg` is a long option prefix (e.g. `--`)
|
|
201
|
+
* @param arg the argument to check
|
|
202
|
+
* @returns whether `arg` is a long option prefix
|
|
203
|
+
*/
|
|
204
|
+
function hasLongOptionPrefix(arg) {
|
|
205
|
+
return arg.length > 2 && ~arg.indexOf(LONG_OPTION_PREFIX);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Check if a `value` is an option value
|
|
209
|
+
* @param value a value to check
|
|
210
|
+
* @returns whether a `value` is an option value
|
|
211
|
+
*/
|
|
212
|
+
function hasOptionValue(value) {
|
|
213
|
+
// eslint-disable-next-line unicorn/no-null
|
|
214
|
+
return !(value == null) && value.codePointAt(0) !== HYPHEN_CODE;
|
|
3
215
|
}
|
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.
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "kazuya kawaguchi",
|
|
7
7
|
"email": "kawakazu80@gmail.com"
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"engines": {
|
|
29
29
|
"node": ">= 18.18"
|
|
30
30
|
},
|
|
31
|
+
"type": "module",
|
|
31
32
|
"files": [
|
|
32
33
|
"lib"
|
|
33
34
|
],
|
|
@@ -70,6 +71,7 @@
|
|
|
70
71
|
"gh-changelogen": "^0.2.8",
|
|
71
72
|
"knip": "^5.44.0",
|
|
72
73
|
"lint-staged": "^15.4.3",
|
|
74
|
+
"mitata": "^1.0.34",
|
|
73
75
|
"pkg-pr-new": "^0.0.39",
|
|
74
76
|
"prettier": "^3.5.0",
|
|
75
77
|
"typescript": "^5.7.3",
|
|
@@ -91,6 +93,8 @@
|
|
|
91
93
|
]
|
|
92
94
|
},
|
|
93
95
|
"scripts": {
|
|
96
|
+
"bench:mitata": "node --expose-gc bench/index.mjs",
|
|
97
|
+
"bench:vitest": "vitest bench --run",
|
|
94
98
|
"build": "pnpm run --parallel --color \"/^build:/\"",
|
|
95
99
|
"build:cjs": "tsc -p ./tsconfig.cjs.json",
|
|
96
100
|
"build:esm": "tsc -p ./tsconfig.esm.json",
|