Skip to content

Commit c9cd561

Browse files
committed
repl: catch promise errors during eval in completion
Fixes: #58903
1 parent 09b4c57 commit c9cd561

File tree

2 files changed

+56
-0
lines changed

2 files changed

+56
-0
lines changed

lib/repl.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ const {
9898

9999
const {
100100
isProxy,
101+
isPromise,
101102
} = require('internal/util/types');
102103

103104
const { BuiltinModule } = require('internal/bootstrap/realm');
@@ -1560,6 +1561,10 @@ function complete(line, callback) {
15601561
const evalExpr = `try { ${expr} } catch {}`;
15611562
this.eval(evalExpr, this.context, getREPLResourceName(), (e, obj) => {
15621563
try {
1564+
if (isPromise(obj)) {
1565+
obj.catch(() => {});
1566+
}
1567+
15631568
let p;
15641569
if ((typeof obj === 'object' && obj !== null) ||
15651570
typeof obj === 'function') {
@@ -1759,6 +1764,7 @@ function findExpressionCompleteTarget(code) {
17591764
* @returns {void}
17601765
*/
17611766
function includesProxiesOrGetters(expr, exprStr, evalFn, ctx, callback) {
1767+
17621768
if (expr?.type !== 'MemberExpression') {
17631769
// If the expression is not a member one for obvious reasons no getters are involved
17641770
return callback(false);
@@ -1796,6 +1802,9 @@ function includesProxiesOrGetters(expr, exprStr, evalFn, ctx, callback) {
17961802
// is the property identifier/literal)
17971803
if (expr.object.type === 'Identifier') {
17981804
return evalFn(`try { ${expr.object.name} } catch {}`, ctx, getREPLResourceName(), (err, obj) => {
1805+
if (isPromise(obj)) {
1806+
obj.catch(() => {});
1807+
}
17991808
if (err) {
18001809
return callback(false);
18011810
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const repl = require('repl');
5+
const ArrayStream = require('../common/arraystream');
6+
const assert = require('assert');
7+
8+
const completionTests = [
9+
{ send: 'Promise.reject().' },
10+
{ send: 'let p = Promise.reject().' },
11+
{ send: 'Promise.resolve().' },
12+
{ send: 'Promise.resolve().then(() => {}).' },
13+
{ send: `async function f() {throw new Error('test');}; f().` },
14+
{ send: `async function f() {}; f().` },
15+
];
16+
17+
(async function() {
18+
await runReplCompleteTests(completionTests);
19+
})().then(common.mustCall());
20+
21+
async function runReplCompleteTests(tests) {
22+
const input = new ArrayStream();
23+
const output = new ArrayStream();
24+
25+
const replServer = repl.start({
26+
prompt: '',
27+
input,
28+
output: output,
29+
allowBlockingCompletions: true,
30+
terminal: true
31+
});
32+
33+
replServer._domain.on('error', (e) => {
34+
assert.fail(`Error in REPL domain: ${e}`);
35+
});
36+
for (const { send } of tests) {
37+
replServer.complete(
38+
send,
39+
common.mustCall((error, data) => {
40+
assert.strictEqual(error, null);
41+
assert.strictEqual(data.length, 2);
42+
assert.strictEqual(typeof data[1], 'string');
43+
assert.ok(send.includes(data[1]));
44+
})
45+
);
46+
}
47+
}

0 commit comments

Comments
 (0)