@reliverse/rempts 1.7.2 โ 1.7.4
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 +16 -14
- package/bin/components/launcher/launcher-mod.d.ts +17 -16
- package/bin/components/launcher/launcher-mod.js +101 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -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
|
|
@@ -94,7 +94,7 @@ import {
|
|
|
94
94
|
|
|
95
95
|
### Notices
|
|
96
96
|
|
|
97
|
-
- `setup`/`cleanup` are now `
|
|
97
|
+
- `setup`/`cleanup` are now `onCmdInit`/`onCmdExit` (old names still work for now).
|
|
98
98
|
|
|
99
99
|
### Prompts Usage Example
|
|
100
100
|
|
|
@@ -145,6 +145,8 @@ await main();
|
|
|
145
145
|
|
|
146
146
|
#### Launcher Usage Example
|
|
147
147
|
|
|
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.
|
|
149
|
+
|
|
148
150
|
```ts
|
|
149
151
|
import { relinka } from "@reliverse/relinka";
|
|
150
152
|
|
|
@@ -156,10 +158,10 @@ const main = defineCommand({
|
|
|
156
158
|
version: "1.0.0",
|
|
157
159
|
description: "Rempts Launcher Playground CLI",
|
|
158
160
|
},
|
|
159
|
-
|
|
161
|
+
onCmdInit() {
|
|
160
162
|
relinka("success", "Setup");
|
|
161
163
|
},
|
|
162
|
-
|
|
164
|
+
onCmdExit() {
|
|
163
165
|
relinka("success", "Cleanup");
|
|
164
166
|
},
|
|
165
167
|
commands: {
|
|
@@ -220,7 +222,7 @@ export default defineCommand({
|
|
|
220
222
|
**Hint**:
|
|
221
223
|
|
|
222
224
|
- Install `bun add -D @reliverse/dler`
|
|
223
|
-
- 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
|
|
224
226
|
|
|
225
227
|
### Advanced Minimal API
|
|
226
228
|
|
|
@@ -287,7 +289,7 @@ await runMain(defineCommand({}));
|
|
|
287
289
|
|
|
288
290
|
```bash
|
|
289
291
|
bun add -D @reliverse/dler # or: bun i -g @reliverse/dler
|
|
290
|
-
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
|
|
291
293
|
# * `--main` is optional, default is `./src/mod.ts`
|
|
292
294
|
# * you can specify multiple commands at once
|
|
293
295
|
```
|
|
@@ -545,18 +547,18 @@ Finally, a full-featured CLI launcher without the ceremony. `@reliverse/rempts`'
|
|
|
545
547
|
- **Lifecycle Hooks:**
|
|
546
548
|
You can define optional lifecycle hooks in your main command:
|
|
547
549
|
- `onLauncherStart` and `onLauncherEnd` (global, called once per CLI process)
|
|
548
|
-
- `
|
|
550
|
+
- `onCmdInit` and `onCmdExit` (per-command, called before/after each command, but NOT for the main `run()` handler)
|
|
549
551
|
|
|
550
552
|
**Global Hooks:**
|
|
551
553
|
- `onLauncherStart`: Called once, before any command/run() is executed.
|
|
552
554
|
- `onLauncherEnd`: Called once, after all command/run() logic is finished (even if an error occurs).
|
|
553
555
|
|
|
554
556
|
**Per-Command Hooks:**
|
|
555
|
-
- `
|
|
556
|
-
- `
|
|
557
|
+
- `onCmdInit`: Called before each command (not for main `run()`).
|
|
558
|
+
- `onCmdExit`: Called after each command (not for main `run()`).
|
|
557
559
|
|
|
558
560
|
This means:
|
|
559
|
-
- If your CLI has multiple commands, `
|
|
561
|
+
- If your CLI has multiple commands, `onCmdInit` and `onCmdExit` will be called for each command invocation, not just once for the whole CLI process.
|
|
560
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.
|
|
561
563
|
- This allows you to perform setup/teardown logic specific to each command execution.
|
|
562
564
|
- If you want logic to run only once for the entire CLI process, use `onLauncherStart` and `onLauncherEnd`.
|
|
@@ -567,18 +569,18 @@ Finally, a full-featured CLI launcher without the ceremony. `@reliverse/rempts`'
|
|
|
567
569
|
const main = defineCommand({
|
|
568
570
|
onLauncherStart() { relinka('info', 'Global setup (once per process)'); },
|
|
569
571
|
onLauncherEnd() { relinka('info', 'Global cleanup (once per process)'); },
|
|
570
|
-
|
|
571
|
-
|
|
572
|
+
onCmdInit() { relinka('info', 'Setup for each command'); },
|
|
573
|
+
onCmdExit() { relinka('info', 'Cleanup for each command'); },
|
|
572
574
|
commands: { ... },
|
|
573
575
|
run() { relinka('info', 'Main run handler (no command)'); },
|
|
574
576
|
});
|
|
575
577
|
// onLauncherStart/onLauncherEnd are called once per process
|
|
576
|
-
//
|
|
578
|
+
// onCmdInit/onCmdExit are called for every command (not for main run())
|
|
577
579
|
// If you want per-run() logic, use the run() handler or global hooks
|
|
578
580
|
```
|
|
579
581
|
|
|
580
582
|
- **Deprecation Notice**
|
|
581
|
-
- 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 `
|
|
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 `onCmdInit` and `onCmdExit` going forward.
|
|
582
584
|
- The `subCommands` property is deprecated as well. Please use `commands` instead. `subCommands` will be removed in a future major version.
|
|
583
585
|
|
|
584
586
|
- **Dynamic Usage Examples:**
|
|
@@ -53,6 +53,7 @@ type CommandContext<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;
|
|
@@ -66,21 +67,21 @@ type DefineCommandOptions<A extends ArgDefinitions = EmptyArgs> = {
|
|
|
66
67
|
*/
|
|
67
68
|
subCommands?: CommandsMap;
|
|
68
69
|
/**
|
|
69
|
-
* Called before the command runs
|
|
70
|
+
* Called before the command runs. Receives `{ args, raw }` (parsed args and raw argv).
|
|
70
71
|
*/
|
|
71
|
-
|
|
72
|
+
onCmdInit?: CommandHook<InferArgTypes<A>>;
|
|
72
73
|
/**
|
|
73
|
-
* Called after the command finishes
|
|
74
|
+
* Called after the command finishes. Receives `{ args, raw }` (parsed args and raw argv).
|
|
74
75
|
*/
|
|
75
|
-
|
|
76
|
+
onCmdExit?: CommandHook<InferArgTypes<A>>;
|
|
76
77
|
/**
|
|
77
|
-
* @deprecated Use
|
|
78
|
+
* @deprecated Use onCmdInit instead
|
|
78
79
|
*/
|
|
79
|
-
setup?:
|
|
80
|
+
setup?: CommandHook<InferArgTypes<A>>;
|
|
80
81
|
/**
|
|
81
|
-
* @deprecated Use
|
|
82
|
+
* @deprecated Use onCmdExit instead
|
|
82
83
|
*/
|
|
83
|
-
cleanup?:
|
|
84
|
+
cleanup?: CommandHook<InferArgTypes<A>>;
|
|
84
85
|
/**
|
|
85
86
|
* Called once per CLI process, before any command/run() is executed
|
|
86
87
|
*/
|
|
@@ -103,21 +104,21 @@ export type Command<A extends ArgDefinitions = EmptyArgs> = {
|
|
|
103
104
|
*/
|
|
104
105
|
subCommands?: CommandsMap;
|
|
105
106
|
/**
|
|
106
|
-
* Called before the command runs
|
|
107
|
+
* Called before the command runs. Receives `{ args, raw }` (parsed args and raw argv).
|
|
107
108
|
*/
|
|
108
|
-
|
|
109
|
+
onCmdInit?: CommandHook<InferArgTypes<A>>;
|
|
109
110
|
/**
|
|
110
|
-
* Called after the command finishes
|
|
111
|
+
* Called after the command finishes. Receives `{ args, raw }` (parsed args and raw argv).
|
|
111
112
|
*/
|
|
112
|
-
|
|
113
|
+
onCmdExit?: CommandHook<InferArgTypes<A>>;
|
|
113
114
|
/**
|
|
114
|
-
* @deprecated Use
|
|
115
|
+
* @deprecated Use onCmdInit instead
|
|
115
116
|
*/
|
|
116
|
-
setup?:
|
|
117
|
+
setup?: CommandHook<InferArgTypes<A>>;
|
|
117
118
|
/**
|
|
118
|
-
* @deprecated Use
|
|
119
|
+
* @deprecated Use onCmdExit instead
|
|
119
120
|
*/
|
|
120
|
-
cleanup?:
|
|
121
|
+
cleanup?: CommandHook<InferArgTypes<A>>;
|
|
121
122
|
/**
|
|
122
123
|
* Called once per CLI process, before any command/run() is executed
|
|
123
124
|
*/
|
|
@@ -57,8 +57,8 @@ function isFlag(str) {
|
|
|
57
57
|
return str.startsWith("-");
|
|
58
58
|
}
|
|
59
59
|
export function defineCommand(options) {
|
|
60
|
-
const
|
|
61
|
-
const
|
|
60
|
+
const onCmdInit = options.onCmdInit || options.setup;
|
|
61
|
+
const onCmdExit = options.onCmdExit || options.cleanup;
|
|
62
62
|
const onLauncherStart = options.onLauncherStart;
|
|
63
63
|
const onLauncherEnd = options.onLauncherEnd;
|
|
64
64
|
let commands = options.commands;
|
|
@@ -70,13 +70,13 @@ export function defineCommand(options) {
|
|
|
70
70
|
args: options.args || {},
|
|
71
71
|
run: options.run,
|
|
72
72
|
commands,
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
onCmdInit,
|
|
74
|
+
onCmdExit,
|
|
75
75
|
onLauncherStart,
|
|
76
76
|
onLauncherEnd,
|
|
77
77
|
// Backward-compatible aliases
|
|
78
|
-
setup:
|
|
79
|
-
cleanup:
|
|
78
|
+
setup: onCmdInit,
|
|
79
|
+
cleanup: onCmdExit
|
|
80
80
|
};
|
|
81
81
|
Object.defineProperty(cmdObj, "subCommands", {
|
|
82
82
|
get() {
|
|
@@ -167,7 +167,7 @@ export async function showUsage(command, parserOptions = {}, displayNotFoundMess
|
|
|
167
167
|
);
|
|
168
168
|
}
|
|
169
169
|
if (subCommandNames.length > 0) {
|
|
170
|
-
relinka("
|
|
170
|
+
relinka("info", "Available commands (run with `help` to see more):");
|
|
171
171
|
subCommandDefs.forEach(({ name, def }) => {
|
|
172
172
|
const desc = def?.meta?.description ?? "";
|
|
173
173
|
relinka("log", `\u2022 ${name}${desc ? ` | ${desc}` : ""}`);
|
|
@@ -217,14 +217,14 @@ export async function showUsage(command, parserOptions = {}, displayNotFoundMess
|
|
|
217
217
|
);
|
|
218
218
|
}
|
|
219
219
|
if (subCommandNames.length > 0) {
|
|
220
|
-
relinka("
|
|
220
|
+
relinka("info", "Available commands (run with `help` to see more):");
|
|
221
221
|
subCommandDefs.forEach(({ name, def }) => {
|
|
222
222
|
const desc = def?.meta?.description ?? "";
|
|
223
223
|
relinka("log", `\u2022 ${name}${desc ? ` | ${desc}` : ""}`);
|
|
224
224
|
});
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
|
-
relinka("
|
|
227
|
+
relinka("info", "Available options:");
|
|
228
228
|
relinka("log", "\u2022 -h, --help | Show help");
|
|
229
229
|
relinka("log", "\u2022 -v, --version | Show version");
|
|
230
230
|
relinka("log", "\u2022 --debug | Enable debug mode");
|
|
@@ -334,14 +334,17 @@ This can cause recursion or unexpected behavior.`
|
|
|
334
334
|
if (fileBasedEnabled && rawArgv.length > 0 && !isFlag(rawArgv[0])) {
|
|
335
335
|
const [subName, ...subCmdArgv] = rawArgv;
|
|
336
336
|
try {
|
|
337
|
-
|
|
338
|
-
|
|
337
|
+
const ctx = getParsedContext(command, rawArgv, parserOptions);
|
|
338
|
+
if (typeof command.onCmdInit === "function")
|
|
339
|
+
await command.onCmdInit(ctx);
|
|
339
340
|
await runFileBasedSubCmd(
|
|
340
341
|
subName,
|
|
341
342
|
subCmdArgv,
|
|
342
343
|
parserOptions.fileBasedCmds,
|
|
343
344
|
parserOptions,
|
|
344
|
-
command.
|
|
345
|
+
command.onCmdExit ? async (_subCtx) => {
|
|
346
|
+
await command.onCmdExit?.(ctx);
|
|
347
|
+
} : void 0
|
|
345
348
|
);
|
|
346
349
|
if (autoExit) process.exit(0);
|
|
347
350
|
return;
|
|
@@ -371,13 +374,16 @@ This can cause recursion or unexpected behavior.`
|
|
|
371
374
|
}
|
|
372
375
|
if (subSpec) {
|
|
373
376
|
try {
|
|
374
|
-
|
|
375
|
-
|
|
377
|
+
const ctx = getParsedContext(command, rawArgv, parserOptions);
|
|
378
|
+
if (typeof command.onCmdInit === "function")
|
|
379
|
+
await command.onCmdInit(ctx);
|
|
376
380
|
await runSubCommand(
|
|
377
381
|
subSpec,
|
|
378
382
|
subCmdArgv,
|
|
379
383
|
parserOptions,
|
|
380
|
-
command.
|
|
384
|
+
command.onCmdExit ? async (_subCtx) => {
|
|
385
|
+
await command.onCmdExit?.(ctx);
|
|
386
|
+
} : void 0
|
|
381
387
|
);
|
|
382
388
|
if (autoExit) process.exit(0);
|
|
383
389
|
return;
|
|
@@ -474,27 +480,41 @@ Info for this CLI's developer: No valid command directory found, expected: ${exp
|
|
|
474
480
|
);
|
|
475
481
|
}
|
|
476
482
|
try {
|
|
477
|
-
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);
|
|
478
491
|
} finally {
|
|
479
|
-
if (typeof parentFinish === "function") await parentFinish();
|
|
480
492
|
}
|
|
481
493
|
}
|
|
482
494
|
async function runSubCommand(spec, argv, parserOptions, parentFinish) {
|
|
483
495
|
const subCommand = await loadSubCommand(spec);
|
|
484
496
|
try {
|
|
485
|
-
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);
|
|
486
505
|
} finally {
|
|
487
|
-
if (typeof parentFinish === "function") await parentFinish();
|
|
488
506
|
}
|
|
489
507
|
}
|
|
490
|
-
async function runCommandWithArgs(command, argv, parserOptions) {
|
|
508
|
+
async function runCommandWithArgs(command, argv, parserOptions, returnCtx) {
|
|
491
509
|
const autoExit = parserOptions.autoExit !== false;
|
|
492
510
|
const booleanKeys = Object.keys(command.args || {}).filter(
|
|
493
511
|
(k) => command.args[k].type === "boolean"
|
|
494
512
|
);
|
|
495
513
|
const defaultMap = {};
|
|
496
514
|
for (const [argKey, def] of Object.entries(command.args || {})) {
|
|
497
|
-
if (def.
|
|
515
|
+
if (def.type === "boolean") {
|
|
516
|
+
defaultMap[argKey] = def.default !== void 0 ? def.default : false;
|
|
517
|
+
} else if (def.default !== void 0) {
|
|
498
518
|
if (def.type === "array" && typeof def.default === "string") {
|
|
499
519
|
defaultMap[argKey] = [def.default];
|
|
500
520
|
} else {
|
|
@@ -543,7 +563,11 @@ async function runCommandWithArgs(command, argv, parserOptions) {
|
|
|
543
563
|
else throw new Error(`Missing required argument: --${key}`);
|
|
544
564
|
}
|
|
545
565
|
try {
|
|
546
|
-
|
|
566
|
+
if (def.type === "boolean") {
|
|
567
|
+
finalArgs[key] = rawVal !== void 0 ? castArgValue(def, rawVal, key) : false;
|
|
568
|
+
} else {
|
|
569
|
+
finalArgs[key] = castArgValue(def, rawVal, key);
|
|
570
|
+
}
|
|
547
571
|
if (def.type === "array" && def.options && finalArgs[key]) {
|
|
548
572
|
const values = finalArgs[key];
|
|
549
573
|
const invalidOptions = values.filter(
|
|
@@ -593,6 +617,8 @@ ${String(err)}`);
|
|
|
593
617
|
if (autoExit) process.exit(1);
|
|
594
618
|
else throw err;
|
|
595
619
|
}
|
|
620
|
+
if (returnCtx) return ctx;
|
|
621
|
+
return void 0;
|
|
596
622
|
}
|
|
597
623
|
function castArgValue(def, rawVal, argName) {
|
|
598
624
|
if (rawVal == null) {
|
|
@@ -643,7 +669,9 @@ export async function runCmd(command, argv = [], parserOptions = {}) {
|
|
|
643
669
|
);
|
|
644
670
|
const defaultMap = {};
|
|
645
671
|
for (const [argKey, def] of Object.entries(command.args || {})) {
|
|
646
|
-
if (def.
|
|
672
|
+
if (def.type === "boolean") {
|
|
673
|
+
defaultMap[argKey] = def.default !== void 0 ? def.default : false;
|
|
674
|
+
} else if (def.default !== void 0) {
|
|
647
675
|
if (def.type === "array" && typeof def.default === "string") {
|
|
648
676
|
defaultMap[argKey] = [def.default];
|
|
649
677
|
} else {
|
|
@@ -708,3 +736,53 @@ export async function runCmd(command, argv = [], parserOptions = {}) {
|
|
|
708
736
|
throw new Error("Command has no run() handler.");
|
|
709
737
|
}
|
|
710
738
|
}
|
|
739
|
+
function getParsedContext(command, argv, parserOptions) {
|
|
740
|
+
const booleanKeys = Object.keys(command.args || {}).filter(
|
|
741
|
+
(k) => command.args[k].type === "boolean"
|
|
742
|
+
);
|
|
743
|
+
const defaultMap = {};
|
|
744
|
+
for (const [argKey, def] of Object.entries(command.args || {})) {
|
|
745
|
+
if (def.type === "boolean") {
|
|
746
|
+
defaultMap[argKey] = def.default !== void 0 ? def.default : false;
|
|
747
|
+
} else if (def.default !== void 0) {
|
|
748
|
+
if (def.type === "array" && typeof def.default === "string") {
|
|
749
|
+
defaultMap[argKey] = [def.default];
|
|
750
|
+
} else {
|
|
751
|
+
defaultMap[argKey] = def.default;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
const mergedParserOptions = {
|
|
756
|
+
...parserOptions,
|
|
757
|
+
boolean: [...parserOptions.boolean || [], ...booleanKeys],
|
|
758
|
+
defaults: { ...defaultMap, ...parserOptions.defaults || {} }
|
|
759
|
+
};
|
|
760
|
+
const parsed = reliArgParser(argv, mergedParserOptions);
|
|
761
|
+
const finalArgs = {};
|
|
762
|
+
const positionalKeys = Object.keys(command.args || {}).filter(
|
|
763
|
+
(k) => command.args[k].type === "positional"
|
|
764
|
+
);
|
|
765
|
+
const leftoverPositionals = [...parsed._ || []];
|
|
766
|
+
for (let i = 0; i < positionalKeys.length; i++) {
|
|
767
|
+
const key = positionalKeys[i];
|
|
768
|
+
const def = command.args?.[key];
|
|
769
|
+
const val = leftoverPositionals[i];
|
|
770
|
+
finalArgs[key] = val != null ? castArgValue(def, val, key) : def.default;
|
|
771
|
+
}
|
|
772
|
+
const otherKeys = Object.keys(command.args || {}).filter(
|
|
773
|
+
(k) => command.args[k].type !== "positional"
|
|
774
|
+
);
|
|
775
|
+
for (const key of otherKeys) {
|
|
776
|
+
const def = command.args?.[key];
|
|
777
|
+
let rawVal = parsed[key];
|
|
778
|
+
if (def.type === "array" && rawVal !== void 0 && !Array.isArray(rawVal)) {
|
|
779
|
+
rawVal = [rawVal];
|
|
780
|
+
}
|
|
781
|
+
if (def.type === "boolean") {
|
|
782
|
+
finalArgs[key] = rawVal !== void 0 ? castArgValue(def, rawVal, key) : false;
|
|
783
|
+
} else {
|
|
784
|
+
finalArgs[key] = castArgValue(def, rawVal, key);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
return { args: finalArgs, raw: argv };
|
|
788
|
+
}
|