Skip to content

Commit

Permalink
Fix(@inquirer/expand): [Typescript] Enforce valid keys
Browse files Browse the repository at this point in the history
  • Loading branch information
SBoudrias committed Sep 1, 2024
1 parent 7fb8aea commit 5f695f3
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 19 deletions.
32 changes: 25 additions & 7 deletions packages/expand/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,31 @@ const answer = await expand({

## Options

| Property | Type | Required | Description |
| -------- | ------------------------------------------------------ | -------- | ----------------------------------------------------------------------------------------- |
| message | `string` | yes | The question to ask |
| choices | `Array<{ key: string, name: string, value?: string }>` | yes | Array of the different allowed choices. The `h`/help option is always provided by default |
| default | `string` | no | Default choices to be selected. (value must be one of the choices `key`) |
| expanded | `boolean` | no | Expand the choices by default |
| theme | [See Theming](#Theming) | no | Customize look of the prompt. |
| Property | Type | Required | Description |
| -------- | ----------------------- | -------- | ----------------------------------------------------------------------------------------- |
| message | `string` | yes | The question to ask |
| choices | `Choice[]` | yes | Array of the different allowed choices. The `h`/help option is always provided by default |
| default | `string` | no | Default choices to be selected. (value must be one of the choices `key`) |
| expanded | `boolean` | no | Expand the choices by default |
| theme | [See Theming](#Theming) | no | Customize look of the prompt. |

### `Choice` object

The `Choice` object is typed as

```ts
type Choice<Value> = {
value: Value;
name?: string;
key: string;
};
```

Here's each property:

- `value`: The value is what will be returned by `await expand()`.
- `name`: The string displayed in the choice list. It'll default to the stringify `value`.
- `key`: The input the use must provide to select the choice. Must be a lowercase single alpha-numeric character string.

## Theming

Expand Down
8 changes: 4 additions & 4 deletions packages/expand/expand.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ import expand from './src/index.mjs';

const overwriteChoices = [
{
key: 'y',
key: 'y' as const,
name: 'Overwrite',
value: 'overwrite',
},
{
key: 'a',
key: 'a' as const,
name: 'Overwrite this one and all next',
value: 'overwrite_all',
},
{
key: 'd',
key: 'd' as const,
name: 'Show diff',
value: 'diff',
},
{
key: 'x',
key: 'x' as const,
name: 'Abort',
value: 'abort',
},
Expand Down
54 changes: 46 additions & 8 deletions packages/expand/src/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,77 @@ import {
import type { PartialDeep } from '@inquirer/type';
import colors from 'yoctocolors-cjs';

type Key =
| 'a'
| 'b'
| 'c'
| 'd'
| 'e'
| 'f'
| 'g'
// | 'h' // Help is excluded since it's a reserved key
| 'i'
| 'j'
| 'k'
| 'l'
| 'm'
| 'n'
| 'o'
| 'p'
| 'q'
| 'r'
| 's'
| 't'
| 'u'
| 'v'
| 'w'
| 'x'
| 'y'
| 'z'
| '0'
| '1'
| '2'
| '3'
| '4'
| '5'
| '6'
| '7'
| '8'
| '9';

type Choice<Value> =
| { key: string; value: Value }
| { key: string; name: string; value: Value };
| { key: Key; value: Value }
| { key: Key; name: string; value: Value };

type NormalizedChoice<Value> = {
value: Value;
name: string;
key: string;
key: Key;
};

type ExpandConfig<
Value,
ChoicesObject = readonly { key: string; name: string }[] | readonly Choice<Value>[],
ChoicesObject = readonly { key: Key; name: string }[] | readonly Choice<Value>[],
> = {
message: string;
choices: ChoicesObject extends readonly { key: string; name: string }[]
choices: ChoicesObject extends readonly { key: Key; name: string }[]
? ChoicesObject
: readonly Choice<Value>[];
default?: string;
default?: Key | 'h';
expanded?: boolean;
theme?: PartialDeep<Theme>;
};

function normalizeChoices<Value>(
choices: readonly { key: string; name: string }[] | readonly Choice<Value>[],
choices: readonly { key: Key; name: string }[] | readonly Choice<Value>[],
): NormalizedChoice<Value>[] {
return choices.map((choice) => {
const name: string = 'name' in choice ? choice.name : String(choice.value);
const value = 'value' in choice ? choice.value : name;
return {
value: value as Value,
name,
key: choice.key.toLowerCase(),
key: choice.key.toLowerCase() as Key,
};
});
}
Expand Down

0 comments on commit 5f695f3

Please sign in to comment.