Repository navigation
Expand file tree
/
Copy pathrepl.js
More file actions
98 lines (86 loc) · 2.69 KB
/
Copy pathrepl.js
File metadata and controls
98 lines (86 loc) · 2.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
'use strict';
const common = require('../common');
const ArrayStream = require('../common/arraystream');
const repl = require('node:repl');
common.skipIfInspectorDisabled();
function startNewREPLServer(replOpts = {}) {
const input = new ArrayStream();
const output = new ArrayStream();
output.accumulator = '';
output.write = (data) => (output.accumulator += `${data}`.replaceAll('\r', ''));
const replServer = repl.start({
prompt: '',
input,
output,
terminal: true,
allowBlockingCompletions: true,
...replOpts,
});
// The REPL evaluates input asynchronously, so a line written to `input` is
// not necessarily reflected in `output` by the time control returns. Track
// outstanding evaluations; `waitForIdle()` resolves once the count is zero
// AND has been zero across a macrotask boundary (so synchronously-pumped
// follow-up lines have a chance to register).
let inFlight = 0;
let settledTurns = 0;
const originalEval = replServer.eval;
replServer.eval = function(code, context, file, cb) {
inFlight++;
let decremented = false;
const settle = () => {
if (!decremented) {
decremented = true;
inFlight--;
settledTurns = 0; // New completion resets the idle streak
}
};
return originalEval.call(this, code, context, file, function(...args) {
settle();
return cb.apply(this, args);
});
};
function waitForIdle() {
return new Promise((resolve) => {
function check() {
if (inFlight === 0) {
// Require two consecutive idle turns so any line the REPL pumped
// synchronously after a completion has registered as in-flight.
if (++settledTurns >= 2) {
resolve();
return;
}
} else {
settledTurns = 0;
}
setImmediate(check);
}
// Start on a macrotask boundary so eval kicked off by `run()` registers.
settledTurns = 0;
setImmediate(check);
});
}
// Feed `data` into the REPL and resolve once evaluation has settled. `data`
// may be a single string (written verbatim) or an array of lines (each is
// written followed by a newline, mirroring `ArrayStream.prototype.run`).
function run(data) {
if (Array.isArray(data)) {
input.run(data);
} else {
input.emit('data', data);
}
return waitForIdle();
}
return { replServer, input, output, waitForIdle, run };
}
function complete(replServer, line) {
return new Promise((resolve, reject) => {
replServer.complete(line, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
module.exports = { startNewREPLServer, complete };