args-tokens 0.17.1 → 0.19.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/lib/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ArgToken, ParserOptions, parseArgs$1 as parseArgs } from "./parser-Bx112mWZ.js";
2
- import { ArgResolveError$1 as ArgResolveError, ArgResolveErrorType, ArgSchema, ArgValues, Args, ResolveArgs, resolveArgs$1 as resolveArgs } from "./resolver--lXziDSy.js";
2
+ import { ArgResolveError$1 as ArgResolveError, ArgResolveErrorType, ArgSchema, ArgValues, Args, ResolveArgs, resolveArgs$1 as resolveArgs } from "./resolver-COGeGqd3.js";
3
3
 
4
4
  //#region src/parse.d.ts
5
5
  /**
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { parseArgs } from "./parser-Dr4iAGaX.js";
2
- import { ArgResolveError, resolveArgs } from "./resolver-DuUwC6Fy.js";
2
+ import { ArgResolveError, resolveArgs } from "./resolver-Df1uPA8Z.js";
3
3
 
4
4
  //#region src/parse.ts
5
5
  const DEFAULT_OPTIONS = {
@@ -5,27 +5,25 @@ import { ArgToken } from "./parser-Bx112mWZ.js";
5
5
  * An argument schema
6
6
  * This schema is similar to the schema of the `node:utils`.
7
7
  * difference is that:
8
- * - `multiple` property is not supported
9
8
  * - `required` property and `description` property are added
10
- * - `type` is not only 'string' and 'boolean', but also 'number', 'enum' and 'positional' too.
9
+ * - `type` is not only 'string' and 'boolean', but also 'number', 'enum', 'positional', 'custom' too.
11
10
  * - `default` property type, not support multiple types
12
11
  */
13
12
  /**
14
13
  * An argument schema
15
14
  * This schema is similar to the schema of the `node:utils`.
16
15
  * difference is that:
17
- * - `multiple` property is not supported
18
16
  * - `required` property and `description` property are added
19
- * - `type` is not only 'string' and 'boolean', but also 'number', 'enum' and 'positional' too.
17
+ * - `type` is not only 'string' and 'boolean', but also 'number', 'enum', 'positional', 'custom' too.
20
18
  * - `default` property type, not support multiple types
21
19
  */
22
20
  interface ArgSchema {
23
21
  /**
24
22
  * Type of argument.
25
23
  */
26
- type: "string" | "boolean" | "number" | "enum" | "positional";
24
+ type: "string" | "boolean" | "number" | "enum" | "positional" | "custom";
27
25
  /**
28
- * A single character alias for the option.
26
+ * A single character alias for the argument.
29
27
  */
30
28
  short?: string;
31
29
  /**
@@ -37,6 +35,10 @@ interface ArgSchema {
37
35
  */
38
36
  required?: true;
39
37
  /**
38
+ * Whether the argument allow multiple values or not.
39
+ */
40
+ multiple?: true;
41
+ /**
40
42
  * Whether the negatable option for `boolean` type
41
43
  */
42
44
  negatable?: boolean;
@@ -49,6 +51,19 @@ interface ArgSchema {
49
51
  * if the type is 'enum', the default value must be one of the allowed values.
50
52
  */
51
53
  default?: string | boolean | number;
54
+ /**
55
+ * Whether to convert the argument name to kebab-case.
56
+ */
57
+ toKebab?: true;
58
+ /**
59
+ * A function to parse the value of the argument. if the type is 'custom', this function is required.
60
+ * If argument value will be invalid, this function have to throw an error.
61
+ * @param value
62
+ * @returns Parsed value
63
+ * @throws An Error, If the value is invalid. Error type should be `Error` or extends it
64
+ */
65
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
66
+ parse?: (value: string) => any;
52
67
  }
53
68
  /**
54
69
  * An object that contains {@link ArgSchema | argument schema}.
@@ -60,12 +75,15 @@ interface Args {
60
75
  * An object that contains the values of the arguments.
61
76
  */
62
77
  type ArgValues<T> = T extends Args ? ResolveArgValues<T, { [Arg in keyof T]: ExtractOptionValue<T[Arg]> }> : {
63
- [option: string]: string | boolean | number | undefined;
78
+ [option: string]: string | boolean | number | (string | boolean | number)[] | undefined;
64
79
  };
80
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
+ type IsFunction<T> = T extends ((...args: any[]) => any) ? true : false;
65
82
  /**
66
83
  * @internal
67
84
  */
68
- type ExtractOptionValue<A extends ArgSchema> = A["type"] extends "string" ? string : A["type"] extends "boolean" ? boolean : A["type"] extends "number" ? number : A["type"] extends "positional" ? string : A["type"] extends "enum" ? A["choices"] extends string[] | readonly string[] ? A["choices"][number] : never : string | boolean | number;
85
+ type ExtractOptionValue<A extends ArgSchema> = A["type"] extends "string" ? ResolveOptionValue<A, string> : A["type"] extends "boolean" ? ResolveOptionValue<A, boolean> : A["type"] extends "number" ? ResolveOptionValue<A, number> : A["type"] extends "positional" ? ResolveOptionValue<A, string> : A["type"] extends "enum" ? A["choices"] extends string[] | readonly string[] ? ResolveOptionValue<A, A["choices"][number]> : never : A["type"] extends "custom" ? IsFunction<A["parse"]> extends true ? ResolveOptionValue<A, ReturnType<NonNullable<A["parse"]>>> : never : ResolveOptionValue<A, string | boolean | number>;
86
+ type ResolveOptionValue<A extends ArgSchema, T> = A["multiple"] extends true ? T[] : T;
69
87
  /**
70
88
  * @internal
71
89
  */
@@ -79,16 +97,21 @@ type FilterArgs<A extends Args, V extends Record<keyof A, unknown>, K extends ke
79
97
  */
80
98
  interface ResolveArgs {
81
99
  /**
82
- * Whether to group short options.
100
+ * Whether to group short arguments.
83
101
  * @default false
84
102
  * @see guideline 5 in https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap12.html
85
103
  */
86
- optionGrouping?: boolean;
104
+ shortGrouping?: boolean;
87
105
  /**
88
106
  * Skip positional arguments index.
89
107
  * @default -1
90
108
  */
91
109
  skipPositional?: number;
110
+ /**
111
+ * Whether to convert the argument name to kebab-case. This option is applied to all arguments as `toKebab: true`, if set to `true`.
112
+ * @default false
113
+ */
114
+ toKebab?: boolean;
92
115
  }
93
116
  /**
94
117
  * Resolve command line arguments.
@@ -98,8 +121,9 @@ interface ResolveArgs {
98
121
  * @returns An object that contains the values of the arguments, positional arguments, rest arguments, and {@link AggregateError | validation errors}.
99
122
  */
100
123
  declare function resolveArgs<A extends Args>(args: A, tokens: ArgToken[], {
101
- optionGrouping,
102
- skipPositional
124
+ shortGrouping,
125
+ skipPositional,
126
+ toKebab
103
127
  }?: ResolveArgs): {
104
128
  values: ArgValues<A>;
105
129
  positionals: string[];
@@ -2,6 +2,9 @@ import { hasLongOptionPrefix, isShortOption } from "./parser-Dr4iAGaX.js";
2
2
 
3
3
  //#region src/resolver.ts
4
4
  const SKIP_POSITIONAL_DEFAULT = -1;
5
+ function kebabnize(str) {
6
+ return str.replace(/[A-Z]/g, (match, offset) => (offset > 0 ? "-" : "") + match.toLowerCase());
7
+ }
5
8
  /**
6
9
  * Resolve command line arguments.
7
10
  * @param args - An arguments that contains {@link ArgSchema | arguments schema}.
@@ -9,11 +12,10 @@ const SKIP_POSITIONAL_DEFAULT = -1;
9
12
  * @param resolveArgs - An arguments that contains {@link ResolveArgs | resolve arguments}.
10
13
  * @returns An object that contains the values of the arguments, positional arguments, rest arguments, and {@link AggregateError | validation errors}.
11
14
  */
12
- function resolveArgs(args, tokens, { optionGrouping = false, skipPositional = SKIP_POSITIONAL_DEFAULT } = {}) {
15
+ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKIP_POSITIONAL_DEFAULT, toKebab = false } = {}) {
13
16
  const skipPositionalIndex = typeof skipPositional === "number" ? Math.max(skipPositional, SKIP_POSITIONAL_DEFAULT) : SKIP_POSITIONAL_DEFAULT;
14
17
  const rest = [];
15
- const longOptionTokens = [];
16
- const shortOptionTokens = [];
18
+ const optionTokens = [];
17
19
  const positionalTokens = [];
18
20
  let currentLongOption;
19
21
  let currentShortOption;
@@ -29,14 +31,14 @@ function resolveArgs(args, tokens, { optionGrouping = false, skipPositional = SK
29
31
  function applyLongOptionValue(value = void 0) {
30
32
  if (currentLongOption) {
31
33
  currentLongOption.value = value;
32
- longOptionTokens.push({ ...currentLongOption });
34
+ optionTokens.push({ ...currentLongOption });
33
35
  currentLongOption = void 0;
34
36
  }
35
37
  }
36
38
  function applyShortOptionValue(value = void 0) {
37
39
  if (currentShortOption) {
38
40
  currentShortOption.value = value || toShortValue();
39
- shortOptionTokens.push({ ...currentShortOption });
41
+ optionTokens.push({ ...currentShortOption });
40
42
  currentShortOption = void 0;
41
43
  }
42
44
  }
@@ -65,18 +67,18 @@ function resolveArgs(args, tokens, { optionGrouping = false, skipPositional = SK
65
67
  } else if (token.kind === "option") if (token.rawName) {
66
68
  if (hasLongOptionPrefix(token.rawName)) {
67
69
  applyLongOptionValue();
68
- if (token.inlineValue) longOptionTokens.push({ ...token });
70
+ if (token.inlineValue) optionTokens.push({ ...token });
69
71
  else currentLongOption = { ...token };
70
72
  applyShortOptionValue();
71
73
  } else if (isShortOption(token.rawName)) if (currentShortOption) {
72
- if (currentShortOption.index === token.index) if (optionGrouping) {
74
+ if (currentShortOption.index === token.index) if (shortGrouping) {
73
75
  currentShortOption.value = token.value;
74
- shortOptionTokens.push({ ...currentShortOption });
76
+ optionTokens.push({ ...currentShortOption });
75
77
  currentShortOption = { ...token };
76
78
  } else expandableShortOptions.push({ ...token });
77
79
  else {
78
80
  currentShortOption.value = toShortValue();
79
- shortOptionTokens.push({ ...currentShortOption });
81
+ optionTokens.push({ ...currentShortOption });
80
82
  currentShortOption = { ...token };
81
83
  }
82
84
  applyLongOptionValue();
@@ -87,7 +89,7 @@ function resolveArgs(args, tokens, { optionGrouping = false, skipPositional = SK
87
89
  } else {
88
90
  if (currentShortOption && currentShortOption.index == token.index && token.inlineValue) {
89
91
  currentShortOption.value = token.value;
90
- shortOptionTokens.push({ ...currentShortOption });
92
+ optionTokens.push({ ...currentShortOption });
91
93
  currentShortOption = void 0;
92
94
  }
93
95
  applyLongOptionValue();
@@ -116,63 +118,43 @@ function resolveArgs(args, tokens, { optionGrouping = false, skipPositional = SK
116
118
  return Math.min(skipPositionalIndex, positionalItemCount);
117
119
  }
118
120
  let positionalsCount = 0;
119
- for (const [option, schema] of Object.entries(args)) {
121
+ for (const [rawArg, schema] of Object.entries(args)) {
122
+ const arg = toKebab || schema.toKebab ? kebabnize(rawArg) : rawArg;
120
123
  if (schema.required) {
121
- const found = longOptionTokens.find((token) => token.name === option) || schema.short && shortOptionTokens.find((token) => token.name === schema.short);
124
+ const found = optionTokens.find((token) => {
125
+ return schema.short && token.name === schema.short || token.rawName && hasLongOptionPrefix(token.rawName) && token.name === arg;
126
+ });
122
127
  if (!found) {
123
- errors.push(createRequireError(option, schema));
128
+ errors.push(createRequireError(arg, schema));
124
129
  continue;
125
130
  }
126
131
  }
127
132
  if (schema.type === "positional") {
128
133
  if (skipPositionalIndex > SKIP_POSITIONAL_DEFAULT) while (positionalsCount <= getPositionalSkipIndex()) positionalsCount++;
129
134
  const positional = positionalTokens[positionalsCount];
130
- if (positional != null) values[option] = positional.value;
131
- else errors.push(createRequireError(option, schema));
135
+ if (positional != null) values[rawArg] = positional.value;
136
+ else errors.push(createRequireError(arg, schema));
132
137
  positionalsCount++;
133
138
  continue;
134
139
  }
135
- for (let i = 0; i < longOptionTokens.length; i++) {
136
- const token = longOptionTokens[i];
137
- if (checkTokenName(option, schema, token) && token.rawName != void 0 && hasLongOptionPrefix(token.rawName)) {
138
- const invalid = validateRequire(token, option, schema);
139
- if (invalid) {
140
- errors.push(invalid);
141
- continue;
142
- }
143
- if (schema.type === "boolean") token.value = void 0;
144
- else {
145
- const invalid$1 = validateValue(token, option, schema);
146
- if (invalid$1) {
147
- errors.push(invalid$1);
148
- continue;
149
- }
150
- }
151
- values[option] = resolveArgumentValue(token, schema);
152
- continue;
153
- }
154
- }
155
- for (let i = 0; i < shortOptionTokens.length; i++) {
156
- const token = shortOptionTokens[i];
157
- if (schema.short === token.name && token.rawName != null && isShortOption(token.rawName)) {
158
- const invalid = validateRequire(token, option, schema);
140
+ for (let i = 0; i < optionTokens.length; i++) {
141
+ const token = optionTokens[i];
142
+ if (checkTokenName(arg, schema, token) && token.rawName != void 0 && hasLongOptionPrefix(token.rawName) || schema.short === token.name && token.rawName != void 0 && isShortOption(token.rawName)) {
143
+ const invalid = validateRequire(token, arg, schema);
159
144
  if (invalid) {
160
145
  errors.push(invalid);
161
146
  continue;
162
147
  }
163
148
  if (schema.type === "boolean") token.value = void 0;
164
- else {
165
- const invalid$1 = validateValue(token, option, schema);
166
- if (invalid$1) {
167
- errors.push(invalid$1);
168
- continue;
169
- }
170
- }
171
- values[option] = resolveArgumentValue(token, schema);
172
- continue;
149
+ const [parsedValue, error] = parse(token, arg, schema);
150
+ if (error) errors.push(error);
151
+ else if (schema.multiple) {
152
+ values[rawArg] ||= [];
153
+ values[rawArg].push(parsedValue);
154
+ } else values[rawArg] = parsedValue;
173
155
  }
174
156
  }
175
- if (values[option] == null && schema.default != null) values[option] = schema.default;
157
+ if (values[rawArg] == null && schema.default != null) values[rawArg] = schema.default;
176
158
  }
177
159
  return {
178
160
  values,
@@ -181,6 +163,29 @@ function resolveArgs(args, tokens, { optionGrouping = false, skipPositional = SK
181
163
  error: errors.length > 0 ? new AggregateError(errors) : void 0
182
164
  };
183
165
  }
166
+ function parse(token, option, schema) {
167
+ switch (schema.type) {
168
+ case "string": return typeof token.value === "string" ? [token.value || schema.default, void 0] : [void 0, createTypeError(option, schema)];
169
+ case "boolean": return token.value ? [token.value || schema.default, void 0] : [!(schema.negatable && token.name.startsWith("no-")), void 0];
170
+ case "number": {
171
+ if (!isNumeric(token.value)) return [void 0, createTypeError(option, schema)];
172
+ return token.value ? [+token.value, void 0] : [+(schema.default || ""), void 0];
173
+ }
174
+ case "enum": {
175
+ if (schema.choices && !schema.choices.includes(token.value)) return [void 0, new ArgResolveError(`Optional argument '--${option}' ${schema.short ? `or '-${schema.short}' ` : ""}should be chosen from '${schema.type}' [${schema.choices.map((c) => JSON.stringify(c)).join(", ")}] values`, option, "type", schema)];
176
+ return [token.value || schema.default, void 0];
177
+ }
178
+ case "custom": {
179
+ if (typeof schema.parse !== "function") throw new TypeError(`argument '${option}' should have a 'parse' function`);
180
+ try {
181
+ return [schema.parse(token.value || String(schema.default || "")), void 0];
182
+ } catch (error) {
183
+ return [void 0, error];
184
+ }
185
+ }
186
+ default: throw new Error(`Unsupported argument type '${schema.type}' for option '${option}'`);
187
+ }
188
+ }
184
189
  function createRequireError(option, schema) {
185
190
  const message = schema.type === "positional" ? `Positional argument '${option}' is required` : `Optional argument '--${option}' ${schema.short ? `or '-${schema.short}' ` : ""}is required`;
186
191
  return new ArgResolveError(message, option, "required", schema);
@@ -203,33 +208,12 @@ var ArgResolveError = class extends Error {
203
208
  function validateRequire(token, option, schema) {
204
209
  if (schema.required && schema.type !== "boolean" && !token.value) return createRequireError(option, schema);
205
210
  }
206
- function validateValue(token, option, schema) {
207
- switch (schema.type) {
208
- case "number": {
209
- if (!isNumeric(token.value)) return createTypeError(option, schema);
210
- break;
211
- }
212
- case "string": {
213
- if (typeof token.value !== "string") return createTypeError(option, schema);
214
- break;
215
- }
216
- case "enum": {
217
- if (schema.choices && !schema.choices.includes(token.value)) return new ArgResolveError(`Optional argument '--${option}' ${schema.short ? `or '-${schema.short}' ` : ""}should be chosen from '${schema.type}' [${schema.choices.map((c) => JSON.stringify(c)).join(", ")}] values`, option, "type", schema);
218
- break;
219
- }
220
- }
221
- }
222
211
  function isNumeric(str) {
223
212
  return str.trim() !== "" && !isNaN(str);
224
213
  }
225
214
  function createTypeError(option, schema) {
226
215
  return new ArgResolveError(`Optional argument '--${option}' ${schema.short ? `or '-${schema.short}' ` : ""}should be '${schema.type}'`, option, "type", schema);
227
216
  }
228
- function resolveArgumentValue(token, schema) {
229
- if (token.value) return schema.type === "number" ? +token.value : token.value;
230
- if (schema.type === "boolean") return schema.negatable && token.name.startsWith("no-") ? false : true;
231
- return schema.type === "number" ? +(schema.default || "") : schema.default;
232
- }
233
217
 
234
218
  //#endregion
235
219
  export { ArgResolveError, resolveArgs };
package/lib/resolver.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import "./parser-Bx112mWZ.js";
2
- import { ArgResolveError$1 as ArgResolveError, ArgResolveErrorType, ArgSchema, ArgValues, Args, ExtractOptionValue, FilterArgs, ResolveArgValues, ResolveArgs, resolveArgs$1 as resolveArgs } from "./resolver--lXziDSy.js";
2
+ import { ArgResolveError$1 as ArgResolveError, ArgResolveErrorType, ArgSchema, ArgValues, Args, ExtractOptionValue, FilterArgs, ResolveArgValues, ResolveArgs, resolveArgs$1 as resolveArgs } from "./resolver-COGeGqd3.js";
3
3
  export { ArgResolveError, ArgResolveErrorType, ArgSchema, ArgValues, Args, ExtractOptionValue, FilterArgs, ResolveArgValues, ResolveArgs, resolveArgs };
package/lib/resolver.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import "./parser-Dr4iAGaX.js";
2
- import { ArgResolveError, resolveArgs } from "./resolver-DuUwC6Fy.js";
2
+ import { ArgResolveError, resolveArgs } from "./resolver-Df1uPA8Z.js";
3
3
 
4
4
  export { ArgResolveError, resolveArgs };
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.17.1",
4
+ "version": "0.19.0",
5
5
  "author": {
6
6
  "name": "kazuya kawaguchi",
7
7
  "email": "kawakazu80@gmail.com"
@@ -68,29 +68,31 @@
68
68
  "@eslint/markdown": "^6.4.0",
69
69
  "@kazupon/eslint-config": "^0.29.0",
70
70
  "@kazupon/prettier-config": "^0.1.1",
71
- "@types/node": "^22.15.3",
72
- "@vitest/eslint-plugin": "^1.1.44",
73
- "bumpp": "^10.1.0",
74
- "deno": "^2.3.1",
75
- "eslint": "^9.26.0",
76
- "eslint-config-prettier": "^10.1.2",
77
- "eslint-plugin-jsonc": "^2.20.0",
71
+ "@types/node": "^22.15.21",
72
+ "@typescript/native-preview": "7.0.0-dev.20250525.1",
73
+ "@vitest/eslint-plugin": "^1.2.0",
74
+ "bumpp": "^10.1.1",
75
+ "deno": "^2.3.3",
76
+ "eslint": "^9.27.0",
77
+ "eslint-config-prettier": "^10.1.5",
78
+ "eslint-plugin-jsonc": "^2.20.1",
78
79
  "eslint-plugin-promise": "^7.2.1",
79
80
  "eslint-plugin-regexp": "^2.7.0",
80
- "eslint-plugin-unicorn": "^59.0.0",
81
+ "eslint-plugin-unicorn": "^59.0.1",
81
82
  "eslint-plugin-yml": "^1.18.0",
82
83
  "gh-changelogen": "^0.2.8",
83
84
  "jsr": "^0.13.4",
84
- "jsr-exports-lint": "^0.2.0",
85
- "knip": "^5.52.0",
86
- "lint-staged": "^15.5.1",
85
+ "jsr-exports-lint": "^0.4.0",
86
+ "knip": "^5.57.1",
87
+ "lint-staged": "^15.5.2",
87
88
  "mitata": "^1.0.34",
88
- "pkg-pr-new": "^0.0.43",
89
+ "pkg-pr-new": "^0.0.50",
89
90
  "prettier": "^3.5.3",
90
- "tsdown": "^0.10.2",
91
+ "tsdown": "^0.12.1",
91
92
  "typescript": "^5.8.3",
92
- "typescript-eslint": "^8.31.1",
93
- "vitest": "^3.1.2"
93
+ "typescript-eslint": "^8.32.1",
94
+ "vitest": "^3.1.4",
95
+ "zod": "^3.25.28"
94
96
  },
95
97
  "prettier": "@kazupon/prettier-config",
96
98
  "lint-staged": {
@@ -123,9 +125,9 @@
123
125
  "lint:knip": "knip",
124
126
  "lint:prettier": "prettier . --check",
125
127
  "release": "bumpp --commit \"release: v%s\" --all --push --tag",
126
- "test": "vitest run",
128
+ "test": "vitest run --typecheck",
127
129
  "typecheck": "pnpm run --parallel --color \"/^typecheck:/\"",
128
130
  "typecheck:deno": "deno check src",
129
- "typecheck:tsc": "tsc --noEmit"
131
+ "typecheck:tsc": "tsgo --noEmit"
130
132
  }
131
133
  }