@topcli/prompts 1.7.0 → 1.8.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/package.json CHANGED
@@ -1,43 +1,55 @@
1
- {
2
- "name": "@topcli/prompts",
3
- "version": "1.7.0",
4
- "description": "Node.js user input library for command-line interfaces.",
5
- "scripts": {
6
- "test": "node --no-warnings=ExperimentalWarning --loader=esmock --test test/",
7
- "coverage": "c8 -r html npm run test",
8
- "lint": "eslint .",
9
- "lint:fix": "eslint . --fix"
10
- },
11
- "main": "./index.js",
12
- "keywords": [
13
- "node.js",
14
- "cli",
15
- "prompt"
16
- ],
17
- "files": [
18
- "index.js",
19
- "index.d.ts",
20
- "src"
21
- ],
22
- "author": "PierreDemailly <pierredemailly.pro@gmail.com>",
23
- "license": "ISC",
24
- "type": "module",
25
- "devDependencies": {
26
- "@nodesecure/eslint-config": "^1.8.0",
27
- "@types/node": "^20.8.5",
28
- "c8": "^8.0.1",
29
- "eslint": "^8.51.0",
30
- "esmock": "^2.5.2"
31
- },
32
- "dependencies": {
33
- "@topcli/wcwidth": "^1.0.1",
34
- "kleur": "^4.1.5"
35
- },
36
- "engines": {
37
- "node": ">=14"
38
- },
39
- "bugs": {
40
- "url": "https://github.com/TopCli/prompts/issues"
41
- },
42
- "homepage": "https://github.com/TopCli/prompts#readme"
43
- }
1
+ {
2
+ "name": "@topcli/prompts",
3
+ "version": "1.8.1",
4
+ "description": "Node.js user input library for command-line interfaces.",
5
+ "scripts": {
6
+ "build": "tsup index.ts --format cjs,esm --dts --clean",
7
+ "prepublishOnly": "npm run build",
8
+ "test": "glob -c \"tsx --no-warnings=ExperimentalWarning --loader=esmock --test\" \"./test/**/*.test.ts\"",
9
+ "coverage": "c8 -r html npm run test",
10
+ "lint": "eslint .",
11
+ "lint:fix": "eslint . --fix"
12
+ },
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "require": "./dist/index.cjs",
18
+ "import": "./dist/index.js",
19
+ "types": "./dist/index.d.ts"
20
+ }
21
+ },
22
+ "keywords": [
23
+ "node.js",
24
+ "cli",
25
+ "prompt"
26
+ ],
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "author": "PierreDemailly <pierredemailly.pro@gmail.com>",
31
+ "license": "ISC",
32
+ "type": "module",
33
+ "devDependencies": {
34
+ "@nodesecure/eslint-config": "^1.9.0",
35
+ "@types/node": "^20.11.6",
36
+ "c8": "^9.1.0",
37
+ "eslint": "^8.56.0",
38
+ "esmock": "^2.6.3",
39
+ "glob": "^10.3.10",
40
+ "tsup": "^8.0.1",
41
+ "tsx": "^4.7.0",
42
+ "typescript": "^5.3.3"
43
+ },
44
+ "dependencies": {
45
+ "@topcli/wcwidth": "^1.0.1",
46
+ "kleur": "^4.1.5"
47
+ },
48
+ "engines": {
49
+ "node": ">=14"
50
+ },
51
+ "bugs": {
52
+ "url": "https://github.com/TopCli/prompts/issues"
53
+ },
54
+ "homepage": "https://github.com/TopCli/prompts#readme"
55
+ }
package/index.d.ts DELETED
@@ -1,77 +0,0 @@
1
- export interface SharedOptions {
2
- stdin?: NodeJS.ReadStream & {
3
- fd: 0;
4
- };
5
- stdout?: NodeJS.WriteStream & {
6
- fd: 1;
7
- };
8
- }
9
-
10
- export interface Validator {
11
- validate: (input: string) => boolean;
12
- error: (input?: string) => string;
13
- }
14
-
15
- export interface QuestionOptions extends SharedOptions {
16
- defaultValue?: string;
17
- validators?: Validator[];
18
- secure?: boolean;
19
- }
20
-
21
- export interface Choice {
22
- value: any;
23
- label: string;
24
- description?: string;
25
- }
26
-
27
- export interface SelectOptions extends SharedOptions {
28
- choices: (Choice | string)[];
29
- maxVisible?: number;
30
- ignoreValues?: (string | number | boolean)[];
31
- }
32
-
33
- export interface MultiselectOptions extends SharedOptions {
34
- choices: (Choice | string)[];
35
- maxVisible?: number;
36
- preSelectedChoices?: (Choice | string)[];
37
- validators?: Validator[];
38
- autocomplete?: boolean;
39
- caseSensitive?: boolean;
40
- }
41
-
42
- export interface ConfirmOptions extends SharedOptions {
43
- initial?: boolean;
44
- }
45
-
46
- export function question(message: string, options?: QuestionOptions): Promise<string>;
47
- export function select(message: string, options: SelectOptions): Promise<string>;
48
- export function multiselect(message: string, options: MultiselectOptions): Promise<string[]>;
49
- export function confirm(message: string, options?: ConfirmOptions): Promise<boolean>;
50
-
51
- export function required(): Validator;
52
-
53
- export class PromptAgent {
54
- /**
55
- * The prompts answers queue.
56
- * When not empty, any prompt will be answered by the first answer in this list.
57
- */
58
- nextAnswers: Array<string | boolean>;
59
-
60
- static get agent(): PromptAgent;
61
-
62
- /**
63
- * Programmatically set the next answer for any prompt (`question()`, `confirm()`, `select()`)
64
- *
65
- * This is useful for testing.
66
- *
67
- * @example
68
- * ```js
69
- * const promptAgent = PromptAgent.agent();
70
- * promptAgent.nextAnswer("toto");
71
- *
72
- * const input = await question("what is your name?");
73
- * assert.equal(input, "toto");
74
- * ```
75
- */
76
- nextAnswer(value: string | boolean | Array<string | boolean>): void
77
- }
package/index.js DELETED
@@ -1,30 +0,0 @@
1
- // Import Internal Dependencies
2
- import * as prompts from "./src/prompts/index.js";
3
- import { required } from "./src/validators.js";
4
- import { PromptAgent } from "./src/prompt-agent.js";
5
-
6
- export async function question(message, options = {}) {
7
- const questionPrompt = new prompts.QuestionPrompt(message, options);
8
-
9
- return questionPrompt.question();
10
- }
11
-
12
- export async function select(message, options) {
13
- const selectPrompt = new prompts.SelectPrompt(message, options);
14
-
15
- return selectPrompt.select();
16
- }
17
-
18
- export async function confirm(message, options) {
19
- const confirmPrompt = new prompts.ConfirmPrompt(message, options);
20
-
21
- return confirmPrompt.confirm();
22
- }
23
-
24
- export async function multiselect(message, options) {
25
- const multiselectPrompt = new prompts.MultiselectPrompt(message, options);
26
-
27
- return multiselectPrompt.multiselect();
28
- }
29
-
30
- export { required, PromptAgent };
package/src/constants.js DELETED
@@ -1,39 +0,0 @@
1
- // Import Third-party Dependencies
2
- import kleur from "kleur";
3
-
4
- // Import Internal Dependencies
5
- import { isUnicodeSupported } from "./utils.js";
6
-
7
- const kMainSymbols = {
8
- tick: "✔",
9
- cross: "✖",
10
- pointer: "›",
11
- previous: "⭡",
12
- next: "⭣",
13
- active: "●",
14
- inactive: "○"
15
- };
16
- const kFallbackSymbols = {
17
- tick: "√",
18
- cross: "×",
19
- pointer: ">",
20
- previous: "↑",
21
- next: "↓",
22
- active: "(+)",
23
- inactive: "(-)"
24
- };
25
- const kSymbols = isUnicodeSupported() || process.env.CI ? kMainSymbols : kFallbackSymbols;
26
- const kPointer = kleur.gray(kSymbols.pointer);
27
-
28
- export const SYMBOLS = {
29
- QuestionMark: kleur.blue().bold("?"),
30
- Tick: kleur.green().bold(kSymbols.tick),
31
- Cross: kleur.red().bold(kSymbols.cross),
32
- Pointer: kPointer,
33
- Previous: kSymbols.previous,
34
- Next: kSymbols.next,
35
- ShowCursor: "\x1B[?25h",
36
- HideCursor: "\x1B[?25l",
37
- Active: kleur.cyan(kSymbols.active),
38
- Inactive: kleur.gray(kSymbols.inactive)
39
- };
@@ -1,56 +0,0 @@
1
- // CONSTANTS
2
- const kPrivateInstancier = Symbol("instancier");
3
-
4
- export class PromptAgent {
5
- /**
6
- * The prompts answers queue.
7
- * When not empty, any prompt will be answered by the first answer in this list.
8
- *
9
- * @type {string[]}
10
- */
11
- nextAnswers = [];
12
-
13
- /**
14
- * The shared PromptAgent.
15
- *
16
- * @type {PromptAgent}
17
- */
18
- static #this;
19
-
20
- static agent() {
21
- // eslint-disable-next-line no-return-assign
22
- return this.#this ??= new PromptAgent(kPrivateInstancier);
23
- }
24
-
25
- constructor(instancier) {
26
- if (instancier !== kPrivateInstancier) {
27
- throw new Error("Cannot instanciate PromptAgent, use PromptAgent.agent() instead");
28
- }
29
- }
30
-
31
- /**
32
- * Programmatically set the next answer for any prompt (`question()`, `confirm()`, `select()`)
33
- *
34
- * This is useful for testing.
35
- *
36
- * @param {string | boolean | Array<string | boolean>} value
37
- *
38
- * @example
39
- * ```js
40
- * const promptAgent = PromptAgent.agent();
41
- * promptAgent.nextAnswer("toto");
42
- *
43
- * const input = await question("what is your name?");
44
- * assert.equal(input, "toto");
45
- * ```
46
- */
47
- nextAnswer(value) {
48
- if (Array.isArray(value)) {
49
- this.nextAnswers.push(...value);
50
-
51
- return;
52
- }
53
-
54
- this.nextAnswers.push(value);
55
- }
56
- }
@@ -1,68 +0,0 @@
1
- // Import Node.js Dependencies
2
- import { EOL } from "node:os";
3
- import { createInterface } from "node:readline";
4
- import { Writable } from "node:stream";
5
-
6
- // Import Internal Dependencies
7
- import { stripAnsi } from "../utils.js";
8
- import { PromptAgent } from "../prompt-agent.js";
9
-
10
- export class AbstractPrompt {
11
- constructor(message, input = process.stdin, output = process.stdout) {
12
- if (this.constructor === AbstractPrompt) {
13
- throw new Error("AbstractPrompt can't be instantiated.");
14
- }
15
-
16
- if (typeof message !== "string") {
17
- throw new TypeError(`message must be string, ${typeof message} given.`);
18
- }
19
-
20
- this.stdin = input;
21
- this.stdout = output;
22
- this.message = message;
23
- this.history = [];
24
- this.agent = PromptAgent.agent();
25
- this.mute = false;
26
-
27
- if (this.stdout.isTTY) {
28
- this.stdin.setRawMode(true);
29
- }
30
- this.rl = createInterface({
31
- input,
32
- output: new Writable({
33
- write: (chunk, encoding, callback) => {
34
- if (!this.mute) {
35
- this.stdout.write(chunk, encoding);
36
- }
37
- callback();
38
- }
39
- }),
40
- terminal: true
41
- });
42
- }
43
-
44
- write(data) {
45
- const formattedData = stripAnsi(data).replace(EOL, "");
46
- if (formattedData) {
47
- this.history.push(formattedData);
48
- }
49
-
50
- return this.stdout.write(data);
51
- }
52
-
53
- clearLastLine() {
54
- const lastLine = this.history.pop();
55
- if (!lastLine) {
56
- return;
57
- }
58
-
59
- const lastLineRows = Math.ceil(stripAnsi(lastLine).length / this.stdout.columns);
60
-
61
- this.stdout.moveCursor(-this.stdout.columns, -lastLineRows);
62
- this.stdout.clearScreenDown();
63
- }
64
-
65
- destroy() {
66
- this.rl.close();
67
- }
68
- }
@@ -1,135 +0,0 @@
1
- // Import Node.js Dependencies
2
- import { EOL } from "node:os";
3
-
4
- // Import Third-party Dependencies
5
- import kleur from "kleur";
6
- import wcwidth from "@topcli/wcwidth";
7
-
8
- // Import Internal Dependencies
9
- import { AbstractPrompt } from "./abstract.js";
10
- import { stripAnsi } from "../utils.js";
11
- import { SYMBOLS } from "../constants.js";
12
-
13
- // CONSTANTS
14
- const kToggleKeys = new Set([
15
- "left",
16
- "right",
17
- "tab",
18
- "q",
19
- "a",
20
- "d",
21
- "h",
22
- "j",
23
- "k",
24
- "l",
25
- "space"
26
- ]);
27
-
28
- export class ConfirmPrompt extends AbstractPrompt {
29
- #boundKeyPressEvent;
30
- #boundExitEvent;
31
-
32
- constructor(message, options = {}) {
33
- const {
34
- stdin = process.stdin,
35
- stdout = process.stdout,
36
- initial = false
37
- } = options;
38
- super(message, stdin, stdout);
39
-
40
- this.initial = initial;
41
- this.selectedValue = initial;
42
- }
43
-
44
- #getHint() {
45
- const Yes = kleur.bold().underline().cyan("Yes");
46
- const No = kleur.bold().underline().cyan("No");
47
-
48
- return this.selectedValue ? `${Yes}/No` : `Yes/${No}`;
49
- }
50
-
51
- #render() {
52
- this.write(this.#getQuestionQuery());
53
- }
54
-
55
- #question() {
56
- return new Promise((resolve) => {
57
- const questionQuery = this.#getQuestionQuery();
58
-
59
- this.write(questionQuery);
60
-
61
- this.#boundKeyPressEvent = this.#onKeypress.bind(this, resolve);
62
- this.stdin.on("keypress", this.#boundKeyPressEvent);
63
-
64
- this.#boundExitEvent = this.#onProcessExit.bind(this);
65
- process.once("exit", this.#boundExitEvent);
66
- });
67
- }
68
-
69
- #onKeypress(resolve, value, key) {
70
- this.stdout.moveCursor(
71
- -this.stdout.columns,
72
- -Math.floor(wcwidth(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns)
73
- );
74
- this.stdout.clearScreenDown();
75
-
76
- if (key.name === "return") {
77
- resolve(this.selectedValue);
78
-
79
- return;
80
- }
81
-
82
- if (kToggleKeys.has(key.name)) {
83
- this.selectedValue = !this.selectedValue;
84
- }
85
-
86
- this.#render();
87
- }
88
-
89
- #onProcessExit() {
90
- this.stdin.off("keypress", this.#boundKeyPressEvent);
91
- }
92
-
93
- #getQuestionQuery() {
94
- const query = kleur.bold(`${SYMBOLS.QuestionMark} ${this.message}`);
95
-
96
- return `${query} ${this.#getHint()}`;
97
- }
98
-
99
- #onQuestionAnswer() {
100
- this.stdout.moveCursor(
101
- -this.stdout.columns,
102
- -(Math.floor(wcwidth(stripAnsi(this.#getQuestionQuery())) / this.stdout.columns) || 1)
103
- );
104
- this.stdout.clearScreenDown();
105
- this.write(`${this.selectedValue ? SYMBOLS.Tick : SYMBOLS.Cross} ${kleur.bold(this.message)}${EOL}`);
106
- }
107
-
108
- async confirm() {
109
- if (this.agent.nextAnswers.length > 0) {
110
- const answer = this.agent.nextAnswers.shift();
111
- this.selectedValue = answer;
112
- this.#onQuestionAnswer();
113
- this.destroy();
114
-
115
- return answer;
116
- }
117
-
118
- this.write(SYMBOLS.HideCursor);
119
-
120
- try {
121
- await this.#question();
122
- this.#onQuestionAnswer();
123
-
124
- return this.selectedValue;
125
- }
126
- finally {
127
- this.write(SYMBOLS.ShowCursor);
128
-
129
- this.#onProcessExit();
130
- process.off("exit", this.#boundExitEvent);
131
-
132
- this.destroy();
133
- }
134
- }
135
- }
@@ -1,12 +0,0 @@
1
- // Import Internal Dependencies
2
- import { QuestionPrompt } from "./question.js";
3
- import { ConfirmPrompt } from "./confirm.js";
4
- import { SelectPrompt } from "./select.js";
5
- import { MultiselectPrompt } from "./multiselect.js";
6
-
7
- export {
8
- QuestionPrompt,
9
- ConfirmPrompt,
10
- SelectPrompt,
11
- MultiselectPrompt
12
- };