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:moduleAPI - 模組:包
- 模組: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,請考慮採用其他方法來減輕這些風險,例如使用安全的輸入機制並避免開放網路介面。