Skip to content

Commit 02ead45

Browse files
committed
repl: add fallback error detection after processTopLevelAwait error
1 parent 4de6f20 commit 02ead45

File tree

3 files changed

+59
-3
lines changed

3 files changed

+59
-3
lines changed

lib/internal/repl/await.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,10 @@ function processTopLevelAwait(src) {
120120
'^\n\n' + RegExpPrototypeSymbolReplace(/ \([^)]+\)/, e.message, '');
121121
// V8 unexpected token errors include the token string.
122122
if (StringPrototypeEndsWith(message, 'Unexpected token'))
123-
message += " '" + src[e.pos - wrapPrefix.length] + "'";
123+
message += " '" +
124+
// Wrapper end may cause acorn to report error position after the source
125+
(src[e.pos - wrapPrefix.length] ?? src[src.length - 1]) +
126+
"'";
124127
// eslint-disable-next-line no-restricted-syntax
125128
throw new SyntaxError(message);
126129
}

lib/repl.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ const {
7878
ReflectApply,
7979
RegExp,
8080
RegExpPrototypeExec,
81+
RegExpPrototypeSymbolReplace,
8182
RegExpPrototypeTest,
8283
SafeSet,
8384
SafeWeakSet,
@@ -434,8 +435,30 @@ function REPLServer(prompt,
434435
awaitPromise = true;
435436
}
436437
} catch (e) {
437-
decorateErrorStack(e);
438-
err = e;
438+
let recoverableError = false;
439+
if (e.name === 'SyntaxError') {
440+
// Remove all "await"s and attempt running the script
441+
// in order to detect if error is truly non recoverable
442+
const fallbackCode = RegExpPrototypeSymbolReplace(/\bawait\b/g, code, '');
443+
try {
444+
vm.createScript(fallbackCode, {
445+
filename: file,
446+
displayErrors: true,
447+
importModuleDynamically: async (specifier) => {
448+
return asyncESM.ESMLoader.import(specifier, parentURL);
449+
}
450+
});
451+
} catch (fallbackError) {
452+
if (isRecoverableError(fallbackError, fallbackCode)) {
453+
recoverableError = true;
454+
err = new Recoverable(e);
455+
}
456+
}
457+
}
458+
if (!recoverableError) {
459+
decorateErrorStack(e);
460+
err = e;
461+
}
439462
}
440463
}
441464

test/parallel/test-repl-top-level-await.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,36 @@ async function ordinaryTests() {
152152
'Unexpected token \'.\'',
153153
],
154154
],
155+
['for (const x of [1,2,3]) {\nawait x\n}', [
156+
'for (const x of [1,2,3]) {\r',
157+
'... await x\r',
158+
'... }\r',
159+
'undefined'
160+
]],
161+
['for (const x of [1,2,3]) {\nawait x;\n}', [
162+
'for (const x of [1,2,3]) {\r',
163+
'... await x;\r',
164+
'... }\r',
165+
'undefined'
166+
]],
167+
['for await (const x of [1,2,3]) {\nconsole.log(x)\n}', [
168+
'for await (const x of [1,2,3]) {\r',
169+
'... console.log(x)\r',
170+
'... }\r',
171+
'1',
172+
'2',
173+
'3',
174+
'undefined'
175+
]],
176+
['for await (const x of [1,2,3]) {\nconsole.log(x);\n}', [
177+
'for await (const x of [1,2,3]) {\r',
178+
'... console.log(x);\r',
179+
'... }\r',
180+
'1',
181+
'2',
182+
'3',
183+
'undefined'
184+
]],
155185
];
156186

157187
for (const [input, expected = [`${input}\r`], options = {}] of testCases) {

0 commit comments

Comments
 (0)