@@ -72,6 +72,7 @@ const {
7272 ERR_CANNOT_WATCH_SIGINT ,
7373 ERR_INVALID_ARG_TYPE ,
7474 ERR_INVALID_REPL_EVAL_CONFIG ,
75+ ERR_INVALID_REPL_INPUT ,
7576 ERR_SCRIPT_EXECUTION_INTERRUPTED
7677} = require ( 'internal/errors' ) . codes ;
7778const { sendInspectorCommand } = require ( 'internal/util/inspector' ) ;
@@ -98,10 +99,13 @@ let processTopLevelAwait;
9899
99100const parentModule = module ;
100101const replMap = new WeakMap ( ) ;
102+ const domainSet = new WeakSet ( ) ;
101103
102104const kBufferedCommandSymbol = Symbol ( 'bufferedCommand' ) ;
103105const kContextId = Symbol ( 'contextId' ) ;
104106
107+ let addedNewListener = false ;
108+
105109try {
106110 // Hack for require.resolve("./relative") to work properly.
107111 module . filename = path . resolve ( 'repl' ) ;
@@ -201,6 +205,28 @@ function REPLServer(prompt,
201205 throw new ERR_INVALID_REPL_EVAL_CONFIG ( ) ;
202206 }
203207
208+ // Add this listener only once and use a WeakSet that contains the REPLs
209+ // domains. Otherwise we'd have to add a single listener to each REPL instance
210+ // and that could trigger the `MaxListenersExceededWarning`.
211+ if ( ! options [ kStandaloneREPL ] && ! addedNewListener ) {
212+ process . prependListener ( 'newListener' , ( event , listener ) => {
213+ if ( event === 'uncaughtException' &&
214+ process . domain &&
215+ listener . name !== 'domainUncaughtExceptionClear' &&
216+ domainSet . has ( process . domain ) ) {
217+ // Throw an error so that the event will not be added and the current
218+ // domain takes over. That way the user is notified about the error
219+ // and the current code evaluation is stopped, just as any other code
220+ // that contains an error.
221+ throw new ERR_INVALID_REPL_INPUT (
222+ 'Listeners for `uncaughtException` cannot be used in the REPL' ) ;
223+ }
224+ } ) ;
225+ addedNewListener = true ;
226+ }
227+
228+ domainSet . add ( this . _domain ) ;
229+
204230 let rli = this ;
205231 Object . defineProperty ( this , 'rli' , {
206232 get : deprecate ( ( ) => rli ,
@@ -261,7 +287,7 @@ function REPLServer(prompt,
261287 // statement rather than an object literal. So, we first try
262288 // to wrap it in parentheses, so that it will be interpreted as
263289 // an expression. Note that if the above condition changes,
264- // lib/internal/repl/recoverable .js needs to be changed to match.
290+ // lib/internal/repl/utils .js needs to be changed to match.
265291 code = `(${ code . trim ( ) } )\n` ;
266292 wrappedCmd = true ;
267293 }
@@ -458,22 +484,31 @@ function REPLServer(prompt,
458484 }
459485 }
460486
461- if ( errStack === '' ) {
462- errStack = `Thrown: ${ self . writer ( e ) } \n` ;
463- } else {
464- const ln = errStack . endsWith ( '\n' ) ? '' : '\n' ;
465- errStack = `Thrown:\n${ errStack } ${ ln } ` ;
466- }
467-
468487 if ( ! self . underscoreErrAssigned ) {
469488 self . lastError = e ;
470489 }
471490
472491 const top = replMap . get ( self ) ;
473- top . outputStream . write ( errStack ) ;
474- top . clearBufferedCommand ( ) ;
475- top . lines . level = [ ] ;
476- top . displayPrompt ( ) ;
492+ if ( options [ kStandaloneREPL ] &&
493+ process . listenerCount ( 'uncaughtException' ) !== 0 ) {
494+ process . nextTick ( ( ) => {
495+ process . emit ( 'uncaughtException' , e ) ;
496+ top . clearBufferedCommand ( ) ;
497+ top . lines . level = [ ] ;
498+ top . displayPrompt ( ) ;
499+ } ) ;
500+ } else {
501+ if ( errStack === '' ) {
502+ errStack = `Thrown: ${ self . writer ( e ) } \n` ;
503+ } else {
504+ const ln = errStack . endsWith ( '\n' ) ? '' : '\n' ;
505+ errStack = `Thrown:\n${ errStack } ${ ln } ` ;
506+ }
507+ top . outputStream . write ( errStack ) ;
508+ top . clearBufferedCommand ( ) ;
509+ top . lines . level = [ ] ;
510+ top . displayPrompt ( ) ;
511+ }
477512 } ) ;
478513
479514 self . resetContext ( ) ;
0 commit comments