Node.js v25.0.0 文档
- Node.js v25.0.0
- 目录
-
索引
- 断言测试
- 异步上下文跟踪
- 异步钩子
- 缓冲区
- C++ 插件
- 使用 Node-API 的 C/C++ 插件
- C++ 嵌入器 API
- 子进程
- 集群
- 命令行选项
- 控制台
- 加密
- 调试器
- 已弃用的 API
- 诊断通道
- DNS
- 域
- 环境变量
- 错误
- 事件
- 文件系统
- 全局对象
- HTTP
- HTTP/2
- HTTPS
- 检查器
- 国际化
- 模块:CommonJS 模块
- 模块:ECMAScript 模块
- 模块:
node:module
API - 模块:包
- 模块:TypeScript
- 网络
- 操作系统
- 路径
- 性能钩子
- 权限
- 进程
- Punycode
- 查询字符串
- 逐行读取
- REPL
- 报告
- 单一可执行文件应用
- SQLite
- 流
- 字符串解码器
- 测试运行器
- 定时器
- TLS/SSL
- 跟踪事件
- TTY
- UDP/数据报
- URL
- 实用工具
- V8
- 虚拟机
- WASI
- Web Crypto API
- Web Streams API
- 工作线程
- Zlib
- 其他版本
- 选项
REPL#
源代码: lib/repl.js
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 的 stream。
repl.REPLServer
的实例支持自动补全输入、补全预览、简单的 Emacs 风格行编辑、多行输入、类似 ZSH 的反向 i-search、类似 ZSH 的基于子字符串的历史搜索、ANSI 风格的输出、保存和恢复当前 REPL 会话状态、错误恢复以及可定制的求值函数。不支持 ANSI 风格和 Emacs 风格行编辑的终端会自动降级为功能受限的模式。
命令与特殊按键#
所有 REPL 实例都支持以下特殊命令:
.break
:当正在输入一个多行表达式时,输入.break
命令(或按 Ctrl+C)可以中止该表达式的进一步输入或处理。.clear
:将 REPL 的context
重置为一个空对象,并清除任何正在输入的多行表达式。.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:在空行上按时,显示全局和局部(作用域内)的变量。在输入其他内容时按,显示相关的自动补全选项。
关于反向 i-search 的键绑定,请参见 反向 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。
反向 i-search#
REPL 支持类似于 ZSH 的双向反向 i-search。使用 Ctrl+R 向后搜索,使用 Ctrl+S 向前搜索。
重复的历史记录条目将被跳过。
一旦按下任何与反向搜索无关的键,条目就会被接受。可以通过按 Esc 或 Ctrl+C 来取消。
立即改变方向会从当前位置开始,在预期的方向上搜索下一个条目。
自定义求值函数#
当创建新的 repl.REPLServer
时,可以提供一个自定义的求值函数。这可以用来实现完全定制的 REPL 应用程序。
一个求值函数接受以下四个参数:
code
<string> 将要执行的代码(例如1 + 1
)。context
<Object> 代码执行的上下文。这可以是 JavaScript 的global
上下文,也可以是特定于 REPL 实例的上下文,具体取决于useGlobal
选项。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
实例使用 util.inspect()
方法格式化输出,然后将输出写入到提供的 Writable
流(默认为 process.stdout
)。showProxy
检查选项默认设置为 true,colors
选项则根据 REPL 的 useColors
选项设置为 true。
可以在构造时指定 useColors
布尔值选项,以指示默认写入器使用 ANSI 样式代码为 util.inspect()
方法的输出着色。
如果 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'
事件。这可能发生在接收到 .exit
命令作为输入、用户按两次 Ctrl+C 发送 SIGINT
信号,或者按 Ctrl+D 在输入流上发送 'end'
信号时。监听器回调函数在调用时不带任何参数。
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 实例准备好接收用户输入,将配置的 prompt
打印到 output
的新行,并恢复 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
。此选项仅在用户或内部output
检查将terminal
设置为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
#
module.builtinModules
。- 类型:<string[]>
一些 Node.js 模块名称的列表,例如 'http'
。
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
,则指定默认求值函数将使用 JavaScript 的global
作为上下文,而不是为 REPL 实例创建一个新的独立上下文。Node CLI REPL 将此值设置为true
。默认值:false
。ignoreUndefined
<boolean> 如果为true
,则指定默认的写入器在命令的求值结果为undefined
时,不会输出其返回值。默认值:false
。writer
<Function> 在写入output
之前,用于格式化每个命令输出的函数。默认值:util.inspect()
。completer
<Function> 一个用于自定义 Tab 自动补全的可选函数。示例请参见readline.InterfaceCompleter
。replMode
<symbol> 一个标志,指定默认求值器是在严格模式还是默认(sloppy)模式下执行所有 JavaScript 命令。可接受的值为:repl.REPL_MODE_SLOPPY
以 sloppy 模式求值表达式。repl.REPL_MODE_STRICT
以严格模式求值表达式。这等同于在每个 repl 语句前加上'use strict'
。
breakEvalOnSigint
<boolean> 当收到SIGINT
信号时(例如按下 Ctrl+C 时),停止求值当前代码块。这不能与自定义的eval
函数一起使用。默认值:false
。preview
<boolean> 定义 repl 是否打印自动补全和输出预览。默认值: 对于默认求值函数为true
,对于使用自定义求值函数的情况为false
。如果terminal
为假值,则没有预览,preview
的值无效。
- 返回:<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,请考虑采用其他方法来减轻这些风险,例如使用安全的输入机制并避免开放网络接口。