mirror of
https://github.com/nodejs/node.git
synced 2026-10-28 18:25:13 +08:00
Signed-off-by: Aviv Keller <me@aviv.sh> PR-URL: https://github.com/nodejs/node/pull/64034 Fixes: https://github.com/nodejs/node/issues/64523 Closes: https://github.com/nodejs/node/pull/64534 Fixes: https://github.com/nodejs/node/issues/39387 Closes: https://github.com/nodejs/node/pull/39392 Fixes: https://github.com/nodejs/node/issues/61390 Fixes: https://github.com/nodejs/node/issues/63126 Fixes: https://github.com/nodejs/node/issues/38503 Fixes: https://github.com/nodejs/node/issues/37445 Fixes: https://github.com/nodejs/node/issues/38145 Fixes: https://github.com/nodejs/node/issues/33369 Fixes: https://github.com/nodejs/node/issues/48131 Fixes: https://github.com/nodejs/node/issues/8309 Fixes: https://github.com/nodejs/node/issues/39689 Fixes: https://github.com/nodejs/node/issues/18931 Reviewed-By: Dario Piotrowicz <dario.piotrowicz@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com>
99 lines
2.7 KiB
JavaScript
99 lines
2.7 KiB
JavaScript
'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 };
|