@topcli/prompts 1.10.1 → 2.0.0
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 +20 -5
- package/dist/index.cjs +75 -53
- package/dist/index.d.cts +12 -3
- package/dist/index.d.ts +12 -3
- package/dist/index.js +74 -53
- package/package.json +12 -13
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|

|
|
5
5
|
[](https://github.com/TopCli/prompts/commit-activity)
|
|
6
6
|
[](https://github.com/TopCli/prompts/blob/main/LICENSE)
|
|
7
|
+
[](https://ossf.github.io/scorecard-visualizer/#/projects/github.com/TopCli/prompts)
|
|
7
8
|

|
|
9
|
+
|
|
10
|
+
<img src="./public/topcli.gif" alt="demo">
|
|
8
11
|
</div>
|
|
9
12
|
|
|
10
13
|
## Requirements
|
|
@@ -52,18 +55,21 @@ Simple prompt, similar to `rl.question()` with an improved UI.
|
|
|
52
55
|
|
|
53
56
|
Use `options.secure` if you need to hide both input and answer.
|
|
54
57
|
|
|
55
|
-
Use `options.validators` to handle user input.
|
|
56
|
-
|
|
57
58
|
Use `options.signal` to set an `AbortSignal` (throws a [AbortError](#aborterror)).
|
|
58
59
|
|
|
60
|
+
Use `options.validators` to handle user input.
|
|
61
|
+
|
|
59
62
|
**Example**
|
|
60
63
|
|
|
61
64
|
```js
|
|
62
65
|
const packageName = await question('Package name', {
|
|
63
66
|
validators: [
|
|
64
67
|
{
|
|
65
|
-
validate: (value) =>
|
|
66
|
-
|
|
68
|
+
validate: (value) => {
|
|
69
|
+
if (!existsSync(join(process.cwd(), value))) {
|
|
70
|
+
return `Folder ${value} already exists`
|
|
71
|
+
}
|
|
72
|
+
}
|
|
67
73
|
}
|
|
68
74
|
]
|
|
69
75
|
});
|
|
@@ -124,7 +130,10 @@ Use `options.signal` to set an `AbortSignal` (throws a [AbortError](#aborterror)
|
|
|
124
130
|
confirm(message: string, options?: ConfirmOptions): Promise<boolean>
|
|
125
131
|
```
|
|
126
132
|
|
|
127
|
-
Boolean prompt,
|
|
133
|
+
Boolean prompt, default to `options.initial` (`false`).
|
|
134
|
+
|
|
135
|
+
> [!TIP]
|
|
136
|
+
> You can answer pressing <kbd>Y</kbd> or <kbd>N</kbd>
|
|
128
137
|
|
|
129
138
|
Use `options.signal` to set an `AbortSignal` (throws a [AbortError](#aborterror)).
|
|
130
139
|
|
|
@@ -221,6 +230,12 @@ export interface ConfirmOptions extends SharedOptions {
|
|
|
221
230
|
}
|
|
222
231
|
```
|
|
223
232
|
|
|
233
|
+
## Contributing
|
|
234
|
+
|
|
235
|
+
Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.
|
|
236
|
+
|
|
237
|
+
Open an issue if you want to provide feedback such as bug reports or enchancements.
|
|
238
|
+
|
|
224
239
|
## Contributors
|
|
225
240
|
|
|
226
241
|
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -40,7 +41,7 @@ module.exports = __toCommonJS(prompts_exports);
|
|
|
40
41
|
|
|
41
42
|
// src/prompts/question.ts
|
|
42
43
|
var import_node_os2 = require("os");
|
|
43
|
-
var
|
|
44
|
+
var import_node_util2 = require("util");
|
|
44
45
|
var import_wcwidth = __toESM(require("@topcli/wcwidth"), 1);
|
|
45
46
|
|
|
46
47
|
// src/prompts/abstract.ts
|
|
@@ -54,7 +55,7 @@ var import_node_process = __toESM(require("process"), 1);
|
|
|
54
55
|
var kAnsiRegex = ansiRegex();
|
|
55
56
|
function ansiRegex({ onlyFirst = false } = {}) {
|
|
56
57
|
const pattern = [
|
|
57
|
-
// eslint-disable-next-line max-len
|
|
58
|
+
// eslint-disable-next-line @stylistic/max-len
|
|
58
59
|
"[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
|
|
59
60
|
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"
|
|
60
61
|
].join("|");
|
|
@@ -182,6 +183,9 @@ var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
|
|
|
182
183
|
}
|
|
183
184
|
this.emit("error", new AbortError("Prompt aborted"));
|
|
184
185
|
};
|
|
186
|
+
if (this.signal.aborted) {
|
|
187
|
+
this.#signalHandler();
|
|
188
|
+
}
|
|
185
189
|
this.signal.addEventListener("abort", this.#signalHandler, { once: true });
|
|
186
190
|
}
|
|
187
191
|
}
|
|
@@ -210,7 +214,7 @@ var AbstractPrompt = class _AbstractPrompt extends import_node_events.default {
|
|
|
210
214
|
};
|
|
211
215
|
|
|
212
216
|
// src/constants.ts
|
|
213
|
-
var
|
|
217
|
+
var import_node_util = require("util");
|
|
214
218
|
var kMainSymbols = {
|
|
215
219
|
tick: "\u2714",
|
|
216
220
|
cross: "\u2716",
|
|
@@ -230,20 +234,45 @@ var kFallbackSymbols = {
|
|
|
230
234
|
inactive: "(-)"
|
|
231
235
|
};
|
|
232
236
|
var kSymbols = isUnicodeSupported() || process.env.CI ? kMainSymbols : kFallbackSymbols;
|
|
233
|
-
var kPointer =
|
|
237
|
+
var kPointer = (0, import_node_util.styleText)("gray", kSymbols.pointer);
|
|
234
238
|
var SYMBOLS = {
|
|
235
|
-
QuestionMark:
|
|
236
|
-
Tick:
|
|
237
|
-
Cross:
|
|
239
|
+
QuestionMark: (0, import_node_util.styleText)(["blue", "bold"], "?"),
|
|
240
|
+
Tick: (0, import_node_util.styleText)(["green", "bold"], kSymbols.tick),
|
|
241
|
+
Cross: (0, import_node_util.styleText)(["red", "bold"], kSymbols.cross),
|
|
238
242
|
Pointer: kPointer,
|
|
239
243
|
Previous: kSymbols.previous,
|
|
240
244
|
Next: kSymbols.next,
|
|
241
245
|
ShowCursor: "\x1B[?25h",
|
|
242
246
|
HideCursor: "\x1B[?25l",
|
|
243
|
-
Active:
|
|
244
|
-
Inactive:
|
|
247
|
+
Active: (0, import_node_util.styleText)("cyan", kSymbols.active),
|
|
248
|
+
Inactive: (0, import_node_util.styleText)("gray", kSymbols.inactive)
|
|
245
249
|
};
|
|
246
250
|
|
|
251
|
+
// src/validators.ts
|
|
252
|
+
function required() {
|
|
253
|
+
return {
|
|
254
|
+
validate: (input) => {
|
|
255
|
+
const isValid2 = Array.isArray(input) ? input.length > 0 : Boolean(input);
|
|
256
|
+
return isValid2 ? null : { isValid: isValid2, error: "required" };
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function isValid(result) {
|
|
261
|
+
if (typeof result === "object") {
|
|
262
|
+
return result?.isValid !== false;
|
|
263
|
+
}
|
|
264
|
+
if (typeof result === "string") {
|
|
265
|
+
return result.length > 0;
|
|
266
|
+
}
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
function resultError(result) {
|
|
270
|
+
if (typeof result === "object") {
|
|
271
|
+
return result.error;
|
|
272
|
+
}
|
|
273
|
+
return result;
|
|
274
|
+
}
|
|
275
|
+
|
|
247
276
|
// src/prompts/question.ts
|
|
248
277
|
var QuestionPrompt = class extends AbstractPrompt {
|
|
249
278
|
defaultValue;
|
|
@@ -283,16 +312,16 @@ var QuestionPrompt = class extends AbstractPrompt {
|
|
|
283
312
|
});
|
|
284
313
|
}
|
|
285
314
|
#getQuestionQuery() {
|
|
286
|
-
return `${
|
|
315
|
+
return `${(0, import_node_util2.styleText)("bold", `${SYMBOLS.QuestionMark} ${this.message}${this.tip}`)} ${this.questionSuffixError}`;
|
|
287
316
|
}
|
|
288
317
|
#setQuestionSuffixError(error) {
|
|
289
|
-
const suffix =
|
|
318
|
+
const suffix = (0, import_node_util2.styleText)("red", `[${error}] `);
|
|
290
319
|
this.questionSuffixError = suffix;
|
|
291
320
|
}
|
|
292
321
|
#writeAnswer() {
|
|
293
322
|
const prefix = this.answer ? SYMBOLS.Tick : SYMBOLS.Cross;
|
|
294
|
-
const answer =
|
|
295
|
-
this.write(`${prefix} ${
|
|
323
|
+
const answer = (0, import_node_util2.styleText)("yellow", this.#secure ? "CONFIDENTIAL" : this.answer ?? "");
|
|
324
|
+
this.write(`${prefix} ${(0, import_node_util2.styleText)("bold", this.message)} ${SYMBOLS.Pointer} ${answer}${import_node_os2.EOL}`);
|
|
296
325
|
}
|
|
297
326
|
#onQuestionAnswer() {
|
|
298
327
|
const questionLineCount = Math.ceil(
|
|
@@ -301,9 +330,9 @@ var QuestionPrompt = class extends AbstractPrompt {
|
|
|
301
330
|
this.stdout.moveCursor(-this.stdout.columns, -questionLineCount);
|
|
302
331
|
this.stdout.clearScreenDown();
|
|
303
332
|
for (const validator of this.#validators) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
this.#setQuestionSuffixError(
|
|
333
|
+
const validationResult = validator.validate(this.answer);
|
|
334
|
+
if (isValid(validationResult) === false) {
|
|
335
|
+
this.#setQuestionSuffixError(resultError(validationResult));
|
|
307
336
|
this.answerBuffer = this.#question();
|
|
308
337
|
return;
|
|
309
338
|
}
|
|
@@ -340,7 +369,7 @@ var QuestionPrompt = class extends AbstractPrompt {
|
|
|
340
369
|
|
|
341
370
|
// src/prompts/confirm.ts
|
|
342
371
|
var import_node_os3 = require("os");
|
|
343
|
-
var
|
|
372
|
+
var import_node_util3 = require("util");
|
|
344
373
|
var import_wcwidth2 = __toESM(require("@topcli/wcwidth"), 1);
|
|
345
374
|
var kToggleKeys = /* @__PURE__ */ new Set([
|
|
346
375
|
"left",
|
|
@@ -371,8 +400,8 @@ var ConfirmPrompt = class extends AbstractPrompt {
|
|
|
371
400
|
this.selectedValue = initial;
|
|
372
401
|
}
|
|
373
402
|
#getHint() {
|
|
374
|
-
const Yes =
|
|
375
|
-
const No =
|
|
403
|
+
const Yes = (0, import_node_util3.styleText)(["cyan", "bold", "underline"], "Yes");
|
|
404
|
+
const No = (0, import_node_util3.styleText)(["cyan", "bold", "underline"], "No");
|
|
376
405
|
return this.selectedValue ? `${Yes}/No` : `Yes/${No}`;
|
|
377
406
|
}
|
|
378
407
|
#render() {
|
|
@@ -418,7 +447,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
|
|
|
418
447
|
this.stdin.off("keypress", this.#boundKeyPressEvent);
|
|
419
448
|
}
|
|
420
449
|
#getQuestionQuery() {
|
|
421
|
-
const query =
|
|
450
|
+
const query = (0, import_node_util3.styleText)("bold", `${SYMBOLS.QuestionMark} ${this.message}`);
|
|
422
451
|
return `${query} ${this.#getHint()}`;
|
|
423
452
|
}
|
|
424
453
|
#onQuestionAnswer() {
|
|
@@ -428,7 +457,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
|
|
|
428
457
|
-Math.floor((0, import_wcwidth2.default)(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns)
|
|
429
458
|
);
|
|
430
459
|
this.stdout.clearScreenDown();
|
|
431
|
-
this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${
|
|
460
|
+
this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${(0, import_node_util3.styleText)("bold", this.message)}${import_node_os3.EOL}`);
|
|
432
461
|
}
|
|
433
462
|
confirm() {
|
|
434
463
|
return new Promise(async (resolve, reject) => {
|
|
@@ -460,7 +489,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
|
|
|
460
489
|
|
|
461
490
|
// src/prompts/select.ts
|
|
462
491
|
var import_node_os4 = require("os");
|
|
463
|
-
var
|
|
492
|
+
var import_node_util4 = require("util");
|
|
464
493
|
var import_wcwidth3 = __toESM(require("@topcli/wcwidth"), 1);
|
|
465
494
|
var kRequiredChoiceProperties = ["label", "value"];
|
|
466
495
|
var SelectPrompt = class extends AbstractPrompt {
|
|
@@ -567,15 +596,15 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
567
596
|
this.longestChoice < 10 ? this.longestChoice : 0
|
|
568
597
|
);
|
|
569
598
|
const formattedDescription = choice.description ? ` - ${choice.description}` : "";
|
|
570
|
-
const
|
|
571
|
-
const str = `${prefix}${
|
|
599
|
+
const styles = isChoiceSelected ? ["white", "bold"] : ["gray"];
|
|
600
|
+
const str = `${prefix}${(0, import_node_util4.styleText)(styles, `${formattedLabel}${formattedDescription}`)}${import_node_os4.EOL}`;
|
|
572
601
|
this.write(str);
|
|
573
602
|
}
|
|
574
603
|
}
|
|
575
604
|
#showAnsweredQuestion(choice) {
|
|
576
605
|
const symbolPrefix = choice === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
|
|
577
|
-
const prefix = `${symbolPrefix} ${
|
|
578
|
-
const formattedChoice =
|
|
606
|
+
const prefix = `${symbolPrefix} ${(0, import_node_util4.styleText)("bold", this.message)} ${SYMBOLS.Pointer}`;
|
|
607
|
+
const formattedChoice = (0, import_node_util4.styleText)("yellow", typeof choice === "string" ? choice : choice.label);
|
|
579
608
|
this.write(`${prefix} ${formattedChoice}${import_node_os4.EOL}`);
|
|
580
609
|
}
|
|
581
610
|
#onProcessExit() {
|
|
@@ -597,9 +626,9 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
597
626
|
const label = typeof choice === "string" ? choice : choice.label;
|
|
598
627
|
const value = typeof choice === "string" ? choice : choice.value;
|
|
599
628
|
for (const validator of this.#validators) {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
render({ error });
|
|
629
|
+
const validationResult = validator.validate(value);
|
|
630
|
+
if (isValid(validationResult) === false) {
|
|
631
|
+
render({ error: resultError(validationResult) });
|
|
603
632
|
return;
|
|
604
633
|
}
|
|
605
634
|
}
|
|
@@ -686,16 +715,16 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
686
715
|
#showQuestion(error = null) {
|
|
687
716
|
let hint = "";
|
|
688
717
|
if (error) {
|
|
689
|
-
hint = ` ${hint.length > 0 ? " " : ""}${
|
|
718
|
+
hint = ` ${hint.length > 0 ? " " : ""}${(0, import_node_util4.styleText)(["red", "bold"], `[${error}]`)}`;
|
|
690
719
|
}
|
|
691
|
-
this.questionMessage = `${SYMBOLS.QuestionMark} ${
|
|
720
|
+
this.questionMessage = `${SYMBOLS.QuestionMark} ${(0, import_node_util4.styleText)("bold", this.message)}${hint}`;
|
|
692
721
|
this.write(`${this.questionMessage}${import_node_os4.EOL}`);
|
|
693
722
|
}
|
|
694
723
|
};
|
|
695
724
|
|
|
696
725
|
// src/prompts/multiselect.ts
|
|
697
726
|
var import_node_os5 = require("os");
|
|
698
|
-
var
|
|
727
|
+
var import_node_util5 = require("util");
|
|
699
728
|
var import_wcwidth4 = __toESM(require("@topcli/wcwidth"), 1);
|
|
700
729
|
var kRequiredChoiceProperties2 = ["label", "value"];
|
|
701
730
|
var MultiselectPrompt = class extends AbstractPrompt {
|
|
@@ -824,15 +853,15 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
824
853
|
this.longestChoice < 10 ? this.longestChoice : 0
|
|
825
854
|
);
|
|
826
855
|
const formattedDescription = choice.description ? ` - ${choice.description}` : "";
|
|
827
|
-
const
|
|
828
|
-
const str = `${prefix} ${
|
|
856
|
+
const styles = isChoiceActive ? ["white", "bold"] : ["gray"];
|
|
857
|
+
const str = `${prefix} ${(0, import_node_util5.styleText)(styles, `${formattedLabel}${formattedDescription}`)}${import_node_os5.EOL}`;
|
|
829
858
|
this.write(str);
|
|
830
859
|
}
|
|
831
860
|
}
|
|
832
861
|
#showAnsweredQuestion(choices, isAgentAnswer = false) {
|
|
833
862
|
const prefixSymbol = this.selectedIndexes.size === 0 && !isAgentAnswer ? SYMBOLS.Cross : SYMBOLS.Tick;
|
|
834
|
-
const prefix = `${prefixSymbol} ${
|
|
835
|
-
const formattedChoice =
|
|
863
|
+
const prefix = `${prefixSymbol} ${(0, import_node_util5.styleText)("bold", this.message)} ${SYMBOLS.Pointer}`;
|
|
864
|
+
const formattedChoice = (0, import_node_util5.styleText)("yellow", choices);
|
|
836
865
|
this.write(`${prefix}${choices ? ` ${formattedChoice}` : ""}${import_node_os5.EOL}`);
|
|
837
866
|
}
|
|
838
867
|
#onProcessExit() {
|
|
@@ -868,9 +897,9 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
868
897
|
return typeof choice === "string" ? choice : choice.value;
|
|
869
898
|
});
|
|
870
899
|
for (const validator of this.#validators) {
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
render({ error });
|
|
900
|
+
const validationResult = validator.validate(values);
|
|
901
|
+
if (isValid(validationResult) === false) {
|
|
902
|
+
render({ error: resultError(validationResult) });
|
|
874
903
|
return;
|
|
875
904
|
}
|
|
876
905
|
}
|
|
@@ -955,26 +984,19 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
955
984
|
});
|
|
956
985
|
}
|
|
957
986
|
#showQuestion(error = null) {
|
|
958
|
-
let hint = this.#showHint ?
|
|
959
|
-
|
|
960
|
-
|
|
987
|
+
let hint = this.#showHint ? (0, import_node_util5.styleText)(
|
|
988
|
+
"gray",
|
|
989
|
+
// eslint-disable-next-line @stylistic/max-len
|
|
990
|
+
`(Press ${(0, import_node_util5.styleText)("bold", "<Ctrl+A>")} to toggle all, ${(0, import_node_util5.styleText)("bold", "<Left/Right>")} to toggle, ${(0, import_node_util5.styleText)("bold", "<Return>")} to submit)`
|
|
961
991
|
) : "";
|
|
962
992
|
if (error) {
|
|
963
|
-
hint += `${hint.length > 0 ? " " : ""}${
|
|
993
|
+
hint += `${hint.length > 0 ? " " : ""}${(0, import_node_util5.styleText)(["red", "bold"], `[${error}]`)}`;
|
|
964
994
|
}
|
|
965
|
-
this.questionMessage = `${SYMBOLS.QuestionMark} ${
|
|
995
|
+
this.questionMessage = `${SYMBOLS.QuestionMark} ${(0, import_node_util5.styleText)("bold", this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
|
|
966
996
|
this.write(`${this.questionMessage}${import_node_os5.EOL}`);
|
|
967
997
|
}
|
|
968
998
|
};
|
|
969
999
|
|
|
970
|
-
// src/validators.ts
|
|
971
|
-
function required() {
|
|
972
|
-
return {
|
|
973
|
-
validate: (input) => Array.isArray(input) ? input.length > 0 : Boolean(input),
|
|
974
|
-
error: () => "required"
|
|
975
|
-
};
|
|
976
|
-
}
|
|
977
|
-
|
|
978
1000
|
// index.ts
|
|
979
1001
|
async function question(message, options = {}) {
|
|
980
1002
|
return new QuestionPrompt({ ...options, message }).question();
|
|
@@ -983,7 +1005,7 @@ async function select(message, options) {
|
|
|
983
1005
|
const selectPrompt = new SelectPrompt({ ...options, message });
|
|
984
1006
|
return selectPrompt.select();
|
|
985
1007
|
}
|
|
986
|
-
async function confirm(message, options) {
|
|
1008
|
+
async function confirm(message, options = {}) {
|
|
987
1009
|
const confirmPrompt = new ConfirmPrompt({ ...options, message });
|
|
988
1010
|
return confirmPrompt.confirm();
|
|
989
1011
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
type ValidResponseObject = {
|
|
2
|
+
isValid?: true;
|
|
3
|
+
};
|
|
4
|
+
type InvalidResponseObject = {
|
|
5
|
+
isValid: false;
|
|
6
|
+
error: string;
|
|
7
|
+
};
|
|
8
|
+
type ValidationResponse = InvalidResponse | ValidResponse;
|
|
9
|
+
type InvalidResponse = string | InvalidResponseObject;
|
|
10
|
+
type ValidResponse = null | undefined | true | ValidResponseObject;
|
|
1
11
|
interface PromptValidator<T = string | string[] | boolean> {
|
|
2
|
-
validate: (input: T) =>
|
|
3
|
-
error: (input: T) => string;
|
|
12
|
+
validate: (input: T) => ValidationResponse;
|
|
4
13
|
}
|
|
5
14
|
declare function required<T = string | string[] | boolean>(): PromptValidator<T>;
|
|
6
15
|
|
|
@@ -80,7 +89,7 @@ interface SelectOptions extends AbstractPromptOptions {
|
|
|
80
89
|
|
|
81
90
|
declare function question(message: string, options?: Omit<QuestionOptions, "message">): Promise<string>;
|
|
82
91
|
declare function select(message: string, options: Omit<SelectOptions, "message">): Promise<string>;
|
|
83
|
-
declare function confirm(message: string, options
|
|
92
|
+
declare function confirm(message: string, options?: Omit<ConfirmOptions, "message">): Promise<boolean>;
|
|
84
93
|
declare function multiselect(message: string, options: Omit<MultiselectOptions, "message">): Promise<string[]>;
|
|
85
94
|
|
|
86
95
|
export { type AbstractPromptOptions, type Choice, type ConfirmOptions, type MultiselectOptions, PromptAgent, type PromptValidator, type QuestionOptions, type SelectOptions, confirm, multiselect, question, required, select };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
type ValidResponseObject = {
|
|
2
|
+
isValid?: true;
|
|
3
|
+
};
|
|
4
|
+
type InvalidResponseObject = {
|
|
5
|
+
isValid: false;
|
|
6
|
+
error: string;
|
|
7
|
+
};
|
|
8
|
+
type ValidationResponse = InvalidResponse | ValidResponse;
|
|
9
|
+
type InvalidResponse = string | InvalidResponseObject;
|
|
10
|
+
type ValidResponse = null | undefined | true | ValidResponseObject;
|
|
1
11
|
interface PromptValidator<T = string | string[] | boolean> {
|
|
2
|
-
validate: (input: T) =>
|
|
3
|
-
error: (input: T) => string;
|
|
12
|
+
validate: (input: T) => ValidationResponse;
|
|
4
13
|
}
|
|
5
14
|
declare function required<T = string | string[] | boolean>(): PromptValidator<T>;
|
|
6
15
|
|
|
@@ -80,7 +89,7 @@ interface SelectOptions extends AbstractPromptOptions {
|
|
|
80
89
|
|
|
81
90
|
declare function question(message: string, options?: Omit<QuestionOptions, "message">): Promise<string>;
|
|
82
91
|
declare function select(message: string, options: Omit<SelectOptions, "message">): Promise<string>;
|
|
83
|
-
declare function confirm(message: string, options
|
|
92
|
+
declare function confirm(message: string, options?: Omit<ConfirmOptions, "message">): Promise<boolean>;
|
|
84
93
|
declare function multiselect(message: string, options: Omit<MultiselectOptions, "message">): Promise<string[]>;
|
|
85
94
|
|
|
86
95
|
export { type AbstractPromptOptions, type Choice, type ConfirmOptions, type MultiselectOptions, PromptAgent, type PromptValidator, type QuestionOptions, type SelectOptions, confirm, multiselect, question, required, select };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/prompts/question.ts
|
|
2
2
|
import { EOL as EOL2 } from "os";
|
|
3
|
-
import
|
|
3
|
+
import { styleText as styleText2 } from "util";
|
|
4
4
|
import wcwidth from "@topcli/wcwidth";
|
|
5
5
|
|
|
6
6
|
// src/prompts/abstract.ts
|
|
@@ -14,7 +14,7 @@ import process2 from "process";
|
|
|
14
14
|
var kAnsiRegex = ansiRegex();
|
|
15
15
|
function ansiRegex({ onlyFirst = false } = {}) {
|
|
16
16
|
const pattern = [
|
|
17
|
-
// eslint-disable-next-line max-len
|
|
17
|
+
// eslint-disable-next-line @stylistic/max-len
|
|
18
18
|
"[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
|
|
19
19
|
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))"
|
|
20
20
|
].join("|");
|
|
@@ -142,6 +142,9 @@ var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
|
|
|
142
142
|
}
|
|
143
143
|
this.emit("error", new AbortError("Prompt aborted"));
|
|
144
144
|
};
|
|
145
|
+
if (this.signal.aborted) {
|
|
146
|
+
this.#signalHandler();
|
|
147
|
+
}
|
|
145
148
|
this.signal.addEventListener("abort", this.#signalHandler, { once: true });
|
|
146
149
|
}
|
|
147
150
|
}
|
|
@@ -170,7 +173,7 @@ var AbstractPrompt = class _AbstractPrompt extends EventEmitter {
|
|
|
170
173
|
};
|
|
171
174
|
|
|
172
175
|
// src/constants.ts
|
|
173
|
-
import
|
|
176
|
+
import { styleText } from "util";
|
|
174
177
|
var kMainSymbols = {
|
|
175
178
|
tick: "\u2714",
|
|
176
179
|
cross: "\u2716",
|
|
@@ -190,20 +193,45 @@ var kFallbackSymbols = {
|
|
|
190
193
|
inactive: "(-)"
|
|
191
194
|
};
|
|
192
195
|
var kSymbols = isUnicodeSupported() || process.env.CI ? kMainSymbols : kFallbackSymbols;
|
|
193
|
-
var kPointer =
|
|
196
|
+
var kPointer = styleText("gray", kSymbols.pointer);
|
|
194
197
|
var SYMBOLS = {
|
|
195
|
-
QuestionMark:
|
|
196
|
-
Tick:
|
|
197
|
-
Cross:
|
|
198
|
+
QuestionMark: styleText(["blue", "bold"], "?"),
|
|
199
|
+
Tick: styleText(["green", "bold"], kSymbols.tick),
|
|
200
|
+
Cross: styleText(["red", "bold"], kSymbols.cross),
|
|
198
201
|
Pointer: kPointer,
|
|
199
202
|
Previous: kSymbols.previous,
|
|
200
203
|
Next: kSymbols.next,
|
|
201
204
|
ShowCursor: "\x1B[?25h",
|
|
202
205
|
HideCursor: "\x1B[?25l",
|
|
203
|
-
Active:
|
|
204
|
-
Inactive:
|
|
206
|
+
Active: styleText("cyan", kSymbols.active),
|
|
207
|
+
Inactive: styleText("gray", kSymbols.inactive)
|
|
205
208
|
};
|
|
206
209
|
|
|
210
|
+
// src/validators.ts
|
|
211
|
+
function required() {
|
|
212
|
+
return {
|
|
213
|
+
validate: (input) => {
|
|
214
|
+
const isValid2 = Array.isArray(input) ? input.length > 0 : Boolean(input);
|
|
215
|
+
return isValid2 ? null : { isValid: isValid2, error: "required" };
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function isValid(result) {
|
|
220
|
+
if (typeof result === "object") {
|
|
221
|
+
return result?.isValid !== false;
|
|
222
|
+
}
|
|
223
|
+
if (typeof result === "string") {
|
|
224
|
+
return result.length > 0;
|
|
225
|
+
}
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
function resultError(result) {
|
|
229
|
+
if (typeof result === "object") {
|
|
230
|
+
return result.error;
|
|
231
|
+
}
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
|
|
207
235
|
// src/prompts/question.ts
|
|
208
236
|
var QuestionPrompt = class extends AbstractPrompt {
|
|
209
237
|
defaultValue;
|
|
@@ -243,16 +271,16 @@ var QuestionPrompt = class extends AbstractPrompt {
|
|
|
243
271
|
});
|
|
244
272
|
}
|
|
245
273
|
#getQuestionQuery() {
|
|
246
|
-
return `${
|
|
274
|
+
return `${styleText2("bold", `${SYMBOLS.QuestionMark} ${this.message}${this.tip}`)} ${this.questionSuffixError}`;
|
|
247
275
|
}
|
|
248
276
|
#setQuestionSuffixError(error) {
|
|
249
|
-
const suffix =
|
|
277
|
+
const suffix = styleText2("red", `[${error}] `);
|
|
250
278
|
this.questionSuffixError = suffix;
|
|
251
279
|
}
|
|
252
280
|
#writeAnswer() {
|
|
253
281
|
const prefix = this.answer ? SYMBOLS.Tick : SYMBOLS.Cross;
|
|
254
|
-
const answer =
|
|
255
|
-
this.write(`${prefix} ${
|
|
282
|
+
const answer = styleText2("yellow", this.#secure ? "CONFIDENTIAL" : this.answer ?? "");
|
|
283
|
+
this.write(`${prefix} ${styleText2("bold", this.message)} ${SYMBOLS.Pointer} ${answer}${EOL2}`);
|
|
256
284
|
}
|
|
257
285
|
#onQuestionAnswer() {
|
|
258
286
|
const questionLineCount = Math.ceil(
|
|
@@ -261,9 +289,9 @@ var QuestionPrompt = class extends AbstractPrompt {
|
|
|
261
289
|
this.stdout.moveCursor(-this.stdout.columns, -questionLineCount);
|
|
262
290
|
this.stdout.clearScreenDown();
|
|
263
291
|
for (const validator of this.#validators) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
this.#setQuestionSuffixError(
|
|
292
|
+
const validationResult = validator.validate(this.answer);
|
|
293
|
+
if (isValid(validationResult) === false) {
|
|
294
|
+
this.#setQuestionSuffixError(resultError(validationResult));
|
|
267
295
|
this.answerBuffer = this.#question();
|
|
268
296
|
return;
|
|
269
297
|
}
|
|
@@ -300,7 +328,7 @@ var QuestionPrompt = class extends AbstractPrompt {
|
|
|
300
328
|
|
|
301
329
|
// src/prompts/confirm.ts
|
|
302
330
|
import { EOL as EOL3 } from "os";
|
|
303
|
-
import
|
|
331
|
+
import { styleText as styleText3 } from "util";
|
|
304
332
|
import wcwidth2 from "@topcli/wcwidth";
|
|
305
333
|
var kToggleKeys = /* @__PURE__ */ new Set([
|
|
306
334
|
"left",
|
|
@@ -331,8 +359,8 @@ var ConfirmPrompt = class extends AbstractPrompt {
|
|
|
331
359
|
this.selectedValue = initial;
|
|
332
360
|
}
|
|
333
361
|
#getHint() {
|
|
334
|
-
const Yes =
|
|
335
|
-
const No =
|
|
362
|
+
const Yes = styleText3(["cyan", "bold", "underline"], "Yes");
|
|
363
|
+
const No = styleText3(["cyan", "bold", "underline"], "No");
|
|
336
364
|
return this.selectedValue ? `${Yes}/No` : `Yes/${No}`;
|
|
337
365
|
}
|
|
338
366
|
#render() {
|
|
@@ -378,7 +406,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
|
|
|
378
406
|
this.stdin.off("keypress", this.#boundKeyPressEvent);
|
|
379
407
|
}
|
|
380
408
|
#getQuestionQuery() {
|
|
381
|
-
const query =
|
|
409
|
+
const query = styleText3("bold", `${SYMBOLS.QuestionMark} ${this.message}`);
|
|
382
410
|
return `${query} ${this.#getHint()}`;
|
|
383
411
|
}
|
|
384
412
|
#onQuestionAnswer() {
|
|
@@ -388,7 +416,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
|
|
|
388
416
|
-Math.floor(wcwidth2(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns)
|
|
389
417
|
);
|
|
390
418
|
this.stdout.clearScreenDown();
|
|
391
|
-
this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${
|
|
419
|
+
this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${styleText3("bold", this.message)}${EOL3}`);
|
|
392
420
|
}
|
|
393
421
|
confirm() {
|
|
394
422
|
return new Promise(async (resolve, reject) => {
|
|
@@ -420,7 +448,7 @@ var ConfirmPrompt = class extends AbstractPrompt {
|
|
|
420
448
|
|
|
421
449
|
// src/prompts/select.ts
|
|
422
450
|
import { EOL as EOL4 } from "os";
|
|
423
|
-
import
|
|
451
|
+
import { styleText as styleText4 } from "util";
|
|
424
452
|
import wcwidth3 from "@topcli/wcwidth";
|
|
425
453
|
var kRequiredChoiceProperties = ["label", "value"];
|
|
426
454
|
var SelectPrompt = class extends AbstractPrompt {
|
|
@@ -527,15 +555,15 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
527
555
|
this.longestChoice < 10 ? this.longestChoice : 0
|
|
528
556
|
);
|
|
529
557
|
const formattedDescription = choice.description ? ` - ${choice.description}` : "";
|
|
530
|
-
const
|
|
531
|
-
const str = `${prefix}${
|
|
558
|
+
const styles = isChoiceSelected ? ["white", "bold"] : ["gray"];
|
|
559
|
+
const str = `${prefix}${styleText4(styles, `${formattedLabel}${formattedDescription}`)}${EOL4}`;
|
|
532
560
|
this.write(str);
|
|
533
561
|
}
|
|
534
562
|
}
|
|
535
563
|
#showAnsweredQuestion(choice) {
|
|
536
564
|
const symbolPrefix = choice === "" ? SYMBOLS.Cross : SYMBOLS.Tick;
|
|
537
|
-
const prefix = `${symbolPrefix} ${
|
|
538
|
-
const formattedChoice =
|
|
565
|
+
const prefix = `${symbolPrefix} ${styleText4("bold", this.message)} ${SYMBOLS.Pointer}`;
|
|
566
|
+
const formattedChoice = styleText4("yellow", typeof choice === "string" ? choice : choice.label);
|
|
539
567
|
this.write(`${prefix} ${formattedChoice}${EOL4}`);
|
|
540
568
|
}
|
|
541
569
|
#onProcessExit() {
|
|
@@ -557,9 +585,9 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
557
585
|
const label = typeof choice === "string" ? choice : choice.label;
|
|
558
586
|
const value = typeof choice === "string" ? choice : choice.value;
|
|
559
587
|
for (const validator of this.#validators) {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
render({ error });
|
|
588
|
+
const validationResult = validator.validate(value);
|
|
589
|
+
if (isValid(validationResult) === false) {
|
|
590
|
+
render({ error: resultError(validationResult) });
|
|
563
591
|
return;
|
|
564
592
|
}
|
|
565
593
|
}
|
|
@@ -646,16 +674,16 @@ var SelectPrompt = class extends AbstractPrompt {
|
|
|
646
674
|
#showQuestion(error = null) {
|
|
647
675
|
let hint = "";
|
|
648
676
|
if (error) {
|
|
649
|
-
hint = ` ${hint.length > 0 ? " " : ""}${
|
|
677
|
+
hint = ` ${hint.length > 0 ? " " : ""}${styleText4(["red", "bold"], `[${error}]`)}`;
|
|
650
678
|
}
|
|
651
|
-
this.questionMessage = `${SYMBOLS.QuestionMark} ${
|
|
679
|
+
this.questionMessage = `${SYMBOLS.QuestionMark} ${styleText4("bold", this.message)}${hint}`;
|
|
652
680
|
this.write(`${this.questionMessage}${EOL4}`);
|
|
653
681
|
}
|
|
654
682
|
};
|
|
655
683
|
|
|
656
684
|
// src/prompts/multiselect.ts
|
|
657
685
|
import { EOL as EOL5 } from "os";
|
|
658
|
-
import
|
|
686
|
+
import { styleText as styleText5 } from "util";
|
|
659
687
|
import wcwidth4 from "@topcli/wcwidth";
|
|
660
688
|
var kRequiredChoiceProperties2 = ["label", "value"];
|
|
661
689
|
var MultiselectPrompt = class extends AbstractPrompt {
|
|
@@ -784,15 +812,15 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
784
812
|
this.longestChoice < 10 ? this.longestChoice : 0
|
|
785
813
|
);
|
|
786
814
|
const formattedDescription = choice.description ? ` - ${choice.description}` : "";
|
|
787
|
-
const
|
|
788
|
-
const str = `${prefix} ${
|
|
815
|
+
const styles = isChoiceActive ? ["white", "bold"] : ["gray"];
|
|
816
|
+
const str = `${prefix} ${styleText5(styles, `${formattedLabel}${formattedDescription}`)}${EOL5}`;
|
|
789
817
|
this.write(str);
|
|
790
818
|
}
|
|
791
819
|
}
|
|
792
820
|
#showAnsweredQuestion(choices, isAgentAnswer = false) {
|
|
793
821
|
const prefixSymbol = this.selectedIndexes.size === 0 && !isAgentAnswer ? SYMBOLS.Cross : SYMBOLS.Tick;
|
|
794
|
-
const prefix = `${prefixSymbol} ${
|
|
795
|
-
const formattedChoice =
|
|
822
|
+
const prefix = `${prefixSymbol} ${styleText5("bold", this.message)} ${SYMBOLS.Pointer}`;
|
|
823
|
+
const formattedChoice = styleText5("yellow", choices);
|
|
796
824
|
this.write(`${prefix}${choices ? ` ${formattedChoice}` : ""}${EOL5}`);
|
|
797
825
|
}
|
|
798
826
|
#onProcessExit() {
|
|
@@ -828,9 +856,9 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
828
856
|
return typeof choice === "string" ? choice : choice.value;
|
|
829
857
|
});
|
|
830
858
|
for (const validator of this.#validators) {
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
render({ error });
|
|
859
|
+
const validationResult = validator.validate(values);
|
|
860
|
+
if (isValid(validationResult) === false) {
|
|
861
|
+
render({ error: resultError(validationResult) });
|
|
834
862
|
return;
|
|
835
863
|
}
|
|
836
864
|
}
|
|
@@ -915,26 +943,19 @@ var MultiselectPrompt = class extends AbstractPrompt {
|
|
|
915
943
|
});
|
|
916
944
|
}
|
|
917
945
|
#showQuestion(error = null) {
|
|
918
|
-
let hint = this.#showHint ?
|
|
919
|
-
|
|
920
|
-
|
|
946
|
+
let hint = this.#showHint ? styleText5(
|
|
947
|
+
"gray",
|
|
948
|
+
// eslint-disable-next-line @stylistic/max-len
|
|
949
|
+
`(Press ${styleText5("bold", "<Ctrl+A>")} to toggle all, ${styleText5("bold", "<Left/Right>")} to toggle, ${styleText5("bold", "<Return>")} to submit)`
|
|
921
950
|
) : "";
|
|
922
951
|
if (error) {
|
|
923
|
-
hint += `${hint.length > 0 ? " " : ""}${
|
|
952
|
+
hint += `${hint.length > 0 ? " " : ""}${styleText5(["red", "bold"], `[${error}]`)}`;
|
|
924
953
|
}
|
|
925
|
-
this.questionMessage = `${SYMBOLS.QuestionMark} ${
|
|
954
|
+
this.questionMessage = `${SYMBOLS.QuestionMark} ${styleText5("bold", this.message)}${hint.length > 0 ? ` ${hint}` : ""}`;
|
|
926
955
|
this.write(`${this.questionMessage}${EOL5}`);
|
|
927
956
|
}
|
|
928
957
|
};
|
|
929
958
|
|
|
930
|
-
// src/validators.ts
|
|
931
|
-
function required() {
|
|
932
|
-
return {
|
|
933
|
-
validate: (input) => Array.isArray(input) ? input.length > 0 : Boolean(input),
|
|
934
|
-
error: () => "required"
|
|
935
|
-
};
|
|
936
|
-
}
|
|
937
|
-
|
|
938
959
|
// index.ts
|
|
939
960
|
async function question(message, options = {}) {
|
|
940
961
|
return new QuestionPrompt({ ...options, message }).question();
|
|
@@ -943,7 +964,7 @@ async function select(message, options) {
|
|
|
943
964
|
const selectPrompt = new SelectPrompt({ ...options, message });
|
|
944
965
|
return selectPrompt.select();
|
|
945
966
|
}
|
|
946
|
-
async function confirm(message, options) {
|
|
967
|
+
async function confirm(message, options = {}) {
|
|
947
968
|
const confirmPrompt = new ConfirmPrompt({ ...options, message });
|
|
948
969
|
return confirmPrompt.confirm();
|
|
949
970
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topcli/prompts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Node.js user input library for command-line interfaces.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsup index.ts --format cjs,esm --dts --clean",
|
|
@@ -31,22 +31,21 @@
|
|
|
31
31
|
"license": "ISC",
|
|
32
32
|
"type": "module",
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@
|
|
35
|
-
"@
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"esmock": "^2.6.
|
|
39
|
-
"glob": "^
|
|
40
|
-
"tsup": "^8.
|
|
41
|
-
"tsx": "^4.
|
|
42
|
-
"typescript": "^5.
|
|
34
|
+
"@openally/config.eslint": "^1.0.0",
|
|
35
|
+
"@openally/config.typescript": "^1.0.3",
|
|
36
|
+
"@types/node": "^22.3.0",
|
|
37
|
+
"c8": "^10.1.2",
|
|
38
|
+
"esmock": "^2.6.7",
|
|
39
|
+
"glob": "^11.0.0",
|
|
40
|
+
"tsup": "^8.2.4",
|
|
41
|
+
"tsx": "^4.17.0",
|
|
42
|
+
"typescript": "^5.5.4"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@topcli/wcwidth": "^1.0.1"
|
|
46
|
-
"kleur": "^4.1.5"
|
|
45
|
+
"@topcli/wcwidth": "^1.0.1"
|
|
47
46
|
},
|
|
48
47
|
"engines": {
|
|
49
|
-
"node": ">=
|
|
48
|
+
"node": "^20.12.0 || >=21.7.0"
|
|
50
49
|
},
|
|
51
50
|
"bugs": {
|
|
52
51
|
"url": "https://github.com/TopCli/prompts/issues"
|