Skip to content
Open
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
2 changes: 2 additions & 0 deletions doc/api/repl.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ The following special commands are supported by all REPL instances:
further input or processing of that expression.
* `.clear`: Resets the REPL `context` to an empty object and clears any
multi-line expression being input.
* `.clearhistory`: Clears the REPL session history, both in memory and in the
persistent history file when one is configured.
* `.exit`: Close the I/O stream, causing the REPL to exit.
* `.help`: Show this list of special commands.
* `.save`: Save the current REPL session to a file:
Expand Down
27 changes: 27 additions & 0 deletions lib/internal/repl/history.js
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,33 @@ class ReplHistory {
return this[kCloseHandle]();
}

/**
* Clears the in-memory history and truncates the persisted history file.
* @returns {Promise<void>}
*/
async clearHistory() {
// Cancel any pending debounced flush so it cannot rewrite the file after
// the history has been cleared.
if (this[kTimer]) {
clearTimeout(this[kTimer]);
this[kTimer] = null;
}
this[kPending] = false;
this[kIsFlushing] = false;

this[kHistory].length = 0;
this[kIndex] = -1;
this[kContext].emit('history', this[kHistory]);

if (this[kHistoryHandle] !== null) {
try {
await this[kHistoryHandle].truncate(0);
} catch (err) {
debug('Error truncating history file:', err);
}
}
}

[kReplHistoryMessage]() {
if (this[kHistory].length === 0) {
ReplHistory[kWriteToOutput](
Expand Down
14 changes: 14 additions & 0 deletions lib/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -1370,6 +1370,20 @@ function defineDefaultCommands(repl) {
},
});

repl.defineCommand('clearhistory', {
help: 'Clear the REPL session history',
action: function() {
this.clearBufferedCommand();
if (this.historyManager) {
this.historyManager.clearHistory();
this.output.write('Clearing history...\n');
} else {
this.output.write('History is not enabled for this REPL session\n');
}
this.displayPrompt();
},
});

repl.defineCommand('exit', {
help: 'Exit the REPL',
action: function() {
Expand Down
52 changes: 52 additions & 0 deletions test/parallel/test-repl-clearhistory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use strict';

const common = require('../common');
const { startNewREPLServer } = require('../common/repl');
const assert = require('node:assert');
const fs = require('node:fs');
const path = require('node:path');

const tmpdir = require('../common/tmpdir');
tmpdir.refresh();

// Tests the `.clearhistory` REPL command, which clears the in-memory history
// and truncates the persisted history file.
// Refs: https://github.com/nodejs/node/issues/63905

// The command clears the in-memory history and resets the navigation index.
{
const { replServer, input, output } = startNewREPLServer({ terminal: false });

replServer.history = ['const a = 1', 'a + 1', 'process.version'];
replServer.historyIndex = 2;

input.run(['.clearhistory']);

assert.strictEqual(replServer.history.length, 0);
assert.strictEqual(replServer.historyIndex, -1);
assert.match(output.accumulator, /Clearing history/);

replServer.close();
}

// The command truncates the persisted history file.
{
const filePath = path.resolve(tmpdir.path, '.node_repl_history_clear');
fs.writeFileSync(filePath, 'const a = 1\na + 1\nprocess.version\n');

const { replServer, input } = startNewREPLServer({ terminal: false });

replServer.setupHistory({ filePath }, common.mustSucceed(() => {
assert.ok(replServer.history.length > 0);
assert.ok(fs.readFileSync(filePath, 'utf8').length > 0);

input.run(['.clearhistory']);
assert.strictEqual(replServer.history.length, 0);

// File truncation is asynchronous; await it before reading the file back.
replServer.historyManager.clearHistory().then(common.mustCall(() => {
assert.strictEqual(fs.readFileSync(filePath, 'utf8'), '');
replServer.close();
}));
}));
}
Loading