REPL


Стабильность: 2 – Стабильная версия

Модуль repl предоставляет реализацию Real-Eval-Print-Loop (Чтение-оценка-печать-цикл, REPL), которая доступна как в виде автономной программы, так и встроенной в другие приложения. К нему можно получить доступ таким образом:


const repl = require('repl');

.

Дизайн и фичи (Design and Features)

Модуль repl экспортирует класс repl.REPLServer. При запуске экземпляры repl.REPLServer принимает индивидуальные строки пользовательского ввода, оценивая их соответственно заданной пользователем функции оценки, а затем выводит результат. Ввод и вывод могут принадлежать stdin и stdout соответственно или могут быть подключены к любому стриму Node.js.

Экземпляры repl.REPLServer поддерживают автоматическое завершение ввода, упрощенную корректировку строку Emacs, многострочный ввод, вывод в ANSI, сохранение и восстановление текущего состояния сессии REPL, исправление ошибок и настраиваемые функции оценки.

Команды и горячие клавиши

Нижеследующие команды поддерживаются всеми экземплярами REPL:

  • .break – В процессе ввода многострочного выражения ввод команды .break (или нажатие комбинации клавиш <Ctrl>-C) прервет последующий ввод или обработку этого выражения.
  • .clear – Сбрасывает context REPL до пустого объекта и стирает все многострочные выражения, которые вводятся в данный момент.
  • .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
// Вход в режим правки (^D чтобы завершить, ^C чтобы отменить)
function welcome(name) {
  return `Hello ${name}!`;
}

welcome('Node.js User');

// ^D
'Hello Node.js User!'
>

Следующие комбинации клавиш в REPL работают так:

  • <Ctrl>-C – При единоразовом нажатии имеет такой же эффект, как команда .break. При двойном в пустой строке – такой же эффект, как и .exit.
  • <Ctrl>-D – Имеет такой же эффект, как команда .exit.
  • <tab> - При нажатии в пустой строке отображает глобальные и локальные переменные. При нажатии во время ввода показывает варианты автозавершения.

Оценка по умолчанию

По умолчанию все экземпляры repl.REPLServer используют функцию оценки, которая оценивает выражения JavaScript и предоставляет доступ к встроенным модулям Node.js. Это дефолтное поведение может быть переопределено путем передачи альтернативной функции оценки при создании экземпляра repl.REPLServer.

Выражения JavaScript

По умолчанию функция оценки поддерживает прямое оценивание выражений JavaScript:


> 1 + 1
2
> const m = 2
undefined
> m + 1
3

Хотя в противном случае результат меняется в пределах блоков и функций, переменные объявляются либо неявно, либо с использованием ключевых слов const, let, var, которые объявляются на глобальном уровне.

Глобальный и локальный уровни

Функция оценки по умолчанию предоставляет доступ к любым существующим переменным на глобальном уровне. Возможно добавить переменную в REPL явным образом путем назначения ее в объект context, ассоциируемый с каждый REPLServer. Например:


const repl = require('repl');
const msg = 'message';

repl.start('> ').context.m = msg;

Свойства в объекте context являются локальными в REPL:


$ node repl_test.js
> m
'message'

Важно помнить, что свойства context не являются открытыми только для чтения по умолчанию. Для задания глобальных, открытых только для чтения, свойства context должны быть определены с помощью Object.defineProperty():


const repl = require('repl');
const msg = 'message';

const r = repl.start('> ');
Object.defineProperty(r.context, 'm', {
  configurable: false,
  enumerable: true,
  value: msg
});

Доступ к модулю Core Node.js

Функция оценки по умолчанию будет автоматически загружать основные модули Node.js в окружение REPL при их использовании. Например, если ввод fs не был объявлен как глобальная или локальная переменная, он будет оценен по запросу как global.fs = require(‘fs’).


> fs.createReadStream('./some/file'); 

Назначение переменной _ (нижнее подчеркивание)

Функция оценки по умолчанию назначает результат наиболее часто оцениваемого выражения в специальную переменную _ (нижнее подчеркивание). Явная настройка _ на значение отменяет это поведение.


> [ 'a', 'b', 'c' ]
[ 'a', 'b', 'c' ]
> _.length
3
> _ += 1
Expression assignment to _ now disabled.
4
> 1 + 1
2
> _
4

Пользовательские функции оценки

При создании нового repl.REPLServer предоставляется пользовательская (или кастомная) функция оценки. Это можно использовать, к примеру, для реализации полностью кастомизированных приложений REPL.

Следующий пример иллюстрирует гипотетический случай REPL, который выполняет перевод текста с одного языка на другой:


const repl = require('repl');
const Translator = require('translator').Translator;

const myTranslator = new Translator('en', 'fr');

function myEval(cmd, context, filename, callback) {
  callback(null, myTranslator.translate(cmd));
}

repl.start({prompt: '> ', 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() перед записью вывода в предоставленный открытый для записи стрим (по умолчанию process.stdout). Boolean опция useColors может быть задана изначально для направления инструкций на функцию записи по умолчанию для использования кодов ANSI, чтобы раскрасить вывод из метода util.inspect().

Возможно полностью кастомизировать вывод из экземпляра repl.REPLServer путем передачи новой функции в используемую опцию writer при написании кода. Следующий пример показывает простую конвертацию любого входящего текста в текст, набранный в верхнем регистре:


const repl = require('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();
}

Class: REPLServer

Добавлено в v0.1.91

Класс repl.REPLServer наследуется из класса readline.Interface. Экземпляры repl.REPLServer создаются с использованием метода repl.start() и не должны создаваться путем непосредственного использования ключевого слова JavaScript new.

Event: ‘exit’

Добавлено в v0.7.7

Событие ‘exit’ генерируется когда REPL завершается либо путем получения команды .exit на ввод, либо когда пользователь дважды нажимает <Ctrl>-C и вызывает сигнал SIGINT, либо путем нажатия <Ctrl>-D для сигнала ‘end’ на входящем стриме. Слушатель функции обратного вызова вызывается без аргументов.


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

Event: ‘reset’

Добавлено в v0.11.0

Событие ‘reset’ генерируется, когда сбрасывается контекст REPL. Это происходит всякий раз при получении команды .clear на ввод, кроме тех случаев, когда REPL использует функцию оценки по умолчанию и экземпляр repl.REPLServer создается со значением true опции useGlobal. Слушатель функции обратного вызова вызывается со ссылкой на объект context в качестве единственного аргумента.

Можно использовать изначально для повторной инициализации контекста REPL в некоторое предопределенное состояние, как показано в примере ниже:


const repl = require('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)

Добавлено в v0.3.0
  • keyword <Строка> Ключевое слово для команды (без .)
  • cmd <Объект> | <Функция> Функция, которая вызывается после обработки команды

Метод replServer.defineCommand() используется для добавления новых команд с приставкой .(точка) в экземпляр REPL. Эти команды вызываются с помощью ключевого слова keyword, перед которым нужно поставить точку. Cmd будет либо функцией, либо объектом со следующими свойствами:

  • help <Строка> Вспомогательный текст, который отображается после ввода .help (опционально)
  • action <Функция> Выполняемая функция, опционально с однострочным аргументом.

Следующий пример показывает добавление двух новый команд в экземпляр REPL:


const repl = require('repl');

const replServer = repl.start({prompt: '> '});
replServer.defineCommand('sayhello', {
  help: 'Say hello',
  action(name) {
    this.lineParser.reset();
    this.bufferedCommand = '';
    console.log(`Hello, ${name}!`);
    this.displayPrompt();
  }
});
replServer.defineCommand('saybye', () => {
  console.log('Goodbye!');
  this.close();
});

Новые команды могут быть использованы внутри экземпляра REPL:


> .sayhello Node.js User
Hello, Node.js User!
> .saybye
Goodbye!

replServer.displayPrompt([preserveCursor])

Добавлено в v0.1.91
  • preserveCursor <Boolean>

Метод replServer.displayPrompt() читает экземпляр REPL, проверяя наличие пользовательского ввода, выводит на экран сконфигурированный prompt в новой строке вывода и возобновляет input для принятия нового ввода.

При многострочном вводе, вместо prompt на экран выводится многоточие.

Если preserveCursor имеет значение true, местопoложение курсора сбрасывается до 0.

Метод replServer.displayPrompt изначально предназначен для вызова из функции action для команд, определенных с помощью метода replServer.defineCommand().

repl.start([options])

Добавлено в v0.1.91
  • options <Объект> | <Строка>
    • prompt <Строка> Отображаемый prompt ввода. По умолчанию: > (с пробелом)
    • input <Открытый для чтения> Открытый для чтения стрим из которого читается ввод REPL. По умолчанию: process.stdin.
    • output <Открытый для записи> Открытый для записи стрим, куда записывается вывод REPL. По умолчанию: process.stdout.
    • terminal <boolean> При значение true определяет вывод как TTY терминал, который имеет записанные в него коды выхода ANSI/VT100. По умолчанию проверяет значение свойством isTTY в стриме output перед установкой.
    • eval <Функция> Функция, использумая при оценке каждой данной строки ввода. По умолчанию – асинхронная обертка JavaScript функции eval(). Функция eval может падать с ошибкой с repl.Recoverable для отображения незаконченного ввода и prompt для дополнительных строк.
    • useColors <boolean> При значении true определяет функцию по умолчанию writer, которая должна включить ANSI-цвета в вывод REPL. Если предоставляется пользовательская функция writer, то ничего не произойдет. По умолчанию принимает значение terminal экземпляра REPL.
    • useGlobal <boolean> При значении true определяет, что функция оценки по умолчанию будет использовать глобальные переменные JavaScript в качестве контекста, противопоставленного созданию отдельного контекста для экземпляра REPL. По умолчанию: false.
    • ignoreUndefined <boolean> При значении true определяет, что дефолтный writer не будет направлять на вывод возвращенное значение команды, если оно оценено как undefined. По умолчанию: false.
    • writer <Функция> Функция, которая вызывается для форматирования вывода каждой команды перед записью в общий вывод output. По умолчанию: util.inspect()
    • completer <Функция> Опциональная функция, используемая для пользовательского автодополнения. См. readline.InterfaceCompleter.
    • replMode – флаг, который определяет, выполняет ли функция оценки по умолчанию все JavaScript команды прямым путем, дефолтным путем или гибридным («волшебный» способ). Приемлемые значения:
      • repl.REPL_MODE_SLOPPY - оценивает выражения в «сыром» режиме
      • repl.REPL_MODE_STRICT – оценивает выражения в «строгом» режиме эквивалентно добавлению к каждому выражению repl ‘use strict’
      • repl.REPL_MODE_MAGIC – пытается оценить выражения в дефолтном режиме. Если выражения неудается парсить, нужно попробовать строгий режим.
    • breakEvalOnSigint – останавливает текущую часть кода при получении SIGINT, как будто происходит ввод Ctrl+C. Не может использоваться вместе с пользовательской функцией eval. По умолчанию: false.

Метод repl.start() создает и запускает экземпляр repl.REPLServer.

Если options – строка, то она определяет prompt ввода:


const repl = require('repl');

// prompt в Unix
repl.start('$ ');

Node.js REPL

Сам Node.js использует модуль repl для предоставления своего собственного интерактивного интерфейса для выполнения JavaScript. Может использоваться путем выполнения бинарного файла Node.js без передачи аргументов (или с передачей -i):


$ node
> const a = [1, 2, 3];
[ 1, 2, 3 ]
> a.forEach((v) => {
...   console.log(v);
...   });
1
2
3

Опции переменных окружения

Различные поведения REPL Node.js можно кастомизировать с помощью следующих переменных окружения:

  • NODE_REPL_HISTORY – Когда задается валидный путь, сохраняемая история REPL скорее будет сохраняться в определенный файл чем в .nore_repl_history в домашней директории пользователя. Настройка этого значения в “” отключает сохраняемую историю REPL. Из значения удаляются пустые места.
  • NODE_REPL_HISTORY_SIZE – по умолчанию 1000. Контролирует, какое количество строк истории будет сохранено, если это возможно. Должно быть положительным числом.
  • NODE_REPL_MODEsloppy, strict или magic. По умолчанию: magic, автоматически запускает выражения “strict mode only” в строгом режиме.

Сохраняемая история

По умолчанию REPL Node.js сохраняет историю между сессиями node в REPL путем сохранения вводов в файл .node_repl_history, который находится в пользовательской домашней директории. Это можно отключить посредством настройки переменной окружения NODE_REPL_HISTORY =””.

NODE_REPL_HISTORY_FILE

Добавлено в v2.0.0 Отклонено с v3.0.0

Стабильность: 0 – Отклонено. См. NODE_REPL_HISTORY

Изначально в Node.js/io.js история REPL контролировалась через переменную окружения NODE_REPL_HISTORY_FILE, и сохранялась в JSON формате. Сейчас эта переменная не используется и старый JSON файл истории REPL автоматически конвертируется в упрощенный текстовый формат. Этот новый файл сохраняется либо в пользовательскую домашнюю директорию, либо в директорию, определенную переменной NODE_REPL_HISTORY, как документировано в «Опциях переменных окружения»

Использование REPL Node.js с продвинутыми редакторами строки

Для использования продвинутых редакторов строки, запустите Node.js с переменной окружения NODE_NO_READLINE=1. Это запустит main и отладчик REPL в каноничных настройках терминала, которые позволят использовать rlwrap.

Например, вы сможете добавить это в свой bashrc файл:


alias node="env NODE_NO_READLINE=1 rlwrap node"

Запуск множественных экземпляров REPL против единственного запущенного экземпляра

Можно создать и запустить множественные экземпляры REPL против одного уже запущенного Node.js, имеющих общий единственный объект global, но разные интерфейсы I/O.

Следующий пример предоставляет разные REPL в stdin, Unix сокет или TCP сокет:


const net = require('net');
const repl = require('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/2209310

Для примера запуска экземпляра REPL на curl(1) см. https://gist.github.com/2053342