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
9 changes: 9 additions & 0 deletions doc/api/repl.md
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,9 @@ npx codemod@latest @nodejs/repl-builtin-modules
<!-- YAML
added: v0.1.91
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/66289
description: The `defineDefaultCommands` option has been added.
- version:
- v25.9.0
pr-url: https://github.com/nodejs/node/pull/62188
Expand Down Expand Up @@ -770,6 +773,11 @@ changes:
The `'unhandled'` value may or may not be desirable in situations
where the `REPLServer` instance has been closed, depending on the particular
use case.
* `defineDefaultCommands` {boolean} If `false`, the built-in commands
(`.break`, `.clear`, `.editor`, `.exit`, `.help`, `.load` and `.save`)
are not registered, so the REPL starts without any commands. Custom
commands can still be added with [`replServer.defineCommand()`][].
**Default:** `true`.
* Returns: {repl.REPLServer}

The `repl.start()` method creates and starts a [`repl.REPLServer`][] instance.
Expand Down Expand Up @@ -1150,6 +1158,7 @@ Original code from <https://gist.github.com/TooTallNate/2053342>.
[`readline.InterfaceCompleter`]: readline.md#use-of-the-completer-function
[`repl.ReplServer`]: #class-replserver
[`repl.start()`]: #replstartoptions
[`replServer.defineCommand()`]: #replserverdefinecommandkeyword-cmd
[`reverse-i-search`]: #reverse-i-search
[`util.inspect()`]: util.md#utilinspectobject-options
[custom evaluation functions]: #custom-evaluation-functions
Expand Down
4 changes: 3 additions & 1 deletion lib/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,9 @@ class REPLServer extends Interface {
self.resetContext();

this.commands = { __proto__: null };
defineDefaultCommands(this);
if (options.defineDefaultCommands !== false) {
defineDefaultCommands(this);
}

// Figure out which "writer" function to use
self.writer = options.writer || module.exports.writer;
Expand Down
52 changes: 52 additions & 0 deletions test/parallel/test-repl-define-default-commands.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { mustCall, mustNotCall } from '../common/index.mjs';
import { startNewREPLServer } from '../common/repl.js';
import assert from 'node:assert';

const defaultCommands =
['break', 'clear', 'editor', 'exit', 'help', 'load', 'save'];

// By default, and when `defineDefaultCommands` is explicitly `true`, the
// built-in commands are registered.
for (const defineDefaultCommands of [undefined, true]) {
const { replServer } = startNewREPLServer({ defineDefaultCommands });
assert.deepStrictEqual(
Object.keys(replServer.commands).sort(),
defaultCommands,
);
replServer.close();
}

// When `defineDefaultCommands` is `false`, no commands are registered.
{
const { replServer, output, run } = startNewREPLServer({
defineDefaultCommands: false,
});
assert.deepStrictEqual(Object.keys(replServer.commands), []);

// The built-in commands are treated as unknown keywords rather than run.
replServer.on('exit', mustNotCall('.exit should not close the REPL'));
await run(['.exit\n']);
assert.match(output.accumulator, /Invalid REPL keyword/);

output.accumulator = '';
await run(['.help\n']);
assert.match(output.accumulator, /Invalid REPL keyword/);

// Custom commands can still be defined, including ones that reuse a
// built-in name.
replServer.defineCommand('help', {
help: 'custom help',
action: mustCall(function() {
this.output.write('custom help output\n');
this.displayPrompt();
}),
});
assert.deepStrictEqual(Object.keys(replServer.commands), ['help']);

output.accumulator = '';
await run(['.help\n']);
assert.match(output.accumulator, /custom help output/);

replServer.removeAllListeners('exit');
replServer.close();
}
Loading