-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathprompts.ts
More file actions
114 lines (108 loc) · 3.11 KB
/
prompts.ts
File metadata and controls
114 lines (108 loc) · 3.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import { checkbox, confirm, input, select } from '@inquirer/prompts';
import { asyncSequential } from '@code-pushup/utils';
import type {
CliArgs,
PluginAnswer,
PluginPromptDescriptor,
PluginSetupBinding,
} from './types.js';
/**
* Resolves which plugins to include in the generated config.
*
* Resolution order (first match wins):
* 1. `--plugins`: user-provided slugs
* 2. `--yes`: recommended plugins
* 3. Interactive: checkbox prompt with recommended plugins pre-checked
*/
export async function promptPluginSelection(
bindings: PluginSetupBinding[],
targetDir: string,
{ plugins, yes }: CliArgs,
): Promise<PluginSetupBinding[]> {
if (bindings.length === 0) {
return [];
}
if (plugins != null && plugins.length > 0) {
return bindings.filter(b => plugins.includes(b.slug));
}
const recommended = await detectRecommended(bindings, targetDir);
if (yes) {
return bindings.filter(({ slug }) => recommended.has(slug));
}
const selected = await checkbox({
message: 'Plugins to include:',
required: true,
choices: bindings.map(({ title, slug }) => ({
name: title,
value: slug,
checked: recommended.has(slug),
})),
});
const selectedSet = new Set(selected);
return bindings.filter(({ slug }) => selectedSet.has(slug));
}
/**
* Calls each binding's `isRecommended` callback (if provided)
* and collects the slugs of bindings that returned `true`.
*/
async function detectRecommended(
bindings: PluginSetupBinding[],
targetDir: string,
): Promise<Set<string>> {
const recommended = new Set<string>();
await Promise.all(
bindings.map(async ({ slug, isRecommended }) => {
if (isRecommended && (await isRecommended(targetDir))) {
recommended.add(slug);
}
}),
);
return recommended;
}
export async function promptPluginOptions(
descriptors: PluginPromptDescriptor[],
cliArgs: CliArgs,
): Promise<Record<string, PluginAnswer>> {
const fallback = cliArgs['yes']
? (descriptor: PluginPromptDescriptor) => descriptor.default
: runPrompt;
const entries = await asyncSequential(descriptors, async descriptor => [
descriptor.key,
cliValue(descriptor.key, cliArgs) ?? (await fallback(descriptor)),
]);
return Object.fromEntries(entries);
}
function cliValue(key: string, cliArgs: CliArgs): PluginAnswer | undefined {
const value = cliArgs[key];
if (typeof value === 'string' || typeof value === 'boolean') {
return value;
}
return undefined;
}
async function runPrompt(
descriptor: PluginPromptDescriptor,
): Promise<PluginAnswer> {
switch (descriptor.type) {
case 'input':
return input({
message: descriptor.message,
default: descriptor.default,
});
case 'select':
return select({
message: descriptor.message,
choices: [...descriptor.choices],
default: descriptor.default,
});
case 'checkbox':
return checkbox({
message: descriptor.message,
choices: [...descriptor.choices],
});
case 'confirm':
return confirm({
message: descriptor.message,
default: descriptor.default,
});
}
}