@optique/inquirer 1.0.0-dev.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 +20 -0
- package/dist/index.cjs +272 -0
- package/dist/index.d.cts +354 -0
- package/dist/index.d.ts +354 -0
- package/dist/index.js +248 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright 2025–2026 Hong Minhee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
const __inquirer_prompts = __toESM(require("@inquirer/prompts"));
|
|
25
|
+
const __optique_core_annotations = __toESM(require("@optique/core/annotations"));
|
|
26
|
+
const __optique_core_message = __toESM(require("@optique/core/message"));
|
|
27
|
+
|
|
28
|
+
//#region src/index.ts
|
|
29
|
+
/**
|
|
30
|
+
* Wraps a parser with an interactive Inquirer.js prompt fallback.
|
|
31
|
+
*
|
|
32
|
+
* When the inner parser finds a value in the CLI arguments (consumed tokens),
|
|
33
|
+
* that value is used directly. When no CLI value is found, an interactive
|
|
34
|
+
* prompt is shown to the user.
|
|
35
|
+
*
|
|
36
|
+
* The returned parser always has `$mode: "async"` because Inquirer.js prompts
|
|
37
|
+
* are inherently asynchronous.
|
|
38
|
+
*
|
|
39
|
+
* Example:
|
|
40
|
+
*
|
|
41
|
+
* ```typescript
|
|
42
|
+
* import { option } from "@optique/core/primitives";
|
|
43
|
+
* import { string } from "@optique/core/valueparser";
|
|
44
|
+
* import { prompt } from "@optique/inquirer";
|
|
45
|
+
*
|
|
46
|
+
* const nameParser = prompt(option("--name", string()), {
|
|
47
|
+
* type: "input",
|
|
48
|
+
* message: "Enter your name:",
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @param parser Inner parser that reads CLI values.
|
|
53
|
+
* @param config Type-safe Inquirer.js prompt configuration.
|
|
54
|
+
* @returns A parser with interactive prompt fallback, always in async mode.
|
|
55
|
+
* @since 1.0.0
|
|
56
|
+
*/
|
|
57
|
+
function prompt(parser, config) {
|
|
58
|
+
const promptBindStateKey = Symbol("@optique/inquirer/promptState");
|
|
59
|
+
function isPromptBindState(value) {
|
|
60
|
+
return value != null && typeof value === "object" && promptBindStateKey in value;
|
|
61
|
+
}
|
|
62
|
+
const cfg = config;
|
|
63
|
+
const PromptBindInitialStateClass = class {
|
|
64
|
+
[promptBindStateKey] = true;
|
|
65
|
+
hasCliValue = false;
|
|
66
|
+
};
|
|
67
|
+
const promptBindInitialState = new PromptBindInitialStateClass();
|
|
68
|
+
let promptCache = null;
|
|
69
|
+
async function executePrompt() {
|
|
70
|
+
if ("prompter" in cfg && cfg.prompter != null) {
|
|
71
|
+
const value = await cfg.prompter();
|
|
72
|
+
if (cfg.type === "number" && value === void 0) return {
|
|
73
|
+
success: false,
|
|
74
|
+
error: __optique_core_message.message`No number provided.`
|
|
75
|
+
};
|
|
76
|
+
return {
|
|
77
|
+
success: true,
|
|
78
|
+
value
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
switch (cfg.type) {
|
|
82
|
+
case "confirm": return {
|
|
83
|
+
success: true,
|
|
84
|
+
value: await (0, __inquirer_prompts.confirm)({
|
|
85
|
+
message: cfg.message,
|
|
86
|
+
...cfg.default !== void 0 ? { default: cfg.default } : {}
|
|
87
|
+
})
|
|
88
|
+
};
|
|
89
|
+
case "number": {
|
|
90
|
+
const numResult = await (0, __inquirer_prompts.number)({
|
|
91
|
+
message: cfg.message,
|
|
92
|
+
...cfg.default !== void 0 ? { default: cfg.default } : {},
|
|
93
|
+
...cfg.min !== void 0 ? { min: cfg.min } : {},
|
|
94
|
+
...cfg.max !== void 0 ? { max: cfg.max } : {},
|
|
95
|
+
...cfg.step !== void 0 ? { step: cfg.step } : {}
|
|
96
|
+
});
|
|
97
|
+
if (numResult === void 0) return {
|
|
98
|
+
success: false,
|
|
99
|
+
error: __optique_core_message.message`No number provided.`
|
|
100
|
+
};
|
|
101
|
+
return {
|
|
102
|
+
success: true,
|
|
103
|
+
value: numResult
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
case "input": return {
|
|
107
|
+
success: true,
|
|
108
|
+
value: await (0, __inquirer_prompts.input)({
|
|
109
|
+
message: cfg.message,
|
|
110
|
+
...cfg.default !== void 0 ? { default: cfg.default } : {},
|
|
111
|
+
...cfg.validate !== void 0 ? { validate: cfg.validate } : {}
|
|
112
|
+
})
|
|
113
|
+
};
|
|
114
|
+
case "password": return {
|
|
115
|
+
success: true,
|
|
116
|
+
value: await (0, __inquirer_prompts.password)({
|
|
117
|
+
message: cfg.message,
|
|
118
|
+
...cfg.mask !== void 0 ? { mask: cfg.mask } : {},
|
|
119
|
+
...cfg.validate !== void 0 ? { validate: cfg.validate } : {}
|
|
120
|
+
})
|
|
121
|
+
};
|
|
122
|
+
case "editor": return {
|
|
123
|
+
success: true,
|
|
124
|
+
value: await (0, __inquirer_prompts.editor)({
|
|
125
|
+
message: cfg.message,
|
|
126
|
+
...cfg.default !== void 0 ? { default: cfg.default } : {},
|
|
127
|
+
...cfg.validate !== void 0 ? { validate: cfg.validate } : {}
|
|
128
|
+
})
|
|
129
|
+
};
|
|
130
|
+
case "select": return {
|
|
131
|
+
success: true,
|
|
132
|
+
value: await (0, __inquirer_prompts.select)({
|
|
133
|
+
message: cfg.message,
|
|
134
|
+
choices: normalizeChoices(cfg.choices),
|
|
135
|
+
...cfg.default !== void 0 ? { default: cfg.default } : {}
|
|
136
|
+
})
|
|
137
|
+
};
|
|
138
|
+
case "rawlist": return {
|
|
139
|
+
success: true,
|
|
140
|
+
value: await (0, __inquirer_prompts.rawlist)({
|
|
141
|
+
message: cfg.message,
|
|
142
|
+
choices: normalizeChoices(cfg.choices),
|
|
143
|
+
...cfg.default !== void 0 ? { default: cfg.default } : {}
|
|
144
|
+
})
|
|
145
|
+
};
|
|
146
|
+
case "expand": return {
|
|
147
|
+
success: true,
|
|
148
|
+
value: await (0, __inquirer_prompts.expand)({
|
|
149
|
+
message: cfg.message,
|
|
150
|
+
choices: cfg.choices,
|
|
151
|
+
...cfg.default !== void 0 ? { default: cfg.default } : {}
|
|
152
|
+
})
|
|
153
|
+
};
|
|
154
|
+
case "checkbox": return {
|
|
155
|
+
success: true,
|
|
156
|
+
value: await (0, __inquirer_prompts.checkbox)({
|
|
157
|
+
message: cfg.message,
|
|
158
|
+
choices: normalizeChoices(cfg.choices)
|
|
159
|
+
})
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
$mode: "async",
|
|
165
|
+
$valueType: parser.$valueType,
|
|
166
|
+
$stateType: parser.$stateType,
|
|
167
|
+
priority: parser.priority,
|
|
168
|
+
usage: [{
|
|
169
|
+
type: "optional",
|
|
170
|
+
terms: parser.usage
|
|
171
|
+
}],
|
|
172
|
+
initialState: promptBindInitialState,
|
|
173
|
+
parse: (context) => {
|
|
174
|
+
const annotations = (0, __optique_core_annotations.getAnnotations)(context.state);
|
|
175
|
+
const innerState = isPromptBindState(context.state) ? context.state.hasCliValue ? context.state.cliState : parser.initialState : context.state;
|
|
176
|
+
const innerContext = innerState !== context.state ? {
|
|
177
|
+
...context,
|
|
178
|
+
state: innerState
|
|
179
|
+
} : context;
|
|
180
|
+
const processResult = (result$1) => {
|
|
181
|
+
if (result$1.success) {
|
|
182
|
+
const cliConsumed = result$1.consumed.length > 0;
|
|
183
|
+
const nextState$1 = {
|
|
184
|
+
[promptBindStateKey]: true,
|
|
185
|
+
hasCliValue: cliConsumed,
|
|
186
|
+
cliState: result$1.next.state,
|
|
187
|
+
...annotations != null ? { [__optique_core_annotations.annotationKey]: annotations } : {}
|
|
188
|
+
};
|
|
189
|
+
return {
|
|
190
|
+
success: true,
|
|
191
|
+
next: {
|
|
192
|
+
...result$1.next,
|
|
193
|
+
state: nextState$1
|
|
194
|
+
},
|
|
195
|
+
consumed: result$1.consumed
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
if (result$1.consumed > 0) return result$1;
|
|
199
|
+
const nextState = {
|
|
200
|
+
[promptBindStateKey]: true,
|
|
201
|
+
hasCliValue: false,
|
|
202
|
+
...annotations != null ? { [__optique_core_annotations.annotationKey]: annotations } : {}
|
|
203
|
+
};
|
|
204
|
+
return {
|
|
205
|
+
success: true,
|
|
206
|
+
next: {
|
|
207
|
+
...innerContext,
|
|
208
|
+
state: nextState
|
|
209
|
+
},
|
|
210
|
+
consumed: []
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
const result = parser.parse(innerContext);
|
|
214
|
+
if (result instanceof Promise) return result.then(processResult);
|
|
215
|
+
return Promise.resolve(processResult(result));
|
|
216
|
+
},
|
|
217
|
+
complete: (state) => {
|
|
218
|
+
if (isPromptBindState(state) && state.hasCliValue) {
|
|
219
|
+
const r = parser.complete(state.cliState);
|
|
220
|
+
if (r instanceof Promise) return r;
|
|
221
|
+
return Promise.resolve(r);
|
|
222
|
+
}
|
|
223
|
+
if (state === promptBindInitialState) {
|
|
224
|
+
if (promptCache !== null) {
|
|
225
|
+
const cached = promptCache;
|
|
226
|
+
promptCache = null;
|
|
227
|
+
return cached;
|
|
228
|
+
}
|
|
229
|
+
promptCache = executePrompt();
|
|
230
|
+
return promptCache;
|
|
231
|
+
}
|
|
232
|
+
return executePrompt();
|
|
233
|
+
},
|
|
234
|
+
suggest: (context, prefix) => {
|
|
235
|
+
const innerState = isPromptBindState(context.state) ? context.state.hasCliValue ? context.state.cliState : parser.initialState : context.state;
|
|
236
|
+
const innerContext = innerState !== context.state ? {
|
|
237
|
+
...context,
|
|
238
|
+
state: innerState
|
|
239
|
+
} : context;
|
|
240
|
+
const innerResult = parser.suggest(innerContext, prefix);
|
|
241
|
+
return async function* () {
|
|
242
|
+
yield* innerResult;
|
|
243
|
+
}();
|
|
244
|
+
},
|
|
245
|
+
getDocFragments(state, upperDefaultValue) {
|
|
246
|
+
const configDefault = "default" in cfg ? cfg.default : void 0;
|
|
247
|
+
const defaultValue = upperDefaultValue ?? configDefault;
|
|
248
|
+
return parser.getDocFragments(state, defaultValue);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
/** Normalize choices to the format Inquirer.js expects. */
|
|
253
|
+
function normalizeChoices(choices) {
|
|
254
|
+
return choices.map((c) => {
|
|
255
|
+
if (typeof c === "string") return {
|
|
256
|
+
value: c,
|
|
257
|
+
name: c
|
|
258
|
+
};
|
|
259
|
+
if (c instanceof __inquirer_prompts.Separator) return c;
|
|
260
|
+
return {
|
|
261
|
+
value: c.value,
|
|
262
|
+
...c.name !== void 0 ? { name: c.name } : {},
|
|
263
|
+
..."description" in c && c.description !== void 0 ? { description: c.description } : {},
|
|
264
|
+
..."short" in c && c.short !== void 0 ? { short: c.short } : {},
|
|
265
|
+
..."disabled" in c && c.disabled !== void 0 ? { disabled: c.disabled } : {}
|
|
266
|
+
};
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
//#endregion
|
|
271
|
+
exports.Separator = __inquirer_prompts.Separator;
|
|
272
|
+
exports.prompt = prompt;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { Separator } from "@inquirer/prompts";
|
|
2
|
+
import { Mode, Parser } from "@optique/core/parser";
|
|
3
|
+
|
|
4
|
+
//#region src/index.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A choice item for selection-type prompts (`select`, `rawlist`, `expand`,
|
|
8
|
+
* `checkbox`).
|
|
9
|
+
*
|
|
10
|
+
* @since 1.0.0
|
|
11
|
+
*/
|
|
12
|
+
interface Choice {
|
|
13
|
+
/**
|
|
14
|
+
* The value returned when this choice is selected.
|
|
15
|
+
*/
|
|
16
|
+
readonly value: string;
|
|
17
|
+
/**
|
|
18
|
+
* Display name shown in the prompt. Defaults to `value`.
|
|
19
|
+
*/
|
|
20
|
+
readonly name?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Additional description shown when this choice is highlighted.
|
|
23
|
+
*/
|
|
24
|
+
readonly description?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Short text shown after selection. Defaults to `name`.
|
|
27
|
+
*/
|
|
28
|
+
readonly short?: string;
|
|
29
|
+
/**
|
|
30
|
+
* If truthy, the choice cannot be selected. A string explains why.
|
|
31
|
+
*/
|
|
32
|
+
readonly disabled?: boolean | string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A choice item for the `expand` prompt type.
|
|
36
|
+
*
|
|
37
|
+
* Unlike {@link Choice}, this requires a `key` field (a single lowercase
|
|
38
|
+
* alphanumeric character) that the user presses to select the item.
|
|
39
|
+
*
|
|
40
|
+
* @since 1.0.0
|
|
41
|
+
*/
|
|
42
|
+
interface ExpandChoice {
|
|
43
|
+
/**
|
|
44
|
+
* The value returned when this choice is selected.
|
|
45
|
+
*/
|
|
46
|
+
readonly value: string;
|
|
47
|
+
/**
|
|
48
|
+
* Display name shown in the prompt. Defaults to `value`.
|
|
49
|
+
*/
|
|
50
|
+
readonly name?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Single lowercase alphanumeric character used as the keyboard shortcut.
|
|
53
|
+
*/
|
|
54
|
+
readonly key: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Configuration for a `confirm` prompt (boolean value).
|
|
58
|
+
*
|
|
59
|
+
* @since 1.0.0
|
|
60
|
+
*/
|
|
61
|
+
interface ConfirmConfig {
|
|
62
|
+
readonly type: "confirm";
|
|
63
|
+
/**
|
|
64
|
+
* The question to display to the user.
|
|
65
|
+
*/
|
|
66
|
+
readonly message: string;
|
|
67
|
+
/**
|
|
68
|
+
* Default answer when the user just presses Enter.
|
|
69
|
+
*/
|
|
70
|
+
readonly default?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Override the prompt execution. When provided, this function is called
|
|
73
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
74
|
+
* testing.
|
|
75
|
+
*/
|
|
76
|
+
readonly prompter?: () => Promise<boolean>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Configuration for a `number` prompt.
|
|
80
|
+
*
|
|
81
|
+
* @since 1.0.0
|
|
82
|
+
*/
|
|
83
|
+
interface NumberPromptConfig {
|
|
84
|
+
readonly type: "number";
|
|
85
|
+
/**
|
|
86
|
+
* The question to display to the user.
|
|
87
|
+
*/
|
|
88
|
+
readonly message: string;
|
|
89
|
+
/**
|
|
90
|
+
* Default number shown to the user.
|
|
91
|
+
*/
|
|
92
|
+
readonly default?: number;
|
|
93
|
+
/**
|
|
94
|
+
* Minimum accepted value.
|
|
95
|
+
*/
|
|
96
|
+
readonly min?: number;
|
|
97
|
+
/**
|
|
98
|
+
* Maximum accepted value.
|
|
99
|
+
*/
|
|
100
|
+
readonly max?: number;
|
|
101
|
+
/**
|
|
102
|
+
* Granularity of valid values. Use `"any"` for arbitrary decimals.
|
|
103
|
+
*/
|
|
104
|
+
readonly step?: number | "any";
|
|
105
|
+
/**
|
|
106
|
+
* Override the prompt execution. When provided, this function is called
|
|
107
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
108
|
+
* testing.
|
|
109
|
+
*
|
|
110
|
+
* Return `undefined` to simulate the user leaving the field empty (which
|
|
111
|
+
* results in a parse failure).
|
|
112
|
+
*/
|
|
113
|
+
readonly prompter?: () => Promise<number | undefined>;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Configuration for an `input` prompt (free-text string).
|
|
117
|
+
*
|
|
118
|
+
* @since 1.0.0
|
|
119
|
+
*/
|
|
120
|
+
interface InputConfig {
|
|
121
|
+
readonly type: "input";
|
|
122
|
+
/**
|
|
123
|
+
* The question to display to the user.
|
|
124
|
+
*/
|
|
125
|
+
readonly message: string;
|
|
126
|
+
/**
|
|
127
|
+
* Default text pre-filled in the prompt.
|
|
128
|
+
*/
|
|
129
|
+
readonly default?: string;
|
|
130
|
+
/**
|
|
131
|
+
* Validation function called when the user submits.
|
|
132
|
+
* Return `true` or a string error message.
|
|
133
|
+
*/
|
|
134
|
+
readonly validate?: (value: string) => boolean | string | Promise<boolean | string>;
|
|
135
|
+
/**
|
|
136
|
+
* Override the prompt execution. When provided, this function is called
|
|
137
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
138
|
+
* testing.
|
|
139
|
+
*/
|
|
140
|
+
readonly prompter?: () => Promise<string>;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Configuration for a `password` prompt (masked input).
|
|
144
|
+
*
|
|
145
|
+
* @since 1.0.0
|
|
146
|
+
*/
|
|
147
|
+
interface PasswordConfig {
|
|
148
|
+
readonly type: "password";
|
|
149
|
+
/**
|
|
150
|
+
* The question to display to the user.
|
|
151
|
+
*/
|
|
152
|
+
readonly message: string;
|
|
153
|
+
/**
|
|
154
|
+
* If `true`, show `*` characters for each keystroke.
|
|
155
|
+
* If `false` or omitted, input is invisible.
|
|
156
|
+
*/
|
|
157
|
+
readonly mask?: boolean;
|
|
158
|
+
/**
|
|
159
|
+
* Validation function called when the user submits.
|
|
160
|
+
* Return `true` or a string error message.
|
|
161
|
+
*/
|
|
162
|
+
readonly validate?: (value: string) => boolean | string | Promise<boolean | string>;
|
|
163
|
+
/**
|
|
164
|
+
* Override the prompt execution. When provided, this function is called
|
|
165
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
166
|
+
* testing.
|
|
167
|
+
*/
|
|
168
|
+
readonly prompter?: () => Promise<string>;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Configuration for an `editor` prompt (external editor).
|
|
172
|
+
*
|
|
173
|
+
* Opens the user's `$VISUAL` or `$EDITOR` for multi-line text input.
|
|
174
|
+
*
|
|
175
|
+
* @since 1.0.0
|
|
176
|
+
*/
|
|
177
|
+
interface EditorConfig {
|
|
178
|
+
readonly type: "editor";
|
|
179
|
+
/**
|
|
180
|
+
* The question to display to the user.
|
|
181
|
+
*/
|
|
182
|
+
readonly message: string;
|
|
183
|
+
/**
|
|
184
|
+
* Default content pre-filled in the editor.
|
|
185
|
+
*/
|
|
186
|
+
readonly default?: string;
|
|
187
|
+
/**
|
|
188
|
+
* Validation function called when the editor is closed.
|
|
189
|
+
* Return `true` or a string error message.
|
|
190
|
+
*/
|
|
191
|
+
readonly validate?: (value: string) => boolean | string | Promise<boolean | string>;
|
|
192
|
+
/**
|
|
193
|
+
* Override the prompt execution. When provided, this function is called
|
|
194
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
195
|
+
* testing.
|
|
196
|
+
*/
|
|
197
|
+
readonly prompter?: () => Promise<string>;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Configuration for a `select` prompt (arrow-key single-select).
|
|
201
|
+
*
|
|
202
|
+
* @since 1.0.0
|
|
203
|
+
*/
|
|
204
|
+
interface SelectConfig {
|
|
205
|
+
readonly type: "select";
|
|
206
|
+
/**
|
|
207
|
+
* The question to display to the user.
|
|
208
|
+
*/
|
|
209
|
+
readonly message: string;
|
|
210
|
+
/**
|
|
211
|
+
* Available choices. Plain strings and {@link Choice} objects can be mixed.
|
|
212
|
+
* Use `new Separator(...)` for visual dividers.
|
|
213
|
+
*/
|
|
214
|
+
readonly choices: readonly (string | Choice | Separator)[];
|
|
215
|
+
/**
|
|
216
|
+
* Initially highlighted choice value.
|
|
217
|
+
*/
|
|
218
|
+
readonly default?: string;
|
|
219
|
+
/**
|
|
220
|
+
* Override the prompt execution. When provided, this function is called
|
|
221
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
222
|
+
* testing.
|
|
223
|
+
*/
|
|
224
|
+
readonly prompter?: () => Promise<string>;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Configuration for a `rawlist` prompt (numbered list).
|
|
228
|
+
*
|
|
229
|
+
* @since 1.0.0
|
|
230
|
+
*/
|
|
231
|
+
interface RawlistConfig {
|
|
232
|
+
readonly type: "rawlist";
|
|
233
|
+
/**
|
|
234
|
+
* The question to display to the user.
|
|
235
|
+
*/
|
|
236
|
+
readonly message: string;
|
|
237
|
+
/**
|
|
238
|
+
* Available choices. Plain strings and {@link Choice} objects can be mixed.
|
|
239
|
+
*/
|
|
240
|
+
readonly choices: readonly (string | Choice)[];
|
|
241
|
+
/**
|
|
242
|
+
* Pre-selected choice value.
|
|
243
|
+
*/
|
|
244
|
+
readonly default?: string;
|
|
245
|
+
/**
|
|
246
|
+
* Override the prompt execution. When provided, this function is called
|
|
247
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
248
|
+
* testing.
|
|
249
|
+
*/
|
|
250
|
+
readonly prompter?: () => Promise<string>;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Configuration for an `expand` prompt (keyboard shortcut single-select).
|
|
254
|
+
*
|
|
255
|
+
* @since 1.0.0
|
|
256
|
+
*/
|
|
257
|
+
interface ExpandConfig {
|
|
258
|
+
readonly type: "expand";
|
|
259
|
+
/**
|
|
260
|
+
* The question to display to the user.
|
|
261
|
+
*/
|
|
262
|
+
readonly message: string;
|
|
263
|
+
/**
|
|
264
|
+
* Available choices. Each choice requires a `key` field.
|
|
265
|
+
*/
|
|
266
|
+
readonly choices: readonly ExpandChoice[];
|
|
267
|
+
/**
|
|
268
|
+
* Default choice key.
|
|
269
|
+
*/
|
|
270
|
+
readonly default?: string;
|
|
271
|
+
/**
|
|
272
|
+
* Override the prompt execution. When provided, this function is called
|
|
273
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
274
|
+
* testing.
|
|
275
|
+
*/
|
|
276
|
+
readonly prompter?: () => Promise<string>;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Configuration for a `checkbox` prompt (multi-select).
|
|
280
|
+
*
|
|
281
|
+
* Use with parsers that return `string[]`, typically via
|
|
282
|
+
* `multiple(option(...))`.
|
|
283
|
+
*
|
|
284
|
+
* @since 1.0.0
|
|
285
|
+
*/
|
|
286
|
+
interface CheckboxConfig {
|
|
287
|
+
readonly type: "checkbox";
|
|
288
|
+
/**
|
|
289
|
+
* The question to display to the user.
|
|
290
|
+
*/
|
|
291
|
+
readonly message: string;
|
|
292
|
+
/**
|
|
293
|
+
* Available choices. Plain strings and {@link Choice} objects can be mixed.
|
|
294
|
+
* Use `new Separator(...)` for visual dividers.
|
|
295
|
+
*/
|
|
296
|
+
readonly choices: readonly (string | Choice | Separator)[];
|
|
297
|
+
/**
|
|
298
|
+
* Override the prompt execution. When provided, this function is called
|
|
299
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
300
|
+
* testing.
|
|
301
|
+
*/
|
|
302
|
+
readonly prompter?: () => Promise<readonly string[]>;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* A union of all string-input prompt configurations.
|
|
306
|
+
*
|
|
307
|
+
* @since 1.0.0
|
|
308
|
+
*/
|
|
309
|
+
type StringPromptConfig = InputConfig | PasswordConfig | EditorConfig | SelectConfig | RawlistConfig | ExpandConfig;
|
|
310
|
+
/**
|
|
311
|
+
* Type-safe prompt configuration for a given parser value type `T`.
|
|
312
|
+
*
|
|
313
|
+
* The available prompt types are constrained by the expected value type:
|
|
314
|
+
*
|
|
315
|
+
* - `boolean` or `boolean | undefined` → {@link ConfirmConfig}
|
|
316
|
+
* - `number` or `number | undefined` → {@link NumberPromptConfig}
|
|
317
|
+
* - `string` or `string | undefined` → {@link StringPromptConfig}
|
|
318
|
+
* - `readonly string[]` → {@link CheckboxConfig}
|
|
319
|
+
*
|
|
320
|
+
* @since 1.0.0
|
|
321
|
+
*/
|
|
322
|
+
type PromptConfig<T> = BasePromptConfig<Exclude<T, null | undefined>>;
|
|
323
|
+
type BasePromptConfig<T> = T extends boolean ? ConfirmConfig : T extends number ? NumberPromptConfig : T extends string ? StringPromptConfig : T extends readonly string[] ? CheckboxConfig : never;
|
|
324
|
+
/**
|
|
325
|
+
* Wraps a parser with an interactive Inquirer.js prompt fallback.
|
|
326
|
+
*
|
|
327
|
+
* When the inner parser finds a value in the CLI arguments (consumed tokens),
|
|
328
|
+
* that value is used directly. When no CLI value is found, an interactive
|
|
329
|
+
* prompt is shown to the user.
|
|
330
|
+
*
|
|
331
|
+
* The returned parser always has `$mode: "async"` because Inquirer.js prompts
|
|
332
|
+
* are inherently asynchronous.
|
|
333
|
+
*
|
|
334
|
+
* Example:
|
|
335
|
+
*
|
|
336
|
+
* ```typescript
|
|
337
|
+
* import { option } from "@optique/core/primitives";
|
|
338
|
+
* import { string } from "@optique/core/valueparser";
|
|
339
|
+
* import { prompt } from "@optique/inquirer";
|
|
340
|
+
*
|
|
341
|
+
* const nameParser = prompt(option("--name", string()), {
|
|
342
|
+
* type: "input",
|
|
343
|
+
* message: "Enter your name:",
|
|
344
|
+
* });
|
|
345
|
+
* ```
|
|
346
|
+
*
|
|
347
|
+
* @param parser Inner parser that reads CLI values.
|
|
348
|
+
* @param config Type-safe Inquirer.js prompt configuration.
|
|
349
|
+
* @returns A parser with interactive prompt fallback, always in async mode.
|
|
350
|
+
* @since 1.0.0
|
|
351
|
+
*/
|
|
352
|
+
declare function prompt<M extends Mode, TValue, TState>(parser: Parser<M, TValue, TState>, config: PromptConfig<TValue>): Parser<"async", TValue, TState>;
|
|
353
|
+
//#endregion
|
|
354
|
+
export { CheckboxConfig, Choice, ConfirmConfig, EditorConfig, ExpandChoice, ExpandConfig, InputConfig, NumberPromptConfig, PasswordConfig, PromptConfig, RawlistConfig, SelectConfig, Separator, StringPromptConfig, prompt };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { Separator } from "@inquirer/prompts";
|
|
2
|
+
import { Mode, Parser } from "@optique/core/parser";
|
|
3
|
+
|
|
4
|
+
//#region src/index.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A choice item for selection-type prompts (`select`, `rawlist`, `expand`,
|
|
8
|
+
* `checkbox`).
|
|
9
|
+
*
|
|
10
|
+
* @since 1.0.0
|
|
11
|
+
*/
|
|
12
|
+
interface Choice {
|
|
13
|
+
/**
|
|
14
|
+
* The value returned when this choice is selected.
|
|
15
|
+
*/
|
|
16
|
+
readonly value: string;
|
|
17
|
+
/**
|
|
18
|
+
* Display name shown in the prompt. Defaults to `value`.
|
|
19
|
+
*/
|
|
20
|
+
readonly name?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Additional description shown when this choice is highlighted.
|
|
23
|
+
*/
|
|
24
|
+
readonly description?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Short text shown after selection. Defaults to `name`.
|
|
27
|
+
*/
|
|
28
|
+
readonly short?: string;
|
|
29
|
+
/**
|
|
30
|
+
* If truthy, the choice cannot be selected. A string explains why.
|
|
31
|
+
*/
|
|
32
|
+
readonly disabled?: boolean | string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A choice item for the `expand` prompt type.
|
|
36
|
+
*
|
|
37
|
+
* Unlike {@link Choice}, this requires a `key` field (a single lowercase
|
|
38
|
+
* alphanumeric character) that the user presses to select the item.
|
|
39
|
+
*
|
|
40
|
+
* @since 1.0.0
|
|
41
|
+
*/
|
|
42
|
+
interface ExpandChoice {
|
|
43
|
+
/**
|
|
44
|
+
* The value returned when this choice is selected.
|
|
45
|
+
*/
|
|
46
|
+
readonly value: string;
|
|
47
|
+
/**
|
|
48
|
+
* Display name shown in the prompt. Defaults to `value`.
|
|
49
|
+
*/
|
|
50
|
+
readonly name?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Single lowercase alphanumeric character used as the keyboard shortcut.
|
|
53
|
+
*/
|
|
54
|
+
readonly key: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Configuration for a `confirm` prompt (boolean value).
|
|
58
|
+
*
|
|
59
|
+
* @since 1.0.0
|
|
60
|
+
*/
|
|
61
|
+
interface ConfirmConfig {
|
|
62
|
+
readonly type: "confirm";
|
|
63
|
+
/**
|
|
64
|
+
* The question to display to the user.
|
|
65
|
+
*/
|
|
66
|
+
readonly message: string;
|
|
67
|
+
/**
|
|
68
|
+
* Default answer when the user just presses Enter.
|
|
69
|
+
*/
|
|
70
|
+
readonly default?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Override the prompt execution. When provided, this function is called
|
|
73
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
74
|
+
* testing.
|
|
75
|
+
*/
|
|
76
|
+
readonly prompter?: () => Promise<boolean>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Configuration for a `number` prompt.
|
|
80
|
+
*
|
|
81
|
+
* @since 1.0.0
|
|
82
|
+
*/
|
|
83
|
+
interface NumberPromptConfig {
|
|
84
|
+
readonly type: "number";
|
|
85
|
+
/**
|
|
86
|
+
* The question to display to the user.
|
|
87
|
+
*/
|
|
88
|
+
readonly message: string;
|
|
89
|
+
/**
|
|
90
|
+
* Default number shown to the user.
|
|
91
|
+
*/
|
|
92
|
+
readonly default?: number;
|
|
93
|
+
/**
|
|
94
|
+
* Minimum accepted value.
|
|
95
|
+
*/
|
|
96
|
+
readonly min?: number;
|
|
97
|
+
/**
|
|
98
|
+
* Maximum accepted value.
|
|
99
|
+
*/
|
|
100
|
+
readonly max?: number;
|
|
101
|
+
/**
|
|
102
|
+
* Granularity of valid values. Use `"any"` for arbitrary decimals.
|
|
103
|
+
*/
|
|
104
|
+
readonly step?: number | "any";
|
|
105
|
+
/**
|
|
106
|
+
* Override the prompt execution. When provided, this function is called
|
|
107
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
108
|
+
* testing.
|
|
109
|
+
*
|
|
110
|
+
* Return `undefined` to simulate the user leaving the field empty (which
|
|
111
|
+
* results in a parse failure).
|
|
112
|
+
*/
|
|
113
|
+
readonly prompter?: () => Promise<number | undefined>;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Configuration for an `input` prompt (free-text string).
|
|
117
|
+
*
|
|
118
|
+
* @since 1.0.0
|
|
119
|
+
*/
|
|
120
|
+
interface InputConfig {
|
|
121
|
+
readonly type: "input";
|
|
122
|
+
/**
|
|
123
|
+
* The question to display to the user.
|
|
124
|
+
*/
|
|
125
|
+
readonly message: string;
|
|
126
|
+
/**
|
|
127
|
+
* Default text pre-filled in the prompt.
|
|
128
|
+
*/
|
|
129
|
+
readonly default?: string;
|
|
130
|
+
/**
|
|
131
|
+
* Validation function called when the user submits.
|
|
132
|
+
* Return `true` or a string error message.
|
|
133
|
+
*/
|
|
134
|
+
readonly validate?: (value: string) => boolean | string | Promise<boolean | string>;
|
|
135
|
+
/**
|
|
136
|
+
* Override the prompt execution. When provided, this function is called
|
|
137
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
138
|
+
* testing.
|
|
139
|
+
*/
|
|
140
|
+
readonly prompter?: () => Promise<string>;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Configuration for a `password` prompt (masked input).
|
|
144
|
+
*
|
|
145
|
+
* @since 1.0.0
|
|
146
|
+
*/
|
|
147
|
+
interface PasswordConfig {
|
|
148
|
+
readonly type: "password";
|
|
149
|
+
/**
|
|
150
|
+
* The question to display to the user.
|
|
151
|
+
*/
|
|
152
|
+
readonly message: string;
|
|
153
|
+
/**
|
|
154
|
+
* If `true`, show `*` characters for each keystroke.
|
|
155
|
+
* If `false` or omitted, input is invisible.
|
|
156
|
+
*/
|
|
157
|
+
readonly mask?: boolean;
|
|
158
|
+
/**
|
|
159
|
+
* Validation function called when the user submits.
|
|
160
|
+
* Return `true` or a string error message.
|
|
161
|
+
*/
|
|
162
|
+
readonly validate?: (value: string) => boolean | string | Promise<boolean | string>;
|
|
163
|
+
/**
|
|
164
|
+
* Override the prompt execution. When provided, this function is called
|
|
165
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
166
|
+
* testing.
|
|
167
|
+
*/
|
|
168
|
+
readonly prompter?: () => Promise<string>;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Configuration for an `editor` prompt (external editor).
|
|
172
|
+
*
|
|
173
|
+
* Opens the user's `$VISUAL` or `$EDITOR` for multi-line text input.
|
|
174
|
+
*
|
|
175
|
+
* @since 1.0.0
|
|
176
|
+
*/
|
|
177
|
+
interface EditorConfig {
|
|
178
|
+
readonly type: "editor";
|
|
179
|
+
/**
|
|
180
|
+
* The question to display to the user.
|
|
181
|
+
*/
|
|
182
|
+
readonly message: string;
|
|
183
|
+
/**
|
|
184
|
+
* Default content pre-filled in the editor.
|
|
185
|
+
*/
|
|
186
|
+
readonly default?: string;
|
|
187
|
+
/**
|
|
188
|
+
* Validation function called when the editor is closed.
|
|
189
|
+
* Return `true` or a string error message.
|
|
190
|
+
*/
|
|
191
|
+
readonly validate?: (value: string) => boolean | string | Promise<boolean | string>;
|
|
192
|
+
/**
|
|
193
|
+
* Override the prompt execution. When provided, this function is called
|
|
194
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
195
|
+
* testing.
|
|
196
|
+
*/
|
|
197
|
+
readonly prompter?: () => Promise<string>;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Configuration for a `select` prompt (arrow-key single-select).
|
|
201
|
+
*
|
|
202
|
+
* @since 1.0.0
|
|
203
|
+
*/
|
|
204
|
+
interface SelectConfig {
|
|
205
|
+
readonly type: "select";
|
|
206
|
+
/**
|
|
207
|
+
* The question to display to the user.
|
|
208
|
+
*/
|
|
209
|
+
readonly message: string;
|
|
210
|
+
/**
|
|
211
|
+
* Available choices. Plain strings and {@link Choice} objects can be mixed.
|
|
212
|
+
* Use `new Separator(...)` for visual dividers.
|
|
213
|
+
*/
|
|
214
|
+
readonly choices: readonly (string | Choice | Separator)[];
|
|
215
|
+
/**
|
|
216
|
+
* Initially highlighted choice value.
|
|
217
|
+
*/
|
|
218
|
+
readonly default?: string;
|
|
219
|
+
/**
|
|
220
|
+
* Override the prompt execution. When provided, this function is called
|
|
221
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
222
|
+
* testing.
|
|
223
|
+
*/
|
|
224
|
+
readonly prompter?: () => Promise<string>;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Configuration for a `rawlist` prompt (numbered list).
|
|
228
|
+
*
|
|
229
|
+
* @since 1.0.0
|
|
230
|
+
*/
|
|
231
|
+
interface RawlistConfig {
|
|
232
|
+
readonly type: "rawlist";
|
|
233
|
+
/**
|
|
234
|
+
* The question to display to the user.
|
|
235
|
+
*/
|
|
236
|
+
readonly message: string;
|
|
237
|
+
/**
|
|
238
|
+
* Available choices. Plain strings and {@link Choice} objects can be mixed.
|
|
239
|
+
*/
|
|
240
|
+
readonly choices: readonly (string | Choice)[];
|
|
241
|
+
/**
|
|
242
|
+
* Pre-selected choice value.
|
|
243
|
+
*/
|
|
244
|
+
readonly default?: string;
|
|
245
|
+
/**
|
|
246
|
+
* Override the prompt execution. When provided, this function is called
|
|
247
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
248
|
+
* testing.
|
|
249
|
+
*/
|
|
250
|
+
readonly prompter?: () => Promise<string>;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Configuration for an `expand` prompt (keyboard shortcut single-select).
|
|
254
|
+
*
|
|
255
|
+
* @since 1.0.0
|
|
256
|
+
*/
|
|
257
|
+
interface ExpandConfig {
|
|
258
|
+
readonly type: "expand";
|
|
259
|
+
/**
|
|
260
|
+
* The question to display to the user.
|
|
261
|
+
*/
|
|
262
|
+
readonly message: string;
|
|
263
|
+
/**
|
|
264
|
+
* Available choices. Each choice requires a `key` field.
|
|
265
|
+
*/
|
|
266
|
+
readonly choices: readonly ExpandChoice[];
|
|
267
|
+
/**
|
|
268
|
+
* Default choice key.
|
|
269
|
+
*/
|
|
270
|
+
readonly default?: string;
|
|
271
|
+
/**
|
|
272
|
+
* Override the prompt execution. When provided, this function is called
|
|
273
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
274
|
+
* testing.
|
|
275
|
+
*/
|
|
276
|
+
readonly prompter?: () => Promise<string>;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Configuration for a `checkbox` prompt (multi-select).
|
|
280
|
+
*
|
|
281
|
+
* Use with parsers that return `string[]`, typically via
|
|
282
|
+
* `multiple(option(...))`.
|
|
283
|
+
*
|
|
284
|
+
* @since 1.0.0
|
|
285
|
+
*/
|
|
286
|
+
interface CheckboxConfig {
|
|
287
|
+
readonly type: "checkbox";
|
|
288
|
+
/**
|
|
289
|
+
* The question to display to the user.
|
|
290
|
+
*/
|
|
291
|
+
readonly message: string;
|
|
292
|
+
/**
|
|
293
|
+
* Available choices. Plain strings and {@link Choice} objects can be mixed.
|
|
294
|
+
* Use `new Separator(...)` for visual dividers.
|
|
295
|
+
*/
|
|
296
|
+
readonly choices: readonly (string | Choice | Separator)[];
|
|
297
|
+
/**
|
|
298
|
+
* Override the prompt execution. When provided, this function is called
|
|
299
|
+
* instead of launching an interactive Inquirer.js prompt. Useful for
|
|
300
|
+
* testing.
|
|
301
|
+
*/
|
|
302
|
+
readonly prompter?: () => Promise<readonly string[]>;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* A union of all string-input prompt configurations.
|
|
306
|
+
*
|
|
307
|
+
* @since 1.0.0
|
|
308
|
+
*/
|
|
309
|
+
type StringPromptConfig = InputConfig | PasswordConfig | EditorConfig | SelectConfig | RawlistConfig | ExpandConfig;
|
|
310
|
+
/**
|
|
311
|
+
* Type-safe prompt configuration for a given parser value type `T`.
|
|
312
|
+
*
|
|
313
|
+
* The available prompt types are constrained by the expected value type:
|
|
314
|
+
*
|
|
315
|
+
* - `boolean` or `boolean | undefined` → {@link ConfirmConfig}
|
|
316
|
+
* - `number` or `number | undefined` → {@link NumberPromptConfig}
|
|
317
|
+
* - `string` or `string | undefined` → {@link StringPromptConfig}
|
|
318
|
+
* - `readonly string[]` → {@link CheckboxConfig}
|
|
319
|
+
*
|
|
320
|
+
* @since 1.0.0
|
|
321
|
+
*/
|
|
322
|
+
type PromptConfig<T> = BasePromptConfig<Exclude<T, null | undefined>>;
|
|
323
|
+
type BasePromptConfig<T> = T extends boolean ? ConfirmConfig : T extends number ? NumberPromptConfig : T extends string ? StringPromptConfig : T extends readonly string[] ? CheckboxConfig : never;
|
|
324
|
+
/**
|
|
325
|
+
* Wraps a parser with an interactive Inquirer.js prompt fallback.
|
|
326
|
+
*
|
|
327
|
+
* When the inner parser finds a value in the CLI arguments (consumed tokens),
|
|
328
|
+
* that value is used directly. When no CLI value is found, an interactive
|
|
329
|
+
* prompt is shown to the user.
|
|
330
|
+
*
|
|
331
|
+
* The returned parser always has `$mode: "async"` because Inquirer.js prompts
|
|
332
|
+
* are inherently asynchronous.
|
|
333
|
+
*
|
|
334
|
+
* Example:
|
|
335
|
+
*
|
|
336
|
+
* ```typescript
|
|
337
|
+
* import { option } from "@optique/core/primitives";
|
|
338
|
+
* import { string } from "@optique/core/valueparser";
|
|
339
|
+
* import { prompt } from "@optique/inquirer";
|
|
340
|
+
*
|
|
341
|
+
* const nameParser = prompt(option("--name", string()), {
|
|
342
|
+
* type: "input",
|
|
343
|
+
* message: "Enter your name:",
|
|
344
|
+
* });
|
|
345
|
+
* ```
|
|
346
|
+
*
|
|
347
|
+
* @param parser Inner parser that reads CLI values.
|
|
348
|
+
* @param config Type-safe Inquirer.js prompt configuration.
|
|
349
|
+
* @returns A parser with interactive prompt fallback, always in async mode.
|
|
350
|
+
* @since 1.0.0
|
|
351
|
+
*/
|
|
352
|
+
declare function prompt<M extends Mode, TValue, TState>(parser: Parser<M, TValue, TState>, config: PromptConfig<TValue>): Parser<"async", TValue, TState>;
|
|
353
|
+
//#endregion
|
|
354
|
+
export { CheckboxConfig, Choice, ConfirmConfig, EditorConfig, ExpandChoice, ExpandConfig, InputConfig, NumberPromptConfig, PasswordConfig, PromptConfig, RawlistConfig, SelectConfig, Separator, StringPromptConfig, prompt };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { Separator, checkbox, confirm, editor, expand, input, number, password, rawlist, select } from "@inquirer/prompts";
|
|
2
|
+
import { annotationKey, getAnnotations } from "@optique/core/annotations";
|
|
3
|
+
import { message } from "@optique/core/message";
|
|
4
|
+
|
|
5
|
+
//#region src/index.ts
|
|
6
|
+
/**
|
|
7
|
+
* Wraps a parser with an interactive Inquirer.js prompt fallback.
|
|
8
|
+
*
|
|
9
|
+
* When the inner parser finds a value in the CLI arguments (consumed tokens),
|
|
10
|
+
* that value is used directly. When no CLI value is found, an interactive
|
|
11
|
+
* prompt is shown to the user.
|
|
12
|
+
*
|
|
13
|
+
* The returned parser always has `$mode: "async"` because Inquirer.js prompts
|
|
14
|
+
* are inherently asynchronous.
|
|
15
|
+
*
|
|
16
|
+
* Example:
|
|
17
|
+
*
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { option } from "@optique/core/primitives";
|
|
20
|
+
* import { string } from "@optique/core/valueparser";
|
|
21
|
+
* import { prompt } from "@optique/inquirer";
|
|
22
|
+
*
|
|
23
|
+
* const nameParser = prompt(option("--name", string()), {
|
|
24
|
+
* type: "input",
|
|
25
|
+
* message: "Enter your name:",
|
|
26
|
+
* });
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @param parser Inner parser that reads CLI values.
|
|
30
|
+
* @param config Type-safe Inquirer.js prompt configuration.
|
|
31
|
+
* @returns A parser with interactive prompt fallback, always in async mode.
|
|
32
|
+
* @since 1.0.0
|
|
33
|
+
*/
|
|
34
|
+
function prompt(parser, config) {
|
|
35
|
+
const promptBindStateKey = Symbol("@optique/inquirer/promptState");
|
|
36
|
+
function isPromptBindState(value) {
|
|
37
|
+
return value != null && typeof value === "object" && promptBindStateKey in value;
|
|
38
|
+
}
|
|
39
|
+
const cfg = config;
|
|
40
|
+
const PromptBindInitialStateClass = class {
|
|
41
|
+
[promptBindStateKey] = true;
|
|
42
|
+
hasCliValue = false;
|
|
43
|
+
};
|
|
44
|
+
const promptBindInitialState = new PromptBindInitialStateClass();
|
|
45
|
+
let promptCache = null;
|
|
46
|
+
async function executePrompt() {
|
|
47
|
+
if ("prompter" in cfg && cfg.prompter != null) {
|
|
48
|
+
const value = await cfg.prompter();
|
|
49
|
+
if (cfg.type === "number" && value === void 0) return {
|
|
50
|
+
success: false,
|
|
51
|
+
error: message`No number provided.`
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
success: true,
|
|
55
|
+
value
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
switch (cfg.type) {
|
|
59
|
+
case "confirm": return {
|
|
60
|
+
success: true,
|
|
61
|
+
value: await confirm({
|
|
62
|
+
message: cfg.message,
|
|
63
|
+
...cfg.default !== void 0 ? { default: cfg.default } : {}
|
|
64
|
+
})
|
|
65
|
+
};
|
|
66
|
+
case "number": {
|
|
67
|
+
const numResult = await number({
|
|
68
|
+
message: cfg.message,
|
|
69
|
+
...cfg.default !== void 0 ? { default: cfg.default } : {},
|
|
70
|
+
...cfg.min !== void 0 ? { min: cfg.min } : {},
|
|
71
|
+
...cfg.max !== void 0 ? { max: cfg.max } : {},
|
|
72
|
+
...cfg.step !== void 0 ? { step: cfg.step } : {}
|
|
73
|
+
});
|
|
74
|
+
if (numResult === void 0) return {
|
|
75
|
+
success: false,
|
|
76
|
+
error: message`No number provided.`
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
success: true,
|
|
80
|
+
value: numResult
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
case "input": return {
|
|
84
|
+
success: true,
|
|
85
|
+
value: await input({
|
|
86
|
+
message: cfg.message,
|
|
87
|
+
...cfg.default !== void 0 ? { default: cfg.default } : {},
|
|
88
|
+
...cfg.validate !== void 0 ? { validate: cfg.validate } : {}
|
|
89
|
+
})
|
|
90
|
+
};
|
|
91
|
+
case "password": return {
|
|
92
|
+
success: true,
|
|
93
|
+
value: await password({
|
|
94
|
+
message: cfg.message,
|
|
95
|
+
...cfg.mask !== void 0 ? { mask: cfg.mask } : {},
|
|
96
|
+
...cfg.validate !== void 0 ? { validate: cfg.validate } : {}
|
|
97
|
+
})
|
|
98
|
+
};
|
|
99
|
+
case "editor": return {
|
|
100
|
+
success: true,
|
|
101
|
+
value: await editor({
|
|
102
|
+
message: cfg.message,
|
|
103
|
+
...cfg.default !== void 0 ? { default: cfg.default } : {},
|
|
104
|
+
...cfg.validate !== void 0 ? { validate: cfg.validate } : {}
|
|
105
|
+
})
|
|
106
|
+
};
|
|
107
|
+
case "select": return {
|
|
108
|
+
success: true,
|
|
109
|
+
value: await select({
|
|
110
|
+
message: cfg.message,
|
|
111
|
+
choices: normalizeChoices(cfg.choices),
|
|
112
|
+
...cfg.default !== void 0 ? { default: cfg.default } : {}
|
|
113
|
+
})
|
|
114
|
+
};
|
|
115
|
+
case "rawlist": return {
|
|
116
|
+
success: true,
|
|
117
|
+
value: await rawlist({
|
|
118
|
+
message: cfg.message,
|
|
119
|
+
choices: normalizeChoices(cfg.choices),
|
|
120
|
+
...cfg.default !== void 0 ? { default: cfg.default } : {}
|
|
121
|
+
})
|
|
122
|
+
};
|
|
123
|
+
case "expand": return {
|
|
124
|
+
success: true,
|
|
125
|
+
value: await expand({
|
|
126
|
+
message: cfg.message,
|
|
127
|
+
choices: cfg.choices,
|
|
128
|
+
...cfg.default !== void 0 ? { default: cfg.default } : {}
|
|
129
|
+
})
|
|
130
|
+
};
|
|
131
|
+
case "checkbox": return {
|
|
132
|
+
success: true,
|
|
133
|
+
value: await checkbox({
|
|
134
|
+
message: cfg.message,
|
|
135
|
+
choices: normalizeChoices(cfg.choices)
|
|
136
|
+
})
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
$mode: "async",
|
|
142
|
+
$valueType: parser.$valueType,
|
|
143
|
+
$stateType: parser.$stateType,
|
|
144
|
+
priority: parser.priority,
|
|
145
|
+
usage: [{
|
|
146
|
+
type: "optional",
|
|
147
|
+
terms: parser.usage
|
|
148
|
+
}],
|
|
149
|
+
initialState: promptBindInitialState,
|
|
150
|
+
parse: (context) => {
|
|
151
|
+
const annotations = getAnnotations(context.state);
|
|
152
|
+
const innerState = isPromptBindState(context.state) ? context.state.hasCliValue ? context.state.cliState : parser.initialState : context.state;
|
|
153
|
+
const innerContext = innerState !== context.state ? {
|
|
154
|
+
...context,
|
|
155
|
+
state: innerState
|
|
156
|
+
} : context;
|
|
157
|
+
const processResult = (result$1) => {
|
|
158
|
+
if (result$1.success) {
|
|
159
|
+
const cliConsumed = result$1.consumed.length > 0;
|
|
160
|
+
const nextState$1 = {
|
|
161
|
+
[promptBindStateKey]: true,
|
|
162
|
+
hasCliValue: cliConsumed,
|
|
163
|
+
cliState: result$1.next.state,
|
|
164
|
+
...annotations != null ? { [annotationKey]: annotations } : {}
|
|
165
|
+
};
|
|
166
|
+
return {
|
|
167
|
+
success: true,
|
|
168
|
+
next: {
|
|
169
|
+
...result$1.next,
|
|
170
|
+
state: nextState$1
|
|
171
|
+
},
|
|
172
|
+
consumed: result$1.consumed
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (result$1.consumed > 0) return result$1;
|
|
176
|
+
const nextState = {
|
|
177
|
+
[promptBindStateKey]: true,
|
|
178
|
+
hasCliValue: false,
|
|
179
|
+
...annotations != null ? { [annotationKey]: annotations } : {}
|
|
180
|
+
};
|
|
181
|
+
return {
|
|
182
|
+
success: true,
|
|
183
|
+
next: {
|
|
184
|
+
...innerContext,
|
|
185
|
+
state: nextState
|
|
186
|
+
},
|
|
187
|
+
consumed: []
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
const result = parser.parse(innerContext);
|
|
191
|
+
if (result instanceof Promise) return result.then(processResult);
|
|
192
|
+
return Promise.resolve(processResult(result));
|
|
193
|
+
},
|
|
194
|
+
complete: (state) => {
|
|
195
|
+
if (isPromptBindState(state) && state.hasCliValue) {
|
|
196
|
+
const r = parser.complete(state.cliState);
|
|
197
|
+
if (r instanceof Promise) return r;
|
|
198
|
+
return Promise.resolve(r);
|
|
199
|
+
}
|
|
200
|
+
if (state === promptBindInitialState) {
|
|
201
|
+
if (promptCache !== null) {
|
|
202
|
+
const cached = promptCache;
|
|
203
|
+
promptCache = null;
|
|
204
|
+
return cached;
|
|
205
|
+
}
|
|
206
|
+
promptCache = executePrompt();
|
|
207
|
+
return promptCache;
|
|
208
|
+
}
|
|
209
|
+
return executePrompt();
|
|
210
|
+
},
|
|
211
|
+
suggest: (context, prefix) => {
|
|
212
|
+
const innerState = isPromptBindState(context.state) ? context.state.hasCliValue ? context.state.cliState : parser.initialState : context.state;
|
|
213
|
+
const innerContext = innerState !== context.state ? {
|
|
214
|
+
...context,
|
|
215
|
+
state: innerState
|
|
216
|
+
} : context;
|
|
217
|
+
const innerResult = parser.suggest(innerContext, prefix);
|
|
218
|
+
return async function* () {
|
|
219
|
+
yield* innerResult;
|
|
220
|
+
}();
|
|
221
|
+
},
|
|
222
|
+
getDocFragments(state, upperDefaultValue) {
|
|
223
|
+
const configDefault = "default" in cfg ? cfg.default : void 0;
|
|
224
|
+
const defaultValue = upperDefaultValue ?? configDefault;
|
|
225
|
+
return parser.getDocFragments(state, defaultValue);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
/** Normalize choices to the format Inquirer.js expects. */
|
|
230
|
+
function normalizeChoices(choices) {
|
|
231
|
+
return choices.map((c) => {
|
|
232
|
+
if (typeof c === "string") return {
|
|
233
|
+
value: c,
|
|
234
|
+
name: c
|
|
235
|
+
};
|
|
236
|
+
if (c instanceof Separator) return c;
|
|
237
|
+
return {
|
|
238
|
+
value: c.value,
|
|
239
|
+
...c.name !== void 0 ? { name: c.name } : {},
|
|
240
|
+
..."description" in c && c.description !== void 0 ? { description: c.description } : {},
|
|
241
|
+
..."short" in c && c.short !== void 0 ? { short: c.short } : {},
|
|
242
|
+
..."disabled" in c && c.disabled !== void 0 ? { disabled: c.disabled } : {}
|
|
243
|
+
};
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
//#endregion
|
|
248
|
+
export { Separator, prompt };
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@optique/inquirer",
|
|
3
|
+
"version": "1.0.0-dev.0",
|
|
4
|
+
"description": "Interactive prompt support for Optique via Inquirer.js",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"CLI",
|
|
7
|
+
"command-line",
|
|
8
|
+
"commandline",
|
|
9
|
+
"parser",
|
|
10
|
+
"prompt",
|
|
11
|
+
"interactive",
|
|
12
|
+
"inquirer"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Hong Minhee",
|
|
17
|
+
"email": "hong@minhee.org",
|
|
18
|
+
"url": "https://hongminhee.org/"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://optique.dev/",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/dahlia/optique.git",
|
|
24
|
+
"directory": "packages/inquirer/"
|
|
25
|
+
},
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/dahlia/optique/issues"
|
|
28
|
+
},
|
|
29
|
+
"funding": [
|
|
30
|
+
"https://github.com/sponsors/dahlia"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20.0.0",
|
|
34
|
+
"bun": ">=1.2.0",
|
|
35
|
+
"deno": ">=2.3.0"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist/",
|
|
39
|
+
"package.json",
|
|
40
|
+
"README.md"
|
|
41
|
+
],
|
|
42
|
+
"type": "module",
|
|
43
|
+
"module": "./dist/index.js",
|
|
44
|
+
"main": "./dist/index.cjs",
|
|
45
|
+
"types": "./dist/index.d.ts",
|
|
46
|
+
"exports": {
|
|
47
|
+
".": {
|
|
48
|
+
"types": {
|
|
49
|
+
"import": "./dist/index.d.ts",
|
|
50
|
+
"require": "./dist/index.d.cts"
|
|
51
|
+
},
|
|
52
|
+
"import": "./dist/index.js",
|
|
53
|
+
"require": "./dist/index.cjs"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"sideEffects": false,
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@inquirer/prompts": "^8.3.0",
|
|
59
|
+
"@optique/core": "1.0.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/node": "^20.19.9",
|
|
63
|
+
"tsdown": "^0.13.0",
|
|
64
|
+
"typescript": "^5.8.3"
|
|
65
|
+
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"build": "tsdown",
|
|
68
|
+
"prepublish": "tsdown",
|
|
69
|
+
"test": "node --experimental-transform-types --test",
|
|
70
|
+
"test:bun": "bun test",
|
|
71
|
+
"test:deno": "deno test",
|
|
72
|
+
"test-all": "tsdown && node --experimental-transform-types --test && bun test && deno test"
|
|
73
|
+
}
|
|
74
|
+
}
|