args-tokens 0.2.4 → 0.3.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.
@@ -0,0 +1,167 @@
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 currentShortOption;
13
+ const expandableShortOptions = [];
14
+ function toValue() {
15
+ if (expandableShortOptions.length === 0) {
16
+ return undefined;
17
+ }
18
+ else {
19
+ const value = expandableShortOptions.map(token => token.name).join('');
20
+ expandableShortOptions.length = 0;
21
+ return value;
22
+ }
23
+ }
24
+ function applyShortOptionValue() {
25
+ if (currentShortOption) {
26
+ currentShortOption.value = toValue();
27
+ shortOptionTokens.push({ ...currentShortOption });
28
+ currentShortOption = undefined;
29
+ }
30
+ }
31
+ /**
32
+ * separate tokens into positionals, long options, and short options, after that resolve values
33
+ */
34
+ // eslint-disable-next-line unicorn/no-for-loop
35
+ for (let i = 0; i < tokens.length; i++) {
36
+ const token = tokens[i];
37
+ if (token.kind === 'positional') {
38
+ positionals.push(token.value);
39
+ applyShortOptionValue(); // check if previous short option is not resolved
40
+ }
41
+ else if (token.kind === 'option') {
42
+ if (token.rawName) {
43
+ if ((0, parser_js_1.hasLongOptionPrefix)(token.rawName)) {
44
+ longOptionTokens.push({ ...token });
45
+ applyShortOptionValue(); // check if previous short option is not resolved
46
+ }
47
+ else if ((0, parser_js_1.isShortOption)(token.rawName)) {
48
+ if (currentShortOption) {
49
+ if (currentShortOption.index === token.index) {
50
+ expandableShortOptions.push({ ...token });
51
+ }
52
+ else {
53
+ currentShortOption.value = toValue();
54
+ shortOptionTokens.push({ ...currentShortOption });
55
+ currentShortOption = { ...token };
56
+ }
57
+ }
58
+ else {
59
+ currentShortOption = { ...token };
60
+ }
61
+ }
62
+ }
63
+ else {
64
+ // short option value
65
+ if (currentShortOption && currentShortOption.index == token.index) {
66
+ currentShortOption.value = token.value;
67
+ shortOptionTokens.push({ ...currentShortOption });
68
+ currentShortOption = undefined;
69
+ }
70
+ }
71
+ }
72
+ else {
73
+ applyShortOptionValue(); // check if previous short option is not resolved
74
+ }
75
+ }
76
+ /**
77
+ * check if the last short option is not resolved
78
+ */
79
+ applyShortOptionValue();
80
+ /**
81
+ * resolve values
82
+ */
83
+ for (const [option, schema] of Object.entries(options)) {
84
+ if (longOptionTokens.length === 0 && shortOptionTokens.length === 0 && schema.required) {
85
+ throw createRequireError(option, schema);
86
+ }
87
+ // eslint-disable-next-line unicorn/no-for-loop
88
+ for (let i = 0; i < longOptionTokens.length; i++) {
89
+ const token = longOptionTokens[i];
90
+ // eslint-disable-next-line unicorn/no-null
91
+ if (option === token.name && token.rawName != null && (0, parser_js_1.hasLongOptionPrefix)(token.rawName)) {
92
+ validateRequire(token, option, schema);
93
+ if (schema.type !== 'boolean') {
94
+ validateValue(token, option, schema);
95
+ }
96
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
97
+ ;
98
+ values[option] = resolveOptionValue(token, schema);
99
+ continue;
100
+ }
101
+ }
102
+ // eslint-disable-next-line unicorn/no-for-loop
103
+ for (let i = 0; i < shortOptionTokens.length; i++) {
104
+ const token = shortOptionTokens[i];
105
+ // eslint-disable-next-line unicorn/no-null
106
+ if (schema.short === token.name && token.rawName != null && (0, parser_js_1.isShortOption)(token.rawName)) {
107
+ validateRequire(token, option, schema);
108
+ if (schema.type !== 'boolean') {
109
+ validateValue(token, option, schema);
110
+ }
111
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
112
+ ;
113
+ values[option] = resolveOptionValue(token, schema);
114
+ continue;
115
+ }
116
+ }
117
+ // eslint-disable-next-line unicorn/no-null
118
+ if (values[option] == null && schema.default != null) {
119
+ // check if the default value is in values
120
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
121
+ ;
122
+ values[option] = schema.default;
123
+ }
124
+ }
125
+ return { values, positionals };
126
+ }
127
+ function createRequireError(option, schema) {
128
+ return new Error(`Option '--${option}' ${schema.short ? `or '-${schema.short}'` : ''} is required`);
129
+ }
130
+ function validateRequire(token, option, schema) {
131
+ if (schema.required && schema.type !== 'boolean' && !token.value) {
132
+ throw createRequireError(option, schema);
133
+ }
134
+ }
135
+ function validateValue(token, option, schema) {
136
+ switch (schema.type) {
137
+ case 'number': {
138
+ if (!isNumeric(token.value)) {
139
+ throw createTypeError(option, schema);
140
+ }
141
+ break;
142
+ }
143
+ case 'string': {
144
+ if (typeof token.value !== 'string') {
145
+ throw createTypeError(option, schema);
146
+ }
147
+ break;
148
+ }
149
+ }
150
+ }
151
+ function isNumeric(str) {
152
+ // @ts-ignore
153
+ // eslint-disable-next-line unicorn/prefer-number-properties
154
+ return str.trim() !== '' && !isNaN(str);
155
+ }
156
+ function createTypeError(option, schema) {
157
+ return new TypeError(`Option '--${option}' ${schema.short ? `or '-${schema.short}'` : ''} should be '${schema.type}'`);
158
+ }
159
+ function resolveOptionValue(token, schema) {
160
+ if (token.value) {
161
+ return schema.type === 'number' ? +token.value : token.value;
162
+ }
163
+ if (schema.type === 'boolean') {
164
+ return true;
165
+ }
166
+ return schema.type === 'number' ? +(schema.default || '') : schema.default;
167
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ (0, vitest_1.test)('ArgValues', () => {
5
+ (0, vitest_1.expectTypeOf)().toEqualTypeOf();
6
+ });
@@ -1,27 +1,3 @@
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 {};
1
+ export { parseArgs } from './parser.js';
2
+ export * from './resolver.js';
3
+ export type { ArgToken, ParseOptions } from './parser';
package/lib/esm/index.js CHANGED
@@ -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;
215
- }
1
+ export { parseArgs } from './parser.js';
2
+ export * from './resolver.js';
@@ -0,0 +1,77 @@
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
+ * Parse Options
40
+ */
41
+ export interface ParseOptions {
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?: ParseOptions): 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): number | false;
77
+ export {};