@reliverse/rempts 1.6.2 → 1.7.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/LICENSE +1 -1
- package/README.md +311 -77
- package/bin/{core-impl → components}/anykey/anykey-mod.d.ts +1 -1
- package/bin/{core-impl → components}/anykey/anykey-mod.js +1 -1
- package/bin/{core-impl → components}/date/date.d.ts +1 -1
- package/bin/{core-impl → components}/editor/editor-mod.d.ts +1 -1
- package/bin/{core-impl → components}/editor/editor-mod.js +7 -7
- package/bin/{core-impl → components}/input/confirm-prompt.d.ts +1 -1
- package/bin/{core-impl → components}/input/confirm-prompt.js +1 -1
- package/bin/{core-impl → components}/input/input-prompt.d.ts +1 -1
- package/bin/{core-impl → components}/input/input-prompt.js +3 -7
- package/bin/{core-impl → components}/launcher/deprecated/_parser.ts.txt +1 -1
- package/bin/{core-impl → components}/launcher/deprecated/_utils.ts.txt +1 -1
- package/bin/{core-impl → components}/launcher/deprecated/args.ts.txt +1 -1
- package/bin/{core-impl → components}/launcher/deprecated/command.ts.txt +1 -1
- package/bin/{core-impl → components}/launcher/deprecated/launcher-mod.ts.txt +1 -1
- package/bin/{core-impl → components}/launcher/deprecated/usage.ts.txt +1 -1
- package/bin/components/launcher/launcher-mod.d.ts +177 -0
- package/bin/components/launcher/launcher-mod.js +626 -0
- package/bin/{core-impl → components}/msg-fmt/colors.d.ts +1 -1
- package/bin/{core-impl → components}/msg-fmt/logger.d.ts +1 -1
- package/bin/{core-impl → components}/msg-fmt/logger.js +1 -4
- package/bin/{core-impl → components}/msg-fmt/mapping.d.ts +1 -1
- package/bin/{core-impl → components}/msg-fmt/messages.d.ts +1 -1
- package/bin/{core-impl → components}/msg-fmt/variants.d.ts +1 -1
- package/bin/{core-impl → components}/next-steps/next-steps.d.ts +2 -2
- package/bin/{core-impl → components}/number/number-mod.d.ts +1 -1
- package/bin/{core-impl → components}/select/multiselect-prompt.d.ts +1 -1
- package/bin/{core-impl → components}/select/multiselect-prompt.js +1 -1
- package/bin/{core-impl → components}/select/nummultiselect-prompt.d.ts +1 -1
- package/bin/{core-impl → components}/select/numselect-prompt.d.ts +1 -1
- package/bin/{core-impl → components}/select/numselect-prompt.js +1 -1
- package/bin/{core-impl → components}/select/select-prompt.d.ts +1 -1
- package/bin/{core-impl → components}/select/select-prompt.js +2 -2
- package/bin/{core-impl → components}/select/toggle-prompt.d.ts +1 -1
- package/bin/{core-impl → components}/select/toggle-prompt.js +1 -1
- package/bin/{core-impl → components}/st-end/end.d.ts +1 -1
- package/bin/{core-impl → components}/st-end/start.d.ts +1 -1
- package/bin/{core-impl → components}/st-end/start.js +2 -2
- package/bin/{core-impl → components}/task/progress.d.ts +1 -1
- package/bin/{core-impl → components}/visual/animate/animate.d.ts +1 -1
- package/bin/hooks/spinner/spinner-mod.d.ts +20 -0
- package/bin/hooks/spinner/spinner-mod.js +26 -0
- package/bin/mod.d.ts +37 -0
- package/bin/mod.js +88 -0
- package/bin/{core-impl/utils → utils}/colorize.d.ts +1 -1
- package/bin/{core-impl/utils → utils}/prevent.d.ts +1 -1
- package/bin/{core-impl/utils → utils}/prevent.js +2 -2
- package/bin/{core-impl/utils → utils}/prompt-end.d.ts +1 -1
- package/bin/{core-impl/utils → utils}/prompt-end.js +2 -2
- package/bin/{core-impl/utils → utils}/stream-text.d.ts +1 -1
- package/bin/{core-impl/utils → utils}/stream-text.js +2 -2
- package/package.json +46 -24
- package/bin/core-impl/figures/figures.test.d.ts +0 -1
- package/bin/core-impl/figures/figures.test.js +0 -474
- package/bin/core-impl/launcher/launcher-mod.d.ts +0 -92
- package/bin/core-impl/launcher/launcher-mod.js +0 -371
- package/bin/main.d.ts +0 -36
- package/bin/main.js +0 -86
- /package/bin/{core-impl → components}/date/date.js +0 -0
- /package/bin/{core-impl → components}/figures/figures-mod.d.ts +0 -0
- /package/bin/{core-impl → components}/figures/figures-mod.js +0 -0
- /package/bin/{core-impl → components}/msg-fmt/colors.js +0 -0
- /package/bin/{core-impl → components}/msg-fmt/mapping.js +0 -0
- /package/bin/{core-impl → components}/msg-fmt/messages.js +0 -0
- /package/bin/{core-impl → components}/msg-fmt/terminal.d.ts +0 -0
- /package/bin/{core-impl → components}/msg-fmt/terminal.js +0 -0
- /package/bin/{core-impl → components}/msg-fmt/variants.js +0 -0
- /package/bin/{core-impl → components}/next-steps/next-steps.js +0 -0
- /package/bin/{core-impl → components}/number/number-mod.js +0 -0
- /package/bin/{core-impl → components}/results/results.d.ts +0 -0
- /package/bin/{core-impl → components}/results/results.js +0 -0
- /package/bin/{core-impl → components}/select/nummultiselect-prompt.js +0 -0
- /package/bin/{core-impl → components}/st-end/end.js +0 -0
- /package/bin/{core-impl → components}/task/progress.js +0 -0
- /package/bin/{core-impl → components}/task/spinner.d.ts +0 -0
- /package/bin/{core-impl → components}/task/spinner.js +0 -0
- /package/bin/{core-impl → components}/visual/animate/animate.js +0 -0
- /package/bin/{core-impl → components}/visual/ascii-art/ascii-art.d.ts +0 -0
- /package/bin/{core-impl → components}/visual/ascii-art/ascii-art.js +0 -0
- /package/bin/{core-types.d.ts → types.d.ts} +0 -0
- /package/bin/{core-types.js → types.js} +0 -0
- /package/bin/{core-impl/utils → utils}/colorize.js +0 -0
- /package/bin/{core-impl/utils → utils}/errors.d.ts +0 -0
- /package/bin/{core-impl/utils → utils}/errors.js +0 -0
- /package/bin/{core-impl/utils → utils}/system.d.ts +0 -0
- /package/bin/{core-impl/utils → utils}/system.js +0 -0
- /package/bin/{core-impl/utils → utils}/validate.d.ts +0 -0
- /package/bin/{core-impl/utils → utils}/validate.js +0 -0
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) Nazar Kornienko (blefnk), Reliverse
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -1,70 +1,176 @@
|
|
|
1
|
-
#
|
|
1
|
+
# rempts • powerful js/ts cli builder
|
|
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
|
+
[💬 Discord](https://discord.gg/3GawfWfAPe) — [📦 NPM](https://npmjs.com/package/@reliverse/rempts) — [🧠 Docs](https://docs.reliverse.org/reliverse/rempts) — [🌐 JSR](https://jsr.io/@reliverse/rempts) — [✨ GitHub](https://github.com/reliverse/rempts)
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Features
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- 📂 File-based commands (optional)
|
|
9
|
+
- 🫂 Rempts prevents you from fighting with your CLI tool
|
|
10
|
+
- ✨ Rempts is your end-to-end CLI UI + command framework
|
|
11
|
+
- 💪 Made for DX precision and high-context terminal UX
|
|
12
|
+
- 📂 File-based commands (app router style by default)
|
|
13
|
+
- 🏎️ Prompt engine that *feels* modern, actually is
|
|
16
14
|
- 🧠 Type-safe from args to prompts
|
|
17
|
-
- 🎨 Customizable themes, styled output
|
|
18
|
-
- 🧩 Router + argument parser built-in
|
|
19
15
|
- ⚡ Blazing-fast, no runtime baggage
|
|
16
|
+
- 🧩 Router + argument parser built-in
|
|
17
|
+
- 🎨 Customizable themes, styled output
|
|
18
|
+
- 🚨 Crash-safe (Ctrl+C, SIGINT, errors)
|
|
20
19
|
- 🪄 Minimal API surface, max expressiveness
|
|
21
|
-
- 🏎️ Prompt engine that *feels* modern, actually is
|
|
22
20
|
- 🧪 Scriptable for testing, stable for production
|
|
23
|
-
-
|
|
24
|
-
-
|
|
21
|
+
- 🆕 Automatic commands creation (via `dler init --cmd my-cool-cmd`)
|
|
22
|
+
- 🏞️ No more hacking together `inquirer`, `citty`, `commander`, `chalk`
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
bun add @reliverse/rempts
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage Examples
|
|
31
|
+
|
|
32
|
+
- [Prompts](#prompts)
|
|
33
|
+
- [Launcher](#launcher)
|
|
25
34
|
|
|
26
35
|
## Screenshot
|
|
27
36
|
|
|
28
|
-

|
|
37
|
+

|
|
29
38
|
|
|
30
|
-
##
|
|
39
|
+
## API Overview
|
|
31
40
|
|
|
32
|
-
|
|
41
|
+
All main prompts APIs are available from the package root:
|
|
33
42
|
|
|
34
|
-
|
|
43
|
+
```ts
|
|
44
|
+
import {
|
|
45
|
+
// ...prompts
|
|
46
|
+
defineCommand, runMain, defineArgs,
|
|
47
|
+
inputPrompt, selectPrompt, multiselectPrompt, numberPrompt,
|
|
48
|
+
confirmPrompt, togglePrompt, spinnerTaskPrompt, progressTaskPrompt,
|
|
49
|
+
startPrompt, endPrompt, resultPrompt, nextStepsPrompt,
|
|
50
|
+
// ...hooks
|
|
51
|
+
useSpinner,
|
|
52
|
+
// ...launcher
|
|
53
|
+
runMain, defineCommand, defineArgs,
|
|
54
|
+
// ...types
|
|
55
|
+
// ...more
|
|
56
|
+
} from "@reliverse/rempts";
|
|
57
|
+
```
|
|
35
58
|
|
|
36
|
-
|
|
37
|
-
Use `subCommands` in your command definition or let the launcher automatically load commands from a specified directory.
|
|
59
|
+
> See [`src/mod.ts`](./src/mod.ts) for the full list of exports.
|
|
38
60
|
|
|
39
|
-
|
|
40
|
-
The launcher scans your specified `cmdsRootPath` for command files matching common patterns such as:
|
|
41
|
-
- `arg-cmdName.{ts,js}`
|
|
42
|
-
- `cmdName/index.{ts,js}`
|
|
43
|
-
- `cmdName/cmdName-mod.{ts,js}`
|
|
44
|
-
- And more — with automatic usage output if a command file is not found.
|
|
61
|
+
## Prompts
|
|
45
62
|
|
|
46
|
-
|
|
47
|
-
Automatically processes global flags such as:
|
|
48
|
-
- `--help` and `-h` to show usage details.
|
|
49
|
-
- `--version` and `-v` to display version information.
|
|
50
|
-
- `--debug` for verbose logging during development.
|
|
63
|
+
### Built-in Prompts
|
|
51
64
|
|
|
52
|
-
|
|
53
|
-
|
|
65
|
+
| Prompt | Description |
|
|
66
|
+
|---------------------------|-----------------------------------------------------------|
|
|
67
|
+
| `inputPrompt` | Single-line input (with mask support, e.g. for passwords) |
|
|
68
|
+
| `selectPrompt` | Single-choice radio menu |
|
|
69
|
+
| `multiselectPrompt` | Multi-choice checkbox menu |
|
|
70
|
+
| `numberPrompt` | Type-safe number input |
|
|
71
|
+
| `confirmPrompt` | Yes/No toggle |
|
|
72
|
+
| `togglePrompt` | Custom on/off toggles |
|
|
73
|
+
| `progressTaskPrompt` | Progress bar for async tasks |
|
|
74
|
+
| `resultPrompt` | Show results in a styled box |
|
|
75
|
+
| `nextStepsPrompt` | Show next steps in a styled list |
|
|
76
|
+
| `startPrompt`/`endPrompt` | Makes CLI start/end flows look nice |
|
|
77
|
+
| `spinnerTaskPrompt` | Async loader with spinner (possibly will be deprecated) |
|
|
78
|
+
| `datePrompt` | Date input with format validation |
|
|
79
|
+
| `anykeyPrompt` | Wait for any keypress |
|
|
54
80
|
|
|
55
|
-
|
|
56
|
-
Options such as `fileBasedCmds.enable`, `cmdsRootPath`, and `autoExit` allow you to tailor the launcher's behavior. For example, you can choose whether the process should exit automatically on error or allow manual error handling.
|
|
81
|
+
### Hooks
|
|
57
82
|
|
|
58
|
-
|
|
59
|
-
|
|
83
|
+
| Hook | Description |
|
|
84
|
+
|--------------|--------------------|
|
|
85
|
+
| `useSpinner` | Start/stop spinner |
|
|
86
|
+
|
|
87
|
+
### Notices
|
|
88
|
+
|
|
89
|
+
- `setup`/`cleanup` are now `onCmdStart`/`onCmdEnd` (old names still work for now).
|
|
90
|
+
|
|
91
|
+
### Prompts Usage Example
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { relinka } from "@reliverse/relinka";
|
|
95
|
+
|
|
96
|
+
import {
|
|
97
|
+
startPrompt,
|
|
98
|
+
inputPrompt,
|
|
99
|
+
selectPrompt,
|
|
100
|
+
defineCommand,
|
|
101
|
+
runMain
|
|
102
|
+
} from "@reliverse/rempts";
|
|
103
|
+
|
|
104
|
+
async function main() {
|
|
105
|
+
await startPrompt({ title: "Project Setup" });
|
|
106
|
+
|
|
107
|
+
const name = await inputPrompt({
|
|
108
|
+
title: "What's your project name?",
|
|
109
|
+
defaultValue: "my-cool-project",
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const framework = await selectPrompt({
|
|
113
|
+
title: "Pick your framework",
|
|
114
|
+
options: [
|
|
115
|
+
{ value: "next", label: "Next.js" },
|
|
116
|
+
{ value: "svelte", label: "SvelteKit" },
|
|
117
|
+
{ value: "start", label: "TanStack Start" },
|
|
118
|
+
],
|
|
119
|
+
defaultValue: "next",
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
console.log("Your result:", { name, framework });
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
await main();
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Launcher
|
|
129
|
+
|
|
130
|
+
### Terminology
|
|
131
|
+
|
|
132
|
+
- **Launcher/Router**: The main entry point for your CLI. Visit [CLI Launcher (Router)](#cli-launcher-router) section to learn more.
|
|
133
|
+
- **Command/Subcommand**: A command is a function that defines the behavior of a CLI.
|
|
134
|
+
- **Argument**: An argument is a value that is passed to a command.
|
|
135
|
+
- **Flag**: A flag is a boolean argument that is used to enable or disable a feature.
|
|
136
|
+
- **Option**: An option is a named argument that is used to configure a command.
|
|
137
|
+
|
|
138
|
+
#### Launcher Usage Example
|
|
139
|
+
|
|
140
|
+
‼️ Go to [Usage Examples](#usage-examples) section for a more detailed example.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
import { relinka } from "@reliverse/relinka";
|
|
144
|
+
|
|
145
|
+
import { defineCommand, runMain } from "~/mod.js";
|
|
146
|
+
|
|
147
|
+
const main = defineCommand({
|
|
148
|
+
meta: {
|
|
149
|
+
name: "rempts",
|
|
150
|
+
version: "1.0.0",
|
|
151
|
+
description: "Rempts Launcher Playground CLI",
|
|
152
|
+
},
|
|
153
|
+
onCmdStart() {
|
|
154
|
+
relinka("success", "Setup");
|
|
155
|
+
},
|
|
156
|
+
onCmdEnd() {
|
|
157
|
+
relinka("success", "Cleanup");
|
|
158
|
+
},
|
|
159
|
+
subCommands: {
|
|
160
|
+
build: () => import("./app/build/cmd.js").then((r) => r.default),
|
|
161
|
+
deploy: () => import("./app/deploy/cmd.js").then((r) => r.default),
|
|
162
|
+
debug: () => import("./app/debug/cmd.js").then((r) => r.default),
|
|
163
|
+
},
|
|
164
|
+
});
|
|
60
165
|
|
|
61
|
-
|
|
166
|
+
await runMain(main);
|
|
167
|
+
```
|
|
62
168
|
|
|
63
169
|
```ts
|
|
64
170
|
await runMain(myCommand, {
|
|
65
171
|
fileBasedCmds: {
|
|
66
172
|
enable: true,
|
|
67
|
-
cmdsRootPath: "
|
|
173
|
+
cmdsRootPath: "my-cmds", // default is `./app`
|
|
68
174
|
},
|
|
69
175
|
// Optionally disable auto-exit to handle errors manually:
|
|
70
176
|
autoExit: false,
|
|
@@ -73,11 +179,12 @@ await runMain(myCommand, {
|
|
|
73
179
|
|
|
74
180
|
This flexibility allows you to easily build a rich, multi-command CLI with minimal boilerplate. The launcher even supports nested subcommands, making it simple to construct complex CLI applications.
|
|
75
181
|
|
|
76
|
-
|
|
182
|
+
#### File-Based Subcommands
|
|
77
183
|
|
|
78
|
-
Drop a `./src/cli/
|
|
184
|
+
Drop a `./src/cli/app/add/index.ts` and it's live.
|
|
79
185
|
|
|
80
186
|
```ts
|
|
187
|
+
import { defineArgs, defineCommand } from "@reliverse/rempts";
|
|
81
188
|
export default defineCommand({
|
|
82
189
|
meta: {
|
|
83
190
|
name: "add",
|
|
@@ -85,11 +192,11 @@ export default defineCommand({
|
|
|
85
192
|
description: "Add stuff to your project",
|
|
86
193
|
},
|
|
87
194
|
args: {
|
|
88
|
-
name: {
|
|
195
|
+
name: defineArgs({ // 💡 PRO TIP: use defineArgs() to get fully correct intellisense
|
|
89
196
|
type: "string",
|
|
90
197
|
required: true,
|
|
91
198
|
description: "Name of what to add",
|
|
92
|
-
},
|
|
199
|
+
}),
|
|
93
200
|
},
|
|
94
201
|
async run({ args }) {
|
|
95
202
|
relinka("info", "Adding:", args.name);
|
|
@@ -106,22 +213,10 @@ export default defineCommand({
|
|
|
106
213
|
|
|
107
214
|
**Hint**:
|
|
108
215
|
|
|
109
|
-
- Install `bun
|
|
110
|
-
- Use `
|
|
111
|
-
|
|
112
|
-
## 📦 Built-In Prompts
|
|
216
|
+
- Install `bun add -D @reliverse/dler`
|
|
217
|
+
- Use `dler init --cmd cmd1 cmd2` to init commands for rempts launcher's automatically
|
|
113
218
|
|
|
114
|
-
|
|
115
|
-
- ✅ `selectPrompt` – Radio menu
|
|
116
|
-
- 🧰 `multiselectPrompt` – Checkbox menu
|
|
117
|
-
- 🔢 `numberPrompt` – Type-safe number input
|
|
118
|
-
- 🔄 `confirmPrompt` – Yes/No toggle
|
|
119
|
-
- 🚥 `togglePrompt` – Custom on/off toggles
|
|
120
|
-
- ⏳ `spinnerPrompt` – Async loaders with status
|
|
121
|
-
- 📜 `logPrompt` – Styled logs / steps
|
|
122
|
-
- 🧼 `clearPrompt` – Clears console with style
|
|
123
|
-
|
|
124
|
-
## 🧱 Minimal, Functional API
|
|
219
|
+
### Advanced Minimal API
|
|
125
220
|
|
|
126
221
|
```ts
|
|
127
222
|
defineCommand({
|
|
@@ -144,14 +239,14 @@ defineCommand({
|
|
|
144
239
|
- Default values, validations, descriptions
|
|
145
240
|
- Full help rendering from metadata
|
|
146
241
|
|
|
147
|
-
|
|
242
|
+
### Theming + Customization
|
|
148
243
|
|
|
149
244
|
- Built-in output formatter and logger
|
|
150
245
|
- Override styles via prompt options
|
|
151
246
|
- Smart layout for small terminals
|
|
152
247
|
- Looks great in plain scripts or full CLI apps
|
|
153
248
|
|
|
154
|
-
|
|
249
|
+
### Playground
|
|
155
250
|
|
|
156
251
|
```bash
|
|
157
252
|
bun i -g @reliverse/rempts-cli
|
|
@@ -170,7 +265,42 @@ bun dev # supported options: name
|
|
|
170
265
|
- Both `rempts examples` from @reliverse/rempts and `bun dev` (which is the same thing) are themselves examples of `launcher` functionality.
|
|
171
266
|
- This launcher will show you a `multiselectPrompt()` where you can choose which CLI prompts you want to play with.
|
|
172
267
|
|
|
173
|
-
###
|
|
268
|
+
### Launcher Usage Examples
|
|
269
|
+
|
|
270
|
+
#### Minimal Usage Example
|
|
271
|
+
|
|
272
|
+
**1 Create a `src/mod.ts` file:**
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
import { runMain, defineCommand } from "@reliverse/rempts";
|
|
276
|
+
|
|
277
|
+
await runMain(defineCommand({}));
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
**2 Run the following:**
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
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
|
|
285
|
+
# * `--main` is optional, default is `./src/mod.ts`
|
|
286
|
+
# * you can specify multiple commands at once
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
**3 Visit `src/app/my-cmd-1/mod.ts` and edit it:**
|
|
290
|
+
|
|
291
|
+
```ts
|
|
292
|
+
export default defineCommand({
|
|
293
|
+
run() { console.log("Hello, world!"); },
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**4. Test it:**
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
bun src/mod.ts
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
#### Medium Usage Example
|
|
174
304
|
|
|
175
305
|
```ts
|
|
176
306
|
import { defineCommand, runMain } from "@reliverse/rempts";
|
|
@@ -187,7 +317,7 @@ const main = defineCommand({
|
|
|
187
317
|
await runMain(main);
|
|
188
318
|
```
|
|
189
319
|
|
|
190
|
-
|
|
320
|
+
#### Classic Usage Example
|
|
191
321
|
|
|
192
322
|
```ts
|
|
193
323
|
import { relinka } from "@reliverse/relinka";
|
|
@@ -209,18 +339,18 @@ const main = defineCommand({
|
|
|
209
339
|
args: {
|
|
210
340
|
name: {
|
|
211
341
|
type: "string",
|
|
212
|
-
required:
|
|
342
|
+
required: true,
|
|
213
343
|
description: "The name of the project",
|
|
214
344
|
},
|
|
215
345
|
},
|
|
216
346
|
async run({ args }) {
|
|
217
347
|
await startPrompt({
|
|
218
|
-
title: "
|
|
348
|
+
title: "Project Setup",
|
|
219
349
|
});
|
|
220
350
|
|
|
221
351
|
const name = await inputPrompt({
|
|
222
352
|
title: "What's your project name?",
|
|
223
|
-
placeholder: args.name
|
|
353
|
+
placeholder: args.name,
|
|
224
354
|
});
|
|
225
355
|
|
|
226
356
|
const framework = await selectPrompt({
|
|
@@ -239,7 +369,7 @@ const main = defineCommand({
|
|
|
239
369
|
await runMain(main);
|
|
240
370
|
```
|
|
241
371
|
|
|
242
|
-
|
|
372
|
+
#### Advanced Usage Example
|
|
243
373
|
|
|
244
374
|
```ts
|
|
245
375
|
import { relinka } from "@reliverse/relinka";
|
|
@@ -258,7 +388,7 @@ import {
|
|
|
258
388
|
* This command demonstrates the full range of launcher features along with all supported argument types:
|
|
259
389
|
*
|
|
260
390
|
* - Global Usage Handling: Automatically processes `--help` and `--version`.
|
|
261
|
-
* - File-Based Subcommands: Scans "
|
|
391
|
+
* - File-Based Subcommands: Scans "app" for subcommands (e.g., `init`).
|
|
262
392
|
* - Comprehensive Argument Parsing: Supports positional, boolean, string, number, and array arguments.
|
|
263
393
|
* - Interactive Prompts: Uses built-in prompt functions for an engaging CLI experience.
|
|
264
394
|
*/
|
|
@@ -273,12 +403,10 @@ const mainCommand = defineCommand({
|
|
|
273
403
|
// Positional arguments
|
|
274
404
|
inputFile: {
|
|
275
405
|
type: "positional",
|
|
276
|
-
required: false,
|
|
277
406
|
description: "Path to the input file (only for the main command).",
|
|
278
407
|
},
|
|
279
408
|
config: {
|
|
280
409
|
type: "positional",
|
|
281
|
-
required: false,
|
|
282
410
|
description: "Path to the configuration file.",
|
|
283
411
|
},
|
|
284
412
|
// Boolean arguments
|
|
@@ -295,20 +423,17 @@ const mainCommand = defineCommand({
|
|
|
295
423
|
// String argument
|
|
296
424
|
name: {
|
|
297
425
|
type: "string",
|
|
298
|
-
required: false,
|
|
299
426
|
description: "The name of the project.",
|
|
300
427
|
},
|
|
301
428
|
// Number argument
|
|
302
429
|
timeout: {
|
|
303
430
|
type: "number",
|
|
304
|
-
required: false,
|
|
305
431
|
default: 30,
|
|
306
432
|
description: "Timeout in seconds for the CLI operation.",
|
|
307
433
|
},
|
|
308
434
|
// Array argument
|
|
309
435
|
tags: {
|
|
310
436
|
type: "array",
|
|
311
|
-
required: false,
|
|
312
437
|
default: ["cli", "rempts"],
|
|
313
438
|
description: "List of tags associated with the project.",
|
|
314
439
|
},
|
|
@@ -322,7 +447,7 @@ const mainCommand = defineCommand({
|
|
|
322
447
|
|
|
323
448
|
// Begin interactive session with a prompt.
|
|
324
449
|
await startPrompt({
|
|
325
|
-
title: "
|
|
450
|
+
title: "Project Setup",
|
|
326
451
|
});
|
|
327
452
|
|
|
328
453
|
// Ask for the project name, falling back to provided argument or a default.
|
|
@@ -358,7 +483,7 @@ const mainCommand = defineCommand({
|
|
|
358
483
|
/**
|
|
359
484
|
* The `runMain()` function sets up the launcher with several advanced features:
|
|
360
485
|
*
|
|
361
|
-
* - File-Based Subcommands: Enables scanning for subcommands within the "
|
|
486
|
+
* - File-Based Subcommands: Enables scanning for subcommands within the "app" directory.
|
|
362
487
|
* - Alias Mapping: Shorthand flags (e.g., `-v`) are mapped to their full names (e.g., `--verbose`).
|
|
363
488
|
* - Strict Mode & Unknown Flag Warnings: Unknown flags are either warned about or handled via a callback.
|
|
364
489
|
* - Negated Boolean Support: Allows flags to be negated (e.g., `--no-verbose`).
|
|
@@ -367,7 +492,7 @@ const mainCommand = defineCommand({
|
|
|
367
492
|
await runMain(mainCommand, {
|
|
368
493
|
fileBasedCmds: {
|
|
369
494
|
enable: true, // Enables file-based subcommand detection.
|
|
370
|
-
cmdsRootPath: "
|
|
495
|
+
cmdsRootPath: "app", // Directory to scan for subcommands.
|
|
371
496
|
},
|
|
372
497
|
alias: {
|
|
373
498
|
v: "verbose", // Maps shorthand flag -v to --verbose.
|
|
@@ -382,6 +507,97 @@ await runMain(mainCommand, {
|
|
|
382
507
|
});
|
|
383
508
|
```
|
|
384
509
|
|
|
510
|
+
### CLI Launcher (Router)
|
|
511
|
+
|
|
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 subcommands and file-based routing, so you can structure your CLI however you like. It automatically detects and loads subcommands 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
|
+
|
|
514
|
+
- **File-Based & Defined Subcommands:**
|
|
515
|
+
Use `subCommands` in your command definition or let the launcher automatically load commands from a specified directory.
|
|
516
|
+
|
|
517
|
+
- **Automatic Command Detection:**
|
|
518
|
+
The launcher scans your specified `cmdsRootPath` for command files matching common patterns such as:
|
|
519
|
+
- `arg-cmdName.{ts,js}`
|
|
520
|
+
- `cmdName/index.{ts,js}`
|
|
521
|
+
- `cmdName/cmdName-mod.{ts,js}`
|
|
522
|
+
- And more — with automatic usage output if a command file is not found.
|
|
523
|
+
|
|
524
|
+
- **Built-In Flag Handling:**
|
|
525
|
+
Automatically processes global flags such as:
|
|
526
|
+
- `--help` and `-h` to show usage details.
|
|
527
|
+
- `--version` and `-v` to display version information.
|
|
528
|
+
- `--debug` for verbose logging during development.
|
|
529
|
+
|
|
530
|
+
- **Unified Argument Parsing:**
|
|
531
|
+
Seamlessly combines positional and named arguments with zero configuration, auto-parsing booleans, strings, numbers, arrays, and even supporting negated flags like `--no-flag`.
|
|
532
|
+
|
|
533
|
+
- **Customizable Behavior:**
|
|
534
|
+
Options such as `fileBasedCmds.enable`, `cmdsRootPath`, and `autoExit` allow you to tailor the launcher's behavior. For example, you can choose whether the process should exit automatically on error or allow manual error handling.
|
|
535
|
+
|
|
536
|
+
- **Error Management & Usage Output:**
|
|
537
|
+
The launcher provides clear error messages for missing required arguments, invalid types, or command import issues, and it automatically displays usage information for your CLI.
|
|
538
|
+
|
|
539
|
+
- **Lifecycle Hooks:**
|
|
540
|
+
You can define optional lifecycle hooks in your main command:
|
|
541
|
+
- `onLauncherStart` and `onLauncherEnd` (global, called once per CLI process)
|
|
542
|
+
- `onCmdStart` and `onCmdEnd` (per-subcommand, called before/after each subcommand, but NOT for the main `run()` handler)
|
|
543
|
+
|
|
544
|
+
**Global Hooks:**
|
|
545
|
+
- `onLauncherStart`: Called once, before any command/subcommand/run() is executed.
|
|
546
|
+
- `onLauncherEnd`: Called once, after all command/subcommand/run() logic is finished (even if an error occurs).
|
|
547
|
+
|
|
548
|
+
**Per-Subcommand Hooks:**
|
|
549
|
+
- `onCmdStart`: Called before each subcommand (not for main `run()`).
|
|
550
|
+
- `onCmdEnd`: Called after each subcommand (not for main `run()`).
|
|
551
|
+
|
|
552
|
+
This means:
|
|
553
|
+
- If your CLI has multiple subcommands, `onCmdStart` and `onCmdEnd` will be called for each subcommand invocation, not just once for the whole CLI process.
|
|
554
|
+
- If your main command has a `run()` handler (and no subcommand is invoked), these hooks are **not** called; use the `run()` handler itself or the global hooks for such logic.
|
|
555
|
+
- This allows you to perform setup/teardown logic specific to each subcommand execution.
|
|
556
|
+
- If you want logic to run only once for the entire CLI process, use `onLauncherStart` and `onLauncherEnd`.
|
|
557
|
+
|
|
558
|
+
**Example:**
|
|
559
|
+
|
|
560
|
+
```ts
|
|
561
|
+
const main = defineCommand({
|
|
562
|
+
onLauncherStart() { relinka('info', 'Global setup (once per process)'); },
|
|
563
|
+
onLauncherEnd() { relinka('info', 'Global cleanup (once per process)'); },
|
|
564
|
+
onCmdStart() { relinka('info', 'Setup for each subcommand'); },
|
|
565
|
+
onCmdEnd() { relinka('info', 'Cleanup for each subcommand'); },
|
|
566
|
+
subCommands: { ... },
|
|
567
|
+
run() { relinka('info', 'Main run handler (no subcommand)'); },
|
|
568
|
+
});
|
|
569
|
+
// onLauncherStart/onLauncherEnd are called once per process
|
|
570
|
+
// onCmdStart/onCmdEnd are called for every subcommand (not for main run())
|
|
571
|
+
// If you want per-run() logic, use the run() handler or global hooks
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
> **Note:** 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.
|
|
575
|
+
|
|
576
|
+
- **Dynamic Usage Examples:**
|
|
577
|
+
- The launcher inspects your available subcommands and their argument definitions, then prints a plausible example CLI invocation for a random subcommand directly in the help output. This helps users understand real-world usage at a glance.
|
|
578
|
+
|
|
579
|
+
- **File-Based & Programmatic Subcommands:**
|
|
580
|
+
- Both file-based and object subcommands are fully supported. The launcher can introspect their argument definitions and metadata for help, usage, and validation.
|
|
581
|
+
- File-based subcommands are auto-discovered from your filesystem, while programmatic subcommands can be defined inline in your main command.
|
|
582
|
+
|
|
583
|
+
- **Context-Aware Help Output:**
|
|
584
|
+
- The help/usage output adapts to your CLI's structure, showing available subcommands, their aliases, argument details, and even dynamic usage examples. It also displays global options and context-specific error messages.
|
|
585
|
+
|
|
586
|
+
- **Error Handling:**
|
|
587
|
+
- 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.
|
|
588
|
+
|
|
589
|
+
- **Unified Argument Parsing:**
|
|
590
|
+
- All arguments (positional, named, boolean, string, number, array) are parsed and validated automatically. Negated flags (like `--no-flag`) are supported out of the box.
|
|
591
|
+
|
|
592
|
+
- **Extensible & Flexible:**
|
|
593
|
+
- The launcher is highly extensible. You can use it with both Bun and Node.js, and it works seamlessly with both file-based and programmatic command definitions. You can also customize its behavior with options like `autoExit`, `cmdsRootPath`, and more.
|
|
594
|
+
|
|
595
|
+
- **Bun & Node.js Support:**
|
|
596
|
+
- The launcher is designed to work in both Bun and Node.js environments, so you can use it in any modern JavaScript/TypeScript project.
|
|
597
|
+
|
|
598
|
+
- **Prompt-First, Modern UX:**
|
|
599
|
+
- The launcher integrates tightly with the prompt engine, so you can build interactive, delightful CLIs with minimal effort.
|
|
600
|
+
|
|
385
601
|
## Contributing
|
|
386
602
|
|
|
387
603
|
Bug report? Prompt idea? Want to build the best DX possible?
|
|
@@ -394,6 +610,24 @@ You're in the right place:
|
|
|
394
610
|
|
|
395
611
|
> *No classes. No magic. Just clean, composable tools for CLI devs.*
|
|
396
612
|
|
|
613
|
+
### Notices For Contributors
|
|
614
|
+
|
|
615
|
+
**TypeScript Support**:
|
|
616
|
+
|
|
617
|
+
All APIs are fully typed. See [`src/types.ts`](./src/types.ts) for advanced customization and type inference.
|
|
618
|
+
|
|
619
|
+
**Examples**:
|
|
620
|
+
|
|
621
|
+
- **Classic CLI:** [`example/launcher/classic.ts`](./example/launcher/classic.ts)
|
|
622
|
+
- **Modern Minimal CLI:** [`example/launcher/modern.ts`](./example/launcher/modern.ts)
|
|
623
|
+
- **Full Prompt Demo:** [`example/prompts/mod.ts`](./example/prompts/mod.ts)
|
|
624
|
+
|
|
625
|
+
**Components and Utilities**:
|
|
626
|
+
|
|
627
|
+
- **components/**: All prompt UIs, CLI output, launcher logic, etc.
|
|
628
|
+
- **utils/**: Color, error, validation, streaming, and system helpers.
|
|
629
|
+
- **hooks/**: Useful hooks for prompt state and effects.
|
|
630
|
+
|
|
397
631
|
### Helpful Links
|
|
398
632
|
|
|
399
633
|
- [CLI application with the Node.js Readline module](https://dev.to/camptocamp-geo/cli-application-with-the-nodejs-readline-module-48ic)
|
|
@@ -2,7 +2,7 @@ import logUpdate from "log-update";
|
|
|
2
2
|
import { cursor } from "sisteransi";
|
|
3
3
|
import { fmt } from "../msg-fmt/messages.js";
|
|
4
4
|
import { endPrompt } from "../st-end/end.js";
|
|
5
|
-
import { streamText } from "
|
|
5
|
+
import { streamText } from "../../utils/stream-text.js";
|
|
6
6
|
const DEFAULT_MESSAGE = "Press any key to continue...";
|
|
7
7
|
const CTRL_C_CODE = 3;
|
|
8
8
|
const terminal = {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { DatePromptOptions } from "../../
|
|
1
|
+
import type { DatePromptOptions } from "../../types.js";
|
|
2
2
|
export declare function datePrompt(opts: DatePromptOptions): Promise<string>;
|
|
@@ -43,11 +43,11 @@ let state = {
|
|
|
43
43
|
theme: {
|
|
44
44
|
// Default Light Theme
|
|
45
45
|
text: (str) => str,
|
|
46
|
-
statusBarBg: (str) => re.
|
|
46
|
+
statusBarBg: (str) => re.bgBrown(str),
|
|
47
47
|
statusBarText: (str) => re.white(str),
|
|
48
|
-
highlight: (str) => re.
|
|
48
|
+
highlight: (str) => re.bgYellow(re.black(str)),
|
|
49
49
|
// For search results, etc.
|
|
50
|
-
lineNumber: (str) => re.
|
|
50
|
+
lineNumber: (str) => re.blue(str)
|
|
51
51
|
},
|
|
52
52
|
syntaxHighlightToggle: false,
|
|
53
53
|
// Toggled state for syntax highlighting
|
|
@@ -91,7 +91,7 @@ function setupTheme(configTheme) {
|
|
|
91
91
|
} else {
|
|
92
92
|
state.theme = {
|
|
93
93
|
text: (str) => re.black(str),
|
|
94
|
-
statusBarBg: (str) => re.
|
|
94
|
+
statusBarBg: (str) => re.bgBrown(str),
|
|
95
95
|
statusBarText: (str) => re.white(str),
|
|
96
96
|
highlight: (str) => re.bgCyan(re.black(str)),
|
|
97
97
|
lineNumber: (str) => re.gray(str)
|
|
@@ -808,10 +808,10 @@ async function initializeEditorState(options) {
|
|
|
808
808
|
state.theme = {
|
|
809
809
|
// Reset theme based on config
|
|
810
810
|
text: (str) => str,
|
|
811
|
-
statusBarBg: (str) => re.
|
|
811
|
+
statusBarBg: (str) => re.bgBrown(str),
|
|
812
812
|
statusBarText: (str) => re.white(str),
|
|
813
|
-
highlight: (str) => re.
|
|
814
|
-
lineNumber: (str) => re.
|
|
813
|
+
highlight: (str) => re.bgYellow(re.black(str)),
|
|
814
|
+
lineNumber: (str) => re.blue(str)
|
|
815
815
|
};
|
|
816
816
|
setupTheme(state.editorConfig.theme);
|
|
817
817
|
} catch (error) {
|
|
@@ -3,7 +3,7 @@ import { stdin as input, stdout as output } from "node:process";
|
|
|
3
3
|
import readline from "node:readline/promises";
|
|
4
4
|
import { bar, msg } from "../msg-fmt/messages.js";
|
|
5
5
|
import { deleteLastLine } from "../msg-fmt/terminal.js";
|
|
6
|
-
import { completePrompt } from "
|
|
6
|
+
import { completePrompt } from "../../utils/prompt-end.js";
|
|
7
7
|
function renderPrompt(params) {
|
|
8
8
|
const {
|
|
9
9
|
title,
|