@thi.ng/args 2.9.3 → 2.10.1

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/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2025-09-01T16:38:35Z
3
+ - **Last updated**: 2025-09-25T11:10:32Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
@@ -11,6 +11,12 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
11
11
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
12
12
  and/or version bumps of transitive dependencies.
13
13
 
14
+ ## [2.10.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/args@2.10.0) (2025-09-04)
15
+
16
+ #### 🚀 Features
17
+
18
+ - update cliApp() to show current cmd info w/ usage ([2991527](https://github.com/thi-ng/umbrella/commit/2991527))
19
+
14
20
  ## [2.9.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/args@2.9.0) (2025-08-06)
15
21
 
16
22
  #### 🚀 Features
package/README.md CHANGED
@@ -15,22 +15,29 @@
15
15
  > GitHub](https://github.com/sponsors/postspectacular). Thank you! ❤️
16
16
 
17
17
  - [About](#about)
18
+ - [Built-in argument types](#built-in-argument-types)
19
+ - [Re-usable argument presets](#re-usable-argument-presets)
20
+ - [CLI app framework](#cli-app-framework)
18
21
  - [Status](#status)
19
22
  - [Installation](#installation)
20
23
  - [Dependencies](#dependencies)
21
24
  - [Projects using this package](#projects-using-this-package)
22
25
  - [API](#api)
23
- - [Basic usage](#basic-usage)
24
- - [Generate & display help](#generate--display-help)
25
- - [Parsing, value coercions & side effects](#parsing-value-coercions--side-effects)
26
+ - [Basic usage](#basic-usage)
27
+ - [Generate & display help](#generate--display-help)
28
+ - [Parsing, value coercions & side effects](#parsing-value-coercions--side-effects)
29
+ - [Declarative, multi-command CLI application](#declarative-multi-command-cli-application)
26
30
  - [Authors](#authors)
27
31
  - [License](#license)
28
32
 
29
33
  ## About
30
34
 
31
- Declarative, functional CLI argument/options parser, value coercions, sub-commands etc..
35
+ Declarative, functional CLI argument/options parser, app framework, arg value coercions, multi/sub-commands, usage generation, error handling etc..
32
36
 
33
- Includes built-in support for the following argument types (of course custom arg types are supported too):
37
+ ### Built-in argument types
38
+
39
+ The parser includes built-in support for the following argument types (of course
40
+ custom arg types are supported too):
34
41
 
35
42
  | **Argument type** | **Multiple** | **Example** | **Result** |
36
43
  |----------------------|--------------|----------------------------|-----------------------------------|
@@ -45,8 +52,44 @@ Includes built-in support for the following argument types (of course custom arg
45
52
 
46
53
  If multiple values/repetitions are allowed for an argument, the values will be
47
54
  collected into an array (apart from KV pairs, which will yield an object).
48
- Furthermore, for multi-args, an optional delimiter can be specified to extract
49
- individual values, e.g. `-a 1,2,3` equals `-a 1 -a 2 -a 3`
55
+ Furthermore, for multi-args and tuples, an optional delimiter can be specified
56
+ to extract individual values, e.g. `-a 1,2,3` equals `-a 1 -a 2 -a 3`
57
+
58
+ ### Re-usable argument presets
59
+
60
+ The following commonly used arguments are available as predefined presets:
61
+
62
+ - [`ARG_DRY_RUN`](https://docs.thi.ng/umbrella/args/variables/ARG_DRY_RUN.html)
63
+ - [`ARG_QUIET`](https://docs.thi.ng/umbrella/args/variables/ARG_QUIET.html)
64
+ - [`ARG_VERBOSE`](https://docs.thi.ng/umbrella/args/variables/ARG_VERBOSE.html)
65
+
66
+ Higher order, configurable preset specs:
67
+
68
+ - [`ARG_OUT_DIR`](https://docs.thi.ng/umbrella/args/functions/ARG_OUT_DIR.html)
69
+ - [`ARG_OUT_FILE`](https://docs.thi.ng/umbrella/args/functions/ARG_OUT_FILE.html)
70
+
71
+ To use these presets, simply import and splice them into your own arg
72
+ definitions (see code examples below).
73
+
74
+ ### CLI app framework
75
+
76
+ The package provides a simple framework to conveniently define single and
77
+ multi-command applications in a declarative and modular manner. Such apps are
78
+ defined via command specs and other configuration options. The framework then
79
+ handles all argument parsing, validation, usage display and delegation to
80
+ sub-commands.
81
+
82
+ The wrapper defines a user-customizable [command
83
+ context](https://docs.thi.ng/umbrella/args/interfaces/CommandCtx.html) with all
84
+ important information which is passed to the commands and also includes a logger
85
+ (writing to `stderr`). Other help/usage and error output also respects the
86
+ [`NO_COLOR` convention](https://no-color.org/).
87
+
88
+ A [fully documented code example](#declarative-multi-command-cli-application) is
89
+ further below.
90
+
91
+ For some _publicly available_ production uses, please see the [related packages
92
+ section](#projects-using-this-package) in this readme.
50
93
 
51
94
  ## Status
52
95
 
@@ -72,7 +115,7 @@ For Node.js REPL:
72
115
  const args = await import("@thi.ng/args");
73
116
  ```
74
117
 
75
- Package sizes (brotli'd, pre-treeshake): ESM: 3.29 KB
118
+ Package sizes (brotli'd, pre-treeshake): ESM: 3.31 KB
76
119
 
77
120
  ## Dependencies
78
121
 
@@ -97,15 +140,17 @@ Note: @thi.ng/api is in _most_ cases a type-only import (not used at runtime)
97
140
  tangling / codegen utility, inspired by org-mode & noweb
98
141
  - [@thi.ng/wasm-api-bindgen](https://thi.ng/wasm-api-bindgen): Polyglot bindings
99
142
  code generators (TS/JS, Zig, C11) for hybrid WebAssembly projects
143
+ - [thing-tools](https://codeberg.org/thi.ng/thing-tools)
100
144
 
101
145
  ## API
102
146
 
103
147
  [Generated API docs](https://docs.thi.ng/umbrella/args/)
104
148
 
105
- ### Basic usage
149
+ ## Basic usage
106
150
 
107
151
  ```ts tangle:export/readme.ts
108
152
  import {
153
+ ARG_VERBOSE,
109
154
  flag,
110
155
  hex,
111
156
  json,
@@ -132,10 +177,14 @@ interface TestArgs {
132
177
  pos?: Tuple<number>;
133
178
  xtra?: { a: number; b: string };
134
179
  define?: KVDict;
180
+ verbose: boolean;
135
181
  }
136
182
 
137
183
  // arg specifications
138
184
  const specs: Args<TestArgs> = {
185
+ // re-use predefined preset (see readme section above)
186
+ ...ARG_VERBOSE,
187
+
139
188
  // string arg
140
189
  configPath: string({
141
190
  alias: "c",
@@ -223,6 +272,7 @@ illegal argument(s): missing arg: --type
223
272
  Flags:
224
273
 
225
274
  -f, --force Force operation
275
+ -v, --verbose Display extra information
226
276
 
227
277
  Main:
228
278
 
@@ -240,7 +290,7 @@ Extra:
240
290
  -x JSON, --xtra JSON Extra options
241
291
  ```
242
292
 
243
- #### Generate & display help
293
+ ### Generate & display help
244
294
 
245
295
  Usage information can be generated via `usage()` and is automatically triggered
246
296
  via the special `--help` option (configurable, see
@@ -256,23 +306,7 @@ By default, ANSI colors are used to format the result string of `usage()`, but
256
306
  can be disabled (see
257
307
  [`UsageOpts`](https://docs.thi.ng/umbrella/args/interfaces/UsageOpts.html)).
258
308
 
259
- ```text
260
- bun index.ts --help
261
-
262
- -f, --force Force operation
263
-
264
- --bg HEX Background color
265
- -c PATH, --config-path PATH Config file path (CLI args always take
266
- precedence over those settings)
267
- -D key=val, --define key=val [multiple] Define dict entry
268
- --pos N,N Lat/Lon
269
- --size WxH Target size
270
- -t ID, --type ID [required] Image type: 'png', 'jpg', 'gif',
271
- 'tiff'
272
- -x JSON, --xtra JSON Extra options
273
- ```
274
-
275
- #### Parsing, value coercions & side effects
309
+ ### Parsing, value coercions & side effects
276
310
 
277
311
  The below invocation demonstrates how the various argument types are handled &
278
312
  represented in the result. Parsing stops with the first non-argument value (here
@@ -295,6 +329,7 @@ bun index.ts \
295
329
  # size: Tuple { value: [640, 480] }
296
330
  # define: { author: 'toxi', date: '2018-03-24' },
297
331
  # xtra: { foo: [23] },
332
+ # verbose: false,
298
333
  # },
299
334
  # index: 15,
300
335
  # rest: [ 'sourcefile.png' ],
@@ -302,6 +337,196 @@ bun index.ts \
302
337
  # }
303
338
  ```
304
339
 
340
+ ## Declarative, multi-command CLI application
341
+
342
+ The following example defines a CLI app with two sub-commands: `hello` and
343
+ `list`. Each command has its own options, in addition to common/shared ones.
344
+ Each command is defined in a modular manner (usually in its own source file).
345
+ All aspects like arg parsing, validation, and command selection/delegation is
346
+ handled by the `cliApp()` wrapper.
347
+
348
+ ```ts tangle:export/readme-cliapp.ts
349
+ import {
350
+ ARG_VERBOSE,
351
+ cliApp,
352
+ configureLogLevel,
353
+ int,
354
+ string,
355
+ type Command,
356
+ type CommandCtx,
357
+ } from "@thi.ng/args";
358
+ import { files } from "@thi.ng/file-io";
359
+
360
+ // common command opts
361
+ interface CommonOpts {
362
+ verbose: boolean;
363
+ }
364
+
365
+ // custom command context
366
+ interface AppCtx<T extends CommonOpts> extends CommandCtx<T, CommonOpts> {
367
+ // plus any custom additions here...
368
+ }
369
+
370
+ // command-specific options
371
+ interface HelloOpts extends CommonOpts {
372
+ name: string;
373
+ }
374
+
375
+ // command definition
376
+ const HELLO: Command<HelloOpts, CommonOpts> = {
377
+ // brief description (for `--help` usage)
378
+ desc: "Print out a greeting",
379
+ // command specific options (arguments)
380
+ // (will be combined with common opts)
381
+ opts: {
382
+ name: string({
383
+ alias: "n",
384
+ desc: "Name for greeting",
385
+ optional: false,
386
+ }),
387
+ },
388
+ // this command does not accept any inputs
389
+ inputs: 0,
390
+ // command implementation
391
+ fn: async (ctx) => {
392
+ // log message only shown if `--verbose`/`-v` given
393
+ ctx.logger.debug("opts", ctx.opts);
394
+ console.log(`Hello, ${ctx.opts.name}!`);
395
+ },
396
+ };
397
+
398
+ // command-specific options
399
+ interface ListFilesOpts extends CommonOpts {
400
+ depth: number;
401
+ filter?: string;
402
+ }
403
+
404
+ // command definition
405
+ const LIST_FILES: Command<ListFilesOpts, CommonOpts> = {
406
+ // brief description (for `--help` usage)
407
+ desc: "List files in given dir",
408
+ // command specific options
409
+ opts: {
410
+ filter: string({
411
+ alias: "f",
412
+ desc: "Filter regexp",
413
+ }),
414
+ depth: int({
415
+ alias: "d",
416
+ desc: "Recursion depth (directory levels)",
417
+ default: Infinity,
418
+ }),
419
+ },
420
+ // this command requires exactly 1 input
421
+ // (if supporting a range, use `[min, max]`)
422
+ inputs: 1,
423
+ // command implementation
424
+ fn: async (ctx) => {
425
+ for (let f of files(ctx.inputs[0], ctx.opts.filter, ctx.opts.depth)) {
426
+ console.log(f);
427
+ }
428
+ },
429
+ };
430
+
431
+ // define & start CLI app
432
+ cliApp<CommonOpts, AppCtx<any>>({
433
+ // app name
434
+ name: "example",
435
+ // process.argv index from which to start parsing from
436
+ start: 2,
437
+ // list common command opts here
438
+ opts: {
439
+ // re-use verbose flag arg spec preset
440
+ ...ARG_VERBOSE,
441
+ },
442
+ // list of commands
443
+ commands: {
444
+ hello: HELLO,
445
+ list: LIST_FILES,
446
+ },
447
+ // set to true if only a single command
448
+ // in this case the command name would NOT be required/expected
449
+ // single: true,
450
+
451
+ // usage opts
452
+ usage: {
453
+ // prefix/header string
454
+ prefix: `Example app
455
+ ===================================
456
+ Usage: example [opts] [inputs]\n`,
457
+ // configure column width for param usage info
458
+ paramWidth: 24,
459
+ lineWidth: 80,
460
+ },
461
+
462
+ // context initialization/augmentation
463
+ // (called before arg parsing commences)
464
+ ctx: async (ctx) => {
465
+ configureLogLevel(ctx.logger, ctx.opts.verbose);
466
+ return ctx;
467
+ },
468
+ });
469
+ ```
470
+
471
+ Example usage (here using `bun` to launch the above CLI app, though the usage
472
+ info is written to assume an `example` launcher/wrapper):
473
+
474
+ ```bash
475
+ bun readme-cliapp.ts
476
+
477
+ # Example app
478
+ # ===================================
479
+ # Usage: example [opts] [inputs]
480
+ #
481
+ # Available commands:
482
+ #
483
+ # hello : Print out a greeting
484
+ # list : List files in given dir
485
+ #
486
+ # -v, --verbose Display extra information
487
+ ```
488
+
489
+ ```bash
490
+ # displaying help for a sub-command
491
+ bun readme-cliapp.ts hello --help
492
+
493
+ # Example app
494
+ # ===================================
495
+ # Usage: example [opts] [inputs]
496
+ #
497
+ # Current command:
498
+ #
499
+ # hello : Print out a greeting
500
+ #
501
+ # -v, --verbose Display extra information
502
+ #
503
+ # -n STR, --name STR [required] Name for greeting
504
+ ```
505
+
506
+ ```bash
507
+ # invoking `hello` sub-command (with verbose flag)
508
+ bun readme-cliapp.ts hello --name thi.ng -v
509
+ # [DEBUG] example: opts {"name":"thi.ng","verbose":true}
510
+ # Hello, thi.ng!
511
+ ```
512
+
513
+ ```bash
514
+ # invoking `list` sub-command
515
+ bun readme-cliapp.ts list -d 2 -f '.js' .
516
+ # ./dev/api.js
517
+ # ./dev/runtime.js
518
+ # ./dev/test/main.js
519
+ # ./index.js
520
+ ```
521
+
522
+ ```bash
523
+ # missing arg error
524
+ bun readme-cliapp.ts hello
525
+ # illegal argument(s): missing arg: --name
526
+ #
527
+ # (...additional usage output omitted for brevity)
528
+ ```
529
+
305
530
  ## Authors
306
531
 
307
532
  - [Karsten Schmidt](https://thi.ng)
package/cli.js CHANGED
@@ -35,6 +35,12 @@ const cliApp = async (config) => {
35
35
  if (!cmd) {
36
36
  usageOpts.prefix += __descriptions(config.commands, usageOpts);
37
37
  __usageAndExit(config, usageOpts);
38
+ } else {
39
+ usageOpts.prefix += __descriptions(
40
+ { [cmdID]: cmd },
41
+ usageOpts,
42
+ "\nCurrent command:\n"
43
+ );
38
44
  }
39
45
  start++;
40
46
  }
@@ -54,7 +60,7 @@ const cliApp = async (config) => {
54
60
  if (isArray(cmd.inputs)) {
55
61
  const [min, max] = cmd.inputs;
56
62
  if (num < min || num > max) {
57
- err = max < Infinity ? `expected ${min}-${max} inputs` : `expected at least ${min} inputs`;
63
+ err = max < Infinity ? `expected ${min}-${max} inputs` : `expected at least ${min} input(s)`;
58
64
  }
59
65
  } else if (num !== cmd.inputs) {
60
66
  err = `expected ${cmd.inputs} input(s)`;
@@ -84,12 +90,12 @@ const __usageAndExit = (config, usageOpts) => {
84
90
  process.stderr.write(usage(config.opts, usageOpts));
85
91
  process.exit(1);
86
92
  };
87
- const __descriptions = (commands, { color, lineWidth = 80 } = {}) => {
93
+ const __descriptions = (commands, { color, lineWidth = 80 } = {}, prefix = "\nAvailable commands:\n") => {
88
94
  const names = Object.keys(commands);
89
95
  const maxLength = Math.max(...names.map((x) => x.length));
90
96
  const theme = __colorTheme(color);
91
97
  return [
92
- "\nAvailable commands:\n",
98
+ prefix,
93
99
  ...names.map(
94
100
  (x) => `${__padRightAnsi(
95
101
  __ansi(x, theme.command),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@thi.ng/args",
3
- "version": "2.9.3",
4
- "description": "Declarative, functional CLI argument/options parser, value coercions, sub-commands etc.",
3
+ "version": "2.10.1",
4
+ "description": "Declarative, functional CLI argument/options parser, app framework, arg value coercions, multi/sub-commands, usage generation, error handling etc.",
5
5
  "type": "module",
6
6
  "module": "./index.js",
7
7
  "typings": "./index.d.ts",
@@ -110,5 +110,5 @@
110
110
  "tag": "cli",
111
111
  "year": 2018
112
112
  },
113
- "gitHead": "e215a3e8de3809736ba0042c93bd8703a5a1a337\n"
113
+ "gitHead": "b7ede4f099767e0175ea8e09257208f73970b220\n"
114
114
  }