Skip to content

Commit e6b69b9

Browse files
BridgeARtniessen
authored andcommitted
repl: fix old history error handling
PR-URL: #13733 Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Tobias Nießen <[email protected]>
1 parent 0279602 commit e6b69b9

File tree

6 files changed

+67
-39
lines changed

6 files changed

+67
-39
lines changed

doc/api/errors.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -683,12 +683,6 @@ passed in an options object.
683683
Used when both `breakEvalOnSigint` and `eval` options are set
684684
in the REPL config, which is not supported.
685685

686-
<a id="ERR_INVALID_REPL_HISTORY"></a>
687-
### ERR_INVALID_REPL_HISTORY
688-
689-
Used in the `repl` in case the old history file is used and an error occurred
690-
while trying to read and parse it.
691-
692686
<a id="ERR_INVALID_SYNC_FORK_INPUT"></a>
693687
### ERR_INVALID_SYNC_FORK_INPUT
694688

lib/internal/errors.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ E('ERR_INVALID_OPT_VALUE',
144144
});
145145
E('ERR_INVALID_REPL_EVAL_CONFIG',
146146
'Cannot specify both "breakEvalOnSigint" and "eval" for REPL');
147-
E('ERR_INVALID_REPL_HISTORY', 'Expected array, got %s');
148147
E('ERR_INVALID_SYNC_FORK_INPUT',
149148
'Asynchronous forks do not support Buffer, Uint8Array or string input: %s');
150149
E('ERR_INVALID_THIS', 'Value of "this" must be of type %s');

lib/internal/repl.js

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@ const fs = require('fs');
77
const os = require('os');
88
const util = require('util');
99
const debug = util.debuglog('repl');
10-
const errors = require('internal/errors');
11-
1210
module.exports = Object.create(REPL);
1311
module.exports.createInternalRepl = createRepl;
1412

1513
// XXX(chrisdickinson): The 15ms debounce value is somewhat arbitrary.
1614
// The debounce is to guard against code pasted into the REPL.
1715
const kDebounceHistoryMS = 15;
1816

17+
function _writeToOutput(repl, message) {
18+
repl._writeToOutput(message);
19+
repl._refreshLine();
20+
}
21+
1922
function createRepl(env, opts, cb) {
2023
if (typeof opts === 'function') {
2124
cb = opts;
@@ -81,9 +84,8 @@ function setupHistory(repl, historyPath, oldHistoryPath, ready) {
8184
try {
8285
historyPath = path.join(os.homedir(), '.node_repl_history');
8386
} catch (err) {
84-
repl._writeToOutput('\nError: Could not get the home directory.\n' +
85-
'REPL session history will not be persisted.\n');
86-
repl._refreshLine();
87+
_writeToOutput(repl, '\nError: Could not get the home directory.\n' +
88+
'REPL session history will not be persisted.\n');
8789

8890
debug(err.stack);
8991
repl._historyPrev = _replHistoryMessage;
@@ -104,9 +106,8 @@ function setupHistory(repl, historyPath, oldHistoryPath, ready) {
104106
if (err) {
105107
// Cannot open history file.
106108
// Don't crash, just don't persist history.
107-
repl._writeToOutput('\nError: Could not open history file.\n' +
108-
'REPL session history will not be persisted.\n');
109-
repl._refreshLine();
109+
_writeToOutput(repl, '\nError: Could not open history file.\n' +
110+
'REPL session history will not be persisted.\n');
110111
debug(err.stack);
111112

112113
repl._historyPrev = _replHistoryMessage;
@@ -133,18 +134,13 @@ function setupHistory(repl, historyPath, oldHistoryPath, ready) {
133134
} else if (oldHistoryPath === historyPath) {
134135
// If pre-v3.0, the user had set NODE_REPL_HISTORY_FILE to
135136
// ~/.node_repl_history, warn the user about it and proceed.
136-
repl._writeToOutput(
137-
'\nThe old repl history file has the same name and location as ' +
137+
_writeToOutput(
138+
repl,
139+
'\nThe old repl history file has the same name and location as ' +
138140
`the new one i.e., ${historyPath} and is empty.\nUsing it as is.\n`);
139-
repl._refreshLine();
140141

141142
} else if (oldHistoryPath) {
142-
// Grab data from the older pre-v3.0 JSON NODE_REPL_HISTORY_FILE format.
143-
repl._writeToOutput(
144-
'\nConverting old JSON repl history to line-separated history.\n' +
145-
`The new repl history file can be found at ${historyPath}.\n`);
146-
repl._refreshLine();
147-
143+
let threw = false;
148144
try {
149145
// Pre-v3.0, repl history was stored as JSON.
150146
// Try and convert it to line separated history.
@@ -153,16 +149,31 @@ function setupHistory(repl, historyPath, oldHistoryPath, ready) {
153149
// Only attempt to use the history if there was any.
154150
if (oldReplJSONHistory) repl.history = JSON.parse(oldReplJSONHistory);
155151

156-
if (!Array.isArray(repl.history)) {
157-
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
158-
typeof repl.history, 'Array');
152+
if (Array.isArray(repl.history)) {
153+
repl.history = repl.history.slice(0, repl.historySize);
154+
} else {
155+
threw = true;
156+
_writeToOutput(
157+
repl,
158+
'\nError: The old history file data has to be an Array.\n' +
159+
'REPL session history will not be persisted.\n');
159160
}
160-
repl.history = repl.history.slice(0, repl.historySize);
161161
} catch (err) {
162-
if (err.code !== 'ENOENT') {
163-
return ready(
164-
new errors.Error('ERR_PARSE_HISTORY_DATA', oldHistoryPath));
165-
}
162+
// Cannot open or parse history file.
163+
// Don't crash, just don't persist history.
164+
threw = true;
165+
const type = err instanceof SyntaxError ? 'parse' : 'open';
166+
_writeToOutput(repl, `\nError: Could not ${type} old history file.\n` +
167+
'REPL session history will not be persisted.\n');
168+
}
169+
if (!threw) {
170+
// Grab data from the older pre-v3.0 JSON NODE_REPL_HISTORY_FILE format.
171+
_writeToOutput(
172+
repl,
173+
'\nConverted old JSON repl history to line-separated history.\n' +
174+
`The new repl history file can be found at ${historyPath}.\n`);
175+
} else {
176+
repl.history = [];
166177
}
167178
}
168179

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

226237
function _replHistoryMessage() {
227238
if (this.history.length === 0) {
228-
this._writeToOutput(
229-
'\nPersistent history support disabled. ' +
239+
_writeToOutput(
240+
this,
241+
'\nPersistent history support disabled. ' +
230242
'Set the NODE_REPL_HISTORY environment\nvariable to ' +
231243
'a valid, user-writable path to enable.\n'
232244
);
233-
this._refreshLine();
234245
}
235246
this._historyPrev = Interface.prototype._historyPrev;
236247
return this._historyPrev();
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
undefined
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"a": "'=^.^='",
3+
"b": "'hello world'"
4+
}

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

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,19 @@ const prompt = '> ';
5656
const replDisabled = '\nPersistent history support disabled. Set the ' +
5757
'NODE_REPL_HISTORY environment\nvariable to a valid, ' +
5858
'user-writable path to enable.\n';
59-
const convertMsg = '\nConverting old JSON repl history to line-separated ' +
59+
const convertMsg = '\nConverted old JSON repl history to line-separated ' +
6060
'history.\nThe new repl history file can be found at ' +
6161
path.join(common.tmpDir, '.node_repl_history') + '.\n';
6262
const homedirErr = '\nError: Could not get the home directory.\n' +
6363
'REPL session history will not be persisted.\n';
6464
const replFailedRead = '\nError: Could not open history file.\n' +
6565
'REPL session history will not be persisted.\n';
66+
const oldHistoryFailedOpen = '\nError: Could not open old history file.\n' +
67+
'REPL session history will not be persisted.\n';
68+
const oldHistoryFailedParse = '\nError: Could not parse old history file.\n' +
69+
'REPL session history will not be persisted.\n';
70+
const oldHistoryObj = '\nError: The old history file data has to be an Array' +
71+
'.\nREPL session history will not be persisted.\n';
6672
const sameHistoryFilePaths = '\nThe old repl history file has the same name ' +
6773
'and location as the new one i.e., ' +
6874
path.join(common.tmpDir, '.node_repl_history') +
@@ -72,6 +78,10 @@ const fixtures = common.fixturesDir;
7278
const historyFixturePath = path.join(fixtures, '.node_repl_history');
7379
const historyPath = path.join(common.tmpDir, '.fixture_copy_repl_history');
7480
const historyPathFail = path.join(common.tmpDir, '.node_repl\u0000_history');
81+
const oldHistoryPathObj = path.join(fixtures,
82+
'old-repl-history-file-obj.json');
83+
const oldHistoryPathFaulty = path.join(fixtures,
84+
'old-repl-history-file-faulty.json');
7585
const oldHistoryPath = path.join(fixtures, 'old-repl-history-file.json');
7686
const enoentHistoryPath = path.join(fixtures, 'enoent-repl-history-file.json');
7787
const emptyHistoryPath = path.join(fixtures, '.empty-repl-history-file');
@@ -93,10 +103,19 @@ const tests = [
93103
expected: [prompt, replDisabled, prompt]
94104
},
95105
{
96-
env: { NODE_REPL_HISTORY: '',
97-
NODE_REPL_HISTORY_FILE: enoentHistoryPath },
106+
env: { NODE_REPL_HISTORY_FILE: enoentHistoryPath },
98107
test: [UP],
99-
expected: [prompt, replDisabled, prompt]
108+
expected: [prompt, oldHistoryFailedOpen, prompt]
109+
},
110+
{
111+
env: { NODE_REPL_HISTORY_FILE: oldHistoryPathObj },
112+
test: [UP],
113+
expected: [prompt, oldHistoryObj, prompt]
114+
},
115+
{
116+
env: { NODE_REPL_HISTORY_FILE: oldHistoryPathFaulty },
117+
test: [UP],
118+
expected: [prompt, oldHistoryFailedParse, prompt]
100119
},
101120
{
102121
env: { NODE_REPL_HISTORY: '',

0 commit comments

Comments
 (0)