@reliverse/rempts 1.7.1 β 1.7.3
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 +141 -45
- package/bin/components/launcher/deprecated/launcher-mod.ts.txt +1 -1
- package/bin/components/launcher/deprecated/usage.ts.txt +1 -1
- package/bin/components/launcher/launcher-mod.d.ts +48 -23
- package/bin/components/launcher/launcher-mod.js +171 -23
- package/bin/components/msg-fmt/messages.js +6 -3
- package/bin/components/select/multiselect-prompt.js +1 -1
- package/bin/components/select/select-prompt.js +1 -1
- package/bin/components/select/toggle-prompt.js +1 -1
- package/bin/components/st-end/start.js +2 -2
- package/bin/components/visual/ascii-art/ascii-art.js +1 -1
- package/bin/mod.d.ts +1 -1
- package/bin/types.d.ts +1 -1
- package/bin/utils/prompt-end.js +8 -8
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> @reliverse/rempts is a modern, type-safe toolkit for building delightful cli experiences. it's fast, flexible, and made for developer happiness. file-based commands keep things simpleβno clutter, just clean and easy workflows. this is how cli should feel.
|
|
4
4
|
|
|
5
|
-
[
|
|
5
|
+
[sponsor](https://github.com/sponsors/blefnk) β [discord](https://discord.gg/Pb8uKbwpsJ) β [repo](https://github.com/reliverse/rempts) β [npm](https://npmjs.com/@reliverse/rempts)
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
- π¨ Crash-safe (Ctrl+C, SIGINT, errors)
|
|
19
19
|
- πͺ Minimal API surface, max expressiveness
|
|
20
20
|
- π§ͺ Scriptable for testing, stable for production
|
|
21
|
-
- π Automatic commands creation (via `dler init --cmd my-cool-cmd`)
|
|
21
|
+
- π Automatic commands creation (via `dler rempts init --cmd my-cool-cmd`)
|
|
22
22
|
- ποΈ No more hacking together `inquirer`, `citty`, `commander`, `chalk`
|
|
23
23
|
|
|
24
24
|
## Installation
|
|
@@ -27,6 +27,14 @@
|
|
|
27
27
|
bun add @reliverse/rempts
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
**Coming soon**:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bun i -g @reliverse/dler
|
|
34
|
+
dler rempts init --cmd my-cmd-1
|
|
35
|
+
dler rempts init --cmds
|
|
36
|
+
```
|
|
37
|
+
|
|
30
38
|
## Usage Examples
|
|
31
39
|
|
|
32
40
|
- [Prompts](#prompts)
|
|
@@ -130,14 +138,14 @@ await main();
|
|
|
130
138
|
### Terminology
|
|
131
139
|
|
|
132
140
|
- **Launcher/Router**: The main entry point for your CLI. Visit [CLI Launcher (Router)](#cli-launcher-router) section to learn more.
|
|
133
|
-
- **Command
|
|
141
|
+
- **Command**: A command is a function that defines the inner script launched by the main script where runMain() is used or by some other command.
|
|
134
142
|
- **Argument**: An argument is a value that is passed to a command.
|
|
135
143
|
- **Flag**: A flag is a boolean argument that is used to enable or disable a feature.
|
|
136
144
|
- **Option**: An option is a named argument that is used to configure a command.
|
|
137
145
|
|
|
138
146
|
#### Launcher Usage Example
|
|
139
147
|
|
|
140
|
-
|
|
148
|
+
**Important**: Ensure your commands don't have `await main();`, `await runMain();`, or something like that β to prevent any unexpected behavior. Only main command should have it.
|
|
141
149
|
|
|
142
150
|
```ts
|
|
143
151
|
import { relinka } from "@reliverse/relinka";
|
|
@@ -156,7 +164,7 @@ const main = defineCommand({
|
|
|
156
164
|
onCmdEnd() {
|
|
157
165
|
relinka("success", "Cleanup");
|
|
158
166
|
},
|
|
159
|
-
|
|
167
|
+
commands: {
|
|
160
168
|
build: () => import("./app/build/cmd.js").then((r) => r.default),
|
|
161
169
|
deploy: () => import("./app/deploy/cmd.js").then((r) => r.default),
|
|
162
170
|
debug: () => import("./app/debug/cmd.js").then((r) => r.default),
|
|
@@ -177,9 +185,9 @@ await runMain(myCommand, {
|
|
|
177
185
|
});
|
|
178
186
|
```
|
|
179
187
|
|
|
180
|
-
This flexibility allows you to easily build a rich, multi-command CLI with minimal boilerplate. The launcher even supports nested
|
|
188
|
+
This flexibility allows you to easily build a rich, multi-command CLI with minimal boilerplate. The launcher even supports nested commands, making it simple to construct complex CLI applications.
|
|
181
189
|
|
|
182
|
-
#### File-Based
|
|
190
|
+
#### File-Based Commands
|
|
183
191
|
|
|
184
192
|
Drop a `./src/cli/app/add/index.ts` and it's live.
|
|
185
193
|
|
|
@@ -199,7 +207,7 @@ export default defineCommand({
|
|
|
199
207
|
}),
|
|
200
208
|
},
|
|
201
209
|
async run({ args }) {
|
|
202
|
-
relinka("
|
|
210
|
+
relinka("log", "Adding:", args.name);
|
|
203
211
|
},
|
|
204
212
|
});
|
|
205
213
|
```
|
|
@@ -214,7 +222,7 @@ export default defineCommand({
|
|
|
214
222
|
**Hint**:
|
|
215
223
|
|
|
216
224
|
- Install `bun add -D @reliverse/dler`
|
|
217
|
-
- Use `dler init --cmd cmd1 cmd2` to init commands for rempts launcher's automatically
|
|
225
|
+
- Use `dler rempts init --cmd cmd1 cmd2` to init commands for rempts launcher's automatically
|
|
218
226
|
|
|
219
227
|
### Advanced Minimal API
|
|
220
228
|
|
|
@@ -227,7 +235,7 @@ defineCommand({
|
|
|
227
235
|
animals: { type: "array", options: ["cat","dog"] },
|
|
228
236
|
},
|
|
229
237
|
async run({ args, raw }) { // or `async run(ctx)`
|
|
230
|
-
relinka("
|
|
238
|
+
relinka("log", args.name, args.verbose, args.animals); // or `relinka("log", ctx.args.name, ...);`
|
|
231
239
|
},
|
|
232
240
|
});
|
|
233
241
|
```
|
|
@@ -281,7 +289,7 @@ await runMain(defineCommand({}));
|
|
|
281
289
|
|
|
282
290
|
```bash
|
|
283
291
|
bun add -D @reliverse/dler # or: bun i -g @reliverse/dler
|
|
284
|
-
bun dler init --cmd my-cmd-1 # or: dler init my-cmd-1 my-cmd-2 --main src/mod.ts
|
|
292
|
+
bun dler rempts init --cmd my-cmd-1 # or: dler rempts init my-cmd-1 my-cmd-2 --main src/mod.ts
|
|
285
293
|
# * `--main` is optional, default is `./src/mod.ts`
|
|
286
294
|
# * you can specify multiple commands at once
|
|
287
295
|
```
|
|
@@ -362,7 +370,7 @@ const main = defineCommand({
|
|
|
362
370
|
],
|
|
363
371
|
});
|
|
364
372
|
|
|
365
|
-
relinka("
|
|
373
|
+
relinka("log", "You have selected:", { name, framework });
|
|
366
374
|
},
|
|
367
375
|
});
|
|
368
376
|
|
|
@@ -388,7 +396,7 @@ import {
|
|
|
388
396
|
* This command demonstrates the full range of launcher features along with all supported argument types:
|
|
389
397
|
*
|
|
390
398
|
* - Global Usage Handling: Automatically processes `--help` and `--version`.
|
|
391
|
-
* - File-Based
|
|
399
|
+
* - File-Based Commands: Scans "app" for commands (e.g., `init`).
|
|
392
400
|
* - Comprehensive Argument Parsing: Supports positional, boolean, string, number, and array arguments.
|
|
393
401
|
* - Interactive Prompts: Uses built-in prompt functions for an engaging CLI experience.
|
|
394
402
|
*/
|
|
@@ -397,7 +405,7 @@ const mainCommand = defineCommand({
|
|
|
397
405
|
name: "rempts",
|
|
398
406
|
version: "1.6.0",
|
|
399
407
|
description:
|
|
400
|
-
"An example CLI that supports file-based
|
|
408
|
+
"An example CLI that supports file-based commands and all argument types.",
|
|
401
409
|
},
|
|
402
410
|
args: {
|
|
403
411
|
// Positional arguments
|
|
@@ -440,10 +448,10 @@ const mainCommand = defineCommand({
|
|
|
440
448
|
},
|
|
441
449
|
async run({ args, raw }) {
|
|
442
450
|
// Display invocation details and parsed arguments.
|
|
443
|
-
relinka("
|
|
444
|
-
relinka("
|
|
445
|
-
relinka("
|
|
446
|
-
relinka("
|
|
451
|
+
relinka("log", "Main command was invoked!");
|
|
452
|
+
relinka("log", "Parsed main-command args:", args);
|
|
453
|
+
relinka("log", "Raw argv:", raw);
|
|
454
|
+
relinka("log", "\nHelp: `rempts --help`, `rempts cmdName --help`");
|
|
447
455
|
|
|
448
456
|
// Begin interactive session with a prompt.
|
|
449
457
|
await startPrompt({
|
|
@@ -467,7 +475,7 @@ const mainCommand = defineCommand({
|
|
|
467
475
|
});
|
|
468
476
|
|
|
469
477
|
// Log all gathered input details.
|
|
470
|
-
relinka("
|
|
478
|
+
relinka("log", "You have selected:", {
|
|
471
479
|
projectName,
|
|
472
480
|
framework,
|
|
473
481
|
inputFile: args.inputFile,
|
|
@@ -483,7 +491,7 @@ const mainCommand = defineCommand({
|
|
|
483
491
|
/**
|
|
484
492
|
* The `runMain()` function sets up the launcher with several advanced features:
|
|
485
493
|
*
|
|
486
|
-
* - File-Based
|
|
494
|
+
* - File-Based Commands: Enables scanning for commands within the "app" directory.
|
|
487
495
|
* - Alias Mapping: Shorthand flags (e.g., `-v`) are mapped to their full names (e.g., `--verbose`).
|
|
488
496
|
* - Strict Mode & Unknown Flag Warnings: Unknown flags are either warned about or handled via a callback.
|
|
489
497
|
* - Negated Boolean Support: Allows flags to be negated (e.g., `--no-verbose`).
|
|
@@ -491,8 +499,8 @@ const mainCommand = defineCommand({
|
|
|
491
499
|
*/
|
|
492
500
|
await runMain(mainCommand, {
|
|
493
501
|
fileBasedCmds: {
|
|
494
|
-
enable: true, // Enables file-based
|
|
495
|
-
cmdsRootPath: "app", // Directory to scan for
|
|
502
|
+
enable: true, // Enables file-based command detection.
|
|
503
|
+
cmdsRootPath: "app", // Directory to scan for commands.
|
|
496
504
|
},
|
|
497
505
|
alias: {
|
|
498
506
|
v: "verbose", // Maps shorthand flag -v to --verbose.
|
|
@@ -509,10 +517,10 @@ await runMain(mainCommand, {
|
|
|
509
517
|
|
|
510
518
|
### CLI Launcher (Router)
|
|
511
519
|
|
|
512
|
-
Finally, a full-featured CLI launcher without the ceremony. `@reliverse/rempts`'s so called "launcher" is a uniquely powerful and ergonomic CLI toolkitβone that helps you build delightful developer experiences with less code and more confidence. The launcher supports both programmatically defined
|
|
520
|
+
Finally, a full-featured CLI launcher without the ceremony. `@reliverse/rempts`'s so called "launcher" is a uniquely powerful and ergonomic CLI toolkitβone that helps you build delightful developer experiences with less code and more confidence. The launcher supports both programmatically defined commands and file-based routing, so you can structure your CLI however you like. It automatically detects and loads commands from your filesystem and provides robust usage and error handling out-of-the-box. The launcher is more than just a command runnerβit's a robust, developer-friendly engine with several advanced features and thoughtful design choices:
|
|
513
521
|
|
|
514
|
-
- **File-Based & Defined
|
|
515
|
-
Use `
|
|
522
|
+
- **File-Based & Defined Commands:**
|
|
523
|
+
Use `commands` in your command definition or let the launcher automatically load commands from a specified directory.
|
|
516
524
|
|
|
517
525
|
- **Automatic Command Detection:**
|
|
518
526
|
The launcher scans your specified `cmdsRootPath` for command files matching common patterns such as:
|
|
@@ -539,20 +547,20 @@ Finally, a full-featured CLI launcher without the ceremony. `@reliverse/rempts`'
|
|
|
539
547
|
- **Lifecycle Hooks:**
|
|
540
548
|
You can define optional lifecycle hooks in your main command:
|
|
541
549
|
- `onLauncherStart` and `onLauncherEnd` (global, called once per CLI process)
|
|
542
|
-
- `onCmdStart` and `onCmdEnd` (per-
|
|
550
|
+
- `onCmdStart` and `onCmdEnd` (per-command, called before/after each command, but NOT for the main `run()` handler)
|
|
543
551
|
|
|
544
552
|
**Global Hooks:**
|
|
545
|
-
- `onLauncherStart`: Called once, before any command/
|
|
546
|
-
- `onLauncherEnd`: Called once, after all command/
|
|
553
|
+
- `onLauncherStart`: Called once, before any command/run() is executed.
|
|
554
|
+
- `onLauncherEnd`: Called once, after all command/run() logic is finished (even if an error occurs).
|
|
547
555
|
|
|
548
|
-
**Per-
|
|
549
|
-
- `onCmdStart`: Called before each
|
|
550
|
-
- `onCmdEnd`: Called after each
|
|
556
|
+
**Per-Command Hooks:**
|
|
557
|
+
- `onCmdStart`: Called before each command (not for main `run()`).
|
|
558
|
+
- `onCmdEnd`: Called after each command (not for main `run()`).
|
|
551
559
|
|
|
552
560
|
This means:
|
|
553
|
-
- If your CLI has multiple
|
|
554
|
-
- If your main command has a `run()` handler (and no
|
|
555
|
-
- This allows you to perform setup/teardown logic specific to each
|
|
561
|
+
- If your CLI has multiple commands, `onCmdStart` and `onCmdEnd` will be called for each command invocation, not just once for the whole CLI process.
|
|
562
|
+
- If your main command has a `run()` handler (and no command is invoked), these hooks are **not** called; use the `run()` handler itself or the global hooks for such logic.
|
|
563
|
+
- This allows you to perform setup/teardown logic specific to each command execution.
|
|
556
564
|
- If you want logic to run only once for the entire CLI process, use `onLauncherStart` and `onLauncherEnd`.
|
|
557
565
|
|
|
558
566
|
**Example:**
|
|
@@ -561,27 +569,29 @@ Finally, a full-featured CLI launcher without the ceremony. `@reliverse/rempts`'
|
|
|
561
569
|
const main = defineCommand({
|
|
562
570
|
onLauncherStart() { relinka('info', 'Global setup (once per process)'); },
|
|
563
571
|
onLauncherEnd() { relinka('info', 'Global cleanup (once per process)'); },
|
|
564
|
-
onCmdStart() { relinka('info', 'Setup for each
|
|
565
|
-
onCmdEnd() { relinka('info', 'Cleanup for each
|
|
566
|
-
|
|
567
|
-
run() { relinka('info', 'Main run handler (no
|
|
572
|
+
onCmdStart() { relinka('info', 'Setup for each command'); },
|
|
573
|
+
onCmdEnd() { relinka('info', 'Cleanup for each command'); },
|
|
574
|
+
commands: { ... },
|
|
575
|
+
run() { relinka('info', 'Main run handler (no command)'); },
|
|
568
576
|
});
|
|
569
577
|
// onLauncherStart/onLauncherEnd are called once per process
|
|
570
|
-
// onCmdStart/onCmdEnd are called for every
|
|
578
|
+
// onCmdStart/onCmdEnd are called for every command (not for main run())
|
|
571
579
|
// If you want per-run() logic, use the run() handler or global hooks
|
|
572
580
|
```
|
|
573
581
|
|
|
574
|
-
|
|
582
|
+
- **Deprecation Notice**
|
|
583
|
+
- The legacy `setup` and `cleanup` names are still supported as aliases for per-command hooks, but will be removed in a future major version. Prefer `onCmdStart` and `onCmdEnd` going forward.
|
|
584
|
+
- The `subCommands` property is deprecated as well. Please use `commands` instead. `subCommands` will be removed in a future major version.
|
|
575
585
|
|
|
576
586
|
- **Dynamic Usage Examples:**
|
|
577
|
-
- The launcher inspects your available
|
|
587
|
+
- The launcher inspects your available commands and their argument definitions, then prints a plausible example CLI invocation for a random command directly in the help output. This helps users understand real-world usage at a glance.
|
|
578
588
|
|
|
579
|
-
- **File-Based & Programmatic
|
|
580
|
-
- Both file-based and object
|
|
581
|
-
- File-based
|
|
589
|
+
- **File-Based & Programmatic Commands:**
|
|
590
|
+
- Both file-based and object commands are fully supported. The launcher can introspect their argument definitions and metadata for help, usage, and validation.
|
|
591
|
+
- File-based commands are auto-discovered from your filesystem, while programmatic commands can be defined inline in your main command.
|
|
582
592
|
|
|
583
593
|
- **Context-Aware Help Output:**
|
|
584
|
-
- The help/usage output adapts to your CLI's structure, showing available
|
|
594
|
+
- The help/usage output adapts to your CLI's structure, showing available commands, their aliases, argument details, and even dynamic usage examples. It also displays global options and context-specific error messages.
|
|
585
595
|
|
|
586
596
|
- **Error Handling:**
|
|
587
597
|
- The launcher provides clear, actionable error messages for missing required arguments, invalid types, unknown commands, and import errors. It always shows relevant usage information to help users recover quickly.
|
|
@@ -598,6 +608,92 @@ Finally, a full-featured CLI launcher without the ceremony. `@reliverse/rempts`'
|
|
|
598
608
|
- **Prompt-First, Modern UX:**
|
|
599
609
|
- The launcher integrates tightly with the prompt engine, so you can build interactive, delightful CLIs with minimal effort.
|
|
600
610
|
|
|
611
|
+
### Launcher Programmatic Execution
|
|
612
|
+
|
|
613
|
+
For larger CLIs or when you want to programmatically run commands (e.g.: [prompt demo](./example/prompts/mod.ts), tests, etc), you can organize your commands in a `cmds.ts` file and use the `runCmd` utility.
|
|
614
|
+
|
|
615
|
+
**Pro Tips & Best Practices**:
|
|
616
|
+
|
|
617
|
+
- Install `dler` globally and run `dler rempts init --cmds` to generate a `src/app/cmds.ts` (custom path is supported) file in your project.
|
|
618
|
+
- You can use any name for the `cmds.ts` file and store it anywhere, but `src/app/cmds.ts` is a good convention you can follow.
|
|
619
|
+
- Use the async function pattern for lazy loading if you have many commands or care about startup performance.
|
|
620
|
+
- Use eager loading (const) for small CLIs or demos where simplicity is preferred.
|
|
621
|
+
|
|
622
|
+
**Lazy Loading (Recommended for Most CLIs)**:
|
|
623
|
+
|
|
624
|
+
```ts
|
|
625
|
+
// example/launcher/app/cmds.ts
|
|
626
|
+
|
|
627
|
+
export async function getCmdHooks() {
|
|
628
|
+
return (await import("./hooks/cmd.js")).default;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
export async function getCmdFoo() {
|
|
632
|
+
return (await import("./foo/cmd.js")).default;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// ...more commands
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
Usage:
|
|
639
|
+
|
|
640
|
+
```ts
|
|
641
|
+
// example/prompts/mod.ts
|
|
642
|
+
|
|
643
|
+
import { getCmdHooks } from "@/launcher/app/cmds.js";
|
|
644
|
+
import { runCmd } from "@reliverse/rempts";
|
|
645
|
+
|
|
646
|
+
await runCmd(await getCmdHooks(), ["--flag"]);
|
|
647
|
+
// OR:
|
|
648
|
+
// const hooksCmd = await getCmdHooks();
|
|
649
|
+
// await runCmd(hooksCmd, ["--flag"]);
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
**Alternative: Eager Loading (All Commands Loaded at Startup)**:
|
|
653
|
+
|
|
654
|
+
```ts
|
|
655
|
+
// example/launcher/app/cmds.ts
|
|
656
|
+
export const hooksCmd = (await import("./hooks/cmd.js")).default;
|
|
657
|
+
export const fooCmd = (await import("./foo/cmd.js")).default;
|
|
658
|
+
// ...more commands
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
Usage:
|
|
662
|
+
|
|
663
|
+
```ts
|
|
664
|
+
import { hooksCmd } from "./cmds.js";
|
|
665
|
+
import { runCmd } from "@reliverse/rempts";
|
|
666
|
+
|
|
667
|
+
await runCmd(hooksCmd, ["--flag"]);
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
**Programmatic Command Execution with `runCmd`**:
|
|
671
|
+
|
|
672
|
+
The `runCmd` utility lets you run a command's `run()` handler with parsed arguments, outside of the full launcher context. This is useful for demos, tests, or custom flows:
|
|
673
|
+
|
|
674
|
+
```ts
|
|
675
|
+
import { runCmd } from "@reliverse/rempts";
|
|
676
|
+
import { hooksCmd } from "./cmds.js";
|
|
677
|
+
|
|
678
|
+
await runCmd(hooksCmd, ["--flag"]); // argv as array of strings
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
Or with lazy loading:
|
|
682
|
+
|
|
683
|
+
```ts
|
|
684
|
+
const hooksCmd = await getCmdHooks();
|
|
685
|
+
await runCmd(hooksCmd, ["--flag"]);
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
**Note:** `runCmd` only runs the command's `run()` handler and does not handle subcommands, file-based commands, or global hooks. For full CLI behavior, use `runMain`.
|
|
689
|
+
|
|
690
|
+
**Performance Note:**
|
|
691
|
+
|
|
692
|
+
- Eager loading (`const`) loads all commands at startup, which may impact performance for large CLIs.
|
|
693
|
+
- Lazy loading (`async function`) loads each command only when needed, improving startup time and memory usage.
|
|
694
|
+
|
|
695
|
+
Choose the pattern that best fits your CLI's size and usage!
|
|
696
|
+
|
|
601
697
|
## Contributing
|
|
602
698
|
|
|
603
699
|
Bug report? Prompt idea? Want to build the best DX possible?
|
|
@@ -27,7 +27,7 @@ export async function runMain<T extends ArgsDef = ArgsDef>(
|
|
|
27
27
|
if (!meta?.version) {
|
|
28
28
|
throw new CLIError("No version specified", "E_NO_VERSION");
|
|
29
29
|
}
|
|
30
|
-
relinka("
|
|
30
|
+
relinka("log", meta.version);
|
|
31
31
|
} else {
|
|
32
32
|
await runCommand(cmd, { rawArgs });
|
|
33
33
|
}
|
|
@@ -12,7 +12,7 @@ export async function showUsage<T extends ArgsDef = ArgsDef>(
|
|
|
12
12
|
) {
|
|
13
13
|
try {
|
|
14
14
|
// biome-ignore lint/style/useTemplate: <explanation>
|
|
15
|
-
relinka("
|
|
15
|
+
relinka("log", (await renderUsage(cmd, parent)) + "\n");
|
|
16
16
|
} catch (error) {
|
|
17
17
|
relinka("error", String(error));
|
|
18
18
|
}
|
|
@@ -44,42 +44,50 @@ type CommandMeta = {
|
|
|
44
44
|
* 2) A lazy import function returning a Promise that resolves to
|
|
45
45
|
* { default: Command<any> } or directly to a Command instance.
|
|
46
46
|
*/
|
|
47
|
-
type
|
|
47
|
+
type CommandSpec = string | (() => Promise<{
|
|
48
48
|
default: Command<any>;
|
|
49
49
|
} | Command<any>>);
|
|
50
|
-
export type
|
|
50
|
+
export type CommandsMap = Record<string, CommandSpec>;
|
|
51
51
|
type CommandContext<ARGS> = {
|
|
52
52
|
args: ARGS;
|
|
53
53
|
raw: string[];
|
|
54
54
|
};
|
|
55
55
|
type CommandRun<ARGS> = (ctx: CommandContext<ARGS>) => void | Promise<void>;
|
|
56
|
+
type CommandHook<ARGS> = (ctx: CommandContext<ARGS>) => void | Promise<void>;
|
|
56
57
|
type DefineCommandOptions<A extends ArgDefinitions = EmptyArgs> = {
|
|
57
58
|
meta?: CommandMeta;
|
|
58
59
|
args?: A;
|
|
59
60
|
run?: CommandRun<InferArgTypes<A>>;
|
|
60
|
-
subCommands?: SubCommandsMap;
|
|
61
61
|
/**
|
|
62
|
-
*
|
|
62
|
+
* Object subcommands for this command.
|
|
63
63
|
*/
|
|
64
|
-
|
|
64
|
+
commands?: CommandsMap;
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
66
|
+
* @deprecated Use `commands` instead. Will be removed in a future major version.
|
|
67
67
|
*/
|
|
68
|
-
|
|
68
|
+
subCommands?: CommandsMap;
|
|
69
|
+
/**
|
|
70
|
+
* Called before the command runs. Receives `{ args, raw }` (parsed args and raw argv).
|
|
71
|
+
*/
|
|
72
|
+
onCmdStart?: CommandHook<InferArgTypes<A>>;
|
|
73
|
+
/**
|
|
74
|
+
* Called after the command finishes. Receives `{ args, raw }` (parsed args and raw argv).
|
|
75
|
+
*/
|
|
76
|
+
onCmdEnd?: CommandHook<InferArgTypes<A>>;
|
|
69
77
|
/**
|
|
70
78
|
* @deprecated Use onCmdStart instead
|
|
71
79
|
*/
|
|
72
|
-
setup?:
|
|
80
|
+
setup?: CommandHook<InferArgTypes<A>>;
|
|
73
81
|
/**
|
|
74
82
|
* @deprecated Use onCmdEnd instead
|
|
75
83
|
*/
|
|
76
|
-
cleanup?:
|
|
84
|
+
cleanup?: CommandHook<InferArgTypes<A>>;
|
|
77
85
|
/**
|
|
78
|
-
* Called once per CLI process, before any command/
|
|
86
|
+
* Called once per CLI process, before any command/run() is executed
|
|
79
87
|
*/
|
|
80
88
|
onLauncherStart?: () => void | Promise<void>;
|
|
81
89
|
/**
|
|
82
|
-
* Called once per CLI process, after all command/
|
|
90
|
+
* Called once per CLI process, after all command/run() logic is finished
|
|
83
91
|
*/
|
|
84
92
|
onLauncherEnd?: () => void | Promise<void>;
|
|
85
93
|
};
|
|
@@ -87,29 +95,36 @@ export type Command<A extends ArgDefinitions = EmptyArgs> = {
|
|
|
87
95
|
meta?: CommandMeta;
|
|
88
96
|
args: A;
|
|
89
97
|
run?: CommandRun<InferArgTypes<A>>;
|
|
90
|
-
subCommands?: SubCommandsMap;
|
|
91
98
|
/**
|
|
92
|
-
*
|
|
99
|
+
* Object subcommands for this command.
|
|
100
|
+
*/
|
|
101
|
+
commands?: CommandsMap;
|
|
102
|
+
/**
|
|
103
|
+
* @deprecated Use `commands` instead. Will be removed in a future major version.
|
|
93
104
|
*/
|
|
94
|
-
|
|
105
|
+
subCommands?: CommandsMap;
|
|
95
106
|
/**
|
|
96
|
-
* Called
|
|
107
|
+
* Called before the command runs. Receives `{ args, raw }` (parsed args and raw argv).
|
|
97
108
|
*/
|
|
98
|
-
|
|
109
|
+
onCmdStart?: CommandHook<InferArgTypes<A>>;
|
|
110
|
+
/**
|
|
111
|
+
* Called after the command finishes. Receives `{ args, raw }` (parsed args and raw argv).
|
|
112
|
+
*/
|
|
113
|
+
onCmdEnd?: CommandHook<InferArgTypes<A>>;
|
|
99
114
|
/**
|
|
100
115
|
* @deprecated Use onCmdStart instead
|
|
101
116
|
*/
|
|
102
|
-
setup?:
|
|
117
|
+
setup?: CommandHook<InferArgTypes<A>>;
|
|
103
118
|
/**
|
|
104
119
|
* @deprecated Use onCmdEnd instead
|
|
105
120
|
*/
|
|
106
|
-
cleanup?:
|
|
121
|
+
cleanup?: CommandHook<InferArgTypes<A>>;
|
|
107
122
|
/**
|
|
108
|
-
* Called once per CLI process, before any command/
|
|
123
|
+
* Called once per CLI process, before any command/run() is executed
|
|
109
124
|
*/
|
|
110
125
|
onLauncherStart?: () => void | Promise<void>;
|
|
111
126
|
/**
|
|
112
|
-
* Called once per CLI process, after all command/
|
|
127
|
+
* Called once per CLI process, after all command/run() logic is finished
|
|
113
128
|
*/
|
|
114
129
|
onLauncherEnd?: () => void | Promise<void>;
|
|
115
130
|
};
|
|
@@ -152,11 +167,11 @@ export declare function showUsage<A extends ArgDefinitions>(command: Command<A>,
|
|
|
152
167
|
/**
|
|
153
168
|
* Primary entry point to run a command. This function supports:
|
|
154
169
|
*
|
|
155
|
-
* - File-based
|
|
156
|
-
* -
|
|
170
|
+
* - File-based Commands: scanning for commands within a given commands root.
|
|
171
|
+
* - Commands defined within the command object.
|
|
157
172
|
* - Standard flags like --help, --version, and --debug.
|
|
158
173
|
*
|
|
159
|
-
* This function passes along remaining arguments to
|
|
174
|
+
* This function passes along remaining arguments to command runners to ensure
|
|
160
175
|
* consistent parsing.
|
|
161
176
|
*/
|
|
162
177
|
export declare function runMain<A extends ArgDefinitions = EmptyArgs>(command: Command<A>, parserOptions?: ReliArgParserOptions & {
|
|
@@ -174,4 +189,14 @@ export declare function runMain<A extends ArgDefinitions = EmptyArgs>(command: C
|
|
|
174
189
|
* precise default value validation (e.g., `options: ["a", "b"] as const`).
|
|
175
190
|
*/
|
|
176
191
|
export declare function defineArgs<A extends ArgDefinitions>(args: A & ValidateArrayDefaults<A>): A;
|
|
192
|
+
/**
|
|
193
|
+
* Programmatically run a command's run() handler with parsed arguments.
|
|
194
|
+
* Does not handle subcommands, file-based commands, or global hooks.
|
|
195
|
+
* Suitable for use in demos, tests, or programmatic invocation.
|
|
196
|
+
*
|
|
197
|
+
* @param command The command definition (from defineCommand)
|
|
198
|
+
* @param argv The argv array to parse (default: [])
|
|
199
|
+
* @param parserOptions Optional reliArgParser options
|
|
200
|
+
*/
|
|
201
|
+
export declare function runCmd<A extends ArgDefinitions = EmptyArgs>(command: Command<A>, argv?: string[], parserOptions?: ReliArgParserOptions): Promise<void>;
|
|
177
202
|
export {};
|
|
@@ -50,7 +50,7 @@ function buildExampleArgs(args) {
|
|
|
50
50
|
const isDebugMode = process.argv.includes("--debug");
|
|
51
51
|
function debugLog(...args) {
|
|
52
52
|
if (isDebugMode) {
|
|
53
|
-
relinka("
|
|
53
|
+
relinka("log", "[DEBUG]", ...args);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
function isFlag(str) {
|
|
@@ -61,11 +61,15 @@ export function defineCommand(options) {
|
|
|
61
61
|
const onCmdEnd = options.onCmdEnd || options.cleanup;
|
|
62
62
|
const onLauncherStart = options.onLauncherStart;
|
|
63
63
|
const onLauncherEnd = options.onLauncherEnd;
|
|
64
|
-
|
|
64
|
+
let commands = options.commands;
|
|
65
|
+
if (!commands) {
|
|
66
|
+
commands = options.subCommands;
|
|
67
|
+
}
|
|
68
|
+
const cmdObj = {
|
|
65
69
|
meta: options.meta,
|
|
66
70
|
args: options.args || {},
|
|
67
71
|
run: options.run,
|
|
68
|
-
|
|
72
|
+
commands,
|
|
69
73
|
onCmdStart,
|
|
70
74
|
onCmdEnd,
|
|
71
75
|
onLauncherStart,
|
|
@@ -74,6 +78,14 @@ export function defineCommand(options) {
|
|
|
74
78
|
setup: onCmdStart,
|
|
75
79
|
cleanup: onCmdEnd
|
|
76
80
|
};
|
|
81
|
+
Object.defineProperty(cmdObj, "subCommands", {
|
|
82
|
+
get() {
|
|
83
|
+
return this.commands;
|
|
84
|
+
},
|
|
85
|
+
enumerable: false,
|
|
86
|
+
configurable: true
|
|
87
|
+
});
|
|
88
|
+
return cmdObj;
|
|
77
89
|
}
|
|
78
90
|
let _cachedDefaultCliName;
|
|
79
91
|
let _cachedDefaultCliVersion;
|
|
@@ -97,7 +109,7 @@ export async function showUsage(command, parserOptions = {}, displayNotFoundMess
|
|
|
97
109
|
const { name: fallbackName, version: fallbackVersion } = await getDefaultCliNameAndVersion();
|
|
98
110
|
const cliName = command.meta?.name || fallbackName;
|
|
99
111
|
const cliVersion = command.meta?.version || fallbackVersion;
|
|
100
|
-
relinka("
|
|
112
|
+
relinka("log", `${cliName}${cliVersion ? ` v${cliVersion}` : ""}`);
|
|
101
113
|
if (parserOptions.metaSettings?.showDescriptionOnMain) {
|
|
102
114
|
let description = command.meta?.description;
|
|
103
115
|
if (!description) {
|
|
@@ -173,8 +185,9 @@ export async function showUsage(command, parserOptions = {}, displayNotFoundMess
|
|
|
173
185
|
} else {
|
|
174
186
|
const subCommandNames = [];
|
|
175
187
|
const subCommandDefs = [];
|
|
176
|
-
|
|
177
|
-
|
|
188
|
+
const objectCommands = command.commands;
|
|
189
|
+
if (objectCommands) {
|
|
190
|
+
for (const [name, spec] of Object.entries(objectCommands)) {
|
|
178
191
|
try {
|
|
179
192
|
const cmd = await loadSubCommand(spec);
|
|
180
193
|
if (!cmd?.meta?.hidden) {
|
|
@@ -183,7 +196,7 @@ export async function showUsage(command, parserOptions = {}, displayNotFoundMess
|
|
|
183
196
|
subCommandDefs.push({ name, def: cmd });
|
|
184
197
|
}
|
|
185
198
|
} catch (err) {
|
|
186
|
-
debugLog(`Error loading
|
|
199
|
+
debugLog(`Error loading command ${name}:`, err);
|
|
187
200
|
}
|
|
188
201
|
}
|
|
189
202
|
}
|
|
@@ -240,7 +253,7 @@ export async function runMain(command, parserOptions = {}) {
|
|
|
240
253
|
if (typeof command.onLauncherStart === "function")
|
|
241
254
|
await command.onLauncherStart();
|
|
242
255
|
try {
|
|
243
|
-
if (!parserOptions.fileBasedCmds && !command.
|
|
256
|
+
if (!parserOptions.fileBasedCmds && !command.commands) {
|
|
244
257
|
let callerDir = process.cwd();
|
|
245
258
|
let callerFile;
|
|
246
259
|
try {
|
|
@@ -289,7 +302,7 @@ This can cause recursion or unexpected behavior.`
|
|
|
289
302
|
}
|
|
290
303
|
const rawArgv = process.argv.slice(2);
|
|
291
304
|
const autoExit = parserOptions.autoExit !== false;
|
|
292
|
-
if (!(parserOptions.fileBasedCmds?.enable || command.
|
|
305
|
+
if (!(parserOptions.fileBasedCmds?.enable || command.commands && Object.keys(command.commands).length > 0 || command.run)) {
|
|
293
306
|
relinka(
|
|
294
307
|
"error",
|
|
295
308
|
"Invalid CLI configuration: No file-based commands, subCommands, or run() handler are defined. This CLI will not do anything.\n\u2502 To fix: add file-based commands (./app), or provide at least one subCommand or a run() handler."
|
|
@@ -310,7 +323,7 @@ This can cause recursion or unexpected behavior.`
|
|
|
310
323
|
if (checkVersion(rawArgv)) {
|
|
311
324
|
if (command.meta?.name) {
|
|
312
325
|
relinka(
|
|
313
|
-
"
|
|
326
|
+
"log",
|
|
314
327
|
`${command.meta?.name} ${command.meta?.version ? `v${command.meta?.version}` : ""}`
|
|
315
328
|
);
|
|
316
329
|
}
|
|
@@ -321,14 +334,17 @@ This can cause recursion or unexpected behavior.`
|
|
|
321
334
|
if (fileBasedEnabled && rawArgv.length > 0 && !isFlag(rawArgv[0])) {
|
|
322
335
|
const [subName, ...subCmdArgv] = rawArgv;
|
|
323
336
|
try {
|
|
337
|
+
const ctx = getParsedContext(command, rawArgv, parserOptions);
|
|
324
338
|
if (typeof command.onCmdStart === "function")
|
|
325
|
-
await command.onCmdStart();
|
|
339
|
+
await command.onCmdStart(ctx);
|
|
326
340
|
await runFileBasedSubCmd(
|
|
327
341
|
subName,
|
|
328
342
|
subCmdArgv,
|
|
329
343
|
parserOptions.fileBasedCmds,
|
|
330
344
|
parserOptions,
|
|
331
|
-
command.onCmdEnd
|
|
345
|
+
command.onCmdEnd ? async (_subCtx) => {
|
|
346
|
+
await command.onCmdEnd?.(ctx);
|
|
347
|
+
} : void 0
|
|
332
348
|
);
|
|
333
349
|
if (autoExit) process.exit(0);
|
|
334
350
|
return;
|
|
@@ -338,10 +354,10 @@ This can cause recursion or unexpected behavior.`
|
|
|
338
354
|
throw err;
|
|
339
355
|
}
|
|
340
356
|
}
|
|
341
|
-
if (!fileBasedEnabled && command.
|
|
357
|
+
if (!fileBasedEnabled && command.commands && rawArgv.length > 0 && !isFlag(rawArgv[0])) {
|
|
342
358
|
const [maybeSub, ...subCmdArgv] = rawArgv;
|
|
343
359
|
let subSpec;
|
|
344
|
-
for (const [key, spec] of Object.entries(command.
|
|
360
|
+
for (const [key, spec] of Object.entries(command.commands)) {
|
|
345
361
|
if (key === maybeSub) {
|
|
346
362
|
subSpec = spec;
|
|
347
363
|
break;
|
|
@@ -353,18 +369,21 @@ This can cause recursion or unexpected behavior.`
|
|
|
353
369
|
break;
|
|
354
370
|
}
|
|
355
371
|
} catch (err) {
|
|
356
|
-
debugLog(`Error checking alias for
|
|
372
|
+
debugLog(`Error checking alias for command ${key}:`, err);
|
|
357
373
|
}
|
|
358
374
|
}
|
|
359
375
|
if (subSpec) {
|
|
360
376
|
try {
|
|
377
|
+
const ctx = getParsedContext(command, rawArgv, parserOptions);
|
|
361
378
|
if (typeof command.onCmdStart === "function")
|
|
362
|
-
await command.onCmdStart();
|
|
379
|
+
await command.onCmdStart(ctx);
|
|
363
380
|
await runSubCommand(
|
|
364
381
|
subSpec,
|
|
365
382
|
subCmdArgv,
|
|
366
383
|
parserOptions,
|
|
367
|
-
command.onCmdEnd
|
|
384
|
+
command.onCmdEnd ? async (_subCtx) => {
|
|
385
|
+
await command.onCmdEnd?.(ctx);
|
|
386
|
+
} : void 0
|
|
368
387
|
);
|
|
369
388
|
if (autoExit) process.exit(0);
|
|
370
389
|
return;
|
|
@@ -461,20 +480,32 @@ Info for this CLI's developer: No valid command directory found, expected: ${exp
|
|
|
461
480
|
);
|
|
462
481
|
}
|
|
463
482
|
try {
|
|
464
|
-
await runCommandWithArgs(
|
|
483
|
+
const subCtx = await runCommandWithArgs(
|
|
484
|
+
subCommand,
|
|
485
|
+
argv,
|
|
486
|
+
parserOptions,
|
|
487
|
+
true
|
|
488
|
+
);
|
|
489
|
+
if (typeof parentFinish === "function" && subCtx)
|
|
490
|
+
await parentFinish(subCtx);
|
|
465
491
|
} finally {
|
|
466
|
-
if (typeof parentFinish === "function") await parentFinish();
|
|
467
492
|
}
|
|
468
493
|
}
|
|
469
494
|
async function runSubCommand(spec, argv, parserOptions, parentFinish) {
|
|
470
495
|
const subCommand = await loadSubCommand(spec);
|
|
471
496
|
try {
|
|
472
|
-
await runCommandWithArgs(
|
|
497
|
+
const subCtx = await runCommandWithArgs(
|
|
498
|
+
subCommand,
|
|
499
|
+
argv,
|
|
500
|
+
parserOptions,
|
|
501
|
+
true
|
|
502
|
+
);
|
|
503
|
+
if (typeof parentFinish === "function" && subCtx)
|
|
504
|
+
await parentFinish(subCtx);
|
|
473
505
|
} finally {
|
|
474
|
-
if (typeof parentFinish === "function") await parentFinish();
|
|
475
506
|
}
|
|
476
507
|
}
|
|
477
|
-
async function runCommandWithArgs(command, argv, parserOptions) {
|
|
508
|
+
async function runCommandWithArgs(command, argv, parserOptions, returnCtx) {
|
|
478
509
|
const autoExit = parserOptions.autoExit !== false;
|
|
479
510
|
const booleanKeys = Object.keys(command.args || {}).filter(
|
|
480
511
|
(k) => command.args[k].type === "boolean"
|
|
@@ -556,7 +587,7 @@ async function runCommandWithArgs(command, argv, parserOptions) {
|
|
|
556
587
|
if (command.run) {
|
|
557
588
|
await command.run(ctx);
|
|
558
589
|
} else {
|
|
559
|
-
const isDispatcher = parserOptions.fileBasedCmds?.enable || command.
|
|
590
|
+
const isDispatcher = parserOptions.fileBasedCmds?.enable || command.commands && Object.keys(command.commands).length > 0;
|
|
560
591
|
const noSubcommandArgInCurrentCall = !argv.some((arg) => !isFlag(arg));
|
|
561
592
|
if (isDispatcher && noSubcommandArgInCurrentCall) {
|
|
562
593
|
relinka("warn", "Please specify a command");
|
|
@@ -580,6 +611,8 @@ ${String(err)}`);
|
|
|
580
611
|
if (autoExit) process.exit(1);
|
|
581
612
|
else throw err;
|
|
582
613
|
}
|
|
614
|
+
if (returnCtx) return ctx;
|
|
615
|
+
return void 0;
|
|
583
616
|
}
|
|
584
617
|
function castArgValue(def, rawVal, argName) {
|
|
585
618
|
if (rawVal == null) {
|
|
@@ -624,3 +657,118 @@ function renderPositional(args) {
|
|
|
624
657
|
export function defineArgs(args) {
|
|
625
658
|
return args;
|
|
626
659
|
}
|
|
660
|
+
export async function runCmd(command, argv = [], parserOptions = {}) {
|
|
661
|
+
const booleanKeys = Object.keys(command.args || {}).filter(
|
|
662
|
+
(k) => command.args[k].type === "boolean"
|
|
663
|
+
);
|
|
664
|
+
const defaultMap = {};
|
|
665
|
+
for (const [argKey, def] of Object.entries(command.args || {})) {
|
|
666
|
+
if (def.default !== void 0) {
|
|
667
|
+
if (def.type === "array" && typeof def.default === "string") {
|
|
668
|
+
defaultMap[argKey] = [def.default];
|
|
669
|
+
} else {
|
|
670
|
+
defaultMap[argKey] = def.default;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
const mergedParserOptions = {
|
|
675
|
+
...parserOptions,
|
|
676
|
+
boolean: [...parserOptions.boolean || [], ...booleanKeys],
|
|
677
|
+
defaults: { ...defaultMap, ...parserOptions.defaults || {} }
|
|
678
|
+
};
|
|
679
|
+
const parsed = reliArgParser(argv, mergedParserOptions);
|
|
680
|
+
debugLog("Parsed arguments (runCmd):", parsed);
|
|
681
|
+
const finalArgs = {};
|
|
682
|
+
const positionalKeys = Object.keys(command.args || {}).filter(
|
|
683
|
+
(k) => command.args[k].type === "positional"
|
|
684
|
+
);
|
|
685
|
+
const leftoverPositionals = [...parsed._ || []];
|
|
686
|
+
for (let i = 0; i < positionalKeys.length; i++) {
|
|
687
|
+
const key = positionalKeys[i];
|
|
688
|
+
const def = command.args?.[key];
|
|
689
|
+
const val = leftoverPositionals[i];
|
|
690
|
+
if (val == null && def.required) {
|
|
691
|
+
throw new Error(`Missing required positional argument: <${key}>`);
|
|
692
|
+
}
|
|
693
|
+
finalArgs[key] = val != null ? castArgValue(def, val, key) : def.default;
|
|
694
|
+
}
|
|
695
|
+
const otherKeys = Object.keys(command.args || {}).filter(
|
|
696
|
+
(k) => command.args[k].type !== "positional"
|
|
697
|
+
);
|
|
698
|
+
for (const key of otherKeys) {
|
|
699
|
+
const def = command.args?.[key];
|
|
700
|
+
let rawVal = parsed[key];
|
|
701
|
+
if (def.type === "array" && rawVal !== void 0 && !Array.isArray(rawVal)) {
|
|
702
|
+
rawVal = [rawVal];
|
|
703
|
+
}
|
|
704
|
+
const valueOrDefault = rawVal ?? defaultMap[key];
|
|
705
|
+
if (valueOrDefault == null && def.required) {
|
|
706
|
+
throw new Error(`Missing required argument: --${key}`);
|
|
707
|
+
}
|
|
708
|
+
finalArgs[key] = castArgValue(def, rawVal, key);
|
|
709
|
+
if (def.type === "array" && def.options && finalArgs[key]) {
|
|
710
|
+
const values = finalArgs[key];
|
|
711
|
+
const invalidOptions = values.filter(
|
|
712
|
+
(v) => def.options && !def.options.includes(v)
|
|
713
|
+
);
|
|
714
|
+
if (invalidOptions.length > 0) {
|
|
715
|
+
throw new Error(
|
|
716
|
+
`Invalid choice(s) for --${key}: ${invalidOptions.join(", ")}. Allowed options: ${def.options.join(", ")}`
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
const ctx = {
|
|
722
|
+
args: finalArgs,
|
|
723
|
+
raw: argv
|
|
724
|
+
};
|
|
725
|
+
if (typeof command.run === "function") {
|
|
726
|
+
await command.run(ctx);
|
|
727
|
+
} else {
|
|
728
|
+
throw new Error("Command has no run() handler.");
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
function getParsedContext(command, argv, parserOptions) {
|
|
732
|
+
const booleanKeys = Object.keys(command.args || {}).filter(
|
|
733
|
+
(k) => command.args[k].type === "boolean"
|
|
734
|
+
);
|
|
735
|
+
const defaultMap = {};
|
|
736
|
+
for (const [argKey, def] of Object.entries(command.args || {})) {
|
|
737
|
+
if (def.default !== void 0) {
|
|
738
|
+
if (def.type === "array" && typeof def.default === "string") {
|
|
739
|
+
defaultMap[argKey] = [def.default];
|
|
740
|
+
} else {
|
|
741
|
+
defaultMap[argKey] = def.default;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
const mergedParserOptions = {
|
|
746
|
+
...parserOptions,
|
|
747
|
+
boolean: [...parserOptions.boolean || [], ...booleanKeys],
|
|
748
|
+
defaults: { ...defaultMap, ...parserOptions.defaults || {} }
|
|
749
|
+
};
|
|
750
|
+
const parsed = reliArgParser(argv, mergedParserOptions);
|
|
751
|
+
const finalArgs = {};
|
|
752
|
+
const positionalKeys = Object.keys(command.args || {}).filter(
|
|
753
|
+
(k) => command.args[k].type === "positional"
|
|
754
|
+
);
|
|
755
|
+
const leftoverPositionals = [...parsed._ || []];
|
|
756
|
+
for (let i = 0; i < positionalKeys.length; i++) {
|
|
757
|
+
const key = positionalKeys[i];
|
|
758
|
+
const def = command.args?.[key];
|
|
759
|
+
const val = leftoverPositionals[i];
|
|
760
|
+
finalArgs[key] = val != null ? castArgValue(def, val, key) : def.default;
|
|
761
|
+
}
|
|
762
|
+
const otherKeys = Object.keys(command.args || {}).filter(
|
|
763
|
+
(k) => command.args[k].type !== "positional"
|
|
764
|
+
);
|
|
765
|
+
for (const key of otherKeys) {
|
|
766
|
+
const def = command.args?.[key];
|
|
767
|
+
let rawVal = parsed[key];
|
|
768
|
+
if (def.type === "array" && rawVal !== void 0 && !Array.isArray(rawVal)) {
|
|
769
|
+
rawVal = [rawVal];
|
|
770
|
+
}
|
|
771
|
+
finalArgs[key] = castArgValue(def, rawVal, key);
|
|
772
|
+
}
|
|
773
|
+
return { args: finalArgs, raw: argv };
|
|
774
|
+
}
|
|
@@ -21,7 +21,10 @@ export const symbols = {
|
|
|
21
21
|
step_active: u("\u25C6", "\u2666"),
|
|
22
22
|
step_error: u("\u{1F5F4}", "x"),
|
|
23
23
|
info: u("\u2139", "i"),
|
|
24
|
-
|
|
24
|
+
log: u("\u2502", "|"),
|
|
25
|
+
success: u("\u2705", "\u2713"),
|
|
26
|
+
warn: u("\u26A0", "!"),
|
|
27
|
+
error: u("\u274C", "x")
|
|
25
28
|
};
|
|
26
29
|
function wrapThenStyle(input, wrap, typographyName, colorName, variantName, borderColor) {
|
|
27
30
|
if (!input) return "";
|
|
@@ -309,8 +312,8 @@ export function msgUndoAll() {
|
|
|
309
312
|
}
|
|
310
313
|
export function printLineBar(text, indent = 2) {
|
|
311
314
|
if (text === "") {
|
|
312
|
-
relinka("
|
|
315
|
+
relinka("log", re.dim("\u2502"));
|
|
313
316
|
} else {
|
|
314
|
-
relinka("
|
|
317
|
+
relinka("log", `${re.dim("\u2502")}${" ".repeat(indent)}${text}`);
|
|
315
318
|
}
|
|
316
319
|
}
|
package/bin/mod.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { startEditor } from "./components/editor/editor-mod.js";
|
|
|
4
4
|
export { mainSymbols, fallbackSymbols, } from "./components/figures/figures-mod.js";
|
|
5
5
|
export { confirmPrompt } from "./components/input/confirm-prompt.js";
|
|
6
6
|
export { inputPrompt } from "./components/input/input-prompt.js";
|
|
7
|
-
export type { ArgDefinition, ArgDefinitions,
|
|
7
|
+
export type { ArgDefinition, ArgDefinitions, CommandsMap, Command, InferArgTypes, FileBasedCmdsOptions, } from "./components/launcher/launcher-mod.js";
|
|
8
8
|
export { defineCommand, defineArgs, showUsage, runMain, } from "./components/launcher/launcher-mod.js";
|
|
9
9
|
export { toBaseColor, toSolidColor } from "./components/msg-fmt/colors.js";
|
|
10
10
|
export { relinkaByRemptsDeprecated, relinkaAsyncByRemptsDeprecated, throwError, } from "./components/msg-fmt/logger.js";
|
package/bin/types.d.ts
CHANGED
|
@@ -241,7 +241,7 @@ export type RenderParams = {
|
|
|
241
241
|
/**
|
|
242
242
|
* Known symbol names that will have IntelliSense support
|
|
243
243
|
*/
|
|
244
|
-
export type SymbolName = "pointer" | "start" | "middle" | "end" | "line" | "corner_top_right" | "step_active" | "step_error" | "
|
|
244
|
+
export type SymbolName = "pointer" | "start" | "middle" | "end" | "line" | "corner_top_right" | "step_active" | "step_error" | "log" | "success" | "info" | "warn" | "error";
|
|
245
245
|
export type Symbols = Record<SymbolName, string>;
|
|
246
246
|
export type FmtMsgOptions = {
|
|
247
247
|
type: MsgType;
|
package/bin/utils/prompt-end.js
CHANGED
|
@@ -21,14 +21,14 @@ export async function completePrompt(prompt, isCtrlC, _endTitle = "", _endTitleC
|
|
|
21
21
|
return value ?? false;
|
|
22
22
|
}
|
|
23
23
|
export function renderEndLine() {
|
|
24
|
-
const lineLength = getExactTerminalWidth() -
|
|
25
|
-
relinka("
|
|
26
|
-
relinka("
|
|
27
|
-
relinka("
|
|
24
|
+
const lineLength = getExactTerminalWidth() - 6;
|
|
25
|
+
relinka("null", re.dim(symbols.middle));
|
|
26
|
+
relinka("null", re.dim(`${symbols.end}${symbols.line.repeat(lineLength)}\u22B1`));
|
|
27
|
+
relinka("null", "");
|
|
28
28
|
}
|
|
29
29
|
export function renderEndLineInput() {
|
|
30
|
-
const lineLength = getExactTerminalWidth() -
|
|
31
|
-
relinka("
|
|
32
|
-
relinka("
|
|
33
|
-
relinka("
|
|
30
|
+
const lineLength = getExactTerminalWidth() - 6;
|
|
31
|
+
relinka("null", "");
|
|
32
|
+
relinka("null", re.dim(`${symbols.end}${symbols.line.repeat(lineLength)}\u22B1`));
|
|
33
|
+
relinka("null", "");
|
|
34
34
|
}
|
package/package.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"dependencies": {
|
|
3
3
|
"@figliolia/chalk-animation": "^1.0.4",
|
|
4
4
|
"@reliverse/reliarg": "^1.0.3",
|
|
5
|
-
"@reliverse/relico": "^1.1.
|
|
6
|
-
"@reliverse/relinka": "^1.4.
|
|
5
|
+
"@reliverse/relico": "^1.1.2",
|
|
6
|
+
"@reliverse/relinka": "^1.4.5",
|
|
7
7
|
"@reliverse/runtime": "^1.0.3",
|
|
8
8
|
"ansi-escapes": "^7.0.0",
|
|
9
9
|
"c12": "^3.0.3",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"name": "@reliverse/rempts",
|
|
30
30
|
"type": "module",
|
|
31
|
-
"version": "1.7.
|
|
31
|
+
"version": "1.7.3",
|
|
32
32
|
"author": "reliverse",
|
|
33
33
|
"bugs": {
|
|
34
34
|
"email": "blefnk@gmail.com",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"eslint-plugin-no-relative-import-paths": "^1.6.1",
|
|
59
59
|
"eslint-plugin-perfectionist": "^4.13.0",
|
|
60
60
|
"jiti": "^2.4.2",
|
|
61
|
-
"knip": "^5.
|
|
61
|
+
"knip": "^5.56.0",
|
|
62
62
|
"typescript": "^5.8.3",
|
|
63
63
|
"typescript-eslint": "^8.32.1",
|
|
64
64
|
"vitest": "^3.1.3"
|