@ -50,6 +50,7 @@ var rl = require('readline');
var Console = require ( 'console' ) . Console ;
var Console = require ( 'console' ) . Console ;
var EventEmitter = require ( 'events' ) . EventEmitter ;
var EventEmitter = require ( 'events' ) . EventEmitter ;
var domain = require ( 'domain' ) ;
var domain = require ( 'domain' ) ;
var debug = util . debuglog ( 'repl' ) ;
// If obj.hasOwnProperty has been overridden, then calling
// If obj.hasOwnProperty has been overridden, then calling
// obj.hasOwnProperty(prop) will break.
// obj.hasOwnProperty(prop) will break.
@ -119,8 +120,9 @@ function REPLServer(prompt, stream, eval_, useGlobal, ignoreUndefined) {
} ) ;
} ) ;
} catch ( e ) {
} catch ( e ) {
err = e ;
err = e ;
err . _ isSyntaxError = isSyntaxError ( err ) ;
debug ( 'parse error %j' , code , e ) ;
}
}
if ( ! err ) {
if ( ! err ) {
try {
try {
if ( self . useGlobal ) {
if ( self . useGlobal ) {
@ -130,21 +132,22 @@ function REPLServer(prompt, stream, eval_, useGlobal, ignoreUndefined) {
}
}
} catch ( e ) {
} catch ( e ) {
err = e ;
err = e ;
err . _ isSyntaxError = false ;
if ( err && process . domain ) {
debug ( 'not recoverable, send to domain' ) ;
process . domain . emit ( 'error' , err ) ;
process . domain . exit ( ) ;
return ;
}
}
}
}
}
if ( err && process . domain && ! err . _ isSyntaxError ) {
process . domain . emit ( 'error' , err ) ;
cb ( err , result ) ;
process . domain . exit ( ) ;
}
else {
cb ( err , result ) ;
}
}
}
self . eval = self . _ domain . bind ( eval_ ) ;
self . eval = self . _ domain . bind ( eval_ ) ;
self . _ domain . on ( 'error' , function ( e ) {
self . _ domain . on ( 'error' , function ( e ) {
debug ( 'domain error' ) ;
self . outputStream . write ( ( e . stack || e ) + '\n' ) ;
self . outputStream . write ( ( e . stack || e ) + '\n' ) ;
self . bufferedCommand = '' ;
self . bufferedCommand = '' ;
self . lines . level = [ ] ;
self . lines . level = [ ] ;
@ -236,6 +239,7 @@ function REPLServer(prompt, stream, eval_, useGlobal, ignoreUndefined) {
} ) ;
} ) ;
rli . on ( 'line' , function ( cmd ) {
rli . on ( 'line' , function ( cmd ) {
debug ( 'line %j' , cmd ) ;
sawSIGINT = false ;
sawSIGINT = false ;
var skipCatchall = false ;
var skipCatchall = false ;
cmd = trimWhitespace ( cmd ) ;
cmd = trimWhitespace ( cmd ) ;
@ -255,60 +259,52 @@ function REPLServer(prompt, stream, eval_, useGlobal, ignoreUndefined) {
}
}
if ( ! skipCatchall ) {
if ( ! skipCatchall ) {
var evalCmd = self . bufferedCommand + cmd + '\n' ;
var evalCmd = self . bufferedCommand + cmd ;
if ( /^\s*\{/ . test ( evalCmd ) && /\}\s*$/ . test ( evalCmd ) ) {
// This try is for determining if the command is complete, or should
// It's confusing for `{ a : 1 }` to be interpreted as a block
// continue onto the next line.
// statement rather than an object literal. So, we first try
// We try to evaluate both expressions e.g.
// to wrap it in parentheses, so that it will be interpreted as
// '{ a : 1 }'
// an expression.
// and statements e.g.
evalCmd = '(' + evalCmd + ')\n' ;
// 'for (var i = 0; i < 10; i++) console.log(i);'
} else {
// otherwise we just append a \n so that it will be either
// First we attempt to eval as expression with parens.
// terminated, or continued onto the next expression if it's an
// This catches '{a : 1}' properly.
// unexpected end of input.
self . eval ( '(' + evalCmd + ')' ,
evalCmd = evalCmd + '\n' ;
self . context ,
}
'repl' ,
function ( e , ret ) {
if ( e && ! e . _ isSyntaxError ) return finish ( e ) ;
if ( util . isFunction ( ret ) &&
/^[\r\n\s]*function/ . test ( evalCmd ) || e ) {
// Now as statement without parens.
self . eval ( evalCmd , self . context , 'repl' , finish ) ;
} else {
finish ( null , ret ) ;
}
} ) ;
debug ( 'eval %j' , evalCmd ) ;
self . eval ( evalCmd , self . context , 'repl' , finish ) ;
} else {
} else {
finish ( null ) ;
finish ( null ) ;
}
}
function finish ( e , ret ) {
function finish ( e , ret ) {
debug ( 'finish' , e , ret ) ;
self . memory ( cmd ) ;
self . memory ( cmd ) ;
if ( e && ! self . bufferedCommand && cmd . trim ( ) . match ( /^npm / ) ) {
self . outputStream . write ( 'npm should be run outside of the ' +
'node repl, in your normal shell.\n' +
'(Press Control-D to exit.)\n' ) ;
self . bufferedCommand = '' ;
self . displayPrompt ( ) ;
return ;
}
// If error was SyntaxError and not JSON.parse error
// If error was SyntaxError and not JSON.parse error
if ( e && e . _ isSyntaxError ) {
if ( e ) {
if ( ! self . bufferedCommand && cmd . trim ( ) . match ( /^npm / ) ) {
if ( isRecoverableError ( e ) ) {
self . outputStream . write ( 'npm should be run outside of the ' +
// Start buffering data like that:
'node repl, in your normal shell.\n' +
// {
'(Press Control-D to exit.)\n' ) ;
// ... x: 1
self . bufferedCommand = '' ;
// ... }
self . bufferedCommand += cmd + '\n' ;
self . displayPrompt ( ) ;
self . displayPrompt ( ) ;
return ;
return ;
} else {
self . _ domain . emit ( 'error' , e ) ;
}
}
// Start buffering data like that:
// {
// ... x: 1
// ... }
self . bufferedCommand += cmd + '\n' ;
self . displayPrompt ( ) ;
return ;
} else if ( e ) {
self . _ domain . emit ( 'error' , e ) ;
}
}
// Clear buffer if no SyntaxErrors
// Clear buffer if no SyntaxErrors
@ -940,15 +936,10 @@ REPLServer.prototype.convertToContext = function(cmd) {
} ;
} ;
/ * *
// If the error is that we've unexpectedly ended the input,
* Returns ` true ` if "e" is a SyntaxError , ` false ` otherwise .
// then let the user try to recover by adding more input.
* filters out strict - mode errors , which are not recoverable
function isRecoverableError ( e ) {
* /
return e &&
function isSyntaxError ( e ) {
e . name === 'SyntaxError' &&
// Convert error to string
/^Unexpected end of input/ . test ( e . message ) ;
e = e && ( e . stack || e . toString ( ) ) ;
return e && e . match ( /^SyntaxError/ ) &&
// "strict mode" syntax errors
! e . match ( /^SyntaxError: .*strict mode.*/i ) &&
! e . match ( /^SyntaxError: Assignment to constant variable/i ) ;
}
}