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;
|
---|