Skip to content

Commit

Permalink
Breaking: Remove async message feature
Browse files Browse the repository at this point in the history
  • Loading branch information
SBoudrias committed Oct 21, 2023
1 parent 6fb4d9c commit 9273ae5
Show file tree
Hide file tree
Showing 11 changed files with 42 additions and 101 deletions.
6 changes: 3 additions & 3 deletions packages/checkbox/src/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
isNumberKey,
isEnterKey,
Separator,
type PromptConfig,
} from '@inquirer/core';
import type {} from '@inquirer/type';
import chalk from 'chalk';
Expand All @@ -26,14 +25,15 @@ type Choice<Value> = {
type?: never;
};

type Config<Value> = PromptConfig<{
type Config<Value> = {
message: string;
prefix?: string;
pageSize?: number;
instructions?: string | boolean;
choices: ReadonlyArray<Choice<Value> | Separator>;
loop?: boolean;
required?: boolean;
}>;
};

type Item<Value> = Separator | Choice<Value>;

Expand Down
6 changes: 3 additions & 3 deletions packages/confirm/src/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean, ConfirmConfig>((config, done) => {
const { transformer = (answer) => (answer ? 'yes' : 'no') } = config;
Expand Down
38 changes: 0 additions & 38 deletions packages/core/core.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -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('');
Expand Down
6 changes: 1 addition & 5 deletions packages/core/src/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
47 changes: 10 additions & 37 deletions packages/core/src/lib/create-prompt.mts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> | (() => Promise<string>);
};

export type PromptConfig<Config> = Prettify<AsyncPromptConfig & Config>;

type ResolvedPromptConfig = { message: string };

type ViewFunction<Value, Config> = (
config: Prettify<Config & ResolvedPromptConfig>,
config: Prettify<Config>,
done: (value: Value) => void,
) => string | [string, string | undefined];

// Take an AsyncPromptConfig and resolves all it's values.
async function getPromptConfig<Config extends AsyncPromptConfig>(
config: Config,
): Promise<Config & ResolvedPromptConfig> {
const message =
typeof config.message === 'function' ? config.message() : config.message;

return {
...config,
message: await message,
};
}

export function createPrompt<Value, Config extends AsyncPromptConfig>(
view: ViewFunction<Value, Config>,
) {
export function createPrompt<Value, Config>(view: ViewFunction<Value, Config>) {
const prompt: Prompt<Value, Config> = (config, context) => {
// Default `input` to stdin
const input = context?.input ?? process.stdin;
Expand Down Expand Up @@ -98,12 +74,12 @@ export function createPrompt<Value, Config extends AsyncPromptConfig>(
});
}

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;
Expand All @@ -116,16 +92,13 @@ export function createPrompt<Value, Config extends AsyncPromptConfig>(
}
}

// 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);
});
});

Expand Down
6 changes: 3 additions & 3 deletions packages/editor/src/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Check warning on line 15 in packages/editor/src/index.mts

View check run for this annotation

Codecov / codecov/patch

packages/editor/src/index.mts#L14-L15

Added lines #L14 - L15 were not covered by tests
default?: string;
postfix?: string;
waitForUseInput?: boolean;
validate?: (value: string) => boolean | string | Promise<string | boolean>;
}>;
};

Check warning on line 20 in packages/editor/src/index.mts

View check run for this annotation

Codecov / codecov/patch

packages/editor/src/index.mts#L20

Added line #L20 was not covered by tests

export default createPrompt<string, EditorConfig>((config, done) => {
const { waitForUseInput = true, validate = () => true } = config;
Expand Down
6 changes: 3 additions & 3 deletions packages/expand/src/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
useKeypress,
usePrefix,
isEnterKey,
type PromptConfig,
} from '@inquirer/core';
import type {} from '@inquirer/type';
import chalk from 'chalk';
Expand All @@ -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<ExpandChoice>;
default?: string;
expanded?: boolean;
}>;
};

const helpChoice = {
key: 'h',
Expand Down
6 changes: 3 additions & 3 deletions packages/input/src/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string | boolean>;
}>;
};

export default createPrompt<string, InputConfig>((config, done) => {
const { validate = () => true } = config;
Expand Down
10 changes: 10 additions & 0 deletions packages/prompts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand Down
6 changes: 3 additions & 3 deletions packages/rawlist/src/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
usePrefix,
isEnterKey,
Separator,
type PromptConfig,
} from '@inquirer/core';
import type {} from '@inquirer/type';
import chalk from 'chalk';
Expand All @@ -18,9 +17,10 @@ type Choice<Value> = {
key?: string;
};

type RawlistConfig<Value> = PromptConfig<{
type RawlistConfig<Value> = {
message: string;
choices: ReadonlyArray<Choice<Value> | Separator>;
}>;
};

function isSelectableChoice<T>(
choice: undefined | Separator | Choice<T>,
Expand Down
6 changes: 3 additions & 3 deletions packages/select/src/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
isDownKey,
isNumberKey,
Separator,
type PromptConfig,
} from '@inquirer/core';
import type {} from '@inquirer/type';
import chalk from 'chalk';
Expand All @@ -26,11 +25,12 @@ type Choice<Value> = {
type?: never;
};

type SelectConfig<Value> = PromptConfig<{
type SelectConfig<Value> = {
message: string;
choices: ReadonlyArray<Choice<Value> | Separator>;
pageSize?: number;
loop?: boolean;
}>;
};

type Item<Value> = Separator | Choice<Value>;

Expand Down

0 comments on commit 9273ae5

Please sign in to comment.