Skip to content


Repository files navigation

Propel Clojars Project CircleCI codecov

Propel helps you start Clojure and ClojureScript REPLs with a prepl.

It consists of a simple command line interface (propel.main) as well as a couple of utility functions (propel.core) that you can execute from your own *.main namespace.

$ clj --main propel.main --write-port-file
[Propel] Started a :jvm prepl at (written to ".prepl-port")

# ---

$ clj --main propel.main --help
  -p, --port PORT             Port number, defaults to random open port
  -a, --address ADDRESS       Address for the server, defaults to
  -f, --port-file-name FILE   File to write the port to, defaults to .prepl-port
  -w, --[no-]write-port-file  Write the port file? Use of --port-file-name implies true, defaults to false
  -e, --env ENV               What REPL to start ([jvm], node, browser, figwheel, lein-figwheel, rhino, graaljs or nashorn)
  -r, --repl-only             Don't start a prepl, only start a local REPL and connect it to the prepl at the specified port and address.
  -b, --figwheel-build BUILD  Build to use when using the figwheel env (not lein-fighweel), defaults to propel
  -x, --extra EDN             Extra options map you want merged in, you can get creative with this one
  -h, --help                  Print this help


You can install Propel from Clojars, just add the appropriate coordinate to your project.

;; deps.edn
olical/propel {:mvn/version "2.0.0"}

;; project.clj
[olical/propel "2.0.0"]



When you execute clj -m propel.main it'll default to starting a JVM REPL with a socket prepl attached on a random port.

As you can see from the help output above we can provide various env values, if you provide node you get a node based ClojureScript REPL as well as a socket prepl attached to that same REPL environment.

$ clj -m propel.main --env node
[Propel] Started a :node prepl at
cljs.user=> (defn hello [n] (str "Hello, " n "!"))

We can also REPL into any existing prepl by specifying a --port (-p) and --repl-only (-r).

$ clj -m propel.main -rp 45297
cljs.user=> (hello "Olical")
"Hello Olical!"

You can specify a port or Propel can write the random port to a file, it will use .prepl-port by default. The port file will be deleted when the prepl is stopped.

# Write to the default file.
$ clj -m propel.main -w

# Select a file name, also enables writing the file.
$ clj -m propel.main -e node --port-file-name .node-prepl-port

You can specify an extra EDN map to be merged into the internal configuration map (see propel.core), this might come in handy if there's some value you want to directly pass to the prepl. Good for hacking around my assumptions.

# Will enable port file writing without using the CLI.
# -w enables this flag for you.
$ clj -m propel.main --extra '{:port-file? true}'


propel.core exposes three functions:

  • (propel.core/start-prepl! opts)
  • (propel.core/stop-prepl! opts-from-start-prepl)
  • (propel.core/repl opts)

The command line maps near enough directly to the opts argument of start-prepl!, you can give it an empty map and all of the defaults will be applied, just like the CLI. To start a node prepl via Clojure instead of the CLI just provide an :env.

(propel.core/start-prepl! {:env :node, :port-file? true})
(propel.core/start-prepl! {:port 5555})

It will return the opts map enriched with all of the defaults, selected port, port file path, accept function symbol and more. It's up to you to decide what you want to do with that data.

You can then pass the result from start-prepl! through to repl which will start a standard REPL attached to the prepl you just created. This is what propel.main does for you, starts a prepl then a REPL using the returned data.

Here's a fairly exhaustive example of the options map, start-prepl! and repl usage.

;; Start a node prepl and REPL into it.
(-> {:env :node
     :port 5555
     :port-file-name ".node-prepl-port"}

;; Open a REPL into an exisisting prepl.
(propel.core/repl {:port 8787})

;; Capture the opts of a prepl so you can stop it.
;; We're relying on all of the default random values.
(let [opts (propel.core/start-prepl! {})]
  ;; Do some things with the prepl...
  ;; And then stop it.
  (propel.core/stop-prepl! opts))


Lein (legacy)

You need to add figwheel-sidecar to your dependencies to use this! Version 0.5.19 was the latest at the time of writing.

When using the lein-figwheel plugin, you may execute something like the following to have figwheel start up from your project.clj configuration. Everything should be inferred and should start up as if you typed lein figwheel but with a prepl connected.

$ clj -m propel.main -e lein-figwheel


You need to add com.bhauman/figwheel-main to your dependencies to use this! Version 0.2.3 was the latest at the time of writing.

You can start a figwheel prepl and REPL just like any other environment through the functions or CLI. The only thing you have to bear in mind is that figwheel-main requires you to create a configuration file, I highly recommend you have a read through the documentation.

The name of the file should be the name of the build suffixed with .cljs.edn, so by default Propel will configure figwheel to look for propel.cljs.edn, which could look like this.

^{:watch-dirs ["cljs-src" "dev"]}
{:main example.core}

Now you can start a figwheel REPL through the CLI or function, let's create one though the CLI but specify a different build name.

$ clj -m propel.main -e figwheel --figwheel-build dev

Figwheel will now start up and read it's configuration from dev.cljs.edn, it should open a browser tab for you that you can interact with through your REPL or prepl.

If you need to, you can provide different :figwheel-opts (which default to {:mode :serve}) through the --extra '{...}' EDN map argument at the CLI.

Propel + Conjure

My Neovim Clojure(Script) plugin, Conjure, relies entirely on prepl. Connecting it to a REPL started by Propel is a breeze and the main reason for Propel even existing.

With the following .conjure.edn file all we need to do is start Propel with a single argument and then open Neovim.

{:conns {:propel {:port #slurp-edn ".prepl-port"}}}

Now we start Propel in one terminal.

$ clj -m propel.main -w

And open a Clojure file in Neovim in another.

$ nvim src/foo/bar.clj

After a couple of seconds Conjure will be connected to your Propel REPL and you'll have evaluation, autocompletion, documentation lookup and go to definition.

This applies to all of the other env values mentioned above, feel free to connect it to node, browser or figwheel too! Just be sure to specify :lang :cljs in your .conjure.edn next to the :port if you want a connection to work with ClojureScript as opposed to Clojure (the default).

What's a prepl?

The name comes from programmable REPL and it's built into Clojure and ClojureScript 1.10+.

It's a REPL that sits behind a socket server, you send code to it and it'll respond with the results, just like a regular REPL. The difference is that it's over a socket (not stdio) and the responses are wrapped in EDN data structures for easy parsing, this is perfect for tool authors.

The more popular community driven equivalent of this concept is the wonderful nREPL, almost all current tooling relies on this system.

You can read more about starting prepl servers in my Clojure socket prepl cookbook post. Another way to learn is to use this project for the basics then dig into the source code yourself to find what you need.

Can you add X?

I don't want Propel to be infinitely customisable otherwise I'll lose all of my time to fixing the bugs introduced by those features. It should only contain things 95% of people should find useful early on in a project.

Copying and modification of the source code to fit your needs is encouraged, as opposed to adding every possible flag and feature to Propel. Think of this like a set of minimal examples that are also executable.

Use Propel to get going where it suits your needs, once you outgrow it you'll want to have your own namespaces anyway.

Issues and pull requests are still appreciated, ideally after discussing what you want to do, but only for small quality of life changes. If what you want will significantly change the project or introduce further complexity you might be better off with a fork.


Find the full unlicense in the UNLICENSE file, but here's a snippet.

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.