Browse Source

repl: Fixed node repl history edge case.

If the deprecated NODE_REPL_HISTORY_FILE is set to default
node history file path ($HOME/.node_repl_history) and the file
doesn't exist, then node creates the file and then crashes when
it tries to parse that file as JSON thinking that it's an older
JSON formatted history file. This fixes that bug.

This patch also prevents node repl from throwing if the old
history file is empty or if $HOME/.node_repl_history is empty.

Fixes: https://github.com/nodejs/node/issues/4102
PR-URL: https://github.com/nodejs/node/pull/4108
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
process-exit-stdio-flushing
Mudit Ameta 9 years ago
committed by Jeremiah Senkpiel
parent
commit
29c4a2af5c
  1. 16
      lib/internal/repl.js
  2. 0
      test/fixtures/.empty-repl-history-file
  3. 16
      test/parallel/test-repl-persistent-history.js

16
lib/internal/repl.js

@ -120,6 +120,14 @@ function setupHistory(repl, historyPath, oldHistoryPath, ready) {
if (data) { if (data) {
repl.history = data.split(/[\n\r]+/, repl.historySize); repl.history = data.split(/[\n\r]+/, repl.historySize);
} else if (oldHistoryPath === historyPath) {
// If pre-v3.0, the user had set NODE_REPL_HISTORY_FILE to
// ~/.node_repl_history, warn the user about it and proceed.
repl._writeToOutput(
'\nThe old repl history file has the same name and location as ' +
`the new one i.e., ${historyPath} and is empty.\nUsing it as is.\n`);
repl._refreshLine();
} else if (oldHistoryPath) { } else if (oldHistoryPath) {
// Grab data from the older pre-v3.0 JSON NODE_REPL_HISTORY_FILE format. // Grab data from the older pre-v3.0 JSON NODE_REPL_HISTORY_FILE format.
repl._writeToOutput( repl._writeToOutput(
@ -128,7 +136,13 @@ function setupHistory(repl, historyPath, oldHistoryPath, ready) {
repl._refreshLine(); repl._refreshLine();
try { try {
repl.history = JSON.parse(fs.readFileSync(oldHistoryPath, 'utf8')); // Pre-v3.0, repl history was stored as JSON.
// Try and convert it to line separated history.
const oldReplJSONHistory = fs.readFileSync(oldHistoryPath, 'utf8');
// Only attempt to use the history if there was any.
if (oldReplJSONHistory) repl.history = JSON.parse(oldReplJSONHistory);
if (!Array.isArray(repl.history)) { if (!Array.isArray(repl.history)) {
throw new Error('Expected array, got ' + typeof repl.history); throw new Error('Expected array, got ' + typeof repl.history);
} }

0
test/fixtures/.empty-repl-history-file

16
test/parallel/test-repl-persistent-history.js

@ -66,6 +66,10 @@ const homedirErr = '\nError: Could not get the home directory.\n' +
'REPL session history will not be persisted.\n'; 'REPL session history will not be persisted.\n';
const replFailedRead = '\nError: Could not open history file.\n' + const replFailedRead = '\nError: Could not open history file.\n' +
'REPL session history will not be persisted.\n'; 'REPL session history will not be persisted.\n';
const sameHistoryFilePaths = '\nThe old repl history file has the same name ' +
'and location as the new one i.e., ' +
path.join(common.tmpDir, '.node_repl_history') +
' and is empty.\nUsing it as is.\n';
// File paths // File paths
const fixtures = path.join(common.testDir, 'fixtures'); const fixtures = path.join(common.testDir, 'fixtures');
const historyFixturePath = path.join(fixtures, '.node_repl_history'); const historyFixturePath = path.join(fixtures, '.node_repl_history');
@ -73,9 +77,9 @@ const historyPath = path.join(common.tmpDir, '.fixture_copy_repl_history');
const historyPathFail = path.join(common.tmpDir, '.node_repl\u0000_history'); const historyPathFail = path.join(common.tmpDir, '.node_repl\u0000_history');
const oldHistoryPath = path.join(fixtures, 'old-repl-history-file.json'); const oldHistoryPath = path.join(fixtures, 'old-repl-history-file.json');
const enoentHistoryPath = path.join(fixtures, 'enoent-repl-history-file.json'); const enoentHistoryPath = path.join(fixtures, 'enoent-repl-history-file.json');
const emptyHistoryPath = path.join(fixtures, '.empty-repl-history-file');
const defaultHistoryPath = path.join(common.tmpDir, '.node_repl_history'); const defaultHistoryPath = path.join(common.tmpDir, '.node_repl_history');
const tests = [{ const tests = [{
env: { NODE_REPL_HISTORY: '' }, env: { NODE_REPL_HISTORY: '' },
test: [UP], test: [UP],
@ -93,6 +97,16 @@ const tests = [{
test: [UP], test: [UP],
expected: [prompt, replDisabled, prompt] expected: [prompt, replDisabled, prompt]
}, },
{
env: { NODE_REPL_HISTORY_FILE: emptyHistoryPath },
test: [UP],
expected: [prompt, convertMsg, prompt]
},
{
env: { NODE_REPL_HISTORY_FILE: defaultHistoryPath },
test: [UP],
expected: [prompt, sameHistoryFilePaths, prompt]
},
{ {
env: { NODE_REPL_HISTORY: historyPath }, env: { NODE_REPL_HISTORY: historyPath },
test: [UP, CLEAR], test: [UP, CLEAR],

Loading…
Cancel
Save