REPL#

安定性: 2 - Stable

ソースコード: 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 のインスタンスはユーザー入力の個々の行を受け付け、ユーザー定義の評価関数に従ってそれらを評価し、結果を出力します。入出力はそれぞれ stdinstdout から行うことも、任意の Node.js ストリームに接続することもできます。

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 

ブロックや関数内でスコープが指定されていない限り、暗黙的に、または constletvar キーワードを使用して宣言された変数は、グローバルスコープで宣言されます。

グローバルスコープとローカルスコープ#

デフォルトのエバリュエータは、グローバルスコープに存在する任意の変数へのアクセスを提供します。各 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 キーワードを使用する既知の制限の1つは、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アプリケーションを実装するために使用できます。

評価関数は、以下の4つの引数を受け入れます。

  • code <string> 実行されるコード (例: 1 + 1)。
  • context <Object> コードが実行されるコンテキスト。これは、useGlobal オプションに応じて、JavaScript の global コンテキストまたは REPL インスタンス固有のコンテキストのいずれかになります。
  • replResourceName <string> 現在のコード評価に関連付けられた REPL リソースの識別子。これはデバッグ目的で役立ちます。
  • callback <Function> コードの評価が完了したときに呼び出す関数。コールバックは2つのパラメータを取ります。
    • 評価中にエラーが発生した場合に提供するエラーオブジェクト、またはエラーが発生しなかった場合は 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 ブーリアンオプションは、構築時に指定して、デフォルトのライターが util.inspect() メソッドからの出力を色付けするために ANSI スタイルコードを使用するように指示することができます。

REPL がスタンドアロンプログラムとして実行されている場合、REPL 内から inspect.replDefaults プロパティを使用して 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#

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'#

'exit' イベントは、REPL が入力として .exit コマンドを受け取ったとき、ユーザーが SIGINT をシグナルするために Ctrl+C を2回押したとき、または入力ストリームで 'end' をシグナルするために Ctrl+D を押したときに発行されます。リスナーコールバックは引数なしで呼び出されます。

replServer.on('exit', () => {
  console.log('Received "exit" event from repl!');
  process.exit();
}); 

イベント: 'reset'#

'reset' イベントは、REPL のコンテキストがリセットされたときに発行されます。これは、入力として .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 を入力することで呼び出されます。cmdFunction または以下のプロパティを持つ Object のいずれかです。

  • help <string> .help が入力されたときに表示されるヘルプテキスト (オプション)。
  • action <Function> 実行する関数。オプションで単一の文字列引数を受け入れます。

次の例は、REPL インスタンスに追加された2つの新しいコマンドを示しています。

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])#

replServer.displayPrompt() メソッドは、ユーザーからの入力のために REPL インスタンスを準備し、設定された promptoutput の新しい行に出力し、新しい入力を受け入れるために input を再開します。

複数行の入力が行われている場合、「プロンプト」ではなくパイプ '|' が表示されます。

preserveCursortrue の場合、カーソル位置は 0 にリセットされません。

replServer.displayPrompt メソッドは、主に replServer.defineCommand() メソッドを使用して登録されたコマンドのアクション関数内から呼び出されることを意図しています。

replServer.clearBufferedCommand()#

replServer.clearBufferedCommand() メソッドは、バッファリングされているがまだ実行されていないコマンドをクリアします。このメソッドは、主に replServer.defineCommand() メソッドを使用して登録されたコマンドのアクション関数内から呼び出されることを意図しています。

replServer.setupHistory(historyConfig, callback)#

  • historyConfig <Object> | <string> 履歴ファイルへのパス。文字列の場合、履歴ファイルへのパスです。オブジェクトの場合、以下のプロパティを持つことができます。
    • filePath <string> 履歴ファイルへのパス
    • size <number> 保持される履歴行の最大数。履歴を無効にするには、この値を 0 に設定します。このオプションは、ユーザーによって、または内部の output チェックによって terminaltrue に設定されている場合にのみ意味があります。そうでない場合、履歴キャッシングメカニズムはまったく初期化されません。デフォルト: 30
    • removeHistoryDuplicates <boolean> true の場合、履歴リストに追加された新しい入力行が古いものと重複する場合、古い行をリストから削除します。デフォルト: false
    • onHistoryFileLoaded <Function> 履歴の書き込み準備ができたとき、またはエラー時に呼び出されます。
  • callback <Function> 履歴の書き込み準備ができたとき、またはエラー時に呼び出されます (historyConfig 内の onHistoryFileLoaded として提供された場合はオプション)。

REPL インスタンスの履歴ログファイルを初期化します。Node.js バイナリを実行し、コマンドライン REPL を使用する場合、履歴ファイルはデフォルトで初期化されます。ただし、プログラムで REPL を作成する場合はそうではありません。このメソッドを使用して、プログラムで REPL インスタンスを操作するときに履歴ログファイルを初期化します。

repl.builtinModules#

安定性: 0 - 非推奨。代わりに module.builtinModules を使用してください。

'http' などの一部の Node.js モジュールの名前のリスト。

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> 与えられた各入力行を評価する際に使用される関数。デフォルト: JavaScript の eval() 関数の非同期ラッパー。eval 関数は、入力が不完全であり追加の行を要求することを示すために repl.Recoverable でエラーを返すことができます。カスタム評価関数のセクションで詳細を参照してください。
    • useColors <boolean> true の場合、デフォルトの writer 関数が REPL 出力に ANSI カラースタイリングを含めるべきであることを指定します。カスタム writer 関数が提供されている場合、これは効果がありません。デフォルト: REPL インスタンスの terminal 値が true の場合に output ストリームの色サポートをチェックします。
    • useGlobal <boolean> true の場合、デフォルトの評価関数が REPL インスタンス用の新しい別のコンテキストを作成するのではなく、JavaScript の global をコンテキストとして使用することを指定します。node CLI REPL はこの値を true に設定します。デフォルト: false
    • ignoreUndefined <boolean> true の場合、コマンドの戻り値が undefined に評価された場合、デフォルトのライターが出力しないことを指定します。デフォルト: false
    • writer <Function> output に書き込む前に各コマンドの出力をフォーマットするために呼び出される関数。デフォルト: util.inspect()
    • completer <Function> カスタムの Tab 自動補完に使用されるオプションの関数。例については readline.InterfaceCompleter を参照してください。
    • replMode <symbol> デフォルトのエバリュエータがすべての JavaScript コマンドを strict モードまたはデフォルト (sloppy) モードで実行するかどうかを指定するフラグ。許容される値は次のとおりです。
      • repl.REPL_MODE_SLOPPY: sloppy モードで式を評価します。
      • repl.REPL_MODE_STRICT: strict モードで式を評価します。これは、すべての repl ステートメントの前に 'use strict' を付けることと同等です。
    • breakEvalOnSigint <boolean> Ctrl+C が押されたときなど、SIGINT を受信したときに現在のコードの評価を停止します。これはカスタムの eval 関数と一緒に使用することはできません。デフォルト: false
    • preview <boolean> repl がオートコンプリートと出力プレビューを印字するかどうかを定義します。デフォルト: デフォルトの eval 関数では true、カスタム eval 関数が使用される場合は false です。terminal が falsy の場合、プレビューはなく、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 プラットフォームでは、空の値を持つ環境変数は無効なので、この変数を1つ以上のスペースに設定して永続的な REPL 履歴を無効にしてください。
  • NODE_REPL_HISTORY_SIZE: 履歴が利用可能な場合に永続化される履歴の行数を制御します。正の数でなければなりません。デフォルト: 1000
  • NODE_REPL_MODE: 'sloppy' または 'strict' のいずれかです。デフォルト: 'sloppy'。これにより、非 strict モードのコードを実行できます。

永続的な履歴#

デフォルトでは、Node.js REPL は、ユーザーのホームディレクトリにある .node_repl_history ファイルに入力を保存することで、node REPL セッション間の履歴を永続化します。これは、環境変数 NODE_REPL_HISTORY='' を設定することで無効にできます。

高度なラインエディタで Node.js REPL を使用する#

高度なラインエディタの場合、環境変数 NODE_NO_READLINE=1 を設定して Node.js を起動します。これにより、メインおよびデバッガの REPL が標準のターミナル設定で起動し、rlwrap との使用が可能になります。

例えば、以下を .bashrc ファイルに追加することができます。

alias node="env NODE_NO_READLINE=1 rlwrap node" 

同一プロセスで複数のREPLインスタンスを開始する#

単一の global オブジェクトを共有する (useGlobal オプションを true に設定することで) が、別々の I/O インターフェースを持つ、単一の実行中の Node.js インスタンスに対して複数の REPL インスタンスを作成して実行することが可能です。

たとえば、次の例では、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 ソケットの両方に接続するために使用できます。

stdin の代わりに Unix ソケットベースのサーバーから REPL を開始することにより、長時間実行されている Node.js プロセスを再起動せずに接続することが可能です。

#

net.Servernet.Socket 経由のフル機能の「ターミナル」REPL#

これは、net.Servernet.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');
  }
});

この例を実行するには、マシンで2つの異なるターミナルを開き、一方のターミナルで node repl-server.js を実行してサーバーを起動し、もう一方のターミナルで node repl-client.js を実行します。

元のコードは https://gist.github.com/TooTallNate/2209310 からです。

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 --no-progress-meter -sSNT. localhost:8000 を実行することで、curl() を使用してサーバーに接続し、その REPL インスタンスに接続できます。

警告 この例は、Node.js REPL が異なる I/O ストリームを使用してどのように開始できるかを示す、純粋に教育的な目的のためのものです。セキュリティが懸念される本番環境やいかなる文脈においても、追加の保護措置なしで使用すべきではありません。実世界のアプリケーションで REPL を実装する必要がある場合は、安全な入力メカニズムの使用やオープンなネットワークインターフェースの回避など、これらのリスクを軽減する代替アプローチを検討してください。

元のコードは https://gist.github.com/TooTallNate/2053342 からです。