UNPKG

2 kBJavaScriptView Raw
1import process from 'node:process';
2import readline from 'node:readline';
3import {BufferListStream} from 'bl';
4
5const ASCII_ETX_CODE = 0x03; // Ctrl+C emits this code
6
7export class StdinDiscarder {
8 #requests = 0;
9 #mutedStream = new BufferListStream();
10 #ourEmit;
11 #rl;
12
13 constructor() {
14 this.#mutedStream.pipe(process.stdout);
15
16 const self = this; // eslint-disable-line unicorn/no-this-assignment
17 this.#ourEmit = function (event, data, ...args) {
18 const {stdin} = process;
19 if (self.#requests > 0 || stdin.emit === self.#ourEmit) {
20 if (event === 'keypress') { // Fixes readline behavior
21 return;
22 }
23
24 if (event === 'data' && data.includes(ASCII_ETX_CODE)) {
25 process.emit('SIGINT');
26 }
27
28 Reflect.apply(self.#ourEmit, this, [event, data, ...args]);
29 } else {
30 Reflect.apply(process.stdin.emit, this, [event, data, ...args]);
31 }
32 };
33 }
34
35 start() {
36 this.#requests++;
37
38 if (this.#requests === 1) {
39 this._realStart();
40 }
41 }
42
43 stop() {
44 if (this.#requests <= 0) {
45 throw new Error('`stop` called more times than `start`');
46 }
47
48 this.#requests--;
49
50 if (this.#requests === 0) {
51 this._realStop();
52 }
53 }
54
55 // TODO: Use private methods when targeting Node.js 14.
56 _realStart() {
57 // No known way to make it work reliably on Windows
58 if (process.platform === 'win32') {
59 return;
60 }
61
62 this.#rl = readline.createInterface({
63 input: process.stdin,
64 output: this.#mutedStream,
65 });
66
67 this.#rl.on('SIGINT', () => {
68 if (process.listenerCount('SIGINT') === 0) {
69 process.emit('SIGINT');
70 } else {
71 this.#rl.close();
72 process.kill(process.pid, 'SIGINT');
73 }
74 });
75 }
76
77 _realStop() {
78 if (process.platform === 'win32') {
79 return;
80 }
81
82 const {stdin} = process;
83 const wasPaused = stdin.isPaused();
84
85 this.#rl.close();
86
87 // Keep stdin unpaused across the readline close if it is already
88 // unpaused. See #209 for more details.
89 if (!wasPaused && stdin.isPaused()) {
90 stdin.resume();
91 }
92
93 this.#rl = undefined;
94 }
95}