Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breaking: Remove async message feature #1322

Merged
merged 1 commit into from
Jan 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
isNumberKey,
isEnterKey,
Separator,
type PromptConfig,
} from '@inquirer/core';
import type {} from '@inquirer/type';
import chalk from 'chalk';
Expand All @@ -26,7 +25,8 @@
type?: never;
};

type Config<Value> = PromptConfig<{
type Config<Value> = {
message: string;
prefix?: string;
pageSize?: number;
instructions?: string | boolean;
Expand All @@ -36,7 +36,7 @@
validate?: (
items: ReadonlyArray<Item<Value>>,
) => boolean | string | Promise<string | boolean>;
}>;
};

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

Expand Down Expand Up @@ -94,7 +94,7 @@

const bounds = useMemo(() => {
const first = items.findIndex(isSelectable);
// TODO: Replace with `findLastIndex` when it's available.

Check warning on line 97 in packages/checkbox/src/index.mts

View workflow job for this annotation

GitHub Actions / Linting

Unexpected 'todo' comment: 'TODO: Replace with `findLastIndex` when...'

Check warning on line 97 in packages/checkbox/src/index.mts

View workflow job for this annotation

GitHub Actions / Linting

Unexpected 'todo' comment: 'TODO: Replace with `findLastIndex` when...'
const last = items.length - 1 - [...items].reverse().findIndex(isSelectable);

if (first < 0) {
Expand All @@ -110,7 +110,7 @@
const [showHelpTip, setShowHelpTip] = useState(true);
const [errorMsg, setError] = useState<string | undefined>(undefined);

useKeypress(async (key) => {

Check warning on line 113 in packages/checkbox/src/index.mts

View workflow job for this annotation

GitHub Actions / Linting

Async arrow function has a complexity of 21. Maximum allowed is 20

Check warning on line 113 in packages/checkbox/src/index.mts

View workflow job for this annotation

GitHub Actions / Linting

Async arrow function has a complexity of 21. Maximum allowed is 20
if (isEnterKey(key)) {
const selection = items.filter(isChecked);
const isValid = await validate([...selection]);
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 @@
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 @@
useKeypress,
usePrefix,
isEnterKey,
type PromptConfig,
} from '@inquirer/core';
import type {} from '@inquirer/type';
import chalk from 'chalk';
Expand All @@ -14,11 +13,12 @@
| { 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 Expand Up @@ -75,7 +75,7 @@
const message = chalk.bold(config.message);

if (status === 'done') {
// TODO: `value` should be the display name instead of the raw value.

Check warning on line 78 in packages/expand/src/index.mts

View workflow job for this annotation

GitHub Actions / Linting

Unexpected 'todo' comment: 'TODO: `value` should be the display name...'

Check warning on line 78 in packages/expand/src/index.mts

View workflow job for this annotation

GitHub Actions / Linting

Unexpected 'todo' comment: 'TODO: `value` should be the display name...'
return `${prefix} ${message} ${chalk.cyan(value)}`;
}

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

export default createPrompt<string, PasswordConfig>((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 @@
isDownKey,
isNumberKey,
Separator,
type PromptConfig,
} from '@inquirer/core';
import type {} from '@inquirer/type';
import chalk from 'chalk';
Expand All @@ -26,12 +25,13 @@
type?: never;
};

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

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

Expand Down Expand Up @@ -68,7 +68,7 @@

const bounds = useMemo(() => {
const first = items.findIndex(isSelectable);
// TODO: Replace with `findLastIndex` when it's available.

Check warning on line 71 in packages/select/src/index.mts

View workflow job for this annotation

GitHub Actions / Linting

Unexpected 'todo' comment: 'TODO: Replace with `findLastIndex` when...'

Check warning on line 71 in packages/select/src/index.mts

View workflow job for this annotation

GitHub Actions / Linting

Unexpected 'todo' comment: 'TODO: Replace with `findLastIndex` when...'
const last = items.length - 1 - [...items].reverse().findIndex(isSelectable);
if (first < 0)
throw new Error(
Expand Down
Loading