node/lib/internal/repl.js
Chris Dickinson 051d482b15 repl: fix _debugger by properly proxying repl
The _debugger module uses the internal REPL module, but expects
to receive the userland REPL module. This fixes the breakage that
occurs by proxying the userland REPL module through the internal
module.

It also fixes an unintended in-REPL bug, where require(node-module)
was not resolving correctly.

PR-URL: https://github.com/iojs/io.js/pull/1605
Reviewed-By: Roman Reiss <me@silverwind.io>
2015-05-04 10:42:42 -07:00

163 lines
3.7 KiB
JavaScript

'use strict';
const Interface = require('readline').Interface;
const REPL = require('repl');
const path = require('path');
module.exports = Object.create(REPL);
module.exports.createInternalRepl = createRepl;
// XXX(chrisdickinson): The 15ms debounce value is somewhat arbitrary.
// The debounce is to guard against code pasted into the REPL.
const kDebounceHistoryMS = 15;
// XXX(chrisdickinson): hack to make sure that the internal debugger
// uses the original repl.
function replStart() {
return REPL.start.apply(REPL, arguments);
}
function createRepl(env, cb) {
const opts = {
ignoreUndefined: false,
terminal: process.stdout.isTTY,
useGlobal: true
};
if (parseInt(env.NODE_NO_READLINE)) {
opts.terminal = false;
}
if (parseInt(env.NODE_DISABLE_COLORS)) {
opts.useColors = false;
}
opts.replMode = {
'strict': REPL.REPL_MODE_STRICT,
'sloppy': REPL.REPL_MODE_SLOPPY,
'magic': REPL.REPL_MODE_MAGIC
}[String(env.NODE_REPL_MODE).toLowerCase().trim()];
if (opts.replMode === undefined) {
opts.replMode = REPL.REPL_MODE_MAGIC;
}
const historySize = Number(env.NODE_REPL_HISTORY_SIZE);
if (!isNaN(historySize) && historySize > 0) {
opts.historySize = historySize;
} else {
// XXX(chrisdickinson): set here to avoid affecting existing applications
// using repl instances.
opts.historySize = 1000;
}
const repl = REPL.start(opts);
if (opts.terminal && env.NODE_REPL_HISTORY_FILE) {
return setupHistory(repl, env.NODE_REPL_HISTORY_FILE, cb);
}
repl._historyPrev = _replHistoryMessage;
cb(null, repl);
}
function setupHistory(repl, historyPath, ready) {
const fs = require('fs');
var timer = null;
var writing = false;
var pending = false;
repl.pause();
fs.open(historyPath, 'a+', oninit);
function oninit(err, hnd) {
if (err) {
return ready(err);
}
fs.close(hnd, onclose);
}
function onclose(err) {
if (err) {
return ready(err);
}
fs.readFile(historyPath, 'utf8', onread);
}
function onread(err, data) {
if (err) {
return ready(err);
}
if (data) {
try {
repl.history = JSON.parse(data);
if (!Array.isArray(repl.history)) {
throw new Error('Expected array, got ' + typeof repl.history);
}
repl.history.slice(-repl.historySize);
} catch (err) {
return ready(
new Error(`Could not parse history data in ${historyPath}.`));
}
}
fs.open(historyPath, 'w', onhandle);
}
function onhandle(err, hnd) {
if (err) {
return ready(err);
}
repl._historyHandle = hnd;
repl.on('line', online);
repl.resume();
return ready(null, repl);
}
// ------ history listeners ------
function online() {
repl._flushing = true;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(flushHistory, kDebounceHistoryMS);
}
function flushHistory() {
timer = null;
if (writing) {
pending = true;
return;
}
writing = true;
const historyData = JSON.stringify(repl.history, null, 2);
fs.write(repl._historyHandle, historyData, 0, 'utf8', onwritten);
}
function onwritten(err, data) {
writing = false;
if (pending) {
pending = false;
online();
} else {
repl._flushing = Boolean(timer);
if (!repl._flushing) {
repl.emit('flushHistory');
}
}
}
}
function _replHistoryMessage() {
if (this.history.length === 0) {
this._writeToOutput(
'\nPersistent history support disabled. ' +
'Set the NODE_REPL_HISTORY_FILE environment variable to ' +
'a valid, user-writable path to enable.\n'
);
this._refreshLine();
}
this._historyPrev = Interface.prototype._historyPrev;
return this._historyPrev();
}