codekingpro/portable-devtools
114k
1const Stream = require('stream')
2
3class MuteStream extends Stream {
4 #isTTY = null
5
6 constructor (opts = {}) {
7 super(opts)
8 this.writable = this.readable = true
9 this.muted = false
10 this.on('pipe', this._onpipe)
11 this.replace = opts.replace
12
13 // For readline-type situations
14 // This much at the start of a line being redrawn after a ctrl char
15 // is seen (such as backspace) won't be redrawn as the replacement
16 this._prompt = opts.prompt || null
17 this._hadControl = false
18 }
19
20 #destSrc (key, def) {
21 if (this._dest) {
22 return this._dest[key]
23 }
24 if (this._src) {
25 return this._src[key]
26 }
27 return def
28 }
29
30 #proxy (method, ...args) {
31 if (typeof this._dest?.[method] === 'function') {
32 this._dest[method](...args)
33 }
34 if (typeof this._src?.[method] === 'function') {
35 this._src[method](...args)
36 }
37 }
38
39 get isTTY () {
40 if (this.#isTTY !== null) {
41 return this.#isTTY
42 }
43 return this.#destSrc('isTTY', false)
44 }
45
46 // basically just get replace the getter/setter with a regular value
47 set isTTY (val) {
48 this.#isTTY = val
49 }
50
51 get rows () {
52 return this.#destSrc('rows')
53 }
54
55 get columns () {
56 return this.#destSrc('columns')
57 }
58
59 mute () {
60 this.muted = true
61 }
62
63 unmute () {
64 this.muted = false
65 }
66
67 _onpipe (src) {
68 this._src = src
69 }
70
71 pipe (dest, options) {
72 this._dest = dest
73 return super.pipe(dest, options)
74 }
75
76 pause () {
77 if (this._src) {
78 return this._src.pause()
79 }
80 }
81
82 resume () {
83 if (this._src) {
84 return this._src.resume()
85 }
86 }
87
88 write (c) {
89 if (this.muted) {
90 if (!this.replace) {
91 return true
92 }
93 // eslint-disable-next-line no-control-regex
94 if (c.match(/^\u001b/)) {
95 if (c.indexOf(this._prompt) === 0) {
96 c = c.slice(this._prompt.length)
97 c = c.replace(/./g, this.replace)
98 c = this._prompt + c
99 }
100 this._hadControl = true
101 return this.emit('data', c)
102 } else {
103 if (this._prompt && this._hadControl &&
104 c.indexOf(this._prompt) === 0) {
105 this._hadControl = false
106 this.emit('data', this._prompt)
107 c = c.slice(this._prompt.length)
108 }
109 c = c.toString().replace(/./g, this.replace)
110 }
111 }
112 this.emit('data', c)
113 }
114
115 end (c) {
116 if (this.muted) {
117 if (c && this.replace) {
118 c = c.toString().replace(/./g, this.replace)
119 } else {
120 c = null
121 }
122 }
123 if (c) {
124 this.emit('data', c)
125 }
126 this.emit('end')
127 }
128
129 destroy (...args) {
130 return this.#proxy('destroy', ...args)
131 }
132
133 destroySoon (...args) {
134 return this.#proxy('destroySoon', ...args)
135 }
136
137 close (...args) {
138 return this.#proxy('close', ...args)
139 }
140}
141
142module.exports = MuteStream
143 