mirror of https://github.com/lukechilds/node.git
Browse Source
This commit addresses https://github.com/nodejs/node/issues/5431 by changing the way that the repl handles assignment to the global _ variable. Prior to this commit, node sets the result of the last expression evaluated in the repl to `_`. This causes problems for users of underscore, lodash and other packages where it is common to assign `_` to the package, e.g. `_ = require('lodash');`. Changes in this commit now result in the following behavior. - If unassigned on the repl, `_` continues to refer to the last evaluated expression. - If assigned, the default behavior of assigning `_` to the last evaluated expression is disabled, and `_` now references whatever value was explicitly set. A warning is issued on the repl - 'expression assignment to _ now disabled'. - If `_` is assigned multiple times, the warning is only displayed once. - When `.clear` is executed in the repl, `_` continues to refer to its most recent value, whatever that is (this is per existing behavior). If `_` had been explicitly set prior to `.clear` it will not change again with the evaluation of the next expression. PR-URL: https://github.com/nodejs/node/pull/5535 Fixes: https://github.com/nodejs/node/issues/5431 Reviewed-By: Roman Reiss <me@silverwind.io> Reviewed-By: James M Snell <jasnell@gmail.com>process-exit-stdio-flushing
Lance Ball
9 years ago
committed by
silverwind
3 changed files with 183 additions and 5 deletions
@ -0,0 +1,156 @@ |
|||
'use strict'; |
|||
|
|||
require('../common'); |
|||
const assert = require('assert'); |
|||
const repl = require('repl'); |
|||
const stream = require('stream'); |
|||
|
|||
testSloppyMode(); |
|||
testStrictMode(); |
|||
testResetContext(); |
|||
testMagicMode(); |
|||
|
|||
function testSloppyMode() { |
|||
const r = initRepl(repl.REPL_MODE_SLOPPY); |
|||
|
|||
// cannot use `let` in sloppy mode
|
|||
r.write(`_; // initial value undefined
|
|||
var x = 10; // evaluates to undefined
|
|||
_; // still undefined
|
|||
y = 10; // evaluates to 10
|
|||
_; // 10 from last eval
|
|||
_ = 20; // explicitly set to 20
|
|||
_; // 20 from user input
|
|||
_ = 30; // make sure we can set it twice and no prompt
|
|||
_; // 30 from user input
|
|||
y = 40; // make sure eval doesn't change _
|
|||
_; // remains 30 from user input
|
|||
`);
|
|||
|
|||
assertOutput(r.output, [ |
|||
'undefined', |
|||
'undefined', |
|||
'undefined', |
|||
'10', |
|||
'10', |
|||
'Expression assignment to _ now disabled.', |
|||
'20', |
|||
'20', |
|||
'30', |
|||
'30', |
|||
'40', |
|||
'30' |
|||
]); |
|||
} |
|||
|
|||
function testStrictMode() { |
|||
const r = initRepl(repl.REPL_MODE_STRICT); |
|||
|
|||
r.write(`_; // initial value undefined
|
|||
var x = 10; // evaluates to undefined
|
|||
_; // still undefined
|
|||
let _ = 20; // use 'let' only in strict mode - evals to undefined
|
|||
_; // 20 from user input
|
|||
_ = 30; // make sure we can set it twice and no prompt
|
|||
_; // 30 from user input
|
|||
var y = 40; // make sure eval doesn't change _
|
|||
_; // remains 30 from user input
|
|||
function f() { let _ = 50; } // undefined
|
|||
f(); // undefined
|
|||
_; // remains 30 from user input
|
|||
`);
|
|||
|
|||
assertOutput(r.output, [ |
|||
'undefined', |
|||
'undefined', |
|||
'undefined', |
|||
'undefined', |
|||
'20', |
|||
'30', |
|||
'30', |
|||
'undefined', |
|||
'30', |
|||
'undefined', |
|||
'undefined', |
|||
'30' |
|||
]); |
|||
} |
|||
|
|||
function testMagicMode() { |
|||
const r = initRepl(repl.REPL_MODE_MAGIC); |
|||
|
|||
r.write(`_; // initial value undefined
|
|||
x = 10; //
|
|||
_; // last eval - 10
|
|||
let _ = 20; // undefined
|
|||
_; // 20 from user input
|
|||
_ = 30; // make sure we can set it twice and no prompt
|
|||
_; // 30 from user input
|
|||
var y = 40; // make sure eval doesn't change _
|
|||
_; // remains 30 from user input
|
|||
function f() { let _ = 50; return _; } // undefined
|
|||
f(); // 50
|
|||
_; // remains 30 from user input
|
|||
`);
|
|||
|
|||
assertOutput(r.output, [ |
|||
'undefined', |
|||
'10', |
|||
'10', |
|||
'undefined', |
|||
'20', |
|||
'30', |
|||
'30', |
|||
'undefined', |
|||
'30', |
|||
'undefined', |
|||
'50', |
|||
'30' |
|||
]); |
|||
} |
|||
|
|||
function testResetContext() { |
|||
const r = initRepl(repl.REPL_MODE_SLOPPY); |
|||
|
|||
r.write(`_ = 10; // explicitly set to 10
|
|||
_; // 10 from user input
|
|||
.clear // Clearing context...
|
|||
_; // remains 10
|
|||
x = 20; // but behavior reverts to last eval
|
|||
_; // expect 20
|
|||
`);
|
|||
|
|||
assertOutput(r.output, [ |
|||
'Expression assignment to _ now disabled.', |
|||
'10', |
|||
'10', |
|||
'Clearing context...', |
|||
'10', |
|||
'20', |
|||
'20' |
|||
]); |
|||
} |
|||
|
|||
function initRepl(mode) { |
|||
const inputStream = new stream.PassThrough(); |
|||
const outputStream = new stream.PassThrough(); |
|||
outputStream.accum = ''; |
|||
|
|||
outputStream.on('data', (data) => { |
|||
outputStream.accum += data; |
|||
}); |
|||
|
|||
return repl.start({ |
|||
input: inputStream, |
|||
output: outputStream, |
|||
useColors: false, |
|||
terminal: false, |
|||
prompt: '', |
|||
replMode: mode |
|||
}); |
|||
} |
|||
|
|||
function assertOutput(output, expected) { |
|||
const lines = output.accum.trim().split('\n'); |
|||
assert.deepStrictEqual(lines, expected); |
|||
} |
Loading…
Reference in new issue