From ab6be90552e5adaa48e47fda9909edeed6985813 Mon Sep 17 00:00:00 2001 From: Simon Boudrias Date: Sat, 27 Jan 2024 14:48:02 -0500 Subject: [PATCH] Breaking: Remove async message feature (#1322) --- packages/checkbox/src/index.mts | 6 ++-- packages/confirm/src/index.mts | 6 ++-- packages/core/core.test.mts | 38 -------------------- packages/core/src/index.mts | 6 +--- packages/core/src/lib/create-prompt.mts | 47 ++++++------------------- packages/editor/src/index.mts | 6 ++-- packages/expand/src/index.mts | 6 ++-- packages/input/src/index.mts | 6 ++-- packages/password/src/index.mts | 6 ++-- packages/prompts/README.md | 10 ++++++ packages/rawlist/src/index.mts | 6 ++-- packages/select/src/index.mts | 6 ++-- 12 files changed, 45 insertions(+), 104 deletions(-) diff --git a/packages/checkbox/src/index.mts b/packages/checkbox/src/index.mts index 1656104504..5b70675d59 100644 --- a/packages/checkbox/src/index.mts +++ b/packages/checkbox/src/index.mts @@ -11,7 +11,6 @@ import { isNumberKey, isEnterKey, Separator, - type PromptConfig, } from '@inquirer/core'; import type {} from '@inquirer/type'; import chalk from 'chalk'; @@ -26,7 +25,8 @@ type Choice = { type?: never; }; -type Config = PromptConfig<{ +type Config = { + message: string; prefix?: string; pageSize?: number; instructions?: string | boolean; @@ -36,7 +36,7 @@ type Config = PromptConfig<{ validate?: ( items: ReadonlyArray>, ) => boolean | string | Promise; -}>; +}; type Item = Separator | Choice; diff --git a/packages/confirm/src/index.mts b/packages/confirm/src/index.mts index 973bc0d4f2..5c8faa575f 100644 --- a/packages/confirm/src/index.mts +++ b/packages/confirm/src/index.mts @@ -5,14 +5,14 @@ import { useKeypress, isEnterKey, usePrefix, - type PromptConfig, } from '@inquirer/core'; import type {} from '@inquirer/type'; -type ConfirmConfig = PromptConfig<{ +type ConfirmConfig = { + message: string; default?: boolean; transformer?: (value: boolean) => string; -}>; +}; export default createPrompt((config, done) => { const { transformer = (answer) => (answer ? 'yes' : 'no') } = config; diff --git a/packages/core/core.test.mts b/packages/core/core.test.mts index bb70fb81ec..8a126e01ba 100644 --- a/packages/core/core.test.mts +++ b/packages/core/core.test.mts @@ -18,44 +18,6 @@ import { } from './src/index.mjs'; describe('createPrompt()', () => { - it('handle async function message', async () => { - const viewFunction = vi.fn(() => ''); - const prompt = createPrompt(viewFunction); - const promise = Promise.resolve('Async message:'); - const renderingDone = render(prompt, { message: () => promise }); - - // Initially, we leave a few ms for message to resolve - expect(viewFunction).not.toHaveBeenCalled(); - - const { answer } = await renderingDone; - expect(viewFunction).toHaveBeenLastCalledWith( - expect.objectContaining({ message: 'Async message:' }), - expect.any(Function), - ); - - answer.cancel(); - await expect(answer).rejects.toBeInstanceOf(Error); - }); - - it('handle deferred message', async () => { - const viewFunction = vi.fn(() => ''); - const prompt = createPrompt(viewFunction); - const promise = Promise.resolve('Async message:'); - const renderingDone = render(prompt, { message: promise }); - - // Initially, we leave a few ms for message to resolve - expect(viewFunction).not.toHaveBeenCalled(); - - const { answer } = await renderingDone; - expect(viewFunction).toHaveBeenLastCalledWith( - expect.objectContaining({ message: 'Async message:' }), - expect.any(Function), - ); - - answer.cancel(); - await expect(answer).rejects.toBeInstanceOf(Error); - }); - it('onKeypress: allow to implement custom behavior on keypress', async () => { const Prompt = (config: { message: string }, done: (value: string) => void) => { const [value, setValue] = useState(''); diff --git a/packages/core/src/index.mts b/packages/core/src/index.mts index 20a51d7fd3..71469a0bb2 100644 --- a/packages/core/src/index.mts +++ b/packages/core/src/index.mts @@ -6,10 +6,6 @@ export { useMemo } from './lib/use-memo.mjs'; export { useRef } from './lib/use-ref.mjs'; export { useKeypress } from './lib/use-keypress.mjs'; export { usePagination } from './lib/pagination/use-pagination.mjs'; -export { - createPrompt, - type PromptConfig, - type AsyncPromptConfig, -} from './lib/create-prompt.mjs'; +export { createPrompt } from './lib/create-prompt.mjs'; export { Separator } from './lib/Separator.mjs'; export { type InquirerReadline } from './lib/read-line.type.mjs'; diff --git a/packages/core/src/lib/create-prompt.mts b/packages/core/src/lib/create-prompt.mts index aa0e5704a5..e9efd962e8 100644 --- a/packages/core/src/lib/create-prompt.mts +++ b/packages/core/src/lib/create-prompt.mts @@ -6,36 +6,12 @@ import ScreenManager from './screen-manager.mjs'; import type { InquirerReadline } from './read-line.type.mjs'; import { withHooks, effectScheduler } from './hook-engine.mjs'; -// @deprecated Prefer using `PromptConfig<{ ... }>` instead -export type AsyncPromptConfig = { - message: string | Promise | (() => Promise); -}; - -export type PromptConfig = Prettify; - -type ResolvedPromptConfig = { message: string }; - type ViewFunction = ( - config: Prettify, + config: Prettify, done: (value: Value) => void, ) => string | [string, string | undefined]; -// Take an AsyncPromptConfig and resolves all it's values. -async function getPromptConfig( - config: Config, -): Promise { - const message = - typeof config.message === 'function' ? config.message() : config.message; - - return { - ...config, - message: await message, - }; -} - -export function createPrompt( - view: ViewFunction, -) { +export function createPrompt(view: ViewFunction) { const prompt: Prompt = (config, context) => { // Default `input` to stdin const input = context?.input ?? process.stdin; @@ -98,12 +74,12 @@ export function createPrompt( }); } - function workLoop(resolvedConfig: Config & ResolvedPromptConfig) { + function workLoop(resolvedConfig: Config) { store.index = 0; store.handleChange = () => workLoop(resolvedConfig); try { - const nextView = view(resolvedConfig, done); + const nextView = view(config, done); const [content, bottomContent] = typeof nextView === 'string' ? [nextView] : nextView; @@ -116,16 +92,13 @@ export function createPrompt( } } - // TODO: we should display a loader while we get the default options. - getPromptConfig(config).then((resolvedConfig) => { - workLoop(resolvedConfig); + workLoop(config); - // 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. - store.rl.input.on('keypress', checkCursorPos); - }, reject); + // 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. + store.rl.input.on('keypress', checkCursorPos); }); }); diff --git a/packages/editor/src/index.mts b/packages/editor/src/index.mts index 8a5f050d62..7756094a6e 100644 --- a/packages/editor/src/index.mts +++ b/packages/editor/src/index.mts @@ -7,17 +7,17 @@ import { useKeypress, usePrefix, isEnterKey, - type PromptConfig, type InquirerReadline, } from '@inquirer/core'; import type {} from '@inquirer/type'; -type EditorConfig = PromptConfig<{ +type EditorConfig = { + message: string; default?: string; postfix?: string; waitForUseInput?: boolean; validate?: (value: string) => boolean | string | Promise; -}>; +}; export default createPrompt((config, done) => { const { waitForUseInput = true, validate = () => true } = config; diff --git a/packages/expand/src/index.mts b/packages/expand/src/index.mts index 2b58330ddb..50f5f6c82f 100644 --- a/packages/expand/src/index.mts +++ b/packages/expand/src/index.mts @@ -4,7 +4,6 @@ import { useKeypress, usePrefix, isEnterKey, - type PromptConfig, } from '@inquirer/core'; import type {} from '@inquirer/type'; import chalk from 'chalk'; @@ -14,11 +13,12 @@ type ExpandChoice = | { key: string; value: string } | { key: string; name: string; value: string }; -type ExpandConfig = PromptConfig<{ +type ExpandConfig = { + message: string; choices: ReadonlyArray; default?: string; expanded?: boolean; -}>; +}; const helpChoice = { key: 'h', diff --git a/packages/input/src/index.mts b/packages/input/src/index.mts index eccb76ed66..808aeb980e 100644 --- a/packages/input/src/index.mts +++ b/packages/input/src/index.mts @@ -5,16 +5,16 @@ import { usePrefix, isEnterKey, isBackspaceKey, - type PromptConfig, } from '@inquirer/core'; import type {} from '@inquirer/type'; import chalk from 'chalk'; -type InputConfig = PromptConfig<{ +type InputConfig = { + message: string; default?: string; transformer?: (value: string, { isFinal }: { isFinal: boolean }) => string; validate?: (value: string) => boolean | string | Promise; -}>; +}; export default createPrompt((config, done) => { const { validate = () => true } = config; diff --git a/packages/password/src/index.mts b/packages/password/src/index.mts index 77d1734e1a..03d768fdeb 100644 --- a/packages/password/src/index.mts +++ b/packages/password/src/index.mts @@ -4,15 +4,15 @@ import { useKeypress, usePrefix, isEnterKey, - type PromptConfig, } from '@inquirer/core'; import chalk from 'chalk'; import ansiEscapes from 'ansi-escapes'; -type PasswordConfig = PromptConfig<{ +type PasswordConfig = { + message: string; mask?: boolean | string; validate?: (value: string) => boolean | string | Promise; -}>; +}; export default createPrompt((config, done) => { const { validate = () => true } = config; diff --git a/packages/prompts/README.md b/packages/prompts/README.md index 9b3de86486..81cde0b763 100644 --- a/packages/prompts/README.md +++ b/packages/prompts/README.md @@ -234,6 +234,16 @@ exec < /dev/tty node my-script.js ``` +## Wait for config + +Maybe some question configuration require to await a value. + +```js +import { confirm } from '@inquirer/prompts'; + +const answer = await confirm({ message: await getMessage() }); +``` + # Community prompts If you created a cool prompt, [send us a PR adding it](https://github.com/SBoudrias/Inquirer.js/edit/master/README.md) to the list below! diff --git a/packages/rawlist/src/index.mts b/packages/rawlist/src/index.mts index c0704fc2bf..606e130c99 100644 --- a/packages/rawlist/src/index.mts +++ b/packages/rawlist/src/index.mts @@ -5,7 +5,6 @@ import { usePrefix, isEnterKey, Separator, - type PromptConfig, } from '@inquirer/core'; import type {} from '@inquirer/type'; import chalk from 'chalk'; @@ -18,9 +17,10 @@ type Choice = { key?: string; }; -type RawlistConfig = PromptConfig<{ +type RawlistConfig = { + message: string; choices: ReadonlyArray | Separator>; -}>; +}; function isSelectableChoice( choice: undefined | Separator | Choice, diff --git a/packages/select/src/index.mts b/packages/select/src/index.mts index 88a7872f8c..634a5b7190 100644 --- a/packages/select/src/index.mts +++ b/packages/select/src/index.mts @@ -11,7 +11,6 @@ import { isDownKey, isNumberKey, Separator, - type PromptConfig, } from '@inquirer/core'; import type {} from '@inquirer/type'; import chalk from 'chalk'; @@ -26,12 +25,13 @@ type Choice = { type?: never; }; -type SelectConfig = PromptConfig<{ +type SelectConfig = { + message: string; choices: ReadonlyArray | Separator>; pageSize?: number; loop?: boolean; default?: Value; -}>; +}; type Item = Separator | Choice;