index.js 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. 'use strict';
  2. const prompts = require('./prompts');
  3. const passOn = ['suggest', 'format', 'onState', 'validate', 'onRender', 'type'];
  4. const noop = () => {};
  5. /**
  6. * Prompt for a series of questions
  7. * @param {Array|Object} questions Single question object or Array of question objects
  8. * @param {Function} [onSubmit] Callback function called on prompt submit
  9. * @param {Function} [onCancel] Callback function called on cancel/abort
  10. * @returns {Object} Object with values from user input
  11. */
  12. async function prompt(questions=[], { onSubmit=noop, onCancel=noop }={}) {
  13. const answers = {};
  14. const override = prompt._override || {};
  15. questions = [].concat(questions);
  16. let answer, question, quit, name, type, lastPrompt;
  17. const getFormattedAnswer = async (question, answer, skipValidation = false) => {
  18. if (!skipValidation && question.validate && question.validate(answer) !== true) {
  19. return;
  20. }
  21. return question.format ? await question.format(answer, answers) : answer
  22. };
  23. for (question of questions) {
  24. ({ name, type } = question);
  25. // evaluate type first and skip if type is a falsy value
  26. if (typeof type === 'function') {
  27. type = await type(answer, { ...answers }, question)
  28. question['type'] = type
  29. }
  30. if (!type) continue;
  31. // if property is a function, invoke it unless it's a special function
  32. for (let key in question) {
  33. if (passOn.includes(key)) continue;
  34. let value = question[key];
  35. question[key] = typeof value === 'function' ? await value(answer, { ...answers }, lastPrompt) : value;
  36. }
  37. lastPrompt = question;
  38. if (typeof question.message !== 'string') {
  39. throw new Error('prompt message is required');
  40. }
  41. // update vars in case they changed
  42. ({ name, type } = question);
  43. if (prompts[type] === void 0) {
  44. throw new Error(`prompt type (${type}) is not defined`);
  45. }
  46. if (override[question.name] !== undefined) {
  47. answer = await getFormattedAnswer(question, override[question.name]);
  48. if (answer !== undefined) {
  49. answers[name] = answer;
  50. continue;
  51. }
  52. }
  53. try {
  54. // Get the injected answer if there is one or prompt the user
  55. answer = prompt._injected ? getInjectedAnswer(prompt._injected, question.initial) : await prompts[type](question);
  56. answers[name] = answer = await getFormattedAnswer(question, answer, true);
  57. quit = await onSubmit(question, answer, answers);
  58. } catch (err) {
  59. quit = !(await onCancel(question, answers));
  60. }
  61. if (quit) return answers;
  62. }
  63. return answers;
  64. }
  65. function getInjectedAnswer(injected, deafultValue) {
  66. const answer = injected.shift();
  67. if (answer instanceof Error) {
  68. throw answer;
  69. }
  70. return (answer === undefined) ? deafultValue : answer;
  71. }
  72. function inject(answers) {
  73. prompt._injected = (prompt._injected || []).concat(answers);
  74. }
  75. function override(answers) {
  76. prompt._override = Object.assign({}, answers);
  77. }
  78. module.exports = Object.assign(prompt, { prompt, prompts, inject, override });