diff --git a/doc/api/cli.md b/doc/api/cli.md index 27947553d34fdb..4cb33a157a7230 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1178,6 +1178,14 @@ Path to the file used to store the persistent REPL history. The default path is `~/.node_repl_history`, which is overridden by this variable. Setting the value to an empty string (`''` or `' '`) disables persistent REPL history. +### `NODE_REPL_EXTERNAL_MODULE=file` + + +Path to a Node.js module which will be loaded in place of the built-in REPL. +Overriding this value to an empty string (`''`) will use the built-in REPL. + ### `NODE_TLS_REJECT_UNAUTHORIZED=value` If `value` equals `'0'`, certificate validation is disabled for TLS connections. diff --git a/lib/internal/main/repl.js b/lib/internal/main/repl.js index 93b932f0bdd15f..693b7048a37f9c 100644 --- a/lib/internal/main/repl.js +++ b/lib/internal/main/repl.js @@ -19,38 +19,44 @@ prepareMainThreadExecution(); markBootstrapComplete(); -// --input-type flag not supported in REPL -if (getOptionValue('--input-type')) { - // If we can't write to stderr, we'd like to make this a noop, - // so use console.error. - console.error('Cannot specify --input-type for REPL'); - process.exit(1); -} +if (process.env.NODE_REPL_EXTERNAL_MODULE) { + require('internal/modules/cjs/loader') + .Module + ._load(process.env.NODE_REPL_EXTERNAL_MODULE, undefined, true); +} else { + // --input-type flag not supported in REPL + if (getOptionValue('--input-type')) { + // If we can't write to stderr, we'd like to make this a noop, + // so use console.error. + console.error('Cannot specify --input-type for REPL'); + process.exit(1); + } -console.log(`Welcome to Node.js ${process.version}.\n` + - 'Type ".help" for more information.'); + console.log(`Welcome to Node.js ${process.version}.\n` + + 'Type ".help" for more information.'); -const cliRepl = require('internal/repl'); -cliRepl.createInternalRepl(process.env, (err, repl) => { - if (err) { - throw err; - } - repl.on('exit', () => { - if (repl._flushing) { - repl.pause(); - return repl.once('flushHistory', () => { - process.exit(); - }); + const cliRepl = require('internal/repl'); + cliRepl.createInternalRepl(process.env, (err, repl) => { + if (err) { + throw err; } - process.exit(); + repl.on('exit', () => { + if (repl._flushing) { + repl.pause(); + return repl.once('flushHistory', () => { + process.exit(); + }); + } + process.exit(); + }); }); -}); - -// If user passed '-e' or '--eval' along with `-i` or `--interactive`, -// evaluate the code in the current context. -if (getOptionValue('[has_eval_string]')) { - evalScript('[eval]', - getOptionValue('--eval'), - getOptionValue('--inspect-brk'), - getOptionValue('--print')); + + // If user passed '-e' or '--eval' along with `-i` or `--interactive`, + // evaluate the code in the current context. + if (getOptionValue('[has_eval_string]')) { + evalScript('[eval]', + getOptionValue('--eval'), + getOptionValue('--inspect-brk'), + getOptionValue('--print')); + } } diff --git a/test/fixtures/external-repl-module.js b/test/fixtures/external-repl-module.js new file mode 100644 index 00000000000000..272ab0a556a4f5 --- /dev/null +++ b/test/fixtures/external-repl-module.js @@ -0,0 +1,3 @@ +'use strict'; + +console.log('42'); diff --git a/test/pseudo-tty/test-repl-external-module.js b/test/pseudo-tty/test-repl-external-module.js new file mode 100644 index 00000000000000..db9ad29712ed70 --- /dev/null +++ b/test/pseudo-tty/test-repl-external-module.js @@ -0,0 +1,14 @@ +'use strict'; + +require('../common'); +const fixtures = require('../common/fixtures'); +const { execSync } = require('child_process'); + +execSync(process.execPath, { + encoding: 'utf8', + stdio: 'inherit', + env: { + ...process.env, + NODE_REPL_EXTERNAL_MODULE: fixtures.path('external-repl-module.js'), + }, +}); diff --git a/test/pseudo-tty/test-repl-external-module.out b/test/pseudo-tty/test-repl-external-module.out new file mode 100644 index 00000000000000..d81cc0710eb6cf --- /dev/null +++ b/test/pseudo-tty/test-repl-external-module.out @@ -0,0 +1 @@ +42