baseUI.js 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. import MuteStream from 'mute-stream';
  2. import readline from 'node:readline';
  3. /**
  4. * Base interface class other can inherits from
  5. */
  6. export default class UI {
  7. constructor(opt) {
  8. // Instantiate the Readline interface
  9. // @Note: Don't reassign if already present (allow test to override the Stream)
  10. if (!this.rl) {
  11. this.rl = readline.createInterface(setupReadlineOptions(opt));
  12. }
  13. this.rl.resume();
  14. this.onForceClose = this.onForceClose.bind(this);
  15. // Make sure new prompt start on a newline when closing
  16. process.on('exit', this.onForceClose);
  17. // Terminate process on SIGINT (which will call process.on('exit') in return)
  18. this.rl.on('SIGINT', this.onForceClose);
  19. }
  20. /**
  21. * Handle the ^C exit
  22. * @return {null}
  23. */
  24. onForceClose() {
  25. this.close();
  26. process.kill(process.pid, 'SIGINT');
  27. console.log('');
  28. }
  29. /**
  30. * Close the interface and cleanup listeners
  31. */
  32. close() {
  33. // Remove events listeners
  34. this.rl.removeListener('SIGINT', this.onForceClose);
  35. process.removeListener('exit', this.onForceClose);
  36. this.rl.output.unmute();
  37. if (this.activePrompt && typeof this.activePrompt.close === 'function') {
  38. this.activePrompt.close();
  39. }
  40. // Close the readline
  41. this.rl.output.end();
  42. this.rl.pause();
  43. this.rl.close();
  44. }
  45. }
  46. function setupReadlineOptions(opt = {}) {
  47. // Inquirer 8.x:
  48. // opt.skipTTYChecks = opt.skipTTYChecks === undefined ? opt.input !== undefined : opt.skipTTYChecks;
  49. opt.skipTTYChecks = opt.skipTTYChecks === undefined ? true : opt.skipTTYChecks;
  50. // Default `input` to stdin
  51. const input = opt.input || process.stdin;
  52. // Check if prompt is being called in TTY environment
  53. // If it isn't return a failed promise
  54. if (!opt.skipTTYChecks && !input.isTTY) {
  55. const nonTtyError = new Error(
  56. 'Prompts can not be meaningfully rendered in non-TTY environments'
  57. );
  58. nonTtyError.isTtyError = true;
  59. throw nonTtyError;
  60. }
  61. // Add mute capabilities to the output
  62. const ms = new MuteStream();
  63. ms.pipe(opt.output || process.stdout);
  64. const output = ms;
  65. return {
  66. terminal: true,
  67. ...opt,
  68. input,
  69. output,
  70. };
  71. }