@reliverse/rempts 1.7.10 → 1.7.12
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.
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { reliArgParser } from "@reliverse/reliarg";
|
|
2
|
+
import { re } from "@reliverse/relico";
|
|
2
3
|
import { relinka, relinkaConfig, relinkaShutdown } from "@reliverse/relinka";
|
|
3
4
|
import fs from "fs-extra";
|
|
4
5
|
import process from "node:process";
|
|
@@ -101,29 +102,49 @@ async function getDefaultCliNameAndVersion() {
|
|
|
101
102
|
return { name: "cli", version: void 0 };
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
|
-
async function
|
|
105
|
+
async function findRecursiveFileBasedCommands(baseDir, currentPath = []) {
|
|
105
106
|
const results = [];
|
|
106
|
-
const items = await fs.readdir(
|
|
107
|
+
const items = await fs.readdir(path.join(baseDir, ...currentPath), {
|
|
108
|
+
withFileTypes: true
|
|
109
|
+
});
|
|
107
110
|
for (const dirent of items) {
|
|
108
111
|
if (dirent.isDirectory()) {
|
|
112
|
+
const newPath = [...currentPath, dirent.name];
|
|
109
113
|
for (const fname of ["cmd.ts", "cmd.js"]) {
|
|
110
|
-
const fpath = path.join(
|
|
114
|
+
const fpath = path.join(baseDir, ...newPath, fname);
|
|
111
115
|
if (await fs.pathExists(fpath)) {
|
|
112
116
|
try {
|
|
113
117
|
const imported = await import(path.resolve(fpath));
|
|
114
118
|
if (imported.default && !imported.default.meta?.hidden) {
|
|
115
|
-
results.push({
|
|
119
|
+
results.push({
|
|
120
|
+
name: dirent.name,
|
|
121
|
+
def: imported.default,
|
|
122
|
+
path: newPath
|
|
123
|
+
});
|
|
116
124
|
}
|
|
117
125
|
} catch (err) {
|
|
118
|
-
debugLog(`Skipping file-based
|
|
126
|
+
debugLog(`Skipping file-based command in ${fpath}:`, err);
|
|
119
127
|
}
|
|
120
128
|
break;
|
|
121
129
|
}
|
|
122
130
|
}
|
|
131
|
+
const subResults = await findRecursiveFileBasedCommands(baseDir, newPath);
|
|
132
|
+
results.push(...subResults);
|
|
123
133
|
}
|
|
124
134
|
}
|
|
125
135
|
return results;
|
|
126
136
|
}
|
|
137
|
+
function calculatePadding(items, minPad = 2) {
|
|
138
|
+
const maxLength = items.reduce(
|
|
139
|
+
(max, item) => Math.max(max, item.text.length),
|
|
140
|
+
0
|
|
141
|
+
);
|
|
142
|
+
return maxLength + minPad;
|
|
143
|
+
}
|
|
144
|
+
function formatTableRow(text, desc, padding) {
|
|
145
|
+
const spaces = " ".repeat(Math.max(0, padding - text.length));
|
|
146
|
+
return `${text}${spaces}| ${desc || ""}`;
|
|
147
|
+
}
|
|
127
148
|
export async function showUsage(command, parserOptions = {}) {
|
|
128
149
|
const { name: fallbackName, version: fallbackVersion } = await getDefaultCliNameAndVersion();
|
|
129
150
|
const cliName = command.meta?.name || fallbackName;
|
|
@@ -146,36 +167,64 @@ export async function showUsage(command, parserOptions = {}) {
|
|
|
146
167
|
const fileCmds = parserOptions.fileBasedCmds;
|
|
147
168
|
if (fileCmds?.enable) {
|
|
148
169
|
const commandsDir = path.resolve(fileCmds.cmdsRootPath);
|
|
149
|
-
const currentDir = parserOptions._fileBasedCurrentDir || commandsDir;
|
|
150
170
|
const pathSegments = parserOptions._fileBasedPathSegments || [];
|
|
151
171
|
let usageLine = [pkgName, ...pathSegments].join(" ");
|
|
152
|
-
const
|
|
153
|
-
|
|
172
|
+
const allCommands = await findRecursiveFileBasedCommands(
|
|
173
|
+
commandsDir,
|
|
174
|
+
pathSegments
|
|
175
|
+
);
|
|
176
|
+
const directCommands = allCommands.filter(
|
|
177
|
+
(cmd) => cmd.path.length === pathSegments.length + 1
|
|
178
|
+
);
|
|
179
|
+
if (directCommands.length > 0) {
|
|
154
180
|
usageLine += " <command> [command's options]";
|
|
155
181
|
} else {
|
|
156
182
|
usageLine += " [command's options]";
|
|
157
183
|
const pos = renderPositional(command.args);
|
|
158
184
|
if (pos) usageLine += ` ${pos}`;
|
|
159
185
|
}
|
|
160
|
-
relinka("log", `Usage: ${usageLine}`);
|
|
161
|
-
if (
|
|
162
|
-
const randomIdx = Math.floor(Math.random() *
|
|
163
|
-
const {
|
|
186
|
+
relinka("log", re.cyan(`Usage: ${usageLine}`));
|
|
187
|
+
if (directCommands.length > 0) {
|
|
188
|
+
const randomIdx = Math.floor(Math.random() * allCommands.length);
|
|
189
|
+
const { path: path2, def: exampleDef } = allCommands[randomIdx];
|
|
164
190
|
const exampleArgs = buildExampleArgs(exampleDef.args || {});
|
|
165
191
|
relinka(
|
|
166
192
|
"log",
|
|
167
|
-
|
|
193
|
+
re.cyan(
|
|
194
|
+
`Example: ${pkgName} ${path2.join(" ")}${exampleArgs ? ` ${exampleArgs}` : ""}`
|
|
195
|
+
)
|
|
168
196
|
);
|
|
169
197
|
}
|
|
170
|
-
if (
|
|
198
|
+
if (allCommands.length > 0) {
|
|
171
199
|
relinka("info", "Available commands (run with `help` to see more):");
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
200
|
+
const commandsByPath = /* @__PURE__ */ new Map();
|
|
201
|
+
for (const cmd of allCommands) {
|
|
202
|
+
const parentPath = cmd.path.slice(0, -1).join("/") || "/";
|
|
203
|
+
if (!commandsByPath.has(parentPath)) {
|
|
204
|
+
commandsByPath.set(parentPath, []);
|
|
205
|
+
}
|
|
206
|
+
commandsByPath.get(parentPath).push(cmd);
|
|
207
|
+
}
|
|
208
|
+
const groupPaddings = /* @__PURE__ */ new Map();
|
|
209
|
+
for (const [parentPath, cmds] of commandsByPath) {
|
|
210
|
+
const items = cmds.map(({ path: path2, def }) => ({
|
|
211
|
+
text: `${parentPath === "/" ? "" : " "}\u2022 ${path2.join(" ")}`,
|
|
212
|
+
desc: def?.meta?.description
|
|
213
|
+
}));
|
|
214
|
+
groupPaddings.set(parentPath, calculatePadding(items));
|
|
215
|
+
}
|
|
216
|
+
for (const [parentPath, cmds] of commandsByPath) {
|
|
217
|
+
if (parentPath !== "/") {
|
|
218
|
+
relinka("log", re.cyanPastel(`Sub-commands in ${parentPath}:`));
|
|
219
|
+
}
|
|
220
|
+
const padding = groupPaddings.get(parentPath);
|
|
221
|
+
for (const { def, path: path2 } of cmds) {
|
|
222
|
+
const desc = def?.meta?.description ?? "";
|
|
223
|
+
const indent = parentPath === "/" ? "" : " ";
|
|
224
|
+
const text = `${indent}\u2022 ${path2.join(" ")}`;
|
|
225
|
+
relinka("log", formatTableRow(text, desc, padding));
|
|
226
|
+
}
|
|
227
|
+
}
|
|
179
228
|
}
|
|
180
229
|
} else {
|
|
181
230
|
const subCommandNames = [];
|
|
@@ -204,46 +253,57 @@ export async function showUsage(command, parserOptions = {}) {
|
|
|
204
253
|
} else {
|
|
205
254
|
usageLine += ` [command's options] ${renderPositional(command.args)}`;
|
|
206
255
|
}
|
|
207
|
-
relinka("log", `Usage: ${usageLine}`);
|
|
256
|
+
relinka("log", re.cyan(`Usage: ${usageLine}`));
|
|
208
257
|
if (subCommandDefs.length > 0) {
|
|
209
258
|
const randomIdx = Math.floor(Math.random() * subCommandDefs.length);
|
|
210
259
|
const { name: exampleCmd, def: exampleDef } = subCommandDefs[randomIdx];
|
|
211
260
|
const exampleArgs = buildExampleArgs(exampleDef.args || {});
|
|
212
261
|
relinka(
|
|
213
262
|
"log",
|
|
214
|
-
|
|
263
|
+
re.cyan(
|
|
264
|
+
`Example: ${pkgName}${parserOptions._isSubcommand ? ` ${cliName}` : ""} ${exampleCmd}${exampleArgs ? ` ${exampleArgs}` : ""}`
|
|
265
|
+
)
|
|
215
266
|
);
|
|
216
267
|
}
|
|
217
268
|
if (subCommandNames.length > 0) {
|
|
218
269
|
relinka("info", "Available commands (run with `help` to see more):");
|
|
219
|
-
subCommandDefs.
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
});
|
|
270
|
+
const commandItems = subCommandDefs.map(({ name, def }) => ({
|
|
271
|
+
text: `\u2022 ${name}`,
|
|
272
|
+
desc: def?.meta?.description
|
|
273
|
+
}));
|
|
274
|
+
const padding = calculatePadding(commandItems);
|
|
275
|
+
for (const { text, desc } of commandItems) {
|
|
276
|
+
relinka("log", formatTableRow(text, desc, padding));
|
|
277
|
+
}
|
|
223
278
|
}
|
|
224
279
|
}
|
|
225
280
|
relinka("info", "Available options:");
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
281
|
+
const optionItems = [
|
|
282
|
+
{ text: "\u2022 -h, --help", desc: "Show help" },
|
|
283
|
+
{ text: "\u2022 -v, --version", desc: "Show version" },
|
|
284
|
+
{ text: "\u2022 --debug", desc: "Enable debug mode" }
|
|
285
|
+
];
|
|
229
286
|
for (const [key, def] of Object.entries(command.args || {})) {
|
|
230
287
|
if (def.type === "positional") {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
);
|
|
288
|
+
optionItems.push({
|
|
289
|
+
text: `\u2022 <${key}>`,
|
|
290
|
+
desc: `${def.description ?? ""}${def.required ? " | required" : ""}`
|
|
291
|
+
});
|
|
235
292
|
} else {
|
|
293
|
+
const text = `\u2022 --${key}${"alias" in def && def.alias ? `, -${def.alias}` : ""}`;
|
|
236
294
|
const parts = [
|
|
237
|
-
`\u2022 --${key}${"alias" in def && def.alias ? `, -${def.alias}` : ""}`,
|
|
238
295
|
def.description ?? "",
|
|
239
|
-
`type=${def.type}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
relinka("log", parts.filter(Boolean).join(" | "));
|
|
296
|
+
`type=${def.type}`,
|
|
297
|
+
def.default !== void 0 ? `default=${JSON.stringify(def.default)}` : null,
|
|
298
|
+
def.required ? "required" : null
|
|
299
|
+
].filter(Boolean);
|
|
300
|
+
optionItems.push({ text, desc: parts.join(" | ") });
|
|
245
301
|
}
|
|
246
302
|
}
|
|
303
|
+
const optionsPadding = calculatePadding(optionItems);
|
|
304
|
+
for (const { text, desc } of optionItems) {
|
|
305
|
+
relinka("log", formatTableRow(text, desc, optionsPadding));
|
|
306
|
+
}
|
|
247
307
|
}
|
|
248
308
|
export async function runMain(command, parserOptions = {}) {
|
|
249
309
|
if (typeof command.onLauncherInit === "function")
|
|
@@ -1,20 +1,64 @@
|
|
|
1
|
+
import { type Color, type Options as OraOptions } from "ora";
|
|
1
2
|
type SpinnerOptions = {
|
|
2
3
|
text: string;
|
|
4
|
+
color?: Color;
|
|
5
|
+
spinner?: OraOptions["spinner"];
|
|
6
|
+
successText?: string;
|
|
7
|
+
failText?: string;
|
|
3
8
|
};
|
|
4
9
|
type SpinnerControls = {
|
|
5
|
-
start: () => SpinnerControls;
|
|
10
|
+
start: (text?: string) => SpinnerControls;
|
|
6
11
|
stop: () => void;
|
|
7
12
|
setText: (text: string) => void;
|
|
13
|
+
succeed: (text?: string) => void;
|
|
14
|
+
fail: (text?: string) => void;
|
|
15
|
+
warn: (text?: string) => void;
|
|
16
|
+
info: (text?: string) => void;
|
|
17
|
+
isSpinning: () => boolean;
|
|
18
|
+
clear: () => void;
|
|
8
19
|
};
|
|
9
20
|
/**
|
|
10
|
-
* Creates a terminal spinner.
|
|
21
|
+
* Creates a terminal spinner with enhanced controls and styling options.
|
|
11
22
|
*
|
|
12
23
|
* @example
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
24
|
+
* ```typescript
|
|
25
|
+
* // Basic usage
|
|
26
|
+
* const spinner = useSpinner({ text: "Loading..." }).start();
|
|
16
27
|
* spinner.stop();
|
|
17
|
-
*
|
|
28
|
+
*
|
|
29
|
+
* // With custom color and spinner
|
|
30
|
+
* const spinner = useSpinner({
|
|
31
|
+
* text: "Processing...",
|
|
32
|
+
* color: "cyan",
|
|
33
|
+
* spinner: "dots"
|
|
34
|
+
* }).start();
|
|
35
|
+
*
|
|
36
|
+
* // With success/failure states
|
|
37
|
+
* const spinner = useSpinner({
|
|
38
|
+
* text: "Uploading...",
|
|
39
|
+
* successText: "Upload complete!",
|
|
40
|
+
* failText: "Upload failed!"
|
|
41
|
+
* }).start();
|
|
42
|
+
* try {
|
|
43
|
+
* await uploadFile();
|
|
44
|
+
* spinner.succeed();
|
|
45
|
+
* } catch (error) {
|
|
46
|
+
* spinner.fail();
|
|
47
|
+
* }
|
|
48
|
+
*
|
|
49
|
+
* // Using the wrapper for async operations
|
|
50
|
+
* await useSpinner.promise(
|
|
51
|
+
* async () => { await longOperation(); },
|
|
52
|
+
* {
|
|
53
|
+
* text: "Working...",
|
|
54
|
+
* successText: "Done!",
|
|
55
|
+
* failText: "Failed!"
|
|
56
|
+
* }
|
|
57
|
+
* );
|
|
58
|
+
* ```
|
|
18
59
|
*/
|
|
19
60
|
export declare function useSpinner(options: SpinnerOptions): SpinnerControls;
|
|
61
|
+
export declare namespace useSpinner {
|
|
62
|
+
var promise: <T>(operation: () => Promise<T>, options: SpinnerOptions) => Promise<T>;
|
|
63
|
+
}
|
|
20
64
|
export {};
|
|
@@ -2,9 +2,18 @@ import ora from "ora";
|
|
|
2
2
|
export function useSpinner(options) {
|
|
3
3
|
let spinnerInstance = null;
|
|
4
4
|
const controls = {
|
|
5
|
-
start: () => {
|
|
5
|
+
start: (text) => {
|
|
6
|
+
if (text) {
|
|
7
|
+
options.text = text;
|
|
8
|
+
}
|
|
6
9
|
if (!spinnerInstance) {
|
|
7
|
-
spinnerInstance = ora(
|
|
10
|
+
spinnerInstance = ora({
|
|
11
|
+
text: options.text,
|
|
12
|
+
color: options.color,
|
|
13
|
+
spinner: options.spinner
|
|
14
|
+
});
|
|
15
|
+
} else {
|
|
16
|
+
spinnerInstance.text = options.text;
|
|
8
17
|
}
|
|
9
18
|
spinnerInstance.start();
|
|
10
19
|
return controls;
|
|
@@ -20,7 +29,46 @@ export function useSpinner(options) {
|
|
|
20
29
|
} else {
|
|
21
30
|
options.text = text;
|
|
22
31
|
}
|
|
32
|
+
},
|
|
33
|
+
succeed: (text) => {
|
|
34
|
+
if (spinnerInstance) {
|
|
35
|
+
spinnerInstance.succeed(text ?? options.successText);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
fail: (text) => {
|
|
39
|
+
if (spinnerInstance) {
|
|
40
|
+
spinnerInstance.fail(text ?? options.failText);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
warn: (text) => {
|
|
44
|
+
if (spinnerInstance) {
|
|
45
|
+
spinnerInstance.warn(text);
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
info: (text) => {
|
|
49
|
+
if (spinnerInstance) {
|
|
50
|
+
spinnerInstance.info(text);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
isSpinning: () => {
|
|
54
|
+
return spinnerInstance?.isSpinning ?? false;
|
|
55
|
+
},
|
|
56
|
+
clear: () => {
|
|
57
|
+
if (spinnerInstance) {
|
|
58
|
+
spinnerInstance.clear();
|
|
59
|
+
}
|
|
23
60
|
}
|
|
24
61
|
};
|
|
25
62
|
return controls;
|
|
26
63
|
}
|
|
64
|
+
useSpinner.promise = async (operation, options) => {
|
|
65
|
+
const spinner = useSpinner(options).start();
|
|
66
|
+
try {
|
|
67
|
+
const result = await operation();
|
|
68
|
+
spinner.succeed();
|
|
69
|
+
return result;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
spinner.fail();
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
};
|
package/package.json
CHANGED
|
@@ -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.12",
|
|
32
32
|
"author": "reliverse",
|
|
33
33
|
"bugs": {
|
|
34
34
|
"email": "blefnk@gmail.com",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@biomejs/biome": "1.9.4",
|
|
46
46
|
"@eslint/js": "^9.26.0",
|
|
47
|
-
"@reliverse/dler": "^1.2.
|
|
47
|
+
"@reliverse/dler": "^1.2.5",
|
|
48
48
|
"@reliverse/relidler-cfg": "^1.1.3",
|
|
49
49
|
"@stylistic/eslint-plugin": "^4.2.0",
|
|
50
50
|
"@total-typescript/ts-reset": "^0.6.1",
|