codekingpro/portable-devtools
114k
1"use strict";
2var _a;
3Object.defineProperty(exports, "__esModule", { value: true });
4exports.unload = exports.load = exports.onExit = exports.signals = void 0;
5// Note: since nyc uses this module to output coverage, any lines
6// that are in the direct sync flow of nyc's outputCoverage are
7// ignored, since we can never get coverage for them.
8// grab a reference to node's real process object right away
9const signals_js_1 = require("./signals.js");
10Object.defineProperty(exports, "signals", { enumerable: true, get: function () { return signals_js_1.signals; } });
11const processOk = (process) => !!process &&
12 typeof process === 'object' &&
13 typeof process.removeListener === 'function' &&
14 typeof process.emit === 'function' &&
15 typeof process.reallyExit === 'function' &&
16 typeof process.listeners === 'function' &&
17 typeof process.kill === 'function' &&
18 typeof process.pid === 'number' &&
19 typeof process.on === 'function';
20const kExitEmitter = Symbol.for('signal-exit emitter');
21const global = globalThis;
22const ObjectDefineProperty = Object.defineProperty.bind(Object);
23// teeny special purpose ee
24class Emitter {
25 emitted = {
26 afterExit: false,
27 exit: false,
28 };
29 listeners = {
30 afterExit: [],
31 exit: [],
32 };
33 count = 0;
34 id = Math.random();
35 constructor() {
36 if (global[kExitEmitter]) {
37 return global[kExitEmitter];
38 }
39 ObjectDefineProperty(global, kExitEmitter, {
40 value: this,
41 writable: false,
42 enumerable: false,
43 configurable: false,
44 });
45 }
46 on(ev, fn) {
47 this.listeners[ev].push(fn);
48 }
49 removeListener(ev, fn) {
50 const list = this.listeners[ev];
51 const i = list.indexOf(fn);
52 /* c8 ignore start */
53 if (i === -1) {
54 return;
55 }
56 /* c8 ignore stop */
57 if (i === 0 && list.length === 1) {
58 list.length = 0;
59 }
60 else {
61 list.splice(i, 1);
62 }
63 }
64 emit(ev, code, signal) {
65 if (this.emitted[ev]) {
66 return false;
67 }
68 this.emitted[ev] = true;
69 let ret = false;
70 for (const fn of this.listeners[ev]) {
71 ret = fn(code, signal) === true || ret;
72 }
73 if (ev === 'exit') {
74 ret = this.emit('afterExit', code, signal) || ret;
75 }
76 return ret;
77 }
78}
79class SignalExitBase {
80}
81const signalExitWrap = (handler) => {
82 return {
83 onExit(cb, opts) {
84 return handler.onExit(cb, opts);
85 },
86 load() {
87 return handler.load();
88 },
89 unload() {
90 return handler.unload();
91 },
92 };
93};
94class SignalExitFallback extends SignalExitBase {
95 onExit() {
96 return () => { };
97 }
98 load() { }
99 unload() { }
100}
101class SignalExit extends SignalExitBase {
102 // "SIGHUP" throws an `ENOSYS` error on Windows,
103 // so use a supported signal instead
104 /* c8 ignore start */
105 #hupSig = process.platform === 'win32' ? 'SIGINT' : 'SIGHUP';
106 /* c8 ignore stop */
107 #emitter = new Emitter();
108 #process;
109 #originalProcessEmit;
110 #originalProcessReallyExit;
111 #sigListeners = {};
112 #loaded = false;
113 constructor(process) {
114 super();
115 this.#process = process;
116 // { <signal>: <listener fn>, ... }
117 this.#sigListeners = {};
118 for (const sig of signals_js_1.signals) {
119 this.#sigListeners[sig] = () => {
120 // If there are no other listeners, an exit is coming!
121 // Simplest way: remove us and then re-send the signal.
122 // We know that this will kill the process, so we can
123 // safely emit now.
124 const listeners = this.#process.listeners(sig);
125 let { count } = this.#emitter;
126 // This is a workaround for the fact that signal-exit v3 and signal
127 // exit v4 are not aware of each other, and each will attempt to let
128 // the other handle it, so neither of them do. To correct this, we
129 // detect if we're the only handler *except* for previous versions
130 // of signal-exit, and increment by the count of listeners it has
131 // created.
132 /* c8 ignore start */
133 const p = process;
134 if (typeof p.__signal_exit_emitter__ === 'object' &&
135 typeof p.__signal_exit_emitter__.count === 'number') {
136 count += p.__signal_exit_emitter__.count;
137 }
138 /* c8 ignore stop */
139 if (listeners.length === count) {
140 this.unload();
141 const ret = this.#emitter.emit('exit', null, sig);
142 /* c8 ignore start */
143 const s = sig === 'SIGHUP' ? this.#hupSig : sig;
144 if (!ret)
145 process.kill(process.pid, s);
146 /* c8 ignore stop */
147 }
148 };
149 }
150 this.#originalProcessReallyExit = process.reallyExit;
151 this.#originalProcessEmit = process.emit;
152 }
153 onExit(cb, opts) {
154 /* c8 ignore start */
155 if (!processOk(this.#process)) {
156 return () => { };
157 }
158 /* c8 ignore stop */
159 if (this.#loaded === false) {
160 this.load();
161 }
162 const ev = opts?.alwaysLast ? 'afterExit' : 'exit';
163 this.#emitter.on(ev, cb);
164 return () => {
165 this.#emitter.removeListener(ev, cb);
166 if (this.#emitter.listeners['exit'].length === 0 &&
167 this.#emitter.listeners['afterExit'].length === 0) {
168 this.unload();
169 }
170 };
171 }
172 load() {
173 if (this.#loaded) {
174 return;
175 }
176 this.#loaded = true;
177 // This is the number of onSignalExit's that are in play.
178 // It's important so that we can count the correct number of
179 // listeners on signals, and don't wait for the other one to
180 // handle it instead of us.
181 this.#emitter.count += 1;
182 for (const sig of signals_js_1.signals) {
183 try {
184 const fn = this.#sigListeners[sig];
185 if (fn)
186 this.#process.on(sig, fn);
187 }
188 catch (_) { }
189 }
190 this.#process.emit = (ev, ...a) => {
191 return this.#processEmit(ev, ...a);
192 };
193 this.#process.reallyExit = (code) => {
194 return this.#processReallyExit(code);
195 };
196 }
197 unload() {
198 if (!this.#loaded) {
199 return;
200 }
201 this.#loaded = false;
202 signals_js_1.signals.forEach(sig => {
203 const listener = this.#sigListeners[sig];
204 /* c8 ignore start */
205 if (!listener) {
206 throw new Error('Listener not defined for signal: ' + sig);
207 }
208 /* c8 ignore stop */
209 try {
210 this.#process.removeListener(sig, listener);
211 /* c8 ignore start */
212 }
213 catch (_) { }
214 /* c8 ignore stop */
215 });
216 this.#process.emit = this.#originalProcessEmit;
217 this.#process.reallyExit = this.#originalProcessReallyExit;
218 this.#emitter.count -= 1;
219 }
220 #processReallyExit(code) {
221 /* c8 ignore start */
222 if (!processOk(this.#process)) {
223 return 0;
224 }
225 this.#process.exitCode = code || 0;
226 /* c8 ignore stop */
227 this.#emitter.emit('exit', this.#process.exitCode, null);
228 return this.#originalProcessReallyExit.call(this.#process, this.#process.exitCode);
229 }
230 #processEmit(ev, ...args) {
231 const og = this.#originalProcessEmit;
232 if (ev === 'exit' && processOk(this.#process)) {
233 if (typeof args[0] === 'number') {
234 this.#process.exitCode = args[0];
235 /* c8 ignore start */
236 }
237 /* c8 ignore start */
238 const ret = og.call(this.#process, ev, ...args);
239 /* c8 ignore start */
240 this.#emitter.emit('exit', this.#process.exitCode, null);
241 /* c8 ignore stop */
242 return ret;
243 }
244 else {
245 return og.call(this.#process, ev, ...args);
246 }
247 }
248}
249const process = globalThis.process;
250// wrap so that we call the method on the actual handler, without
251// exporting it directly.
252_a = signalExitWrap(processOk(process) ? new SignalExit(process) : new SignalExitFallback()),
253/**
254 * Called when the process is exiting, whether via signal, explicit
255 * exit, or running out of stuff to do.
256 *
257 * If the global process object is not suitable for instrumentation,
258 * then this will be a no-op.
259 *
260 * Returns a function that may be used to unload signal-exit.
261 */
262exports.onExit = _a.onExit,
263/**
264 * Load the listeners. Likely you never need to call this, unless
265 * doing a rather deep integration with signal-exit functionality.
266 * Mostly exposed for the benefit of testing.
267 *
268 * @internal
269 */
270exports.load = _a.load,
271/**
272 * Unload the listeners. Likely you never need to call this, unless
273 * doing a rather deep integration with signal-exit functionality.
274 * Mostly exposed for the benefit of testing.
275 *
276 * @internal
277 */
278exports.unload = _a.unload;
279//# sourceMappingURL=index.js.map