Mastering the Node.js REPL (part 2)

Roman Coedo
Trabe
Published in
5 min readAug 6, 2018

In the first part of this series we have covered the basics of the Node.js REPL. Now is time to learn how to use the programmatic API to transform it into a powerful tool for development.

Building our own custom REPL

Hello, world

We are going to begin by building a REPL version of a hello world. We can start our greeter loop with just a few lines of code:

That is pretty much self explanatory. We import the repl node library, we print a message and we start the loop. We use the USER environment variable and the current working directory to print a nice message:

Adding some colors

We can highlight some parts of the welcome message to make things easier to read:

We colorized the username and the current working directory to make it look a bit better:

Customizing the prompt

The REPL prompt is just a string which can be passed to the start() function:

Now we have a nice prompt that shows the node version that we are running:

The exit event

The start function returns the loop server. Using the loop server instance we can listen to server events like the exit event:

Understanding the REPL context

The basics

Let’s say we run our last example in the terminal using the CLI:

As you can see, the variables we defined in our code are not available in our REPL. This happens because when we invoke the start method, a new execution context is created for the REPL. This context is different from the context where the rest of our code is executed, so we won’t have access to the variables we define in it.

We can define properties in the context by using the reference to the loop server instance that the start method returns.

By defining properties in the server’s context object we expose them to be used within the REPL:

Read-only context

The context properties are not read-only by default, so they can be overwritten if we mess up:

We can define them as read-only using Object.defineProperty():

We could also write a nice utility function to extend our context easily, It would be something like this:

After doing this, our context properties cannot be overwritten any more:

Handling context reloads

The context can be anything we want (for instance, it could hold state). For this reason it sometimes makes sense to reset the context to its default value.

The REPL command .clear can be used to reset the context. Let’s try it out:

The context is not being reloaded properly because we need to listen for the reset event and re-initialize it when the event is fired:

The reset listener should be a function context => { ... } that receives the context and does whatever it wants with it.

Now our context is being properly reloaded:

Using the global context

There is a way to force the REPL to use the global context instead of a separate context. This is the setting that the default node REPL uses.

Setting the REPL to use the global context can be useful if you need to define the context before the loop server is actually instantiated. You’ll lose, however, the ability to reload it:

Custom node commands

Another cool trick you can do with it is defining new commands. You can define dot commands using the loop server method defineCommand.

The defineCommand method takes a command name and a { help, action } object:

It’s worth noting that the action function will be bound to the loop server, so we should invoke the server’s functions using the this keyword. Avoid using an arrow function here, since they have lexical scope.

Now that we have defined our command, it will be available for us within the REPL and it will be shown when we invoke .help:

Using the await keyword

Just like we covered in our previous post, we can also run our custom REPL with the experimental await support using the flag --experimental-repl-await:

Wrapping it up once again

In this post we have covered the basics of the REPL’s programmatic API. Our complete code looks like this:

If you made it this far you may want to play a bit with what you’ve learned today. You can find all the code we used in this repository.

In the next part of the series we’ll make a custom REPL for a real project. Stay tuned!

--

--