UNPKG

6.05 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = _default;
7
8/* ========================================================================
9 * PROMPT BYPASSING
10 * -----------------
11 * this allows a user to bypass a prompt by supplying input before
12 * the prompts are run. we handle input differently depending on the
13 * type of prompt that's in play (ie "y" means "true" for a confirm prompt)
14 * ======================================================================== */
15/////
16// HELPER FUNCTIONS
17//
18// pull the "value" out of a choice option
19const getChoiceValue = choice => {
20 const isObject = typeof choice === 'object';
21
22 if (isObject && choice.value != null) {
23 return choice.value;
24 }
25
26 if (isObject && choice.name != null) {
27 return choice.name;
28 }
29
30 if (isObject && choice.key != null) {
31 return choice.key;
32 }
33
34 return choice;
35}; // check if a bypass value matches some aspect of
36// a particular choice option (index, value, key, etc)
37
38
39const choiceMatchesValue = (choice, choiceIdx, value) => {
40 const choiceValue = getChoiceValue(choice);
41 const valueMatchesChoice = choiceValue && choiceValue.toLowerCase() === value.toLowerCase();
42 const valueMatchesChoiceKey = typeof choice.key === 'string' && choice.key.toLowerCase() === value.toLowerCase();
43 const valueMatchesChoiceName = typeof choice.name === 'string' && choice.name.toLowerCase() === value.toLowerCase();
44 const valueMatchesChoiceIndex = choiceIdx.toString() === value;
45 return valueMatchesChoice || valueMatchesChoiceKey || valueMatchesChoiceName || valueMatchesChoiceIndex;
46}; // check if a value matches a particular set of flagged input options
47
48
49const isFlag = (list, v) => list.includes(v.toLowerCase()); // input values that represent different types of responses
50
51
52const flag = {
53 isTrue: v => isFlag(['yes', 'y', 'true', 't'], v),
54 isFalse: v => isFlag(['no', 'n', 'false', 'f'], v),
55 isPrompt: v => /^_+$/.test(v)
56}; // generic list bypass function. used for all types of lists.
57// accepts value, index, or key as matching criteria
58
59const listTypeBypass = (v, prompt) => {
60 const choice = prompt.choices.find((c, idx) => choiceMatchesValue(c, idx, v));
61
62 if (choice != null) {
63 return getChoiceValue(choice);
64 }
65
66 throw Error('invalid choice');
67}; /////
68// BYPASS FUNCTIONS
69//
70// list of prompt bypass functions by prompt type
71
72
73const typeBypass = {
74 confirm(v) {
75 if (flag.isTrue(v)) {
76 return true;
77 }
78
79 if (flag.isFalse(v)) {
80 return false;
81 }
82
83 throw Error('invalid input');
84 },
85
86 checkbox(v, prompt) {
87 const valList = v.split(',');
88 const valuesNoMatch = valList.filter(val => !prompt.choices.some((c, idx) => choiceMatchesValue(c, idx, val)));
89
90 if (valuesNoMatch.length) {
91 throw Error(`no match for "${valuesNoMatch.join('", "')}"`);
92 }
93
94 return valList.map(val => getChoiceValue(prompt.choices.find((c, idx) => choiceMatchesValue(c, idx, val))));
95 },
96
97 list: listTypeBypass,
98 rawlist: listTypeBypass,
99 expand: listTypeBypass
100}; /////
101// MAIN LOGIC
102//
103// returns new prompts, initial answers object, and any failures
104
105function _default(prompts, bypassArr, plop) {
106 const noop = [prompts, {}, []]; // bail out if we don't have prompts or bypass data
107
108 if (!Array.isArray(prompts)) {
109 return noop;
110 }
111
112 if (bypassArr.length === 0) {
113 return noop;
114 } // pull registered prompts out of inquirer
115
116
117 const {
118 prompts: inqPrompts
119 } = plop.inquirer.prompt;
120 const answers = {};
121 const bypassFailures = []; // generate a list of pompts that the user is bypassing
122
123 const bypassedPrompts = prompts.filter(function (p, idx) {
124 // if the user didn't provide value for this prompt, skip it
125 if (idx >= bypassArr.length) {
126 return false;
127 }
128
129 const val = bypassArr[idx].toString(); // if the user asked to be given this prompt, skip it
130
131 if (flag.isPrompt(val)) {
132 return false;
133 } // if this prompt is dynamic, throw error because we can't know if
134 // the pompt bypass values given line up with the path this user
135 // has taken through the prompt tree.
136
137
138 if (typeof p.when === 'function') {
139 bypassFailures.push(`You can not bypass conditional prompts: ${p.name}`);
140 return false;
141 }
142
143 try {
144 const inqPrompt = inqPrompts[p.type] || {}; // try to find a bypass function to run
145
146 const bypass = p.bypass || inqPrompt.bypass || typeBypass[p.type] || null; // get the real answer data out of the bypass function and attach it
147 // to the answer data object
148
149 const bypassIsFunc = typeof bypass === 'function';
150 const value = bypassIsFunc ? bypass.call(null, val, p) : val; // if inquirer prompt has a filter function - call it
151
152 const answer = p.filter ? p.filter(value) : value; // if inquirer prompt has a validate function - call it
153
154 if (p.validate) {
155 const validation = p.validate(value);
156
157 if (validation !== true) {
158 // if validation failed return validation error
159 bypassFailures.push(validation);
160 return false;
161 }
162 }
163
164 answers[p.name] = answer;
165 } catch (err) {
166 // if we encounter an error above... assume the bypass value was invalid
167 bypassFailures.push(`The "${p.name}" prompt did not recognize "${val}" as a valid ${p.type} value (ERROR: ${err.message})`);
168 return false;
169 } // if we got this far, we successfully bypassed this prompt
170
171
172 return true;
173 }); // rip out any prompts that have been bypassed
174
175 const promptsAfterBypass = [// first prompt will copy the bypass answer data so it's available
176 // for prompts and actions to use
177 {
178 when: data => (Object.assign(data, answers), false)
179 }, // inlcude any prompts that were NOT bypassed
180 ...prompts.filter(p => !bypassedPrompts.includes(p))]; // if we have failures, throw the first one
181
182 if (bypassFailures.length) {
183 throw Error(bypassFailures[0]);
184 } else {
185 // return the prompts that still need to be run
186 return [promptsAfterBypass, answers];
187 } // BOOM!
188
189}
\No newline at end of file