Better ClojureScript Node REPL Defaults
Hi there! Welcome back — to you as much as to me. It’s been a while that I’ve published anything but here we go: a little quality of life improvement for anyone driving a Node.js REPL from ClojureScript.
The Problems
There’s two issues I often run into when working with ClojureScript and Node.js REPLs:
- many values are async, resulting in a
<#Promise>
return value - uncaught errors will cause the Node.js process to exit
The first applies to any kind of ClojureScript REPL while the second is a more Node-specific problem. Losing your REPL state whenever something fails is annoying. This behavior makes sense when you run Node.js in production but for a REPL… not ideal.
A Workaround
Fortunately a bandaid solution is pretty trivial. To solve 1) we can make use of the excellent portal tool. For 2) we can install a handler for unhandledRejection
events, catching the error and reporting it in whatever way we like.
Below is a namespace that can be added to your :preloads
or just required when you start a new REPL session.
(ns acme.node-repl-preloads
(:require [portal.api :as portal]))
(js/process.on "unhandledRejection"
(fn [err]
(js/console.log "unhandledRejection" err)
(tap> {:unhandledRejection err})))
(when (.-ACME_DEV js/process.env)
(portal/open)
(add-tap portal/submit))
I add this to my node-repl
helper like this:
(shadow/node-repl
{:config-merge [{:devtools {:preloads '[acme.node-repl-preloads]}}]})
Now, with ACME_DEV
set, we’ll get a Portal window whenever we start a Node
REPL, allowing us to chain promises into tap>
and inspecting their value that
way.
In addition to that any errors will also be logged to the console and to the Portal window — without crashing the process 🙂 From where I stand this would be a good default behavior but messing with error handling obviously comes with it’s own tradeoffs.
Adding another handler for unhandledException
is probably a good idea.
Anyways, nice to be back. I hope this is a slight improvement to someone’s setup 🤗