args-tokens 0.22.2 → 0.23.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,7 +2,7 @@
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
+ [![InstallSize][install-size-src]][install-size-href]
6
6
  [![CI][ci-src]][ci-href]
7
7
 
8
8
  > [`parseArgs` tokens](https://nodejs.org/api/util.html#parseargs-tokens) compatibility and more high-performance parser
@@ -95,7 +95,7 @@ Breaking changes might not follow SemVer, please pin Vitest's version when using
95
95
 
96
96
  ```
97
97
 
98
- ## ❓ What's different about parseArgs tokens?
98
+ ## ❓ What's different about `parseArgs` tokens?
99
99
 
100
100
  The token output for the short option `-x=v` is different:
101
101
 
@@ -184,7 +184,7 @@ bun add args-tokens
184
184
 
185
185
  ### Parse args to tokens
186
186
 
187
- `parseArgs` will transform arguments into tokens. This function is useful if you want to analyze arguments yourself based on the tokens. It's faster than `node:util` parseArgs because it only focuses on token transformation.
187
+ `parseArgs` will transform arguments into tokens. This function is useful if you want to analyze arguments yourself based on the tokens. It's faster than `parseArgs` of `node:util` because it only focuses on token transformation.
188
188
 
189
189
  ```js
190
190
  import { parseArgs } from 'args-tokens' // for Node.js and Bun
@@ -239,7 +239,7 @@ console.log('positionals:', positionals)
239
239
 
240
240
  ## Convenient argument parsing
241
241
 
242
- Using the `parse,` you can transform the arguments into tokens and resolve the argument values once:
242
+ Using the `parse` you can transform the arguments into tokens and resolve the argument values once:
243
243
 
244
244
  ```js
245
245
  import { parse } from 'args-tokens' // for Node.js and Bun
@@ -302,7 +302,7 @@ const tokens = parseArgs(['-a=1'], { allowCompatible: true }) // add `allowCompa
302
302
  deepStrictEqual(tokensNode, tokens)
303
303
  ```
304
304
 
305
- ## ArgSchema Reference
305
+ ## `ArgSchema` Reference
306
306
 
307
307
  The `ArgSchema` interface defines the configuration for command-line arguments. This schema is similar to Node.js `util.parseArgs` but with extended features.
308
308
 
@@ -533,6 +533,44 @@ Custom parsing function for `type: 'custom'` arguments. Required when `type: 'cu
533
533
  }
534
534
  ```
535
535
 
536
+ #### `conflicts` (optional)
537
+
538
+ Specifies other options that cannot be used together with this option. When conflicting options are provided together, an `ArgResolveError` will be thrown.
539
+
540
+ Conflicts only need to be defined on one side - if option A defines a conflict with option B, the conflict is automatically detected when both are used.
541
+
542
+ <!-- eslint-skip -->
543
+
544
+ ```js
545
+ {
546
+ // Single conflict
547
+ port: {
548
+ type: 'number',
549
+ conflicts: 'socket' // Cannot use --port with --socket
550
+ },
551
+ socket: {
552
+ type: 'string'
553
+ // No need to define conflicts: 'port' here
554
+ }
555
+ }
556
+
557
+ // Multiple conflicts (mutually exclusive options)
558
+ {
559
+ tcp: {
560
+ type: 'number',
561
+ conflicts: ['udp', 'unix'] // Cannot use with --udp or --unix
562
+ },
563
+ udp: {
564
+ type: 'number',
565
+ conflicts: ['tcp', 'unix']
566
+ },
567
+ unix: {
568
+ type: 'string',
569
+ conflicts: ['tcp', 'udp']
570
+ }
571
+ }
572
+ ```
573
+
536
574
  ## 🙌 Contributing guidelines
537
575
 
538
576
  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.
package/lib/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { ArgToken, ParserOptions, parseArgs$1 as parseArgs } from "./parser-DEYiqyo1.js";
2
- import { ArgExplicitlyProvided, ArgResolveError$1 as ArgResolveError, ArgResolveErrorType, ArgSchema, ArgValues, Args, ResolveArgs, resolveArgs$1 as resolveArgs } from "./resolver-7JOAwfSr.js";
1
+ import { ArgToken, ParserOptions, parseArgs } from "./parser-C6MbpZjd.js";
2
+ import { ArgExplicitlyProvided, ArgResolveError, ArgResolveErrorType, ArgSchema, ArgValues, Args, ResolveArgs, resolveArgs } from "./resolver-D64nGlCD.js";
3
3
 
4
4
  //#region src/parse.d.ts
5
5
 
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { parseArgs } from "./parser-M-ayhS1h.js";
2
2
  import "./utils-1LQrGCWG.js";
3
- import { ArgResolveError, resolveArgs } from "./resolver-DBvNkaE7.js";
3
+ import { ArgResolveError, resolveArgs } from "./resolver-D0hj6HpX.js";
4
4
 
5
5
  //#region src/parse.ts
6
6
  const DEFAULT_OPTIONS = {
@@ -95,4 +95,4 @@ declare function isShortOption(arg: string): boolean;
95
95
  */
96
96
  declare function hasLongOptionPrefix(arg: string): boolean;
97
97
  //#endregion
98
- export { ArgToken, ParserOptions, hasLongOptionPrefix as hasLongOptionPrefix$1, isShortOption as isShortOption$1, parseArgs as parseArgs$1 };
98
+ export { ArgToken, ParserOptions, hasLongOptionPrefix, isShortOption, parseArgs };
package/lib/parser.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { ArgToken, ParserOptions, hasLongOptionPrefix$1 as hasLongOptionPrefix, isShortOption$1 as isShortOption, parseArgs$1 as parseArgs } from "./parser-DEYiqyo1.js";
1
+ import { ArgToken, ParserOptions, hasLongOptionPrefix, isShortOption, parseArgs } from "./parser-C6MbpZjd.js";
2
2
  export { ArgToken, ParserOptions, hasLongOptionPrefix, isShortOption, parseArgs };
@@ -134,6 +134,7 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
134
134
  const values = Object.create(null);
135
135
  const errors = [];
136
136
  const explicit = Object.create(null);
137
+ const actualInputNames = /* @__PURE__ */ new Map();
137
138
  function checkTokenName(option, schema, token) {
138
139
  return token.name === (schema.type === "boolean" ? schema.negatable && token.name?.startsWith("no-") ? `no-${option}` : option : option);
139
140
  }
@@ -179,6 +180,8 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
179
180
  continue;
180
181
  }
181
182
  explicit[rawArg] = true;
183
+ const actualInputName = isShortOption(token.rawName) ? `-${token.name}` : `--${arg}`;
184
+ actualInputNames.set(rawArg, actualInputName);
182
185
  if (schema.type === "boolean") token.value = void 0;
183
186
  const [parsedValue, error] = parse(token, arg, schema);
184
187
  if (error) errors.push(error);
@@ -190,6 +193,8 @@ function resolveArgs(args, tokens, { shortGrouping = false, skipPositional = SKI
190
193
  }
191
194
  if (values[rawArg] == null && schema.default != null) values[rawArg] = schema.default;
192
195
  }
196
+ const conflictErrors = checkConflicts(args, explicit, toKebab, actualInputNames);
197
+ errors.push(...conflictErrors);
193
198
  return {
194
199
  values,
195
200
  positionals: positionalTokens.map((token) => token.value),
@@ -202,22 +207,19 @@ function parse(token, option, schema) {
202
207
  switch (schema.type) {
203
208
  case "string": return typeof token.value === "string" ? [token.value || schema.default, void 0] : [void 0, createTypeError(option, schema)];
204
209
  case "boolean": return token.value ? [token.value || schema.default, void 0] : [!(schema.negatable && token.name.startsWith("no-")), void 0];
205
- case "number": {
210
+ case "number":
206
211
  if (!isNumeric(token.value)) return [void 0, createTypeError(option, schema)];
207
212
  return token.value ? [+token.value, void 0] : [+(schema.default || ""), void 0];
208
- }
209
- case "enum": {
213
+ case "enum":
210
214
  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)];
211
215
  return [token.value || schema.default, void 0];
212
- }
213
- case "custom": {
216
+ case "custom":
214
217
  if (typeof schema.parse !== "function") throw new TypeError(`argument '${option}' should have a 'parse' function`);
215
218
  try {
216
219
  return [schema.parse(token.value || String(schema.default || "")), void 0];
217
220
  } catch (error) {
218
221
  return [void 0, error];
219
222
  }
220
- }
221
223
  default: throw new Error(`Unsupported argument type '${schema.type}' for option '${option}'`);
222
224
  }
223
225
  }
@@ -234,7 +236,7 @@ var ArgResolveError = class extends Error {
234
236
  schema;
235
237
  type;
236
238
  /**
237
- * Create an instance of ArgResolveError.
239
+ * Create an `ArgResolveError` instance.
238
240
  *
239
241
  * @param message - the error message
240
242
  * @param name - the name of the argument
@@ -257,6 +259,25 @@ function isNumeric(str) {
257
259
  function createTypeError(option, schema) {
258
260
  return new ArgResolveError(`Optional argument '--${option}' ${schema.short ? `or '-${schema.short}' ` : ""}should be '${schema.type}'`, option, "type", schema);
259
261
  }
262
+ function checkConflicts(args, explicit, toKebab, actualInputNames) {
263
+ for (const rawArg in args) {
264
+ const schema = args[rawArg];
265
+ if (!explicit[rawArg]) continue;
266
+ if (!schema.conflicts) continue;
267
+ const conflicts = Array.isArray(schema.conflicts) ? schema.conflicts : [schema.conflicts];
268
+ for (let i = 0; i < conflicts.length; i++) {
269
+ const conflictingArg = conflicts[i];
270
+ if (!explicit[conflictingArg]) continue;
271
+ const arg = toKebab || schema.toKebab ? kebabnize(rawArg) : rawArg;
272
+ const conflictingArgKebab = toKebab || args[conflictingArg]?.toKebab ? kebabnize(conflictingArg) : conflictingArg;
273
+ const optionActualName = actualInputNames.get(rawArg) || `--${arg}`;
274
+ const conflictingActualName = actualInputNames.get(conflictingArg) || `--${conflictingArgKebab}`;
275
+ const message = `Optional argument '${optionActualName}' conflicts with '${conflictingActualName}'`;
276
+ return [new ArgResolveError(message, rawArg, "conflict", schema)];
277
+ }
278
+ }
279
+ return [];
280
+ }
260
281
 
261
282
  //#endregion
262
283
  export { ArgResolveError, resolveArgs };
@@ -1,4 +1,4 @@
1
- import { ArgToken } from "./parser-DEYiqyo1.js";
1
+ import { ArgToken } from "./parser-C6MbpZjd.js";
2
2
 
3
3
  //#region src/resolver.d.ts
4
4
 
@@ -275,6 +275,93 @@ interface ArgSchema {
275
275
  * ```
276
276
  */
277
277
  toKebab?: true;
278
+ /**
279
+ * Names of other options that conflict with this option.
280
+ *
281
+ * When this option is used together with any of the conflicting options,
282
+ * an `ArgResolveError` with type 'conflict' will be thrown.
283
+ *
284
+ * Conflicts only need to be defined on one side - if option A defines a conflict
285
+ * with option B, the conflict is automatically detected when both are used,
286
+ * regardless of whether B also defines a conflict with A.
287
+ *
288
+ * Supports both single option name or array of option names.
289
+ * Option names must match the property keys in the schema object exactly
290
+ * (no automatic conversion between camelCase and kebab-case).
291
+ *
292
+ * @example
293
+ * Single conflict (bidirectional definition):
294
+ * ```ts
295
+ * {
296
+ * summer: {
297
+ * type: 'boolean',
298
+ * conflicts: 'autumn' // Cannot use --summer with --autumn
299
+ * },
300
+ * autumn: {
301
+ * type: 'boolean',
302
+ * conflicts: 'summer' // Can define on both sides for clarity
303
+ * }
304
+ * }
305
+ * ```
306
+ *
307
+ * @example
308
+ * Single conflict (one-way definition):
309
+ * ```ts
310
+ * {
311
+ * summer: {
312
+ * type: 'boolean',
313
+ * conflicts: 'autumn' // Only defined on summer side
314
+ * },
315
+ * autumn: {
316
+ * type: 'boolean'
317
+ * // No conflicts defined, but still cannot use with --summer
318
+ * }
319
+ * }
320
+ * // Usage: --summer --autumn will throw error
321
+ * // Error: "Optional argument '--summer' conflicts with '--autumn'"
322
+ * ```
323
+ *
324
+ * @example
325
+ * Multiple conflicts:
326
+ * ```ts
327
+ * {
328
+ * port: {
329
+ * type: 'number',
330
+ * conflicts: ['socket', 'pipe'], // Cannot use with --socket or --pipe
331
+ * description: 'TCP port number'
332
+ * },
333
+ * socket: {
334
+ * type: 'string',
335
+ * conflicts: ['port', 'pipe'], // Cannot use with --port or --pipe
336
+ * description: 'Unix socket path'
337
+ * },
338
+ * pipe: {
339
+ * type: 'string',
340
+ * conflicts: ['port', 'socket'], // Cannot use with --port or --socket
341
+ * description: 'Named pipe path'
342
+ * }
343
+ * }
344
+ * // These three options are mutually exclusive
345
+ * ```
346
+ *
347
+ * @example
348
+ * With kebab-case conversion:
349
+ * ```ts
350
+ * {
351
+ * summerSeason: {
352
+ * type: 'boolean',
353
+ * toKebab: true, // Accessible as --summer-season
354
+ * conflicts: 'autumnSeason' // Must use property key, not CLI name
355
+ * },
356
+ * autumnSeason: {
357
+ * type: 'boolean',
358
+ * toKebab: true // Accessible as --autumn-season
359
+ * }
360
+ * }
361
+ * // Error: "Optional argument '--summer-season' conflicts with '--autumn-season'"
362
+ * ```
363
+ */
364
+ conflicts?: string | string[];
278
365
  /**
279
366
  * Custom parsing function for `type: 'custom'` arguments.
280
367
  *
@@ -328,7 +415,7 @@ interface Args {
328
415
  /**
329
416
  * An object that contains the values of the arguments.
330
417
  *
331
- * @typeParam T - Arguments which is an object that defines the command line arguments.
418
+ * @typeParam T - {@link Args | Arguments} which is an object that defines the command line arguments.
332
419
  */
333
420
  type ArgValues<T> = T extends Args ? ResolveArgValues<T, { [Arg in keyof T]: ExtractOptionValue<T[Arg]> }> : {
334
421
  [option: string]: string | boolean | number | (string | boolean | number)[] | undefined;
@@ -337,7 +424,7 @@ type IsFunction<T> = T extends ((...args: any[]) => any) ? true : false;
337
424
  /**
338
425
  * Extracts the value type from the argument schema.
339
426
  *
340
- * @typeParam A - Argument schema which is an object that defines command line arguments.
427
+ * @typeParam A - {@link ArgSchema | Argument schema} which is an object that defines command line arguments.
341
428
  *
342
429
  * @internal
343
430
  */
@@ -451,7 +538,7 @@ declare function resolveArgs<A extends Args>(args: A, tokens: ArgToken[], {
451
538
  /**
452
539
  * An error type for {@link ArgResolveError}.
453
540
  */
454
- type ArgResolveErrorType = 'type' | 'required';
541
+ type ArgResolveErrorType = 'type' | 'required' | 'conflict';
455
542
  /**
456
543
  * An error that occurs when resolving arguments.
457
544
  * This error is thrown when the argument is not valid.
@@ -461,7 +548,7 @@ declare class ArgResolveError extends Error {
461
548
  schema: ArgSchema;
462
549
  type: ArgResolveErrorType;
463
550
  /**
464
- * Create an instance of ArgResolveError.
551
+ * Create an `ArgResolveError` instance.
465
552
  *
466
553
  * @param message - the error message
467
554
  * @param name - the name of the argument
@@ -471,4 +558,4 @@ declare class ArgResolveError extends Error {
471
558
  constructor(message: string, name: string, type: ArgResolveErrorType, schema: ArgSchema);
472
559
  }
473
560
  //#endregion
474
- export { ArgExplicitlyProvided, ArgResolveError as ArgResolveError$1, ArgResolveErrorType, ArgSchema, ArgValues, Args, ExtractOptionValue, FilterArgs, FilterPositionalArgs, ResolveArgValues, ResolveArgs, resolveArgs as resolveArgs$1 };
561
+ export { ArgExplicitlyProvided, ArgResolveError, ArgResolveErrorType, ArgSchema, ArgValues, Args, ExtractOptionValue, FilterArgs, FilterPositionalArgs, ResolveArgValues, ResolveArgs, resolveArgs };
package/lib/resolver.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import "./parser-DEYiqyo1.js";
2
- import { ArgExplicitlyProvided, ArgResolveError$1 as ArgResolveError, ArgResolveErrorType, ArgSchema, ArgValues, Args, ExtractOptionValue, FilterArgs, FilterPositionalArgs, ResolveArgValues, ResolveArgs, resolveArgs$1 as resolveArgs } from "./resolver-7JOAwfSr.js";
1
+ import "./parser-C6MbpZjd.js";
2
+ import { ArgExplicitlyProvided, ArgResolveError, ArgResolveErrorType, ArgSchema, ArgValues, Args, ExtractOptionValue, FilterArgs, FilterPositionalArgs, ResolveArgValues, ResolveArgs, resolveArgs } from "./resolver-D64nGlCD.js";
3
3
  export { ArgExplicitlyProvided, ArgResolveError, ArgResolveErrorType, ArgSchema, ArgValues, Args, ExtractOptionValue, FilterArgs, FilterPositionalArgs, ResolveArgValues, ResolveArgs, resolveArgs };
package/lib/resolver.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./parser-M-ayhS1h.js";
2
2
  import "./utils-1LQrGCWG.js";
3
- import { ArgResolveError, resolveArgs } from "./resolver-DBvNkaE7.js";
3
+ import { ArgResolveError, resolveArgs } from "./resolver-D0hj6HpX.js";
4
4
 
5
5
  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.22.2",
4
+ "version": "0.23.0",
5
5
  "author": {
6
6
  "name": "kazuya kawaguchi",
7
7
  "email": "kawakazu80@gmail.com"
@@ -71,11 +71,11 @@
71
71
  }
72
72
  },
73
73
  "devDependencies": {
74
- "@eslint/markdown": "^6.6.0",
75
- "@kazupon/eslint-config": "^0.33.3",
74
+ "@eslint/markdown": "^7.1.0",
75
+ "@kazupon/eslint-config": "^0.35.0",
76
76
  "@kazupon/prettier-config": "^0.1.1",
77
- "@types/node": "^22.17.0",
78
- "@typescript/native-preview": "7.0.0-dev.20250801.1",
77
+ "@types/node": "^24.1.0",
78
+ "@typescript/native-preview": "7.0.0-dev.20250810.1",
79
79
  "@vitest/eslint-plugin": "^1.3.4",
80
80
  "bumpp": "^10.2.2",
81
81
  "deno": "^2.4.3",
@@ -83,8 +83,9 @@
83
83
  "eslint-config-prettier": "^10.1.8",
84
84
  "eslint-plugin-jsdoc": "^52.0.2",
85
85
  "eslint-plugin-jsonc": "^2.20.1",
86
+ "eslint-plugin-markdown-preferences": "^0.10.0",
86
87
  "eslint-plugin-promise": "^7.2.1",
87
- "eslint-plugin-regexp": "^2.9.0",
88
+ "eslint-plugin-regexp": "^2.9.1",
88
89
  "eslint-plugin-unicorn": "^60.0.0",
89
90
  "eslint-plugin-yml": "^1.18.0",
90
91
  "gh-changelogen": "^0.2.8",
@@ -95,7 +96,7 @@
95
96
  "mitata": "^1.0.34",
96
97
  "pkg-pr-new": "^0.0.54",
97
98
  "prettier": "^3.6.2",
98
- "tsdown": "^0.13.1",
99
+ "tsdown": "^0.14.0",
99
100
  "typescript": "^5.9.2",
100
101
  "typescript-eslint": "^8.38.0",
101
102
  "vitest": "^3.2.4",