@@ -72,6 +72,7 @@ const {
72
72
ERR_CANNOT_WATCH_SIGINT ,
73
73
ERR_INVALID_ARG_TYPE ,
74
74
ERR_INVALID_REPL_EVAL_CONFIG ,
75
+ ERR_INVALID_REPL_INPUT ,
75
76
ERR_SCRIPT_EXECUTION_INTERRUPTED
76
77
} = require ( 'internal/errors' ) . codes ;
77
78
const { sendInspectorCommand } = require ( 'internal/util/inspector' ) ;
@@ -101,10 +102,13 @@ let processTopLevelAwait;
101
102
102
103
const parentModule = module ;
103
104
const replMap = new WeakMap ( ) ;
105
+ const domainSet = new WeakSet ( ) ;
104
106
105
107
const kBufferedCommandSymbol = Symbol ( 'bufferedCommand' ) ;
106
108
const kContextId = Symbol ( 'contextId' ) ;
107
109
110
+ let addedNewListener = false ;
111
+
108
112
try {
109
113
// Hack for require.resolve("./relative") to work properly.
110
114
module . filename = path . resolve ( 'repl' ) ;
@@ -204,6 +208,28 @@ function REPLServer(prompt,
204
208
throw new ERR_INVALID_REPL_EVAL_CONFIG ( ) ;
205
209
}
206
210
211
+ // Add this listener only once and use a WeakSet that contains the REPLs
212
+ // domains. Otherwise we'd have to add a single listener to each REPL instance
213
+ // and that could trigger the `MaxListenersExceededWarning`.
214
+ if ( ! options [ kStandaloneREPL ] && ! addedNewListener ) {
215
+ process . prependListener ( 'newListener' , ( event , listener ) => {
216
+ if ( event === 'uncaughtException' &&
217
+ process . domain &&
218
+ listener . name !== 'domainUncaughtExceptionClear' &&
219
+ domainSet . has ( process . domain ) ) {
220
+ // Throw an error so that the event will not be added and the current
221
+ // domain takes over. That way the user is notified about the error
222
+ // and the current code evaluation is stopped, just as any other code
223
+ // that contains an error.
224
+ throw new ERR_INVALID_REPL_INPUT (
225
+ 'Listeners for `uncaughtException` cannot be used in the REPL' ) ;
226
+ }
227
+ } ) ;
228
+ addedNewListener = true ;
229
+ }
230
+
231
+ domainSet . add ( this . _domain ) ;
232
+
207
233
let rli = this ;
208
234
Object . defineProperty ( this , 'rli' , {
209
235
get : deprecate ( ( ) => rli ,
@@ -264,7 +290,7 @@ function REPLServer(prompt,
264
290
// statement rather than an object literal. So, we first try
265
291
// to wrap it in parentheses, so that it will be interpreted as
266
292
// an expression. Note that if the above condition changes,
267
- // lib/internal/repl/recoverable .js needs to be changed to match.
293
+ // lib/internal/repl/utils .js needs to be changed to match.
268
294
code = `(${ code . trim ( ) } )\n` ;
269
295
wrappedCmd = true ;
270
296
}
@@ -461,22 +487,31 @@ function REPLServer(prompt,
461
487
}
462
488
}
463
489
464
- if ( errStack === '' ) {
465
- errStack = `Thrown: ${ self . writer ( e ) } \n` ;
466
- } else {
467
- const ln = errStack . endsWith ( '\n' ) ? '' : '\n' ;
468
- errStack = `Thrown:\n${ errStack } ${ ln } ` ;
469
- }
470
-
471
490
if ( ! self . underscoreErrAssigned ) {
472
491
self . lastError = e ;
473
492
}
474
493
475
494
const top = replMap . get ( self ) ;
476
- top . outputStream . write ( errStack ) ;
477
- top . clearBufferedCommand ( ) ;
478
- top . lines . level = [ ] ;
479
- top . displayPrompt ( ) ;
495
+ if ( options [ kStandaloneREPL ] &&
496
+ process . listenerCount ( 'uncaughtException' ) !== 0 ) {
497
+ process . nextTick ( ( ) => {
498
+ process . emit ( 'uncaughtException' , e ) ;
499
+ top . clearBufferedCommand ( ) ;
500
+ top . lines . level = [ ] ;
501
+ top . displayPrompt ( ) ;
502
+ } ) ;
503
+ } else {
504
+ if ( errStack === '' ) {
505
+ errStack = `Thrown: ${ self . writer ( e ) } \n` ;
506
+ } else {
507
+ const ln = errStack . endsWith ( '\n' ) ? '' : '\n' ;
508
+ errStack = `Thrown:\n${ errStack } ${ ln } ` ;
509
+ }
510
+ top . outputStream . write ( errStack ) ;
511
+ top . clearBufferedCommand ( ) ;
512
+ top . lines . level = [ ] ;
513
+ top . displayPrompt ( ) ;
514
+ }
480
515
} ) ;
481
516
482
517
self . resetContext ( ) ;
0 commit comments