REPL
Исходный код: lib/repl.js
Модуль node:repl предоставляет реализацию цикла чтения-вычисления-вывода (REPL), доступную как в виде отдельной программы, так и для включения в другие приложения. Получить к нему доступ можно следующим образом:
Модули JavaScript
import repl from 'node:repl';
CommonJS
const repl = require('node:repl');Устройство и возможности
Модуль node:repl экспортирует класс repl.REPLServer. Во время работы экземпляры repl.REPLServer принимают отдельные строки пользовательского ввода, вычисляют их с помощью определённой пользователем функции вычисления, а затем выводят результат. Ввод и вывод могут выполняться соответственно через stdin и stdout либо быть подключены к любому потоку Node.js.
Экземпляры repl.REPLServer поддерживают автоматическое дополнение ввода, предварительный просмотр вариантов дополнения, упрощённое редактирование строк в стиле Emacs, ввод нескольких строк, обратный поиск по истории в стиле ZSH, поиск по подстроке в истории в стиле ZSH, вывод с оформлением ANSI, сохранение и восстановление текущего состояния сеанса REPL, восстановление после ошибок и настраиваемые функции вычисления. Терминалы, не поддерживающие стили ANSI и редактирование строк в стиле Emacs, автоматически переходят к ограниченному набору возможностей.
Команды и специальные клавиши
Все экземпляры REPL поддерживают следующие специальные команды:
-
.break: Во время ввода многострочного выражения введите команду.break(или нажмите Ctrl+C), чтобы прекратить дальнейший ввод или обработку этого выражения. -
.clear: СбрасываетcontextREPL в пустой объект и удаляет вводимое многострочное выражение. -
.exit: Закрывает поток ввода-вывода, в результате чего 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!'
> copy Следующие сочетания клавиш в 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 copy
Если переменные не ограничены блоками или функциями, переменные, объявленные неявно или с помощью ключевых слов const, let или var, объявляются в глобальной области видимости.
Глобальная и локальная области видимости
Средство вычисления по умолчанию предоставляет доступ к любым переменным, существующим в глобальной области видимости. Переменную можно явно сделать доступной в REPL, присвоив её объекту context, связанному с каждым REPLServer:
Модули JavaScript
import repl from 'node:repl';
const msg = 'message';
repl.start('> ').context.m = msg;CommonJS
const repl = require('node:repl');
const msg = 'message';
repl.start('> ').context.m = msg;Свойства объекта context отображаются в REPL как локальные:
$ node repl_test.js > m 'message' copy
По умолчанию свойства контекста доступны для записи. Чтобы указать глобальные переменные только для чтения, свойства контекста необходимо определять с помощью Object.defineProperty():
Модули JavaScript
import repl from 'node:repl';
const msg = 'message';
const r = repl.start('> ');
Object.defineProperty(r.context, 'm', {
configurable: false,
enumerable: true,
value: msg,
});CommonJS
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'); copy Глобальные необработанные исключения
REPL использует модуль domain, чтобы перехватывать все необработанные исключения в рамках данного сеанса REPL.
Использование модуля domain в REPL имеет следующие побочные эффекты:
-
Необработанные исключения вызывают событие
'uncaughtException'только в отдельной программе REPL. Добавление слушателя этого события в REPL внутри другой программы Node.js приводит к ошибке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(); copy -
Попытка использовать
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 copy
Аналогично, _error будет ссылаться на последнюю возникшую ошибку, если такая была. Явное присваивание значения _error отключает это поведение.
> throw new Error('foo');
Uncaught Error: foo
> _error.message
'foo' copy Ключевое слово
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 copy Одно из известных ограничений использования ключевого слова await в REPL заключается в том, что оно нарушает лексическую область видимости ключевых слов const и let.
Например:
> const m = await Promise.resolve(123) undefined > m 123 > const m = await Promise.resolve(234) undefined > m 234 copy
Параметр --no-experimental-repl-await отключает await на верхнем уровне в REPL.
Обратный поиск по истории
REPL поддерживает двунаправленный обратный поиск по истории, аналогичный ZSH. Для поиска назад нажмите Ctrl+R, а для поиска вперёд — Ctrl+S.
Повторяющиеся записи истории пропускаются.
Запись принимается при нажатии любой клавиши, не относящейся к обратному поиску. Отменить поиск можно клавишей Esc или сочетанием Ctrl+C.
Изменение направления немедленно запускает поиск следующей записи в выбранном направлении, начиная с текущей позиции.
Пользовательские функции вычисления
При создании нового экземпляра repl.REPLServer можно указать пользовательскую функцию вычисления. Это можно использовать, например, для реализации полностью настраиваемых приложений REPL.
Функция вычисления принимает следующие четыре аргумента:
-
code<string> Код, который необходимо выполнить (например,1 + 1). -
context<Object> Контекст, в котором выполняется код. Это может быть контекст JavaScriptglobalили контекст, относящийся к экземпляру REPL, в зависимости от параметраuseGlobal. -
replResourceName<string> Идентификатор ресурса REPL, связанного с текущим вычислением кода. Он может быть полезен для отладки. -
callback<Function> Функция, вызываемая после завершения вычисления кода. Обратный вызов принимает два параметра:- Объект ошибки, передаваемый при возникновении ошибки во время вычисления, либо
null/undefined, если ошибки не было. - Результат вычисления кода (этот параметр не имеет значения, если передана ошибка).
- Объект ошибки, передаваемый при возникновении ошибки во время вычисления, либо
В следующем примере показан REPL, возводящий заданное число в квадрат; если введённое значение на самом деле не является числом, вместо этого выводится ошибка:
Модули JavaScript
import repl from 'node:repl';
function byThePowerOfTwo(number) {
return number * number;
}
function myEval(code, context, replResourceName, callback) {
if (Number.isNaN(code)) {
callback(new Error(`${code.trim()} is not a number`));
} else {
callback(null, byThePowerOfTwo(code));
}
}
repl.start({ prompt: 'Enter a number: ', eval: myEval });CommonJS
const repl = require('node:repl');
function byThePowerOfTwo(number) {
return number * number;
}
function myEval(code, context, replResourceName, callback) {
if (Number.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;
} copy Настройка вывода REPL
По умолчанию экземпляры repl.REPLServer форматируют вывод с помощью метода util.inspect(), прежде чем записать его в предоставленный поток Writable (по умолчанию process.stdout). По умолчанию параметр инспектирования showProxy имеет значение true, а параметр colors получает значение true в зависимости от параметра useColors объекта REPL.
При создании можно указать логический параметр useColors, чтобы стандартный обработчик вывода использовал коды стилей ANSI для раскрашивания вывода метода util.inspect().
Если REPL запущен как отдельная программа, значения параметров инспектирования REPL по умолчанию также можно изменить непосредственно в REPL с помощью свойства inspect.replDefaults, соответствующего параметру defaultOptions из util.inspect().
> util.inspect.replDefaults.compact = false; false > [1] [ 1 ] > copy
Чтобы полностью настроить вывод экземпляра repl.REPLServer, при создании передайте новую функцию в параметре writer. Например, в следующем примере любой введённый текст просто преобразуется в верхний регистр:
Модули JavaScript
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();
}CommonJS
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.
Модули JavaScript
import repl from 'node:repl';
const options = { useColors: true };
const firstInstance = repl.start(options);
const secondInstance = new repl.REPLServer(options);CommonJS
const repl = require('node:repl');
const options = { useColors: true };
const firstInstance = repl.start(options);
const secondInstance = new repl.REPLServer(options);Событие: 'exit'
Событие 'exit' возникает, когда работа REPL завершается: при получении в качестве ввода команды .exit, при двукратном нажатии пользователем Ctrl+C для отправки сигнала SIGINT или при нажатии Ctrl+D для отправки сигнала 'end' в поток ввода. Функция-слушатель вызывается без аргументов.
replServer.on('exit', () => {
console.log('Received "exit" event from repl!');
process.exit();
}); copy Событие: 'reset'
Событие 'reset' возникает при сбросе контекста REPL. Это происходит при получении команды .clear в качестве ввода, если только REPL не использует средство вычисления по умолчанию и экземпляр repl.REPLServer не был создан с параметром useGlobal, установленным в true. Функция-слушатель будет вызвана со ссылкой на объект context в качестве единственного аргумента.
Это можно использовать, прежде всего, для повторной инициализации контекста REPL до некоторого предварительно заданного состояния:
Модули JavaScript
import repl from 'node:repl';
function initializeContext(context) {
context.m = 'test';
}
const r = repl.start({ prompt: '> ' });
initializeContext(r.context);
r.on('reset', initializeContext);CommonJS
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' > copy
replServer.defineCommand(keyword, cmd)
-
keyword<string> Ключевое слово команды (без начального символа.). -
cmd<Object> | <Function> Функция, вызываемая при обработке команды.
Метод replServer.defineCommand() используется для добавления в экземпляр REPL новых команд с префиксом .. Такие команды вызываются вводом символа ., за которым следует keyword. cmd — это либо Function, либо Object со следующими свойствами:
-
help<string> Текст справки, отображаемый при вводе.help(необязательно). -
action<Function> Функция, которую необходимо выполнить; она может принимать один строковый аргумент.
В следующем примере показано добавление двух новых команд в экземпляр REPL:
Модули JavaScript
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();
});CommonJS
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! copy
replServer.displayPrompt([preserveCursor])
-
preserveCursor<boolean>
Метод replServer.displayPrompt() подготавливает экземпляр REPL к вводу пользователя: выводит настроенное prompt с новой строки в output и возобновляет input для приёма новых данных.
При вводе нескольких строк вместо «приглашения» выводится многоточие.
Если preserveCursor имеет значение true, положение курсора не будет сброшено в 0.
Метод replServer.displayPrompt предназначен главным образом для вызова из функции действия команд, зарегистрированных с помощью метода replServer.defineCommand().
replServer.clearBufferedCommand()
Метод replServer.clearBufferedCommand() удаляет все команды, помещённые в буфер, но ещё не выполненные. Этот метод предназначен главным образом для вызова из функции действия команд, зарегистрированных с помощью метода replServer.defineCommand().
replServer.setupHistory(historyPath, callback)
-
historyPath<string> путь к файлу истории -
callback<Function> вызывается, когда запись истории становится доступной или при возникновении ошибки-
err<Error> -
repl<repl.REPLServer>
-
Инициализирует файл журнала истории для экземпляра REPL. При запуске двоичного файла Node.js и использовании REPL командной строки файл истории создаётся по умолчанию. Однако при программном создании REPL это не происходит. Используйте этот метод для инициализации файла журнала истории при программной работе с экземплярами REPL.
repl.builtinModules
Список имён всех модулей Node.js, например 'http'.
repl.start([options])
-
options<Object> | <string>-
prompt<string> Приглашение, отображаемое для ввода. По умолчанию:'> '(с пробелом в конце). -
input<stream.Readable> ПотокReadable, из которого будет считываться ввод REPL. По умолчанию:process.stdin. -
output<stream.Writable> ПотокWritable, в который будет записываться вывод REPL. По умолчанию:process.stdout. -
terminal<boolean> Если значение равноtrue, указывает, чтоoutputследует считать терминалом TTY. По умолчанию: проверка значения свойстваisTTYпотокаoutputпри создании экземпляра. -
eval<Function> Функция, используемая для вычисления каждой заданной строки ввода. По умолчанию: асинхронная обёртка для функции JavaScripteval(). Функцияevalможет завершиться с ошибкойrepl.Recoverable, указывающей, что ввод неполон и необходимо запросить дополнительные строки. Подробнее см. в разделе Пользовательские функции вычисления. -
useColors<boolean> Если значение равноtrue, указывает, что функцияwriterпо умолчанию должна использовать стилизацию цветов ANSI для вывода REPL. Если задана пользовательская функцияwriter, этот параметр не действует. По умолчанию: проверка поддержки цвета в потокеoutput, если значениеterminalэкземпляра REPL равноtrue. -
useGlobal<boolean> Если значение равноtrue, указывает, что функция вычисления по умолчанию будет использовать контекст JavaScriptglobalвместо создания отдельного контекста для экземпляра REPL. REPL интерфейса командной строки Node устанавливает это значение в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для вычисления выражений в строгом режиме. Это равносильно добавлению'use strict'перед каждым оператором repl.
-
-
breakEvalOnSigint<boolean> Прекращает вычисление текущего фрагмента кода при полученииSIGINT, например при нажатии Ctrl+C. Нельзя использовать вместе с пользовательской функциейeval. По умолчанию:false. -
preview<boolean> Определяет, отображает ли repl предварительный просмотр автодополнения и вывода. По умолчанию:trueпри использовании функции eval по умолчанию иfalseпри использовании пользовательской функции eval. Если значениеterminalложно, предварительный просмотр не отображается, а значениеpreviewне имеет значения.
-
- Возвращает: <repl.REPLServer>
Метод repl.start() создаёт и запускает экземпляр repl.REPLServer.
Если options является строкой, она задаёт приглашение для ввода:
Модули JavaScript
import repl from 'node:repl';
// a Unix style prompt
repl.start('$ ');CommonJS
const repl = require('node:repl');
// a Unix style prompt
repl.start('$ ');REPL Node.js
В 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 copy Параметры переменных среды
Различные режимы работы REPL Node.js можно настроить с помощью следующих переменных среды:
-
NODE_REPL_HISTORY: Если указать допустимый путь, история REPL будет сохраняться в указанный файл, а не в.node_repl_historyв домашнем каталоге пользователя. Чтобы отключить сохранение истории REPL, задайте этому параметру значение''(пустую строку). Пробелы в начале и конце значения будут удалены. В Windows переменные среды с пустыми значениями недопустимы, поэтому задайте этой переменной одно или несколько пробельных символов, чтобы отключить сохранение истории REPL. -
NODE_REPL_HISTORY_SIZE: Определяет, сколько строк истории будет сохранено, если история доступна. Значение должно быть положительным числом. По умолчанию:1000. -
NODE_REPL_MODE: Может иметь значение'sloppy'или'strict'. По умолчанию:'sloppy', что позволяет выполнять код в нестрогом режиме.
Сохранение истории
По умолчанию REPL Node.js сохраняет историю между node сеансами REPL, записывая введённые данные в файл .node_repl_history, расположенный в домашнем каталоге пользователя. Это можно отключить, задав переменную среды NODE_REPL_HISTORY=''.
Использование REPL Node.js с расширенными редакторами строк
Чтобы использовать расширенные редакторы строк, запустите Node.js с переменной среды NODE_NO_READLINE=1. Это запустит основной REPL и REPL отладчика с каноническими настройками терминала, что позволит использовать rlwrap.
Например, в файл .bashrc можно добавить следующее:
alias node="env NODE_NO_READLINE=1 rlwrap node" copy
Запуск нескольких экземпляров REPL для одного работающего экземпляра
Можно создать и запустить несколько экземпляров REPL для одного работающего экземпляра Node.js. Они будут совместно использовать один объект global, но иметь отдельные интерфейсы ввода-вывода.
Например, следующий пример предоставляет отдельные экземпляры REPL в stdin, Unix-сокете и TCP-сокете:
Модули JavaScript
import net from 'node:net';
import repl from 'node:repl';
import process from 'node:process';
let connections = 0;
repl.start({
prompt: 'Node.js via stdin> ',
input: process.stdin,
output: process.stdout,
});
net.createServer((socket) => {
connections += 1;
repl.start({
prompt: 'Node.js via Unix socket> ',
input: socket,
output: socket,
}).on('exit', () => {
socket.end();
});
}).listen('/tmp/node-repl-sock');
net.createServer((socket) => {
connections += 1;
repl.start({
prompt: 'Node.js via TCP socket> ',
input: socket,
output: socket,
}).on('exit', () => {
socket.end();
});
}).listen(5001);CommonJS
const net = require('node:net');
const repl = require('node:repl');
let connections = 0;
repl.start({
prompt: 'Node.js via stdin> ',
input: process.stdin,
output: process.stdout,
});
net.createServer((socket) => {
connections += 1;
repl.start({
prompt: 'Node.js via Unix socket> ',
input: socket,
output: socket,
}).on('exit', () => {
socket.end();
});
}).listen('/tmp/node-repl-sock');
net.createServer((socket) => {
connections += 1;
repl.start({
prompt: 'Node.js via TCP socket> ',
input: socket,
output: socket,
}).on('exit', () => {
socket.end();
});
}).listen(5001);Запуск этого приложения из командной строки запустит REPL в stdin. Другие клиенты REPL могут подключаться через Unix-сокет или TCP-сокет. Например, telnet подходит для подключения к TCP-сокетам, а socat можно использовать для подключения как к Unix-сокетам, так и к TCP-сокетам.
Если запустить REPL с сервера на основе Unix-сокета, а не stdin, можно подключиться к длительно работающему процессу Node.js, не перезапуская его.
Пример запуска «полнофункционального» REPL (terminal) через экземпляр net.Server и net.Socket см. здесь: https://gist.github.com/TooTallNate/2209310.
Пример запуска экземпляра REPL через curl(1) см. здесь: https://gist.github.com/TooTallNate/2053342.
Этот пример предназначен исключительно для обучения и демонстрирует, как можно запускать REPL Node.js с использованием различных потоков ввода-вывода. Его не следует использовать в производственной среде или в любом контексте, где важна безопасность, без дополнительных мер защиты. Если вам нужно реализовать REPL в реальном приложении, рассмотрите альтернативные подходы, снижающие эти риски, например использование безопасных механизмов ввода и отказ от открытых сетевых интерфейсов.
© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/dist/latest-v20.x/docs/api/repl.html