@topcli/prompts 0.1.0 → 0.1.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/README.md +23 -1
- package/index.d.ts +8 -1
- package/index.js +2 -2
- package/package.json +4 -5
- package/src/abstract-prompt.js +23 -2
- package/src/confirm-prompt.js +23 -13
- package/src/select-prompt.js +93 -49
- package/src/text-prompt.js +54 -14
package/README.md
CHANGED
|
@@ -43,10 +43,24 @@ console.log(name, runner, isCLI)
|
|
|
43
43
|
### `prompt()`
|
|
44
44
|
|
|
45
45
|
```ts
|
|
46
|
-
prompt(message: string): Promise<string>
|
|
46
|
+
prompt(message: string, options?: PromptOptions): Promise<string>
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
Simple prompt, similar to `rl.question()` with an improved UI.
|
|
50
|
+
Use `options.validators` to handle user input.
|
|
51
|
+
|
|
52
|
+
**Example**
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
const packageName = await prompt('Package name', {
|
|
56
|
+
validators: [
|
|
57
|
+
{
|
|
58
|
+
validate: (value) => !existsSync(join(process.cwd(), value)),
|
|
59
|
+
error: (value) => `Folder ${value} already exists`
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
})
|
|
63
|
+
```
|
|
50
64
|
|
|
51
65
|
### `select()`
|
|
52
66
|
|
|
@@ -67,6 +81,14 @@ Boolean prompt, return `options.initial` if user input is different from "y"/"ye
|
|
|
67
81
|
|
|
68
82
|
## Interfaces
|
|
69
83
|
|
|
84
|
+
```ts
|
|
85
|
+
export interface PromptOptions {
|
|
86
|
+
validators?: {
|
|
87
|
+
validate: (input: string) => boolean;
|
|
88
|
+
error: (input: string) => string;
|
|
89
|
+
}[];
|
|
90
|
+
}
|
|
91
|
+
```
|
|
70
92
|
```ts
|
|
71
93
|
export interface Choice {
|
|
72
94
|
value: any;
|
package/index.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
export interface PromptOptions {
|
|
2
|
+
validators?: {
|
|
3
|
+
validate: (input: string) => boolean;
|
|
4
|
+
error: (input: string) => string;
|
|
5
|
+
}[];
|
|
6
|
+
}
|
|
7
|
+
|
|
1
8
|
export interface Choice {
|
|
2
9
|
value: any;
|
|
3
10
|
label: string;
|
|
@@ -14,6 +21,6 @@ export interface ConfirmOptions {
|
|
|
14
21
|
initial?: boolean;
|
|
15
22
|
}
|
|
16
23
|
|
|
17
|
-
export function prompt(message: string): Promise<string>;
|
|
24
|
+
export function prompt(message: string, options?: PromptOptions): Promise<string>;
|
|
18
25
|
export function select(message: string, options: SelectOptions): Promise<string>;
|
|
19
26
|
export function confirm(message: string, options?: ConfirmOptions): Promise<string>;
|
package/index.js
CHANGED
|
@@ -3,8 +3,8 @@ import { ConfirmPrompt } from "./src/confirm-prompt.js";
|
|
|
3
3
|
import { SelectPrompt } from "./src/select-prompt.js";
|
|
4
4
|
import { TextPrompt } from "./src/text-prompt.js";
|
|
5
5
|
|
|
6
|
-
export async function prompt(message) {
|
|
7
|
-
const textPrompt = new TextPrompt(message);
|
|
6
|
+
export async function prompt(message, options = {}) {
|
|
7
|
+
const textPrompt = new TextPrompt(message, options);
|
|
8
8
|
|
|
9
9
|
return textPrompt.question();
|
|
10
10
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@topcli/prompts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Node.js user input library for command-line interfaces.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --loader=esmock --test ./test/**.test.js",
|
|
@@ -26,12 +26,11 @@
|
|
|
26
26
|
"@nodesecure/eslint-config": "^1.7.0",
|
|
27
27
|
"c8": "^7.13.0",
|
|
28
28
|
"eslint": "^8.38.0",
|
|
29
|
-
"esmock": "^2.1.0"
|
|
30
|
-
"snazzy": "^9.0.0",
|
|
31
|
-
"strip-ansi": "^7.0.1"
|
|
29
|
+
"esmock": "^2.1.0"
|
|
32
30
|
},
|
|
33
31
|
"dependencies": {
|
|
34
|
-
"ansi-styles": "^6.2.1"
|
|
32
|
+
"ansi-styles": "^6.2.1",
|
|
33
|
+
"strip-ansi": "^7.0.1"
|
|
35
34
|
},
|
|
36
35
|
"engines": {
|
|
37
36
|
"node": ">=14"
|
package/src/abstract-prompt.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// Import Node.js Dependencies
|
|
2
|
+
import { EOL } from "node:os";
|
|
2
3
|
import { createInterface } from "node:readline";
|
|
3
4
|
|
|
5
|
+
// Import Third-party Dependencies
|
|
6
|
+
import stripAnsi from "strip-ansi";
|
|
7
|
+
|
|
4
8
|
export class AbstractPrompt {
|
|
5
9
|
constructor(message, input = process.stdin, output = process.stdout) {
|
|
6
10
|
if (this.constructor === AbstractPrompt) {
|
|
@@ -14,6 +18,7 @@ export class AbstractPrompt {
|
|
|
14
18
|
this.stdin = input;
|
|
15
19
|
this.stdout = output;
|
|
16
20
|
this.message = message;
|
|
21
|
+
this.history = [];
|
|
17
22
|
|
|
18
23
|
if (this.stdout.isTTY) {
|
|
19
24
|
this.stdin.setRawMode(true);
|
|
@@ -21,9 +26,25 @@ export class AbstractPrompt {
|
|
|
21
26
|
this.rl = createInterface({ input, output });
|
|
22
27
|
}
|
|
23
28
|
|
|
29
|
+
write(data) {
|
|
30
|
+
const formattedData = stripAnsi(data).replace(EOL, "");
|
|
31
|
+
if (formattedData) {
|
|
32
|
+
this.history.push(formattedData);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return this.stdout.write(data);
|
|
36
|
+
}
|
|
37
|
+
|
|
24
38
|
clearLastLine() {
|
|
25
|
-
this.
|
|
26
|
-
|
|
39
|
+
const lastLine = this.history.pop();
|
|
40
|
+
if (!lastLine) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const lastLineRows = Math.ceil(stripAnsi(lastLine).length / this.stdout.columns);
|
|
45
|
+
|
|
46
|
+
this.stdout.moveCursor(0, -lastLineRows);
|
|
47
|
+
this.stdout.clearScreenDown();
|
|
27
48
|
}
|
|
28
49
|
|
|
29
50
|
destroy() {
|
package/src/confirm-prompt.js
CHANGED
|
@@ -8,35 +8,45 @@ import ansi from "ansi-styles";
|
|
|
8
8
|
import { AbstractPrompt } from "./abstract-prompt.js";
|
|
9
9
|
import { SYMBOLS } from "./constants.js";
|
|
10
10
|
|
|
11
|
-
const kDefaultConfirmOptions = { initial: false };
|
|
12
|
-
|
|
13
11
|
export class ConfirmPrompt extends AbstractPrompt {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
constructor(message, options = {}) {
|
|
13
|
+
const {
|
|
14
|
+
stdin = process.stdin,
|
|
15
|
+
stdout = process.stdout,
|
|
16
|
+
initial = false
|
|
17
|
+
} = options;
|
|
18
18
|
super(message, stdin, stdout);
|
|
19
19
|
|
|
20
|
-
const { initial } = { ...kDefaultConfirmOptions, ...options };
|
|
21
20
|
const Yes = `${ansi.bold.open}Yes${ansi.bold.close}`;
|
|
22
21
|
const No = `${ansi.bold.open}No${ansi.bold.close}`;
|
|
23
22
|
const tip = initial ? `${Yes}/no` : `yes/${No}`;
|
|
24
23
|
|
|
25
24
|
this.initial = initial;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
#question() {
|
|
28
|
+
return new Promise((resolve) => {
|
|
29
|
+
const questionQuery = this.#getQuestionQuery();
|
|
30
|
+
|
|
31
|
+
this.rl.question(questionQuery,
|
|
30
32
|
(answer) => {
|
|
33
|
+
this.history.push(questionQuery + answer);
|
|
34
|
+
|
|
31
35
|
resolve(answer);
|
|
32
36
|
}
|
|
33
37
|
);
|
|
34
38
|
});
|
|
35
39
|
}
|
|
36
40
|
|
|
41
|
+
#getQuestionQuery() {
|
|
42
|
+
const query = `${ansi.bold.open}${SYMBOLS.QuestionMark} ${this.message}${ansi.bold.close}`;
|
|
43
|
+
|
|
44
|
+
return `${query} ${ansi.grey.open}(${this.initial ? "Yes/no" : "yes/No"})${ansi.grey.close} `;
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
#onQuestionAnswer() {
|
|
38
48
|
this.clearLastLine();
|
|
39
|
-
this.
|
|
49
|
+
this.write(`${this.answer ? SYMBOLS.Tick : SYMBOLS.Cross} ${this.message}${EOL}`);
|
|
40
50
|
}
|
|
41
51
|
|
|
42
52
|
#validateResult(result) {
|
|
@@ -49,7 +59,7 @@ export class ConfirmPrompt extends AbstractPrompt {
|
|
|
49
59
|
|
|
50
60
|
async confirm() {
|
|
51
61
|
try {
|
|
52
|
-
const result = await this.#question;
|
|
62
|
+
const result = await this.#question();
|
|
53
63
|
this.answer = this.#validateResult(result) ? ["y", "yes"].includes(result.toLocaleLowerCase()) : this.initial;
|
|
54
64
|
this.#onQuestionAnswer();
|
|
55
65
|
|
package/src/select-prompt.js
CHANGED
|
@@ -11,8 +11,17 @@ import { SYMBOLS } from "./constants.js";
|
|
|
11
11
|
export class SelectPrompt extends AbstractPrompt {
|
|
12
12
|
activeIndex = 0;
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
get choices() {
|
|
15
|
+
return this.options.choices;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
constructor(message, options) {
|
|
19
|
+
const {
|
|
20
|
+
stdin = process.stdin,
|
|
21
|
+
stdout = process.stdout,
|
|
22
|
+
choices
|
|
23
|
+
} = options ?? {};
|
|
24
|
+
|
|
16
25
|
super(message, stdin, stdout);
|
|
17
26
|
|
|
18
27
|
if (!options) {
|
|
@@ -21,14 +30,12 @@ export class SelectPrompt extends AbstractPrompt {
|
|
|
21
30
|
}
|
|
22
31
|
|
|
23
32
|
this.options = options;
|
|
24
|
-
const { choices } = options;
|
|
25
33
|
|
|
26
34
|
if (!choices?.length) {
|
|
27
35
|
this.destroy();
|
|
28
36
|
throw new TypeError("Missing required param: choices");
|
|
29
37
|
}
|
|
30
38
|
|
|
31
|
-
this.choices = choices;
|
|
32
39
|
this.longestChoice = Math.max(...choices.map((choice) => {
|
|
33
40
|
if (typeof choice === "string") {
|
|
34
41
|
return choice.length;
|
|
@@ -47,60 +54,92 @@ export class SelectPrompt extends AbstractPrompt {
|
|
|
47
54
|
}));
|
|
48
55
|
}
|
|
49
56
|
|
|
50
|
-
|
|
51
|
-
this.
|
|
52
|
-
this.stdout.write(`${ansi.bold.open}${SYMBOLS.QuestionMark} ${this.message}${ansi.bold.close}${EOL}`);
|
|
53
|
-
|
|
54
|
-
let lastRender = null;
|
|
55
|
-
const render = (initialRender = false, { reset } = {}) => {
|
|
56
|
-
function getVisibleChoices(currentIndex, total, maxVisible) {
|
|
57
|
-
let startIndex = Math.min(total - maxVisible, currentIndex - Math.floor(maxVisible / 2));
|
|
58
|
-
if (startIndex < 0) {
|
|
59
|
-
startIndex = 0;
|
|
60
|
-
}
|
|
57
|
+
#getFormattedChoice(choiceIndex) {
|
|
58
|
+
const choice = this.choices[choiceIndex];
|
|
61
59
|
|
|
62
|
-
|
|
60
|
+
if (typeof choice === "string") {
|
|
61
|
+
return { value: choice, label: choice };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return choice;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#getVisibleChoices() {
|
|
68
|
+
const maxVisible = this.options.maxVisible || 8;
|
|
69
|
+
let startIndex = Math.min(this.choices.length - maxVisible, this.activeIndex - Math.floor(maxVisible / 2));
|
|
70
|
+
if (startIndex < 0) {
|
|
71
|
+
startIndex = 0;
|
|
72
|
+
}
|
|
63
73
|
|
|
64
|
-
|
|
74
|
+
const endIndex = Math.min(startIndex + maxVisible, this.choices.length);
|
|
75
|
+
|
|
76
|
+
return { startIndex, endIndex };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
#showChoices() {
|
|
80
|
+
const { startIndex, endIndex } = this.#getVisibleChoices();
|
|
81
|
+
this.lastRender = { startIndex, endIndex };
|
|
82
|
+
|
|
83
|
+
for (let choiceIndex = startIndex; choiceIndex < endIndex; choiceIndex++) {
|
|
84
|
+
const choice = this.#getFormattedChoice(choiceIndex);
|
|
85
|
+
const showPreviousChoicesArrow = startIndex > 0 && choiceIndex === startIndex;
|
|
86
|
+
const showNextChoicesArrow = endIndex < this.choices.length && choiceIndex === endIndex - 1;
|
|
87
|
+
|
|
88
|
+
let prefixArrow = " ";
|
|
89
|
+
if (showPreviousChoicesArrow) {
|
|
90
|
+
prefixArrow = SYMBOLS.Previous;
|
|
65
91
|
}
|
|
92
|
+
else if (showNextChoicesArrow) {
|
|
93
|
+
prefixArrow = SYMBOLS.Next;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const prefix = `${prefixArrow}${choiceIndex === this.activeIndex ? `${SYMBOLS.Active} ` : `${SYMBOLS.Inactive} `}`;
|
|
97
|
+
const formattedLabel = choice.label.padEnd(
|
|
98
|
+
this.longestChoice < 10 ? this.longestChoice : 0
|
|
99
|
+
);
|
|
100
|
+
const formattedDescription = choice.description ? ` - ${choice.description}` : "";
|
|
101
|
+
const str = `${prefix}${formattedLabel}${formattedDescription}${ansi.reset.open}${EOL}`;
|
|
66
102
|
|
|
67
|
-
|
|
103
|
+
this.write(str);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#showAnsweredQuestion(choice) {
|
|
108
|
+
const prefix = `${ansi.bold.open}${SYMBOLS.Tick} ${this.message} ${SYMBOLS.Pointer}`;
|
|
109
|
+
const formattedChoice = `${ansi.yellow.open}${choice.label ?? choice}${ansi.reset.open}`;
|
|
110
|
+
|
|
111
|
+
this.write(`${prefix} ${formattedChoice}${EOL}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async select() {
|
|
115
|
+
this.write(SYMBOLS.HideCursor);
|
|
116
|
+
this.#showQuestion();
|
|
117
|
+
|
|
118
|
+
const render = (options = {}) => {
|
|
119
|
+
const {
|
|
120
|
+
initialRender = false,
|
|
121
|
+
clearRender = false
|
|
122
|
+
} = options;
|
|
68
123
|
|
|
69
124
|
if (!initialRender) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
125
|
+
let linesToClear = this.lastRender.endIndex - this.lastRender.startIndex;
|
|
126
|
+
while (linesToClear > 0) {
|
|
127
|
+
this.clearLastLine();
|
|
128
|
+
linesToClear--;
|
|
129
|
+
}
|
|
73
130
|
}
|
|
74
131
|
|
|
75
|
-
if (
|
|
76
|
-
this.
|
|
77
|
-
this.
|
|
132
|
+
if (clearRender) {
|
|
133
|
+
this.stdout.moveCursor(0, -2);
|
|
134
|
+
this.stdout.clearScreenDown();
|
|
78
135
|
|
|
79
136
|
return;
|
|
80
137
|
}
|
|
81
138
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
for (let i = startIndex; i < endIndex; i++) {
|
|
85
|
-
const choice = typeof this.choices[i] === "string" ? { value: this.choices[i], label: this.choices[i] } : this.choices[i];
|
|
86
|
-
const showPreviousChoicesArrow = startIndex > 0 && i === startIndex;
|
|
87
|
-
const showNextChoicesArrow = endIndex < this.choices.length && i === endIndex - 1;
|
|
88
|
-
let prefixArrow = " ";
|
|
89
|
-
if (showPreviousChoicesArrow) {
|
|
90
|
-
prefixArrow = SYMBOLS.Previous;
|
|
91
|
-
}
|
|
92
|
-
else if (showNextChoicesArrow) {
|
|
93
|
-
prefixArrow = SYMBOLS.Next;
|
|
94
|
-
}
|
|
95
|
-
const prefix = `${prefixArrow}${i === this.activeIndex ? `${SYMBOLS.Active} ` : `${SYMBOLS.Inactive} `}`;
|
|
96
|
-
const str = `${prefix}${choice.label.padEnd(
|
|
97
|
-
this.longestChoice < 10 ? this.longestChoice : 0
|
|
98
|
-
)}${choice.description ? ` - ${choice.description}` : ""}${ansi.reset.open}${EOL}`;
|
|
99
|
-
this.stdout.write(str);
|
|
100
|
-
}
|
|
139
|
+
this.#showChoices();
|
|
101
140
|
};
|
|
102
141
|
|
|
103
|
-
render(true);
|
|
142
|
+
render({ initialRender: true });
|
|
104
143
|
|
|
105
144
|
return new Promise((resolve) => {
|
|
106
145
|
const onKeypress = (value, key) => {
|
|
@@ -115,17 +154,18 @@ export class SelectPrompt extends AbstractPrompt {
|
|
|
115
154
|
else if (key.name === "return") {
|
|
116
155
|
this.stdin.off("keypress", onKeypress);
|
|
117
156
|
|
|
118
|
-
render(
|
|
157
|
+
render({ clearRender: true });
|
|
158
|
+
|
|
119
159
|
const currentChoice = this.choices[this.activeIndex];
|
|
120
160
|
const value = currentChoice.value ?? currentChoice;
|
|
161
|
+
|
|
121
162
|
if (!this.options.ignoreValues?.includes(value)) {
|
|
122
|
-
|
|
123
|
-
const choice = `${ansi.yellow.open}${currentChoice.label ?? currentChoice}${ansi.reset.open}`;
|
|
124
|
-
this.stdout.write(`${prefix} ${choice}${EOL}`);
|
|
163
|
+
this.#showAnsweredQuestion(currentChoice);
|
|
125
164
|
}
|
|
126
165
|
|
|
127
|
-
this.
|
|
166
|
+
this.write(SYMBOLS.ShowCursor);
|
|
128
167
|
this.destroy();
|
|
168
|
+
|
|
129
169
|
resolve(value);
|
|
130
170
|
}
|
|
131
171
|
};
|
|
@@ -133,4 +173,8 @@ export class SelectPrompt extends AbstractPrompt {
|
|
|
133
173
|
this.stdin.on("keypress", onKeypress);
|
|
134
174
|
});
|
|
135
175
|
}
|
|
176
|
+
|
|
177
|
+
#showQuestion() {
|
|
178
|
+
this.write(`${ansi.bold.open}${SYMBOLS.QuestionMark} ${this.message}${ansi.bold.close}${EOL}`);
|
|
179
|
+
}
|
|
136
180
|
}
|
package/src/text-prompt.js
CHANGED
|
@@ -3,41 +3,81 @@ import { EOL } from "node:os";
|
|
|
3
3
|
|
|
4
4
|
// Import Third-party Dependencies
|
|
5
5
|
import ansi from "ansi-styles";
|
|
6
|
+
import stripAnsi from "strip-ansi";
|
|
6
7
|
|
|
7
8
|
// Import Internal Dependencies
|
|
8
9
|
import { AbstractPrompt } from "./abstract-prompt.js";
|
|
9
10
|
import { SYMBOLS } from "./constants.js";
|
|
10
11
|
|
|
11
12
|
export class TextPrompt extends AbstractPrompt {
|
|
12
|
-
#
|
|
13
|
+
#validators;
|
|
13
14
|
|
|
14
|
-
constructor(message,
|
|
15
|
+
constructor(message, options = {}) {
|
|
16
|
+
const { stdin = process.stdin, stdout = process.stdout, validators = [] } = options;
|
|
15
17
|
super(message, stdin, stdout);
|
|
16
18
|
|
|
17
|
-
this.#
|
|
18
|
-
|
|
19
|
+
this.#validators = validators;
|
|
20
|
+
this.questionPrefix = `${ansi.bold.open}${SYMBOLS.QuestionMark} `;
|
|
21
|
+
this.questionSuffix = `${ansi.bold.close} `;
|
|
22
|
+
this.questionSuffixError = "";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#question() {
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
const questionQuery = this.#getQuestionQuery();
|
|
28
|
+
|
|
29
|
+
this.rl.question(questionQuery, (answer) => {
|
|
30
|
+
this.history.push(questionQuery + answer);
|
|
31
|
+
|
|
19
32
|
resolve(answer);
|
|
20
33
|
});
|
|
21
34
|
});
|
|
22
35
|
}
|
|
23
36
|
|
|
24
|
-
#
|
|
25
|
-
this.
|
|
37
|
+
#getQuestionQuery() {
|
|
38
|
+
return `${this.questionPrefix}${this.message}${this.questionSuffix}${this.questionSuffixError}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#setQuestionSuffixError(error) {
|
|
42
|
+
const suffix = `${ansi.red.open}[${error}]${ansi.red.close} `;
|
|
43
|
+
this.questionSuffixError = suffix;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#writeAnswer() {
|
|
26
47
|
const prefix = `${ansi.bold.open}${this.answer ? SYMBOLS.Tick : SYMBOLS.Cross}`;
|
|
27
48
|
const suffix = `${ansi.yellow.close}${ansi.bold.close}${EOL}`;
|
|
28
|
-
|
|
49
|
+
|
|
50
|
+
this.write(`${prefix} ${this.message} ${SYMBOLS.Pointer} ${ansi.yellow.open}${this.answer ?? ""}${suffix}`);
|
|
29
51
|
}
|
|
30
52
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.answer = await this.#question;
|
|
53
|
+
#onQuestionAnswer() {
|
|
54
|
+
this.clearLastLine();
|
|
34
55
|
|
|
35
|
-
|
|
56
|
+
for (const validator of this.#validators) {
|
|
57
|
+
if (!validator.validate(this.answer)) {
|
|
58
|
+
const error = validator.error(this.answer);
|
|
59
|
+
this.#setQuestionSuffixError(error);
|
|
60
|
+
this.answer = this.#question();
|
|
36
61
|
|
|
37
|
-
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
38
64
|
}
|
|
39
|
-
|
|
40
|
-
|
|
65
|
+
|
|
66
|
+
this.#writeAnswer();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async question() {
|
|
70
|
+
this.answer = await this.#question();
|
|
71
|
+
|
|
72
|
+
this.#onQuestionAnswer();
|
|
73
|
+
|
|
74
|
+
while (this.answer?.constructor.name === "Promise") {
|
|
75
|
+
this.answer = await this.answer;
|
|
76
|
+
this.#onQuestionAnswer();
|
|
41
77
|
}
|
|
78
|
+
|
|
79
|
+
this.destroy();
|
|
80
|
+
|
|
81
|
+
return this.answer;
|
|
42
82
|
}
|
|
43
83
|
}
|