index.js 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. import { createPrompt, useState, useKeypress, usePrefix, isEnterKey, isBackspaceKey, makeTheme, } from '@inquirer/core';
  2. const inputTheme = {
  3. validationFailureMode: 'keep',
  4. };
  5. export default createPrompt((config, done) => {
  6. const { required, validate = () => true } = config;
  7. const theme = makeTheme(inputTheme, config.theme);
  8. const [status, setStatus] = useState('idle');
  9. const [defaultValue = '', setDefaultValue] = useState(config.default);
  10. const [errorMsg, setError] = useState();
  11. const [value, setValue] = useState('');
  12. const prefix = usePrefix({ status, theme });
  13. useKeypress(async (key, rl) => {
  14. // Ignore keypress while our prompt is doing other processing.
  15. if (status !== 'idle') {
  16. return;
  17. }
  18. if (isEnterKey(key)) {
  19. const answer = value || defaultValue;
  20. setStatus('loading');
  21. const isValid = required && !answer ? 'You must provide a value' : await validate(answer);
  22. if (isValid === true) {
  23. setValue(answer);
  24. setStatus('done');
  25. done(answer);
  26. }
  27. else {
  28. if (theme.validationFailureMode === 'clear') {
  29. setValue('');
  30. }
  31. else {
  32. // Reset the readline line value to the previous value. On line event, the value
  33. // get cleared, forcing the user to re-enter the value instead of fixing it.
  34. rl.write(value);
  35. }
  36. setError(isValid || 'You must provide a valid value');
  37. setStatus('idle');
  38. }
  39. }
  40. else if (isBackspaceKey(key) && !value) {
  41. setDefaultValue(undefined);
  42. }
  43. else if (key.name === 'tab' && !value) {
  44. setDefaultValue(undefined);
  45. rl.clearLine(0); // Remove the tab character.
  46. rl.write(defaultValue);
  47. setValue(defaultValue);
  48. }
  49. else {
  50. setValue(rl.line);
  51. setError(undefined);
  52. }
  53. });
  54. const message = theme.style.message(config.message, status);
  55. let formattedValue = value;
  56. if (typeof config.transformer === 'function') {
  57. formattedValue = config.transformer(value, { isFinal: status === 'done' });
  58. }
  59. else if (status === 'done') {
  60. formattedValue = theme.style.answer(value);
  61. }
  62. let defaultStr;
  63. if (defaultValue && status !== 'done' && !value) {
  64. defaultStr = theme.style.defaultAnswer(defaultValue);
  65. }
  66. let error = '';
  67. if (errorMsg) {
  68. error = theme.style.error(errorMsg);
  69. }
  70. return [
  71. [prefix, message, defaultStr, formattedValue]
  72. .filter((v) => v !== undefined)
  73. .join(' '),
  74. error,
  75. ];
  76. });