Node.js v26.0.0 文档
- Node.js v26.0.0
- 目录
- 索引
- 关于本文档
- 用法与示例
- 断言测试
- 异步上下文跟踪
- 异步钩子
- 缓冲区
- C++ 插件
- 使用 Node-API 的 C/C++ 插件
- C++ 嵌入器 API
- 子进程
- 集群
- 命令行选项
- 控制台
- 加密
- 调试器
- 已弃用的 API
- 诊断通道
- DNS
- 域
- 环境变量
- 错误
- 事件
- 文件系统
- 全局对象
- HTTP
- HTTP/2
- HTTPS
- 检查器
- 国际化
- 模块:CommonJS 模块
- 模块:ECMAScript 模块
- 模块:
node:moduleAPI - 模块:包
- 模块:TypeScript
- 网络
- 可迭代流 API
- 操作系统
- 路径
- 性能钩子
- 权限
- 进程
- Punycode
- 查询字符串
- 逐行读取
- REPL
- 报告
- 单一可执行文件应用
- SQLite
- 流
- 字符串解码器
- 测试运行器
- 定时器
- TLS/SSL
- 跟踪事件
- TTY
- UDP/数据报
- URL
- 实用工具
- V8
- 虚拟机
- WASI
- Web Crypto API
- Web Streams API
- 工作线程
- Zlib
- Zlib 可迭代压缩
- 其他版本
- 选项
REPL#
稳定性:2 - 稳定
node:repl 模块提供了一种 Read-Eval-Print-Loop (REPL) 实现,既可以作为独立程序使用,也可以包含在其他应用程序中。可以通过以下方式访问:
import repl from 'node:repl';const repl = require('node:repl');
设计与特性#
node:repl 模块导出了 repl.REPLServer 类。在运行时,repl.REPLServer 的实例会接收用户输入的每一行,根据用户定义的求值函数进行求值,然后输出结果。输入和输出可以分别来自 stdin 和 stdout,也可以连接到任何 Node.js 流。
repl.REPLServer 的实例支持自动补全输入、补全预览、简易的 Emacs 风格行编辑、多行输入、类似 ZSH 的反向搜索 (reverse-i-search)、类似 ZSH 的基于子字符串的历史搜索、ANSI 样式输出、保存和恢复当前 REPL 会话状态、错误恢复以及自定义求值函数。不支持 ANSI 样式和 Emacs 风格行编辑的终端会自动回退到受限的功能集。
命令与特殊按键#
所有 REPL 实例均支持以下特殊命令
.break:在输入多行表达式的过程中,输入.break命令(或按下 Ctrl+C)以中止进一步输入或处理该表达式。.clear:将 REPLcontext重置为空对象,并清除正在输入的所有多行表达式。.exit:关闭 I/O 流,导致 REPL 退出。.help:显示此特殊命令列表。.save:将当前 REPL 会话保存到文件:> .save ./file/to/save.js.load:将文件加载到当前 REPL 会话。> .load ./file/to/load.js.editor:进入编辑器模式(Ctrl+D 完成,Ctrl+C 取消)。
> .editor
// Entering editor mode (^D to finish, ^C to cancel)
function welcome(name) {
return `Hello ${name}!`;
}
welcome('Node.js User');
// ^D
'Hello Node.js User!'
>
REPL 中的以下组合键具有特殊效果
- Ctrl+C:按一次时,与
.break命令效果相同。在空白行上按两次时,与.exit命令效果相同。 - Ctrl+D:与
.exit命令效果相同。 - Tab:在空白行上按下时,显示全局和局部(作用域)变量。在输入其他内容时按下时,显示相关的自动补全选项。
有关反向搜索相关的键绑定,请参见 reverse-i-search。对于所有其他键绑定,请参见 TTY 键绑定。
默认求值#
默认情况下,所有 repl.REPLServer 实例都使用一个求值函数,该函数对 JavaScript 表达式求值并提供对 Node.js 内置模块的访问权限。此默认行为可以通过在创建 repl.REPLServer 实例时传入替代求值函数来重写。
JavaScript 表达式#
默认求值器支持对 JavaScript 表达式进行直接求值
> 1 + 1
2
> const m = 2
undefined
> m + 1
3
除非在块或函数内定义作用域,否则隐式声明或使用 const、let 或 var 关键字声明的变量均声明在全局作用域中。
全局与局部作用域#
默认求值器提供对全局作用域中存在的任何变量的访问。可以通过将变量分配给与每个 REPLServer 关联的 context 对象,显式地将其暴露给 REPL。
import repl from 'node:repl'; const msg = 'message'; repl.start('> ').context.m = msg;const repl = require('node:repl'); const msg = 'message'; repl.start('> ').context.m = msg;
context 对象中的属性在 REPL 中表现为局部变量。
$ node repl_test.js
> m
'message'
上下文属性默认并非只读。要指定只读全局变量,必须使用 Object.defineProperty() 定义上下文属性。
import repl from 'node:repl'; const msg = 'message'; const r = repl.start('> '); Object.defineProperty(r.context, 'm', { configurable: false, enumerable: true, value: msg, });const repl = require('node:repl'); const msg = 'message'; const r = repl.start('> '); Object.defineProperty(r.context, 'm', { configurable: false, enumerable: true, value: msg, });
访问核心 Node.js 模块#
默认求值器在需要时会自动将 Node.js 核心模块加载到 REPL 环境中。例如,除非另行声明为全局或作用域变量,否则输入 fs 将按需计算为 global.fs = require('node:fs')。
> fs.createReadStream('./some/file');
全局未捕获异常#
REPL 使用 domain 模块来捕获该 REPL 会话的所有未捕获异常。
REPL 中使用 domain 模块具有以下副作用:
-
未捕获异常仅在独立 REPL 中触发
'uncaughtException'事件。在另一个 Node.js 程序内的 REPL 中为该事件添加监听器会导致ERR_INVALID_REPL_INPUT。const r = repl.start(); r.write('process.on("uncaughtException", () => console.log("Foobar"));\n'); // Output stream includes: // TypeError [ERR_INVALID_REPL_INPUT]: Listeners for `uncaughtException` // cannot be used in the REPL r.close(); -
尝试使用
process.setUncaughtExceptionCaptureCallback()会抛出ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE错误。
_(下划线)变量的赋值#
默认情况下,默认求值器会将最近一次求值表达式的结果赋值给特殊变量 _(下划线)。显式地为 _ 赋值将禁用此行为。
> [ 'a', 'b', 'c' ]
[ 'a', 'b', 'c' ]
> _.length
3
> _ += 1
Expression assignment to _ now disabled.
4
> 1 + 1
2
> _
4
类似地,_error 将指向最后一次遇到的错误(如果有)。显式地为 _error 赋值将禁用此行为。
> throw new Error('foo');
Uncaught Error: foo
> _error.message
'foo'
await 关键字#
在顶层启用对 await 关键字的支持。
> await Promise.resolve(123)
123
> await Promise.reject(new Error('REPL await'))
Uncaught Error: REPL await
at REPL2:1:54
> const timeout = util.promisify(setTimeout);
undefined
> const old = Date.now(); await timeout(1000); console.log(Date.now() - old);
1002
undefined
在 REPL 中使用 await 关键字的一个已知限制是,它会使 const 关键字的词法作用域失效。
例如:
> const m = await Promise.resolve(123)
undefined
> m
123
> m = await Promise.resolve(234)
234
// redeclaring the constant does error
> const m = await Promise.resolve(345)
Uncaught SyntaxError: Identifier 'm' has already been declared
--no-experimental-repl-await 将禁用 REPL 中的顶层 await。
反向搜索 (Reverse-i-search)#
REPL 支持类似于 ZSH 的双向反向搜索。通过 Ctrl+R 向后搜索,通过 Ctrl+S 向前搜索。
重复的历史记录条目将被跳过。
一旦按下任何不对应于反向搜索的按键,条目即被接受。可以通过按下 Esc 或 Ctrl+C 取消。
改变方向会立即从当前位置开始沿预期方向搜索下一个条目。
自定义求值函数#
创建新的 repl.REPLServer 时,可以提供自定义求值函数。这可用于实现完全自定义的 REPL 应用程序。
求值函数接收以下四个参数:
code<string>要执行的代码(例如1 + 1)。context<Object>执行代码的上下文。根据useGlobal选项,这可以是 JavaScript 的global上下文,也可以是特定于 REPL 实例的上下文。replResourceName<string>与当前代码求值关联的 REPL 资源标识符。这对于调试非常有用。callback<Function>代码求值完成后调用的函数。该回调函数接收两个参数:- 如果求值期间发生错误,则提供一个错误对象;如果未发生错误,则为
null/undefined。 - 代码求值的结果(如果提供了错误,则此参数无关紧要)。
- 如果求值期间发生错误,则提供一个错误对象;如果未发生错误,则为
以下示例演示了一个将给定数字平方的 REPL,如果提供的输入实际上不是数字,则会打印错误。
import repl from 'node:repl'; function byThePowerOfTwo(number) { return number * number; } function myEval(code, context, replResourceName, callback) { if (isNaN(code)) { callback(new Error(`${code.trim()} is not a number`)); } else { callback(null, byThePowerOfTwo(code)); } } repl.start({ prompt: 'Enter a number: ', eval: myEval });const repl = require('node:repl'); function byThePowerOfTwo(number) { return number * number; } function myEval(code, context, replResourceName, callback) { if (isNaN(code)) { callback(new Error(`${code.trim()} is not a number`)); } else { callback(null, byThePowerOfTwo(code)); } } repl.start({ prompt: 'Enter a number: ', eval: myEval });
可恢复的错误#
在 REPL 提示符处,按下 Enter 会将当前的输入行发送到 eval 函数。为了支持多行输入,eval 函数可以向提供的回调函数返回一个 repl.Recoverable 实例。
function myEval(cmd, context, filename, callback) {
let result;
try {
result = vm.runInThisContext(cmd);
} catch (e) {
if (isRecoverableError(e)) {
return callback(new repl.Recoverable(e));
}
}
callback(null, result);
}
function isRecoverableError(error) {
if (error.name === 'SyntaxError') {
return /^(Unexpected end of input|Unexpected token)/.test(error.message);
}
return false;
}
自定义 REPL 输出#
默认情况下,repl.REPLServer 实例在将输出写入提供的 Writable 流(默认为 process.stdout)之前,使用 util.inspect() 方法格式化输出。showProxy 检查选项默认设置为 true,且 colors 选项根据 REPL 的 useColors 选项设置为 true。
可以在构造时指定 useColors 布尔选项,以指示默认写入器使用 ANSI 样式代码为 util.inspect() 方法的输出着色。如果提供了自定义 writer 函数,则此选项无效。
如果 REPL 作为独立程序运行,也可以通过 inspect.replDefaults 属性在 REPL 内部更改 REPL 的 检查默认值,该属性反映了 util.inspect() 中的 defaultOptions。
> util.inspect.replDefaults.compact = false;
false
> [1]
[
1
]
>
要完全自定义 repl.REPLServer 实例的输出,请在构造时为 writer 选项传入一个新函数。例如,以下示例仅将任何输入文本转换为大写。
import repl from 'node:repl'; const r = repl.start({ prompt: '> ', eval: myEval, writer: myWriter }); function myEval(cmd, context, filename, callback) { callback(null, cmd); } function myWriter(output) { return output.toUpperCase(); }const repl = require('node:repl'); const r = repl.start({ prompt: '> ', eval: myEval, writer: myWriter }); function myEval(cmd, context, filename, callback) { callback(null, cmd); } function myWriter(output) { return output.toUpperCase(); }
类:REPLServer#
options<Object>|<string>参见repl.start()- 继承自:
<readline.Interface>
repl.REPLServer 的实例可以使用 repl.start() 方法创建,或直接使用 JavaScript 的 new 关键字创建。
import repl from 'node:repl'; const options = { useColors: true }; const firstInstance = repl.start(options); const secondInstance = new repl.REPLServer(options);const repl = require('node:repl'); const options = { useColors: true }; const firstInstance = repl.start(options); const secondInstance = new repl.REPLServer(options);
事件: 'exit'#
当 REPL 通过接收 .exit 命令作为输入、用户按两次 Ctrl+C 发出 SIGINT 信号、或者按 Ctrl+D 在输入流上发出 'end' 信号而退出时,会触发 'exit' 事件。监听器回调函数在调用时不带任何参数。
replServer.on('exit', () => {
console.log('Received "exit" event from repl!');
process.exit();
});
事件:'reset'#
当 REPL 的上下文重置时,会触发 'reset' 事件。每当接收到 .clear 命令作为输入时,就会发生这种情况,除非 REPL 正在使用默认求值器,并且创建 repl.REPLServer 实例时将 useGlobal 选项设置为 true。监听器回调将以 context 对象的引用作为唯一参数被调用。
这主要可用于将 REPL 上下文重新初始化为某种预定义状态。
import repl from 'node:repl'; function initializeContext(context) { context.m = 'test'; } const r = repl.start({ prompt: '> ' }); initializeContext(r.context); r.on('reset', initializeContext);const repl = require('node:repl'); function initializeContext(context) { context.m = 'test'; } const r = repl.start({ prompt: '> ' }); initializeContext(r.context); r.on('reset', initializeContext);
执行此代码时,全局变量 'm' 可以被修改,但随后可以使用 .clear 命令将其重置为初始值。
$ ./node example.js
> m
'test'
> m = 1
1
> m
1
> .clear
Clearing context...
> m
'test'
>
replServer.defineCommand(keyword, cmd)#
keyword<string>命令关键字(不带前导.字符)。cmd<Object>|<Function>处理命令时调用的函数。
replServer.defineCommand() 方法用于向 REPL 实例添加新的以 . 开头的命令。此类命令通过输入 . 后跟 keyword 来调用。cmd 可以是 Function,或者是具有以下属性的 Object:
help<string>输入.help时显示的帮助文本(可选)。action<Function>要执行的函数,可选择性地接受一个字符串参数。
以下示例展示了向 REPL 实例添加的两个新命令。
import repl from 'node:repl'; const replServer = repl.start({ prompt: '> ' }); replServer.defineCommand('sayhello', { help: 'Say hello', action(name) { this.clearBufferedCommand(); console.log(`Hello, ${name}!`); this.displayPrompt(); }, }); replServer.defineCommand('saybye', function saybye() { console.log('Goodbye!'); this.close(); });const repl = require('node:repl'); const replServer = repl.start({ prompt: '> ' }); replServer.defineCommand('sayhello', { help: 'Say hello', action(name) { this.clearBufferedCommand(); console.log(`Hello, ${name}!`); this.displayPrompt(); }, }); replServer.defineCommand('saybye', function saybye() { console.log('Goodbye!'); this.close(); });
之后可以在 REPL 实例内部使用这些新命令。
> .sayhello Node.js User
Hello, Node.js User!
> .saybye
Goodbye!
replServer.displayPrompt([preserveCursor])#
preserveCursor<boolean>
replServer.displayPrompt() 方法为 REPL 实例做好接收用户输入的准备,在 output 中打印已配置的 prompt 到新行,并恢复 input 以接受新的输入。
当输入多行时,会打印一个管道符 '|',而不是提示符。
当 preserveCursor 为 true 时,光标位置不会重置为 0。
replServer.displayPrompt 方法主要旨在从使用 replServer.defineCommand() 方法注册的命令的 action 函数中调用。
replServer.clearBufferedCommand()#
replServer.clearBufferedCommand() 方法清除任何已缓冲但尚未执行的命令。此方法主要旨在从使用 replServer.defineCommand() 方法注册的命令的 action 函数中调用。
replServer.setupHistory(historyConfig, callback)#
historyConfig<Object>|<string>历史记录文件的路径。如果是字符串,则为历史记录文件的路径。如果是对象,则可以具有以下属性:filePath<string>历史记录文件的路径。size<number>保留的历史记录行数上限。要禁用历史记录,请将此值设置为0。此选项仅在用户将terminal设置为true或由内部output检查确定为true时才有意义,否则不会初始化历史记录缓存机制。默认值:30。removeHistoryDuplicates<boolean>如果为true,则当添加到历史记录列表的新输入行与旧行重复时,将从列表中删除旧行。默认值:false。onHistoryFileLoaded<Function>当历史记录准备好写入或出错时调用。err<Error>repl<repl.REPLServer>
callback<Function>当历史记录准备好写入或出错时调用(如果在historyConfig中提供了onHistoryFileLoaded,则为可选)。err<Error>repl<repl.REPLServer>
为 REPL 实例初始化历史记录日志文件。当执行 Node.js 二进制文件并使用命令行 REPL 时,默认会初始化一个历史记录文件。然而,以编程方式创建 REPL 时并非如此。在使用 REPL 实例进行编程时,请使用此方法初始化历史记录日志文件。
repl.builtinModules#
稳定性:0 - 已弃用。请改为使用 module.builtinModules。
- 类型:
<string[]>
一些 Node.js 模块的名称列表,例如 'http'。
提供有自动迁移工具(源代码)
npx codemod@latest @nodejs/repl-builtin-modules
repl.start([options])#
options<Object>|<string>prompt<string>要显示的输入提示符。默认值:'> '(带一个尾随空格)。input<stream.Readable>将读取 REPL 输入的Readable流。默认值:process.stdin。output<stream.Writable>将写入 REPL 输出的Writable流。默认值:process.stdout。terminal<boolean>如果为true,指定应将output视为 TTY 终端。默认值: 实例化时检查output流上的isTTY属性值。eval<Function>求值每一行给定输入时使用的函数。默认值: JavaScripteval()函数的异步包装器。eval函数可以抛出repl.Recoverable错误以指示输入不完整并提示输入额外行。有关更多详细信息,请参见 自定义求值函数 部分。useColors<boolean>如果为true,指定默认writer函数应在 REPL 输出中包含 ANSI 颜色样式。如果提供了自定义writer函数,则此选项无效。默认值: 如果 REPL 实例的terminal值为true,则检查output流上的颜色支持。useGlobal<boolean>如果为true,指定默认求值函数将使用 JavaScriptglobal作为上下文,而不是为 REPL 实例创建新的独立上下文。Node CLI REPL 将此值设置为true。默认值:false。ignoreUndefined<boolean>如果为true,指定默认写入器在命令计算结果为undefined时不输出其返回值。默认值:false。writer<Function>在写入output之前格式化每个命令输出的函数。默认值:util.inspect()。completer<Function>用于自定义 Tab 自动补全的可选函数。请参见readline.InterfaceCompleter获取示例。replMode<symbol>指定默认求值器是以严格模式还是默认(宽松)模式执行所有 JavaScript 命令的标志。可接受的值为:repl.REPL_MODE_SLOPPY以宽松模式求值表达式。repl.REPL_MODE_STRICT以严格模式求值表达式。这等同于在每个 repl 语句前加上'use strict'。
breakEvalOnSigint<boolean>当接收到SIGINT(例如按下 Ctrl+C)时,停止求值当前的代码片段。此选项不能与自定义eval函数同时使用。默认值:false。preview<boolean>定义 repl 是否打印自动补全和输出预览。默认值: 使用默认 eval 函数时为true;如果使用自定义 eval 函数,则为false。如果terminal为假值,则没有预览,preview的值不起作用。handleError<Function>此函数自定义 REPL 中的错误处理。它接收抛出的异常作为其第一个参数,并且必须同步返回以下值之一:'print'将错误打印到输出流(默认行为)。'ignore'跳过所有剩余的错误处理。'unhandled'将异常视为完全未处理。在这种情况下,错误将传递给进程范围的异常处理程序,例如'uncaughtException'事件。取决于具体用例,在REPLServer实例已关闭的情况下,'unhandled'值可能是可取的,也可能不是。
- 返回:
<repl.REPLServer>
repl.start() 方法创建并启动一个 repl.REPLServer 实例。
如果 options 是一个字符串,则它指定输入提示符。
import repl from 'node:repl'; // a Unix style prompt repl.start('$ ');const repl = require('node:repl'); // a Unix style prompt repl.start('$ ');
Node.js REPL#
Node.js 本身使用 node:repl 模块来提供其自己的交互式界面用于执行 JavaScript。这可以通过在不传递任何参数的情况下执行 Node.js 二进制文件(或通过传递 -i 参数)来使用。
$ node
> const a = [1, 2, 3];
undefined
> a
[ 1, 2, 3 ]
> a.forEach((v) => {
... console.log(v);
... });
1
2
3
环境变量选项#
Node.js REPL 的各种行为可以使用以下环境变量进行自定义:
NODE_REPL_HISTORY:当给出有效路径时,持久的 REPL 历史记录将保存到指定文件,而不是用户主目录中的.node_repl_history。将此值设置为''(空字符串)将禁用持久的 REPL 历史记录。值中的空白字符将被修剪。在 Windows 平台上,具有空值的环境变量是无效的,因此请将此变量设置为一个或多个空格以禁用持久的 REPL 历史记录。NODE_REPL_HISTORY_SIZE:控制在历史记录可用时将保留多少行历史记录。必须是正数。默认值:1000。NODE_REPL_MODE:可以是'sloppy'或'strict'。默认值:'sloppy',这将允许运行非严格模式的代码。
持久历史记录#
默认情况下,Node.js REPL 会通过将输入保存到用户主目录中的 .node_repl_history 文件来在 node REPL 会话之间持久化历史记录。这可以通过设置环境变量 NODE_REPL_HISTORY='' 来禁用。
将 Node.js REPL 与高级行编辑器配合使用#
对于高级行编辑器,请使用环境变量 NODE_NO_READLINE=1 启动 Node.js。这将在规范终端设置中启动主 REPL 和调试器 REPL,从而允许与 rlwrap 一起使用。
例如,可以将以下内容添加到 .bashrc 文件中。
alias node="env NODE_NO_READLINE=1 rlwrap node"
在同一进程中启动多个 REPL 实例#
可以在单个运行中的 Node.js 实例上创建和运行多个 REPL 实例,它们共享单个 global 对象(通过将 useGlobal 选项设置为 true),但具有独立的 I/O 接口。
例如,以下示例在 stdin、Unix 套接字和 TCP 套接字上提供了独立的 REPL,它们全部共享同一个 global 对象。
import net from 'node:net'; import repl from 'node:repl'; import process from 'node:process'; import fs from 'node:fs'; let connections = 0; repl.start({ prompt: 'Node.js via stdin> ', useGlobal: true, input: process.stdin, output: process.stdout, }); const unixSocketPath = '/tmp/node-repl-sock'; // If the socket file already exists let's remove it fs.rmSync(unixSocketPath, { force: true }); net.createServer((socket) => { connections += 1; repl.start({ prompt: 'Node.js via Unix socket> ', useGlobal: true, input: socket, output: socket, }).on('exit', () => { socket.end(); }); }).listen(unixSocketPath); net.createServer((socket) => { connections += 1; repl.start({ prompt: 'Node.js via TCP socket> ', useGlobal: true, input: socket, output: socket, }).on('exit', () => { socket.end(); }); }).listen(5001);const net = require('node:net'); const repl = require('node:repl'); const fs = require('node:fs'); let connections = 0; repl.start({ prompt: 'Node.js via stdin> ', useGlobal: true, input: process.stdin, output: process.stdout, }); const unixSocketPath = '/tmp/node-repl-sock'; // If the socket file already exists let's remove it fs.rmSync(unixSocketPath, { force: true }); net.createServer((socket) => { connections += 1; repl.start({ prompt: 'Node.js via Unix socket> ', useGlobal: true, input: socket, output: socket, }).on('exit', () => { socket.end(); }); }).listen(unixSocketPath); net.createServer((socket) => { connections += 1; repl.start({ prompt: 'Node.js via TCP socket> ', useGlobal: true, input: socket, output: socket, }).on('exit', () => { socket.end(); }); }).listen(5001);
从命令行运行此应用程序将启动一个 stdin 上的 REPL。其他 REPL 客户端可以通过 Unix 套接字或 TCP 套接字连接。例如,telnet 对于连接 TCP 套接字非常有用,而 socat 可用于连接 Unix 和 TCP 套接字。
通过从基于 Unix 套接字的服务器而不是 stdin 启动 REPL,可以在不重启的情况下连接到长时间运行的 Node.js 进程。
示例#
通过 net.Server 和 net.Socket 实现全功能“终端” REPL#
这是一个关于如何使用 net.Server 和 net.Socket 运行“全功能”(终端)REPL 的示例。
以下脚本在端口 1337 上启动 HTTP 服务器,允许客户端建立套接字连接到其 REPL 实例。
// repl-server.js import repl from 'node:repl'; import net from 'node:net'; net .createServer((socket) => { const r = repl.start({ prompt: `socket ${socket.remoteAddress}:${socket.remotePort}> `, input: socket, output: socket, terminal: true, useGlobal: false, }); r.on('exit', () => { socket.end(); }); r.context.socket = socket; }) .listen(1337);// repl-server.js const repl = require('node:repl'); const net = require('node:net'); net .createServer((socket) => { const r = repl.start({ prompt: `socket ${socket.remoteAddress}:${socket.remotePort}> `, input: socket, output: socket, terminal: true, useGlobal: false, }); r.on('exit', () => { socket.end(); }); r.context.socket = socket; }) .listen(1337);
而以下脚本实现了一个可以与上述定义的服务器通过端口 1337 建立套接字连接的客户端。
// repl-client.js import net from 'node:net'; import process from 'node:process'; const sock = net.connect(1337); process.stdin.pipe(sock); sock.pipe(process.stdout); sock.on('connect', () => { process.stdin.resume(); process.stdin.setRawMode(true); }); sock.on('close', () => { process.stdin.setRawMode(false); process.stdin.pause(); sock.removeListener('close', done); }); process.stdin.on('end', () => { sock.destroy(); console.log(); }); process.stdin.on('data', (b) => { if (b.length === 1 && b[0] === 4) { process.stdin.emit('end'); } });// repl-client.js const net = require('node:net'); const sock = net.connect(1337); process.stdin.pipe(sock); sock.pipe(process.stdout); sock.on('connect', () => { process.stdin.resume(); process.stdin.setRawMode(true); }); sock.on('close', () => { process.stdin.setRawMode(false); process.stdin.pause(); sock.removeListener('close', done); }); process.stdin.on('end', () => { sock.destroy(); console.log(); }); process.stdin.on('data', (b) => { if (b.length === 1 && b[0] === 4) { process.stdin.emit('end'); } });
要运行此示例,请在您的计算机上打开两个不同的终端,在一个终端中使用 node repl-server.js 启动服务器,在另一个终端中使用 node repl-client.js 启动客户端。
通过 curl 实现 REPL#
这是一个关于如何通过 curl() 运行 REPL 实例的示例。
以下脚本在端口 8000 上启动 HTTP 服务器,该服务器可以接受通过 curl() 建立的连接。
import http from 'node:http'; import repl from 'node:repl'; const server = http.createServer((req, res) => { res.setHeader('content-type', 'multipart/octet-stream'); repl.start({ prompt: 'curl repl> ', input: req, output: res, terminal: false, useColors: true, useGlobal: false, }); }); server.listen(8000);const http = require('node:http'); const repl = require('node:repl'); const server = http.createServer((req, res) => { res.setHeader('content-type', 'multipart/octet-stream'); repl.start({ prompt: 'curl repl> ', input: req, output: res, terminal: false, useColors: true, useGlobal: false, }); }); server.listen(8000);
当上述脚本运行时,您可以使用 curl() 通过运行 curl --no-progress-meter -sSNT. localhost:8000 来连接到服务器并连接到其 REPL 实例。
警告 此示例纯粹用于教育目的,旨在演示如何使用不同的 I/O 流启动 Node.js REPL。在没有额外保护措施的情况下,不应在生产环境或任何安全性敏感的环境中使用它。如果您需要在实际应用程序中实现 REPL,请考虑使用其他能够降低风险的方法,例如使用安全的输入机制并避免公开网络接口。