Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

repl: fix error message #13733

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -682,12 +682,6 @@ passed in an options object.
Used when both `breakEvalOnSigint` and `eval` options are set
in the REPL config, which is not supported.

<a id="ERR_INVALID_REPL_HISTORY"></a>
### ERR_INVALID_REPL_HISTORY

Used in the `repl` in case the old history file is used and an error occurred
while trying to read and parse it.

<a id="ERR_INVALID_SYNC_FORK_INPUT"></a>
### ERR_INVALID_SYNC_FORK_INPUT

Expand Down
1 change: 0 additions & 1 deletion lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ E('ERR_INVALID_OPT_VALUE',
});
E('ERR_INVALID_REPL_EVAL_CONFIG',
'Cannot specify both "breakEvalOnSigint" and "eval" for REPL');
E('ERR_INVALID_REPL_HISTORY', 'Expected array, got %s');
E('ERR_INVALID_SYNC_FORK_INPUT',
'Asynchronous forks do not support Buffer, Uint8Array or string input: %s');
E('ERR_INVALID_THIS', 'Value of "this" must be of type %s');
Expand Down
67 changes: 39 additions & 28 deletions lib/internal/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ const fs = require('fs');
const os = require('os');
const util = require('util');
const debug = util.debuglog('repl');
const errors = require('internal/errors');

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;

function _writeToOutput(repl, message) {
repl._writeToOutput(message);
repl._refreshLine();
}

function createRepl(env, opts, cb) {
if (typeof opts === 'function') {
cb = opts;
Expand Down Expand Up @@ -81,9 +84,8 @@ function setupHistory(repl, historyPath, oldHistoryPath, ready) {
try {
historyPath = path.join(os.homedir(), '.node_repl_history');
} catch (err) {
repl._writeToOutput('\nError: Could not get the home directory.\n' +
'REPL session history will not be persisted.\n');
repl._refreshLine();
_writeToOutput(repl, '\nError: Could not get the home directory.\n' +
'REPL session history will not be persisted.\n');

debug(err.stack);
repl._historyPrev = _replHistoryMessage;
Expand All @@ -104,9 +106,8 @@ function setupHistory(repl, historyPath, oldHistoryPath, ready) {
if (err) {
// Cannot open history file.
// Don't crash, just don't persist history.
repl._writeToOutput('\nError: Could not open history file.\n' +
'REPL session history will not be persisted.\n');
repl._refreshLine();
_writeToOutput(repl, '\nError: Could not open history file.\n' +
'REPL session history will not be persisted.\n');
debug(err.stack);

repl._historyPrev = _replHistoryMessage;
Expand All @@ -133,18 +134,13 @@ function setupHistory(repl, historyPath, oldHistoryPath, ready) {
} 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 ' +
_writeToOutput(
repl,
'\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) {
// Grab data from the older pre-v3.0 JSON NODE_REPL_HISTORY_FILE format.
repl._writeToOutput(
'\nConverting old JSON repl history to line-separated history.\n' +
`The new repl history file can be found at ${historyPath}.\n`);
repl._refreshLine();

let threw = false;
try {
// Pre-v3.0, repl history was stored as JSON.
// Try and convert it to line separated history.
Expand All @@ -153,16 +149,31 @@ function setupHistory(repl, historyPath, oldHistoryPath, ready) {
// Only attempt to use the history if there was any.
if (oldReplJSONHistory) repl.history = JSON.parse(oldReplJSONHistory);

if (!Array.isArray(repl.history)) {
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
typeof repl.history, 'Array');
if (Array.isArray(repl.history)) {
repl.history = repl.history.slice(0, repl.historySize);
} else {
threw = true;
_writeToOutput(
repl,
'\nError: The old history file data has to be an Array.\n' +
'REPL session history will not be persisted.\n');
}
repl.history = repl.history.slice(0, repl.historySize);
} catch (err) {
if (err.code !== 'ENOENT') {
return ready(
new errors.Error('ERR_PARSE_HISTORY_DATA', oldHistoryPath));
}
// Cannot open or parse history file.
// Don't crash, just don't persist history.
threw = true;
const type = err instanceof SyntaxError ? 'parse' : 'open';
_writeToOutput(repl, `\nError: Could not ${type} old history file.\n` +
'REPL session history will not be persisted.\n');
}
if (!threw) {
// Grab data from the older pre-v3.0 JSON NODE_REPL_HISTORY_FILE format.
_writeToOutput(
repl,
'\nConverted old JSON repl history to line-separated history.\n' +
`The new repl history file can be found at ${historyPath}.\n`);
} else {
repl.history = [];
}
}

Expand Down Expand Up @@ -225,12 +236,12 @@ function setupHistory(repl, historyPath, oldHistoryPath, ready) {

function _replHistoryMessage() {
if (this.history.length === 0) {
this._writeToOutput(
'\nPersistent history support disabled. ' +
_writeToOutput(
this,
'\nPersistent history support disabled. ' +
'Set the NODE_REPL_HISTORY environment\nvariable to ' +
'a valid, user-writable path to enable.\n'
);
this._refreshLine();
}
this._historyPrev = Interface.prototype._historyPrev;
return this._historyPrev();
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/old-repl-history-file-faulty.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
undefined
4 changes: 4 additions & 0 deletions test/fixtures/old-repl-history-file-obj.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"a": "'=^.^='",
"b": "'hello world'"
}
27 changes: 23 additions & 4 deletions test/parallel/test-repl-persistent-history.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,19 @@ const prompt = '> ';
const replDisabled = '\nPersistent history support disabled. Set the ' +
'NODE_REPL_HISTORY environment\nvariable to a valid, ' +
'user-writable path to enable.\n';
const convertMsg = '\nConverting old JSON repl history to line-separated ' +
const convertMsg = '\nConverted old JSON repl history to line-separated ' +
'history.\nThe new repl history file can be found at ' +
path.join(common.tmpDir, '.node_repl_history') + '.\n';
const homedirErr = '\nError: Could not get the home directory.\n' +
'REPL session history will not be persisted.\n';
const replFailedRead = '\nError: Could not open history file.\n' +
'REPL session history will not be persisted.\n';
const oldHistoryFailedOpen = '\nError: Could not open old history file.\n' +
'REPL session history will not be persisted.\n';
const oldHistoryFailedParse = '\nError: Could not parse old history file.\n' +
'REPL session history will not be persisted.\n';
const oldHistoryObj = '\nError: The old history file data has to be an Array' +
'.\nREPL 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') +
Expand All @@ -72,6 +78,10 @@ const fixtures = common.fixturesDir;
const historyFixturePath = path.join(fixtures, '.node_repl_history');
const historyPath = path.join(common.tmpDir, '.fixture_copy_repl_history');
const historyPathFail = path.join(common.tmpDir, '.node_repl\u0000_history');
const oldHistoryPathObj = path.join(fixtures,
'old-repl-history-file-obj.json');
const oldHistoryPathFaulty = path.join(fixtures,
'old-repl-history-file-faulty.json');
const oldHistoryPath = path.join(fixtures, 'old-repl-history-file.json');
const enoentHistoryPath = path.join(fixtures, 'enoent-repl-history-file.json');
const emptyHistoryPath = path.join(fixtures, '.empty-repl-history-file');
Expand All @@ -93,10 +103,19 @@ const tests = [
expected: [prompt, replDisabled, prompt]
},
{
env: { NODE_REPL_HISTORY: '',
NODE_REPL_HISTORY_FILE: enoentHistoryPath },
env: { NODE_REPL_HISTORY_FILE: enoentHistoryPath },
test: [UP],
expected: [prompt, replDisabled, prompt]
expected: [prompt, oldHistoryFailedOpen, prompt]
},
{
env: { NODE_REPL_HISTORY_FILE: oldHistoryPathObj },
test: [UP],
expected: [prompt, oldHistoryObj, prompt]
},
{
env: { NODE_REPL_HISTORY_FILE: oldHistoryPathFaulty },
test: [UP],
expected: [prompt, oldHistoryFailedParse, prompt]
},
{
env: { NODE_REPL_HISTORY: '',
Expand Down