Skip to content

Commit b168ec2

Browse files
ejose19targos
authored andcommitted
repl: processTopLevelAwait fallback error handling
PR-URL: #39290 Reviewed-By: Guy Bedford <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent a101fe6 commit b168ec2

File tree

3 files changed

+68
-3
lines changed

3 files changed

+68
-3
lines changed

‎lib/internal/repl/await.js‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,10 @@ function processTopLevelAwait(src){
184184
'^\n\n'+RegExpPrototypeSymbolReplace(/\([^)]+\)/,e.message,'');
185185
// V8 unexpected token errors include the token string.
186186
if(StringPrototypeEndsWith(message,'Unexpected token'))
187-
message+=" '"+src[e.pos-wrapPrefix.length]+"'";
187+
message+=" '"+
188+
// Wrapper end may cause acorn to report error position after the source
189+
(src[e.pos-wrapPrefix.length]??src[src.length-1])+
190+
"'";
188191
// eslint-disable-next-line no-restricted-syntax
189192
thrownewSyntaxError(message);
190193
}

‎lib/repl.js‎

Lines changed: 34 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,39 @@ function REPLServer(prompt,
434435
awaitPromise=true;
435436
}
436437
}catch(e){
437-
decorateErrorStack(e);
438-
err=e;
438+
letrecoverableError=false;
439+
if(e.name==='SyntaxError'){
440+
letparentURL;
441+
try{
442+
const{ pathToFileURL }=require('url');
443+
// Adding `/repl` prevents dynamic imports from loading relative
444+
// to the parent of `process.cwd()`.
445+
parentURL=pathToFileURL(path.join(process.cwd(),'repl')).href;
446+
}catch{
447+
}
448+
449+
// Remove all "await"s and attempt running the script
450+
// in order to detect if error is truly non recoverable
451+
constfallbackCode=RegExpPrototypeSymbolReplace(/\bawait\b/g,code,'');
452+
try{
453+
vm.createScript(fallbackCode,{
454+
filename: file,
455+
displayErrors: true,
456+
importModuleDynamically: async(specifier)=>{
457+
returnasyncESM.ESMLoader.import(specifier,parentURL);
458+
}
459+
});
460+
}catch(fallbackError){
461+
if(isRecoverableError(fallbackError,fallbackCode)){
462+
recoverableError=true;
463+
err=newRecoverable(e);
464+
}
465+
}
466+
}
467+
if(!recoverableError){
468+
decorateErrorStack(e);
469+
err=e;
470+
}
439471
}
440472
}
441473

‎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={}]oftestCases){

0 commit comments

Comments
(0)