mirror of https://github.com/nodejs/node.git
repl: don't use deprecated `domain` module
This commit is contained in:
parent
73414f34e8
commit
b0d6991d96
|
@ -2016,13 +2016,6 @@ An invalid `options.protocol` was passed to `http.request()`.
|
|||
Both `breakEvalOnSigint` and `eval` options were set in the [`REPL`][] config,
|
||||
which is not supported.
|
||||
|
||||
<a id="ERR_INVALID_REPL_INPUT"></a>
|
||||
|
||||
### `ERR_INVALID_REPL_INPUT`
|
||||
|
||||
The input may not be used in the [`REPL`][]. The conditions under which this
|
||||
error is used are described in the [`REPL`][] documentation.
|
||||
|
||||
<a id="ERR_INVALID_RETURN_PROPERTY"></a>
|
||||
|
||||
### `ERR_INVALID_RETURN_PROPERTY`
|
||||
|
@ -3538,6 +3531,13 @@ removed: v16.7.0
|
|||
While using the Performance Timing API (`perf_hooks`), a performance mark is
|
||||
invalid.
|
||||
|
||||
<a id="ERR_INVALID_REPL_INPUT"></a>
|
||||
|
||||
### `ERR_INVALID_REPL_INPUT`
|
||||
|
||||
The input may not be used in the [`REPL`][]. The conditions under which this
|
||||
error is used are described in the [`REPL`][] documentation.
|
||||
|
||||
<a id="ERR_INVALID_TRANSFER_OBJECT"></a>
|
||||
|
||||
### `ERR_INVALID_TRANSFER_OBJECT`
|
||||
|
|
|
@ -147,39 +147,6 @@ global or scoped variable, the input `fs` will be evaluated on-demand as
|
|||
> fs.createReadStream('./some/file');
|
||||
```
|
||||
|
||||
#### Global uncaught exceptions
|
||||
|
||||
<!-- YAML
|
||||
changes:
|
||||
- version: v12.3.0
|
||||
pr-url: https://github.com/nodejs/node/pull/27151
|
||||
description: The `'uncaughtException'` event is from now on triggered if the
|
||||
repl is used as standalone program.
|
||||
-->
|
||||
|
||||
The REPL uses the [`domain`][] module to catch all uncaught exceptions for that
|
||||
REPL session.
|
||||
|
||||
This use of the [`domain`][] module in the REPL has these side effects:
|
||||
|
||||
* Uncaught exceptions only emit the [`'uncaughtException'`][] event in the
|
||||
standalone REPL. Adding a listener for this event in a REPL within
|
||||
another Node.js program results in [`ERR_INVALID_REPL_INPUT`][].
|
||||
|
||||
```js
|
||||
const r = repl.start();
|
||||
|
||||
r.write('process.on("uncaughtException", () => console.log("Foobar"));\n');
|
||||
// Output stream includes:
|
||||
// TypeError [ERR_INVALID_REPL_INPUT]: Listeners for `uncaughtException`
|
||||
// cannot be used in the REPL
|
||||
|
||||
r.close();
|
||||
```
|
||||
|
||||
* Trying to use [`process.setUncaughtExceptionCaptureCallback()`][] throws
|
||||
an [`ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE`][] error.
|
||||
|
||||
#### Assignment of the `_` (underscore) variable
|
||||
|
||||
<!-- YAML
|
||||
|
@ -768,13 +735,8 @@ avoiding open network interfaces.
|
|||
|
||||
[TTY keybindings]: readline.md#tty-keybindings
|
||||
[ZSH]: https://en.wikipedia.org/wiki/Z_shell
|
||||
[`'uncaughtException'`]: process.md#event-uncaughtexception
|
||||
[`--no-experimental-repl-await`]: cli.md#--no-experimental-repl-await
|
||||
[`ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE`]: errors.md#err_domain_cannot_set_uncaught_exception_capture
|
||||
[`ERR_INVALID_REPL_INPUT`]: errors.md#err_invalid_repl_input
|
||||
[`curl(1)`]: https://curl.haxx.se/docs/manpage.html
|
||||
[`domain`]: domain.md
|
||||
[`process.setUncaughtExceptionCaptureCallback()`]: process.md#processsetuncaughtexceptioncapturecallbackfn
|
||||
[`readline.InterfaceCompleter`]: readline.md#use-of-the-completer-function
|
||||
[`repl.ReplServer`]: #class-replserver
|
||||
[`repl.start()`]: #replstartoptions
|
||||
|
|
|
@ -1502,7 +1502,6 @@ E('ERR_INVALID_PROTOCOL',
|
|||
TypeError);
|
||||
E('ERR_INVALID_REPL_EVAL_CONFIG',
|
||||
'Cannot specify both "breakEvalOnSigint" and "eval" for REPL', TypeError);
|
||||
E('ERR_INVALID_REPL_INPUT', '%s', TypeError);
|
||||
E('ERR_INVALID_RETURN_PROPERTY', (input, name, prop, value) => {
|
||||
return `Expected a valid ${input} to be returned for the "${prop}" from the` +
|
||||
` "${name}" hook but got ${determineSpecificType(value)}.`;
|
||||
|
|
143
lib/repl.js
143
lib/repl.js
|
@ -60,6 +60,7 @@ const {
|
|||
ArrayPrototypeUnshift,
|
||||
Boolean,
|
||||
Error: MainContextError,
|
||||
FunctionPrototypeApply,
|
||||
FunctionPrototypeBind,
|
||||
JSONStringify,
|
||||
MathMaxApply,
|
||||
|
@ -76,9 +77,9 @@ const {
|
|||
ReflectApply,
|
||||
RegExp,
|
||||
RegExpPrototypeExec,
|
||||
SafeMap,
|
||||
SafePromiseRace,
|
||||
SafeSet,
|
||||
SafeWeakSet,
|
||||
StringPrototypeCharAt,
|
||||
StringPrototypeCodePointAt,
|
||||
StringPrototypeEndsWith,
|
||||
|
@ -138,7 +139,6 @@ ArrayPrototypeForEach(
|
|||
BuiltinModule.getSchemeOnlyModuleNames(),
|
||||
(lib) => ArrayPrototypePush(nodeSchemeBuiltinLibs, `node:${lib}`),
|
||||
);
|
||||
const domain = require('domain');
|
||||
let debug = require('internal/util/debuglog').debuglog('repl', (fn) => {
|
||||
debug = fn;
|
||||
});
|
||||
|
@ -147,7 +147,6 @@ const {
|
|||
codes: {
|
||||
ERR_CANNOT_WATCH_SIGINT,
|
||||
ERR_INVALID_REPL_EVAL_CONFIG,
|
||||
ERR_INVALID_REPL_INPUT,
|
||||
ERR_MISSING_ARGS,
|
||||
ERR_SCRIPT_EXECUTION_INTERRUPTED,
|
||||
},
|
||||
|
@ -191,6 +190,7 @@ const {
|
|||
const {
|
||||
makeContextifyScript,
|
||||
} = require('internal/vm');
|
||||
const { createHook } = require('async_hooks');
|
||||
let nextREPLResourceNumber = 1;
|
||||
// This prevents v8 code cache from getting confused and using a different
|
||||
// cache from a resource of the same name
|
||||
|
@ -205,13 +205,48 @@ const globalBuiltins =
|
|||
new SafeSet(vm.runInNewContext('Object.getOwnPropertyNames(globalThis)'));
|
||||
|
||||
const parentModule = module;
|
||||
const domainSet = new SafeWeakSet();
|
||||
|
||||
const kBufferedCommandSymbol = Symbol('bufferedCommand');
|
||||
const kContextId = Symbol('contextId');
|
||||
const kLoadingSymbol = Symbol('loading');
|
||||
const kListeningREPLs = new SafeSet();
|
||||
const kAsyncREPLMap = new SafeMap();
|
||||
let kActiveREPL;
|
||||
const kAsyncHook = createHook({
|
||||
init(asyncId) {
|
||||
if (kActiveREPL) {
|
||||
kAsyncREPLMap.set(asyncId, kActiveREPL);
|
||||
}
|
||||
},
|
||||
|
||||
let addedNewListener = false;
|
||||
before(asyncId) {
|
||||
kActiveREPL = kAsyncREPLMap.get(asyncId) || kActiveREPL;
|
||||
},
|
||||
|
||||
destroy(asyncId) {
|
||||
kAsyncREPLMap.delete(asyncId);
|
||||
},
|
||||
});
|
||||
|
||||
let kHasSetUncaughtListener = false;
|
||||
|
||||
function handleUncaughtException(er) {
|
||||
if (kActiveREPL) {
|
||||
kActiveREPL._onEvalError(er);
|
||||
// If there are no other event listeners, throw the uncaught exception.
|
||||
} else if (process.listenerCount('uncaughtException') <= 1) {
|
||||
throw er;
|
||||
}
|
||||
}
|
||||
|
||||
function removeListeningREPL(repl) {
|
||||
kListeningREPLs.delete(repl);
|
||||
if (kListeningREPLs.size === 0) {
|
||||
kAsyncHook.disable();
|
||||
kHasSetUncaughtListener = false;
|
||||
process.off('uncaughtException', handleUncaughtException);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Hack for require.resolve("./relative") to work properly.
|
||||
|
@ -346,7 +381,6 @@ function REPLServer(prompt,
|
|||
|
||||
this.allowBlockingCompletions = !!options.allowBlockingCompletions;
|
||||
this.useColors = !!options.useColors;
|
||||
this._domain = options.domain || domain.create();
|
||||
this.useGlobal = !!useGlobal;
|
||||
this.ignoreUndefined = !!ignoreUndefined;
|
||||
this.replMode = replMode || module.exports.REPL_MODE_SLOPPY;
|
||||
|
@ -369,28 +403,8 @@ function REPLServer(prompt,
|
|||
// It is possible to introspect the running REPL accessing this variable
|
||||
// from inside the REPL. This is useful for anyone working on the REPL.
|
||||
module.exports.repl = this;
|
||||
} else if (!addedNewListener) {
|
||||
// Add this listener only once and use a WeakSet that contains the REPLs
|
||||
// domains. Otherwise we'd have to add a single listener to each REPL
|
||||
// instance and that could trigger the `MaxListenersExceededWarning`.
|
||||
process.prependListener('newListener', (event, listener) => {
|
||||
if (event === 'uncaughtException' &&
|
||||
process.domain &&
|
||||
listener.name !== 'domainUncaughtExceptionClear' &&
|
||||
domainSet.has(process.domain)) {
|
||||
// Throw an error so that the event will not be added and the current
|
||||
// domain takes over. That way the user is notified about the error
|
||||
// and the current code evaluation is stopped, just as any other code
|
||||
// that contains an error.
|
||||
throw new ERR_INVALID_REPL_INPUT(
|
||||
'Listeners for `uncaughtException` cannot be used in the REPL');
|
||||
}
|
||||
});
|
||||
addedNewListener = true;
|
||||
}
|
||||
|
||||
domainSet.add(this._domain);
|
||||
|
||||
const savedRegExMatches = ['', '', '', '', '', '', '', '', '', ''];
|
||||
const sep = '\u0000\u0000\u0000';
|
||||
const regExMatcher = new RegExp(`^${sep}(.*)${sep}(.*)${sep}(.*)${sep}(.*)` +
|
||||
|
@ -612,13 +626,8 @@ function REPLServer(prompt,
|
|||
}
|
||||
} catch (e) {
|
||||
err = e;
|
||||
|
||||
if (process.domain) {
|
||||
debug('not recoverable, send to domain');
|
||||
process.domain.emit('error', err);
|
||||
process.domain.exit();
|
||||
return;
|
||||
}
|
||||
self._onEvalError(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (awaitPromise && !err) {
|
||||
|
@ -644,10 +653,8 @@ function REPLServer(prompt,
|
|||
const result = (await promise)?.value;
|
||||
finishExecution(null, result);
|
||||
} catch (err) {
|
||||
if (err && process.domain) {
|
||||
debug('not recoverable, send to domain');
|
||||
process.domain.emit('error', err);
|
||||
process.domain.exit();
|
||||
if (err) {
|
||||
self._onEvalError(err);
|
||||
return;
|
||||
}
|
||||
finishExecution(err);
|
||||
|
@ -665,10 +672,24 @@ function REPLServer(prompt,
|
|||
}
|
||||
}
|
||||
|
||||
self.eval = self._domain.bind(eval_);
|
||||
self.eval = function(...args) {
|
||||
kActiveREPL = this;
|
||||
|
||||
self._domain.on('error', function debugDomainError(e) {
|
||||
debug('domain error');
|
||||
const cb = args[3];
|
||||
args[3] = (...cbArgs) => {
|
||||
kActiveREPL = null;
|
||||
FunctionPrototypeApply(cb, null, cbArgs);
|
||||
};
|
||||
|
||||
try {
|
||||
FunctionPrototypeApply(eval_, this, args);
|
||||
} catch (e) {
|
||||
self._onEvalError(e);
|
||||
}
|
||||
};
|
||||
|
||||
self._onEvalError = function _onEvalError(e) {
|
||||
debug('eval error');
|
||||
let errStack = '';
|
||||
|
||||
if (typeof e === 'object' && e !== null) {
|
||||
|
@ -696,11 +717,6 @@ function REPLServer(prompt,
|
|||
});
|
||||
decorateErrorStack(e);
|
||||
|
||||
if (e.domainThrown) {
|
||||
delete e.domain;
|
||||
delete e.domainThrown;
|
||||
}
|
||||
|
||||
if (isError(e)) {
|
||||
if (e.stack) {
|
||||
if (e.name === 'SyntaxError') {
|
||||
|
@ -740,10 +756,13 @@ function REPLServer(prompt,
|
|||
self.lastError = e;
|
||||
}
|
||||
|
||||
if (options[kStandaloneREPL] &&
|
||||
process.listenerCount('uncaughtException') !== 0) {
|
||||
if (options[kStandaloneREPL] && process.listenerCount('uncaughtException') > 1) {
|
||||
process.nextTick(() => {
|
||||
process.emit('uncaughtException', e);
|
||||
const listeners = process.listeners('uncaughtException');
|
||||
for (let i = 0; i < listeners.length; i++) {
|
||||
const listener = listeners[i];
|
||||
if (listener !== handleUncaughtException) listener(e);
|
||||
}
|
||||
self.clearBufferedCommand();
|
||||
self.lines.level = [];
|
||||
self.displayPrompt();
|
||||
|
@ -778,7 +797,14 @@ function REPLServer(prompt,
|
|||
self.lines.level = [];
|
||||
self.displayPrompt();
|
||||
}
|
||||
});
|
||||
};
|
||||
kListeningREPLs.add(self);
|
||||
|
||||
if (!kHasSetUncaughtListener) {
|
||||
kAsyncHook.enable();
|
||||
process.on('uncaughtException', handleUncaughtException);
|
||||
kHasSetUncaughtListener = true;
|
||||
}
|
||||
|
||||
self.clearBufferedCommand();
|
||||
|
||||
|
@ -951,7 +977,7 @@ function REPLServer(prompt,
|
|||
self.displayPrompt();
|
||||
return;
|
||||
}
|
||||
self._domain.emit('error', e.err || e);
|
||||
self._onEvalError(e.err || e);
|
||||
}
|
||||
|
||||
// Clear buffer if no SyntaxErrors
|
||||
|
@ -971,8 +997,7 @@ function REPLServer(prompt,
|
|||
self.output.write(self.writer(ret) + '\n');
|
||||
}
|
||||
|
||||
// Display prompt again (unless we already did by emitting the 'error'
|
||||
// event on the domain instance).
|
||||
// Display prompt again
|
||||
if (!e) {
|
||||
self.displayPrompt();
|
||||
}
|
||||
|
@ -1082,15 +1107,17 @@ REPLServer.prototype.clearBufferedCommand = function clearBufferedCommand() {
|
|||
REPLServer.prototype.close = function close() {
|
||||
if (this.terminal && this._flushing && !this._closingOnFlush) {
|
||||
this._closingOnFlush = true;
|
||||
this.once('flushHistory', () =>
|
||||
ReflectApply(Interface.prototype.close, this, []),
|
||||
);
|
||||
this.once('flushHistory', () => {
|
||||
removeListeningREPL(this);
|
||||
ReflectApply(Interface.prototype.close, this, []);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
process.nextTick(() =>
|
||||
ReflectApply(Interface.prototype.close, this, []),
|
||||
);
|
||||
process.nextTick(() => {
|
||||
removeListeningREPL(this);
|
||||
ReflectApply(Interface.prototype.close, this, []);
|
||||
});
|
||||
};
|
||||
|
||||
REPLServer.prototype.createContext = function() {
|
||||
|
|
|
@ -32,9 +32,7 @@ const putIn = new ArrayStream();
|
|||
const testMe = repl.start('', putIn);
|
||||
|
||||
// Some errors are passed to the domain, but do not callback.
|
||||
testMe._domain.on('error', function(err) {
|
||||
throw err;
|
||||
});
|
||||
testMe._onEvalError = (err) => { throw err };
|
||||
|
||||
// Nesting of structures causes REPL to use a nested REPL for completion.
|
||||
putIn.run([
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
require('../common');
|
||||
const ArrayStream = require('../common/arraystream');
|
||||
|
||||
const repl = require('repl');
|
||||
|
||||
const putIn = new ArrayStream();
|
||||
repl.start('', putIn);
|
||||
|
||||
putIn.write = function(data) {
|
||||
// Don't use assert for this because the domain might catch it, and
|
||||
// give a false negative. Don't throw, just print and exit.
|
||||
if (data === 'OK\n') {
|
||||
console.log('ok');
|
||||
} else {
|
||||
console.error(data);
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
putIn.run([
|
||||
'require("domain").create().on("error", function() { console.log("OK") })' +
|
||||
'.run(function() { throw new Error("threw") })',
|
||||
]);
|
|
@ -35,11 +35,11 @@ const putIn = new ArrayStream();
|
|||
const testMe = repl.start('', putIn);
|
||||
|
||||
// Some errors might be passed to the domain.
|
||||
testMe._domain.on('error', function(reason) {
|
||||
testMe._onEvalError = function(reason) {
|
||||
const err = new Error('Test failed');
|
||||
err.reason = reason;
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
|
||||
const testFile = [
|
||||
'let inner = (function() {',
|
||||
|
|
|
@ -26,7 +26,7 @@ const testMe = repl.start({
|
|||
});
|
||||
|
||||
// Some errors are passed to the domain, but do not callback
|
||||
testMe._domain.on('error', assert.ifError);
|
||||
testMe._onEvalError = assert.ifError;
|
||||
|
||||
// Tab complete provides built in libs for import()
|
||||
testMe.complete('import(\'', common.mustCall((error, data) => {
|
||||
|
|
|
@ -61,7 +61,7 @@ const testMe = repl.start({
|
|||
});
|
||||
|
||||
// Some errors are passed to the domain, but do not callback
|
||||
testMe._domain.on('error', assert.ifError);
|
||||
testMe._onEvalError = assert.ifError;
|
||||
|
||||
// Tab Complete will not break in an object literal
|
||||
putIn.run([
|
||||
|
|
|
@ -11,7 +11,7 @@ const testMe = repl.start('', putIn, function(cmd, context, filename,
|
|||
callback(null, cmd);
|
||||
});
|
||||
|
||||
testMe._domain.on('error', common.mustNotCall());
|
||||
testMe._onEvalError = common.mustNotCall();
|
||||
|
||||
testMe.complete('', function(err, results) {
|
||||
assert.strictEqual(err, null);
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
// This verifies that adding an `uncaughtException` listener in an REPL instance
|
||||
// does not suppress errors in the whole application. Adding such listener
|
||||
// should throw.
|
||||
|
||||
require('../common');
|
||||
const ArrayStream = require('../common/arraystream');
|
||||
const repl = require('repl');
|
||||
const assert = require('assert');
|
||||
|
||||
let accum = '';
|
||||
|
||||
const output = new ArrayStream();
|
||||
output.write = (data) => accum += data.replace('\r', '');
|
||||
|
||||
const r = repl.start({
|
||||
prompt: '',
|
||||
input: new ArrayStream(),
|
||||
output,
|
||||
terminal: false,
|
||||
useColors: false,
|
||||
global: false
|
||||
});
|
||||
|
||||
r.write(
|
||||
'process.nextTick(() => {\n' +
|
||||
' process.on("uncaughtException", () => console.log("Foo"));\n' +
|
||||
' throw new TypeError("foobar");\n' +
|
||||
'});\n'
|
||||
);
|
||||
r.write(
|
||||
'setTimeout(() => {\n' +
|
||||
' throw new RangeError("abc");\n' +
|
||||
'}, 1);console.log()\n'
|
||||
);
|
||||
r.close();
|
||||
|
||||
setTimeout(() => {
|
||||
const len = process.listenerCount('uncaughtException');
|
||||
process.removeAllListeners('uncaughtException');
|
||||
assert.strictEqual(len, 0);
|
||||
assert.match(accum, /ERR_INVALID_REPL_INPUT.*(?!Type)RangeError: abc/s);
|
||||
}, 2);
|
|
@ -1,77 +1,74 @@
|
|||
'use strict';
|
||||
require('../common');
|
||||
|
||||
const common = require('../common');
|
||||
const ArrayStream = require('../common/arraystream');
|
||||
const assert = require('assert');
|
||||
const repl = require('repl');
|
||||
|
||||
let count = 0;
|
||||
const results = [];
|
||||
|
||||
function run({ command, expected, useColors = false }) {
|
||||
let accum = '';
|
||||
// Function to run a single test case
|
||||
async function runTest({ command, expected, useColors = false }) {
|
||||
const result = { output: '' };
|
||||
|
||||
const output = new ArrayStream();
|
||||
output.write = (data) => accum += data.replace('\r', '');
|
||||
// Custom stream to capture REPL output
|
||||
const replOutput = new ArrayStream();
|
||||
replOutput.write = (data) => { result.output += data.replace('\r', ''); };
|
||||
|
||||
const r = repl.start({
|
||||
// Start REPL instance
|
||||
const replInstance = repl.start({
|
||||
prompt: '',
|
||||
input: new ArrayStream(),
|
||||
output,
|
||||
output: replOutput,
|
||||
terminal: false,
|
||||
useColors
|
||||
useColors,
|
||||
});
|
||||
|
||||
r.write(`${command}\n`);
|
||||
if (typeof expected === 'string') {
|
||||
assert.strictEqual(accum, expected);
|
||||
} else {
|
||||
assert.match(accum, expected);
|
||||
}
|
||||
// Execute the command
|
||||
replInstance.write(`${command}\n`);
|
||||
|
||||
// Verify that the repl is still working as expected.
|
||||
accum = '';
|
||||
r.write('1 + 1\n');
|
||||
// eslint-disable-next-line no-control-regex
|
||||
assert.strictEqual(accum.replace(/\u001b\[[0-9]+m/g, ''), '2\n');
|
||||
r.close();
|
||||
count++;
|
||||
// Validate output
|
||||
assert.strictEqual(result.output, expected);
|
||||
|
||||
// Store REPL instance for future cleanup
|
||||
result.replInstance = replInstance;
|
||||
results.push(result);
|
||||
}
|
||||
|
||||
const tests = [
|
||||
{
|
||||
useColors: true,
|
||||
command: 'x',
|
||||
expected: 'Uncaught ReferenceError: x is not defined\n'
|
||||
},
|
||||
{
|
||||
useColors: true,
|
||||
command: 'throw { foo: "test" }',
|
||||
expected: "Uncaught { foo: \x1B[32m'test'\x1B[39m }\n"
|
||||
},
|
||||
{
|
||||
command: 'process.on("uncaughtException", () => console.log("Foobar"));\n',
|
||||
expected: /^Uncaught:\nTypeError \[ERR_INVALID_REPL_INPUT]: Listeners for `/
|
||||
},
|
||||
{
|
||||
command: 'x;\n',
|
||||
expected: 'Uncaught ReferenceError: x is not defined\n'
|
||||
},
|
||||
{
|
||||
command: 'process.on("uncaughtException", () => console.log("Foobar"));' +
|
||||
'console.log("Baz");\n',
|
||||
expected: /^Uncaught:\nTypeError \[ERR_INVALID_REPL_INPUT]: Listeners for `/
|
||||
},
|
||||
{
|
||||
command: 'console.log("Baz");' +
|
||||
'process.on("uncaughtException", () => console.log("Foobar"));\n',
|
||||
expected: /^Baz\nUncaught:\nTypeError \[ERR_INVALID_REPL_INPUT]:.*uncaughtException/
|
||||
},
|
||||
// Test cases
|
||||
const testCases = [
|
||||
{ useColors: true, command: 'x', expected: 'Uncaught ReferenceError: x is not defined\n' },
|
||||
{ useColors: true, command: 'throw { foo: "test" }', expected: "Uncaught { foo: \x1B[32m'test'\x1B[39m }\n" },
|
||||
{ command: 'x;\n', expected: 'Uncaught ReferenceError: x is not defined\n' },
|
||||
];
|
||||
|
||||
process.on('exit', () => {
|
||||
// To actually verify that the test passed we have to make sure no
|
||||
// `uncaughtException` listeners exist anymore.
|
||||
process.removeAllListeners('uncaughtException');
|
||||
assert.strictEqual(count, tests.length);
|
||||
});
|
||||
// Execute tests
|
||||
testCases.forEach(runTest);
|
||||
|
||||
tests.forEach(run);
|
||||
// Verify all tests ran
|
||||
assert.strictEqual(results.length, testCases.length);
|
||||
|
||||
// Check 'uncaughtException' listener count
|
||||
assert.strictEqual(process.listenerCount('uncaughtException'), 1);
|
||||
|
||||
// Test uncaught exception handling
|
||||
const errorToThrow = new Error('Thrown');
|
||||
process.once('uncaughtException', common.mustCall((err) => {
|
||||
assert.strictEqual(err, errorToThrow);
|
||||
}));
|
||||
|
||||
// Trigger uncaught exception
|
||||
process.nextTick(() => { throw errorToThrow; });
|
||||
|
||||
// Cleanup
|
||||
setTimeout(common.mustCall(() => {
|
||||
results.forEach(({ replInstance }) => {
|
||||
replInstance.close();
|
||||
});
|
||||
setTimeout(common.mustCall(() => {
|
||||
results.forEach(({ output }) => {
|
||||
assert.doesNotMatch(output, /Uncaught Error: Thrown/);
|
||||
});
|
||||
assert.strictEqual(process.listenerCount('uncaughtException'), 0);
|
||||
}), 100);
|
||||
}), 100);
|
||||
|
|
|
@ -242,6 +242,10 @@ function initRepl(mode, useGlobal) {
|
|||
}
|
||||
|
||||
function assertOutput(output, expected) {
|
||||
const lines = output.accum.trim().split('\n');
|
||||
const lines = output.accum
|
||||
.trim()
|
||||
.split('\n')
|
||||
// Remove stack trace
|
||||
.filter((line) => !line.trim().startsWith('at'));
|
||||
assert.deepStrictEqual(lines, expected);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue