@inquirer/core
Advanced tools
Comparing version 9.1.0 to 9.2.0
@@ -35,3 +35,3 @@ "use strict"; | ||
const screen_manager_mjs_1 = __importDefault(require('./screen-manager.js')); | ||
const type_1 = require("@inquirer/type"); | ||
const promise_polyfill_mjs_1 = require('./promise-polyfill.js'); | ||
const hook_engine_mjs_1 = require('./hook-engine.js'); | ||
@@ -44,2 +44,3 @@ const errors_mjs_1 = require('./errors.js'); | ||
const { input = process.stdin, signal } = context; | ||
const cleanups = new Set(); | ||
// Add mute capabilities to the output | ||
@@ -54,18 +55,10 @@ const output = new mute_stream_1.default(); | ||
const screen = new screen_manager_mjs_1.default(rl); | ||
const cleanups = new Set(); | ||
const { promise, resolve, reject } = type_1.CancelablePromise.withResolver(); | ||
function onExit() { | ||
cleanups.forEach((cleanup) => cleanup()); | ||
screen.done({ clearContent: Boolean(context === null || context === void 0 ? void 0 : context.clearPromptOnDone) }); | ||
output.end(); | ||
} | ||
function fail(error) { | ||
onExit(); | ||
reject(error); | ||
} | ||
const { promise, resolve, reject } = promise_polyfill_mjs_1.PromisePolyfill.withResolver(); | ||
/** @deprecated pass an AbortSignal in the context options instead. See {@link https://github.com/SBoudrias/Inquirer.js#canceling-prompt} */ | ||
const cancel = () => reject(new errors_mjs_1.CancelPromptError()); | ||
if (signal) { | ||
const abort = () => fail(new errors_mjs_1.AbortPromptError({ cause: signal.reason })); | ||
const abort = () => reject(new errors_mjs_1.AbortPromptError({ cause: signal.reason })); | ||
if (signal.aborted) { | ||
abort(); | ||
return promise; | ||
return Object.assign(promise, { cancel }); | ||
} | ||
@@ -75,38 +68,24 @@ signal.addEventListener('abort', abort); | ||
} | ||
(0, hook_engine_mjs_1.withHooks)(rl, (cycle) => { | ||
cleanups.add((0, signal_exit_1.onExit)((code, signal) => { | ||
fail(new errors_mjs_1.ExitPromptError(`User force closed the prompt with ${code} ${signal}`)); | ||
})); | ||
const hooksCleanup = node_async_hooks_1.AsyncResource.bind(() => { | ||
try { | ||
hook_engine_mjs_1.effectScheduler.clearAll(); | ||
} | ||
catch (error) { | ||
reject(error); | ||
} | ||
}); | ||
cleanups.add(hooksCleanup); | ||
// Re-renders only happen when the state change; but the readline cursor could change position | ||
// and that also requires a re-render (and a manual one because we mute the streams). | ||
// We set the listener after the initial workLoop to avoid a double render if render triggered | ||
// by a state change sets the cursor to the right position. | ||
const checkCursorPos = () => screen.checkCursorPos(); | ||
rl.input.on('keypress', checkCursorPos); | ||
cleanups.add(() => rl.input.removeListener('keypress', checkCursorPos)); | ||
cleanups.add((0, signal_exit_1.onExit)((code, signal) => { | ||
reject(new errors_mjs_1.ExitPromptError(`User force closed the prompt with ${code} ${signal}`)); | ||
})); | ||
// Re-renders only happen when the state change; but the readline cursor could change position | ||
// and that also requires a re-render (and a manual one because we mute the streams). | ||
// We set the listener after the initial workLoop to avoid a double render if render triggered | ||
// by a state change sets the cursor to the right position. | ||
const checkCursorPos = () => screen.checkCursorPos(); | ||
rl.input.on('keypress', checkCursorPos); | ||
cleanups.add(() => rl.input.removeListener('keypress', checkCursorPos)); | ||
return (0, hook_engine_mjs_1.withHooks)(rl, (cycle) => { | ||
// The close event triggers immediately when the user press ctrl+c. SignalExit on the other hand | ||
// triggers after the process is done (which happens after timeouts are done triggering.) | ||
// We triggers the hooks cleanup phase on rl `close` so active timeouts can be cleared. | ||
const hooksCleanup = node_async_hooks_1.AsyncResource.bind(() => hook_engine_mjs_1.effectScheduler.clearAll()); | ||
rl.on('close', hooksCleanup); | ||
cleanups.add(() => rl.removeListener('close', hooksCleanup)); | ||
function done(value) { | ||
// Delay execution to let time to the hookCleanup functions to registers. | ||
setImmediate(() => { | ||
onExit(); | ||
// Finally we resolve our promise | ||
resolve(value); | ||
}); | ||
} | ||
cycle(() => { | ||
try { | ||
const nextView = view(config, done); | ||
const nextView = view(config, (value) => { | ||
setImmediate(() => resolve(value)); | ||
}); | ||
const [content, bottomContent] = typeof nextView === 'string' ? [nextView] : nextView; | ||
@@ -117,12 +96,24 @@ screen.render(content, bottomContent); | ||
catch (error) { | ||
fail(error); | ||
reject(error); | ||
} | ||
}); | ||
return Object.assign(promise | ||
.then((answer) => { | ||
hook_engine_mjs_1.effectScheduler.clearAll(); | ||
return answer; | ||
}, (error) => { | ||
hook_engine_mjs_1.effectScheduler.clearAll(); | ||
throw error; | ||
}) | ||
// Wait for the promise to settle, then cleanup. | ||
.finally(() => { | ||
cleanups.forEach((cleanup) => cleanup()); | ||
screen.done({ clearContent: Boolean(context === null || context === void 0 ? void 0 : context.clearPromptOnDone) }); | ||
output.end(); | ||
}) | ||
// Once cleanup is done, let the expose promise resolve/reject to the internal one. | ||
.then(() => promise), { cancel }); | ||
}); | ||
promise.cancel = () => { | ||
fail(new errors_mjs_1.CancelPromptError()); | ||
}; | ||
return promise; | ||
}; | ||
return prompt; | ||
} |
@@ -35,3 +35,3 @@ "use strict"; | ||
} | ||
cb(cycle); | ||
return cb(cycle); | ||
}); | ||
@@ -38,0 +38,0 @@ } |
@@ -8,8 +8,12 @@ "use strict"; | ||
const yoctocolors_cjs_1 = __importDefault(require("yoctocolors-cjs")); | ||
const cli_spinners_1 = __importDefault(require("cli-spinners")); | ||
const figures_1 = __importDefault(require("@inquirer/figures")); | ||
exports.defaultTheme = { | ||
prefix: yoctocolors_cjs_1.default.green('?'), | ||
prefix: { | ||
idle: yoctocolors_cjs_1.default.blue('?'), | ||
// TODO: use figure | ||
done: yoctocolors_cjs_1.default.green(figures_1.default.tick), | ||
}, | ||
spinner: { | ||
interval: cli_spinners_1.default.dots.interval, | ||
frames: cli_spinners_1.default.dots.frames.map((frame) => yoctocolors_cjs_1.default.yellow(frame)), | ||
interval: 80, | ||
frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'].map((frame) => yoctocolors_cjs_1.default.yellow(frame)), | ||
}, | ||
@@ -16,0 +20,0 @@ style: { |
@@ -8,3 +8,3 @@ "use strict"; | ||
const make_theme_mjs_1 = require('./make-theme.js'); | ||
function usePrefix({ isLoading = false, theme, }) { | ||
function usePrefix({ status = 'idle', theme, }) { | ||
const [showLoader, setShowLoader] = (0, use_state_mjs_1.useState)(false); | ||
@@ -14,3 +14,3 @@ const [tick, setTick] = (0, use_state_mjs_1.useState)(0); | ||
(0, use_effect_mjs_1.useEffect)(() => { | ||
if (isLoading) { | ||
if (status === 'loading') { | ||
let tickInterval; | ||
@@ -34,7 +34,9 @@ let inc = -1; | ||
} | ||
}, [isLoading]); | ||
}, [status]); | ||
if (showLoader) { | ||
return spinner.frames[tick]; | ||
} | ||
return prefix; | ||
// There's a delay before we show the loader. So we want to ignore `loading` here, and pass idle instead. | ||
const iconName = status === 'loading' ? 'idle' : status; | ||
return typeof prefix === 'string' ? prefix : prefix[iconName]; | ||
} |
@@ -10,5 +10,5 @@ export * from './lib/key.js'; | ||
export { makeTheme } from './lib/make-theme.js'; | ||
export type { Theme } from './lib/theme.js'; | ||
export type { Theme, Status } from './lib/theme.js'; | ||
export { usePagination } from './lib/pagination/use-pagination.js'; | ||
export { createPrompt } from './lib/create-prompt.js'; | ||
export { Separator } from './lib/Separator.js'; |
import type { InquirerReadline } from '@inquirer/type'; | ||
export declare function withHooks(rl: InquirerReadline, cb: (cycle: (render: () => void) => void) => void): void; | ||
export declare function withHooks<T>(rl: InquirerReadline, cb: (cycle: (render: () => void) => void) => T): T; | ||
export declare function readline(): InquirerReadline; | ||
@@ -4,0 +4,0 @@ export declare function withUpdates<R, T extends (...args: any[]) => R>(fn: T): (...args: Parameters<T>) => R; |
import type { Prettify } from '@inquirer/type'; | ||
/** | ||
* Union type representing the possible statuses of a prompt. | ||
* | ||
* - `'loading'`: The prompt is currently loading. | ||
* - `'idle'`: The prompt is loaded and currently waiting for the user to | ||
* submit an answer. | ||
* - `'done'`: The user has submitted an answer and the prompt is finished. | ||
*/ | ||
export type Status = 'loading' | 'idle' | 'done'; | ||
type DefaultTheme = { | ||
prefix: string; | ||
/** | ||
* Prefix to prepend to the message. If a function is provided, it will be | ||
* called with the current status of the prompt, and the return value will be | ||
* used as the prefix. | ||
* | ||
* @remarks | ||
* If `status === 'loading'`, this property is ignored and the spinner (styled | ||
* by the `spinner` property) will be displayed instead. | ||
* | ||
* @defaultValue | ||
* ```ts | ||
* // import colors from 'yoctocolors-cjs'; | ||
* (status) => status === 'done' ? colors.green('✔') : colors.blue('?') | ||
* ``` | ||
*/ | ||
prefix: string | Prettify<Omit<Record<Status, string>, 'loading'>>; | ||
/** | ||
* Configuration for the spinner that is displayed when the prompt is in the | ||
* `'loading'` state. | ||
* | ||
* We recommend the use of {@link https://github.com/sindresorhus/cli-spinners|cli-spinners} for a list of available spinners. | ||
*/ | ||
spinner: { | ||
/** | ||
* The time interval between frames, in milliseconds. | ||
* | ||
* @defaultValue | ||
* ```ts | ||
* 80 | ||
* ``` | ||
*/ | ||
interval: number; | ||
/** | ||
* A list of frames to show for the spinner. | ||
* | ||
* @defaultValue | ||
* ```ts | ||
* ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'] | ||
* ``` | ||
*/ | ||
frames: string[]; | ||
}; | ||
/** | ||
* Object containing functions to style different parts of the prompt. | ||
*/ | ||
style: { | ||
/** | ||
* Style to apply to the user's answer once it has been submitted. | ||
* | ||
* @param text - The user's answer. | ||
* @returns The styled answer. | ||
* | ||
* @defaultValue | ||
* ```ts | ||
* // import colors from 'yoctocolors-cjs'; | ||
* (text) => colors.cyan(text) | ||
* ``` | ||
*/ | ||
answer: (text: string) => string; | ||
message: (text: string) => string; | ||
/** | ||
* Style to apply to the message displayed to the user. | ||
* | ||
* @param text - The message to style. | ||
* @param status - The current status of the prompt. | ||
* @returns The styled message. | ||
* | ||
* @defaultValue | ||
* ```ts | ||
* // import colors from 'yoctocolors-cjs'; | ||
* (text, status) => colors.bold(text) | ||
* ``` | ||
*/ | ||
message: (text: string, status: Status) => string; | ||
/** | ||
* Style to apply to error messages. | ||
* | ||
* @param text - The error message. | ||
* @returns The styled error message. | ||
* | ||
* @defaultValue | ||
* ```ts | ||
* // import colors from 'yoctocolors-cjs'; | ||
* (text) => colors.red(`> ${text}`) | ||
* ``` | ||
*/ | ||
error: (text: string) => string; | ||
/** | ||
* Style to apply to the default answer when one is provided. | ||
* | ||
* @param text - The default answer. | ||
* @returns The styled default answer. | ||
* | ||
* @defaultValue | ||
* ```ts | ||
* // import colors from 'yoctocolors-cjs'; | ||
* (text) => colors.dim(`(${text})`) | ||
* ``` | ||
*/ | ||
defaultAnswer: (text: string) => string; | ||
/** | ||
* Style to apply to help text. | ||
* | ||
* @param text - The help text. | ||
* @returns The styled help text. | ||
* | ||
* @defaultValue | ||
* ```ts | ||
* // import colors from 'yoctocolors-cjs'; | ||
* (text) => colors.dim(text) | ||
* ``` | ||
*/ | ||
help: (text: string) => string; | ||
/** | ||
* Style to apply to highlighted text. | ||
* | ||
* @param text - The text to highlight. | ||
* @returns The highlighted text. | ||
* | ||
* @defaultValue | ||
* ```ts | ||
* // import colors from 'yoctocolors-cjs'; | ||
* (text) => colors.cyan(text) | ||
* ``` | ||
*/ | ||
highlight: (text: string) => string; | ||
/** | ||
* Style to apply to keyboard keys referred to in help texts. | ||
* | ||
* @param text - The key to style. | ||
* @returns The styled key. | ||
* | ||
* @defaultValue | ||
* ```ts | ||
* // import colors from 'yoctocolors-cjs'; | ||
* (text) => colors.cyan(colors.bold(`<${text}>`)) | ||
* ``` | ||
*/ | ||
key: (text: string) => string; | ||
@@ -16,0 +150,0 @@ }; |
@@ -1,5 +0,5 @@ | ||
import { type Theme } from './theme.js'; | ||
export declare function usePrefix({ isLoading, theme, }: { | ||
isLoading?: boolean; | ||
import type { Theme, Status } from './theme.js'; | ||
export declare function usePrefix({ status, theme, }: { | ||
status?: Status; | ||
theme?: Theme; | ||
}): string; |
type NotFunction<T> = T extends (...args: never) => unknown ? never : T; | ||
export declare function useState<Value>(defaultValue: NotFunction<Value> | (() => Value)): [Value, (newValue: Value) => void]; | ||
export declare function useState<Value>(defaultValue?: NotFunction<Value> | (() => Value)): [Value | undefined, (newValue?: Value | undefined) => void]; | ||
export declare function useState<Value>(defaultValue?: NotFunction<Value> | (() => Value)): [Value | undefined, (newValue?: Value) => void]; | ||
export {}; |
{ | ||
"name": "@inquirer/core", | ||
"version": "9.1.0", | ||
"version": "9.2.0", | ||
"engines": { | ||
@@ -61,8 +61,7 @@ "node": ">=18" | ||
"@inquirer/figures": "^1.0.5", | ||
"@inquirer/type": "^1.5.3", | ||
"@inquirer/type": "^1.5.4", | ||
"@types/mute-stream": "^0.0.4", | ||
"@types/node": "^22.5.2", | ||
"@types/node": "^22.5.5", | ||
"@types/wrap-ansi": "^3.0.0", | ||
"ansi-escapes": "^4.3.2", | ||
"cli-spinners": "^2.9.2", | ||
"cli-width": "^4.1.0", | ||
@@ -76,3 +75,3 @@ "mute-stream": "^1.0.0", | ||
"devDependencies": { | ||
"@inquirer/testing": "^2.1.32" | ||
"@inquirer/testing": "^2.1.33" | ||
}, | ||
@@ -101,3 +100,3 @@ "scripts": { | ||
"sideEffects": false, | ||
"gitHead": "0c039599ef88fe9eb804fe083ee386ec906a856f" | ||
"gitHead": "5fe03a4686d349d0d6cf281f86f12a4f7c992ad4" | ||
} |
@@ -157,3 +157,3 @@ # `@inquirer/core` | ||
const input = createPrompt((config, done) => { | ||
const prefix = usePrefix({ isLoading }); | ||
const prefix = usePrefix({ status }); | ||
@@ -269,3 +269,3 @@ return `${prefix} My question`; | ||
const prefix = usePrefix({ isLoading, theme }); | ||
const prefix = usePrefix({ status, theme }); | ||
@@ -295,3 +295,3 @@ return `${prefix} ${theme.style.highlight('hello')}`; | ||
const prefix = usePrefix({ isLoading, theme }); | ||
const prefix = usePrefix({ status, theme }); | ||
@@ -306,3 +306,3 @@ return `${prefix} ${theme.icon}`; | ||
type DefaultTheme = { | ||
prefix: string; | ||
prefix: string | { idle: string; done: string }; | ||
spinner: { | ||
@@ -314,3 +314,3 @@ interval: number; | ||
answer: (text: string) => string; | ||
message: (text: string) => string; | ||
message: (text: string, status: 'idle' | 'done' | 'loading') => string; | ||
error: (text: string) => string; | ||
@@ -346,2 +346,3 @@ defaultAnswer: (text: string) => string; | ||
usePrefix, | ||
type Status, | ||
} from '@inquirer/core'; | ||
@@ -351,3 +352,3 @@ | ||
(config, done) => { | ||
const [status, setStatus] = useState('pending'); | ||
const [status, setStatus] = useState<Status>('idle'); | ||
const [value, setValue] = useState(''); | ||
@@ -354,0 +355,0 @@ const prefix = usePrefix({}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
107837
12
103
2105
384
- Removedcli-spinners@^2.9.2
- Removedcli-spinners@2.9.2(transitive)
Updated@inquirer/type@^1.5.4
Updated@types/node@^22.5.5