Node.js REPL in Depth

Published 09 July 2018

REPL stands for read-eval-print-loop, or just an interactive session (usually in your terminal), where you can enter some expression and immediately evaluate it, seeing the result. After evaluating, the whole flow repeats, and it works until you exit the process. So, R stands for reading your command, E stands for evaluating it, P stands for printing the result of the execution, and L means to run the whole process again, “in the loop”.

In order to run Node.js REPL, you need to run the node command without any arguments:

1
> node

REPL session is similar to the console window in the browser – you can create and store variables, declare functions, require modules and installed dependencies (check module object). However, one important difference between executing a file and working in a REPL session is that a REPL session is not wrapped into the function, so you don’t have __dirname and __filename variables. So, you can require any files as you would do in any existing commonjs file. To illustrate, let’s create a new file where we export some string, and require it from the REPL:

1
2
3
4
5
6
7
> touch example.js
> echo "exports.some = 'some text';" >> example.js
> node
> require('./example');
{ some: 'some text' }
> _.some
'some text'

I did not assign the result of the require expression, but I was able to access it anyway! I got the last expression’s result by using underscore variable – Node.js REPL automatically assigns the last expression’s result to _ variable. It is interesting, that despite being a popular choice for lodash, you can’t assign to this variable. It will save the value, but after the next expression it will be different:

1
2
3
4
5
6
7
8
9
10
> node
> _ = 'something'
Expression assignment to _ now disabled.
'something'
> _
'something'
> _ = 'something else'
'something else'
> _
'something else'

Special commands

Node.js REPL has special commands, even though I feel they are not so widely known. First, let’s look at the multiline command. By default, every line is evaluated after you type it and hit “Enter”: there is no way in JavaScript to tell when we need to continue or can evaluate our code (like it is done in Python REPL, for example). Because of that it is challenging to write functions, since it is very unreadable and very error-prone to type all in one line. However, there is an .editor command, which allows you to type as many lines as you need:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> node
> .editor
// Entering editor mode (^D to finish, ^C to cancel)
function sum(a, b) {
  return a + b;
}

sum(5, 4);

// I type ^D at this point

9

> sum(10, 6)
16

Other special commands (from the official docs):

Automatically imported modules

If you hit the TAB key twice, it will give you all possible autocompletions (also, if you already typed something, and there is only one option, it will do it automatically). We can use it to check what is available globally (keep in mind that everything you declare in your session, is also available there).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
> node
// I tap TAB twice
Array                         Boolean                       Date                          Error                         EvalError                     Function                      Infinity
JSON                          Math                          NaN                           Number                        Object                        RangeError                    ReferenceError
RegExp                        String                        SyntaxError                   TypeError                     URIError                      decodeURI                     decodeURIComponent
encodeURI                     encodeURIComponent            eval                          isFinite                      isNaN                         parseFloat                    parseInt
undefined

ArrayBuffer                   Atomics                       Buffer                        DTRACE_HTTP_CLIENT_REQUEST    DTRACE_HTTP_CLIENT_RESPONSE   DTRACE_HTTP_SERVER_REQUEST    DTRACE_HTTP_SERVER_RESPONSE
DTRACE_NET_SERVER_CONNECTION  DTRACE_NET_STREAM_END         DataView                      Float32Array                  Float64Array                  GLOBAL                        Int16Array
Int32Array                    Int8Array                     Intl                          Map                           Promise                       Proxy                         Reflect
Set                           SharedArrayBuffer             Symbol                        Uint16Array                   Uint32Array                   Uint8Array                    Uint8ClampedArray
WeakMap                       WeakSet                       WebAssembly                   _                             assert                        async_hooks                   buffer
child_process                 clearImmediate                clearInterval                 clearTimeout                  cluster                       console                       crypto
dgram                         dns                           domain                        escape                        events                        fs                            global
http                          http2                         https                         module                        net                           os                            path
perf_hooks                    process                       punycode                      querystring                   readline                      repl                          require
root                          setImmediate                  setInterval                   setTimeout                    stream                        string_decoder                tls
tty                           unescape                      url                           util                          v8                            vm                            zlib

__defineGetter__              __defineSetter__              __lookupGetter__              __lookupSetter__              __proto__                     constructor                   hasOwnProperty
isPrototypeOf                 propertyIsEnumerable          toLocaleString                toString                      valueOf

What is important here, aside from the expected global objects and methods, is that you automatically have access to core Node.js modules, like fs, http, os, path and so on. While it is not a big deal, it might be a very convienent thing, especially when you are trying out some ideas in your REPL.

You can use these imported modules in a special way of running Node.js: if you provide a -e argument with a following string, containing JS code, it will be evaluated immediately:

1
2
> node -e "console.log(os.platform())"
darwin

Keep in mind, that in this mode node does not print anything in the console by default, and you would have to wrap your code in console.log if you want to see some result. This execution might be helpful, if you don’t want to enter REPL, and don’t want to create a separate file for it.

Using repl module

You can import the repl module directly and use it within your Node.js application. All Node.js does by default can be done using the repl.start method. It will use all the usual defaults, standard input and output streams. To run an interactive shell with our own prompt symbol, we have to create a small javascript file:

1
2
3
4
const repl = require('repl');

// Our own prompt
repl.start('Code:: ');

Now we can run this file, and it will work as before, but with the custom prompt:

1
2
3
4
5
6
> node repl.js
Code:: const sum = (...args) => args.reduce((a, b) => a + b, 0)
undefined
Code:: sum(1, 2, 3)
6
Code:: .exit

As you can see, all commands work as expected. You can cusomize plently of things, so feel free to play around with your own REPL session! For example, you can add your own variables to the scope of REPL, so you don’t need to require them:

1
2
3
4
5
6
const repl = require('repl');

const r = repl.start('Code:: ');
r.context.enrich = (obj) => {
  return Object.assign(obj, { prop: 'something' });
}

Now we will have this function in our scope available immediately:

1
2
3
> node repl.js
Code:: enrich({ num: 123 })
{ num: 123, prop: 'something' }