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