Use ClojureScript from npm packages with Lumo

April 11, 2018
Andrea Richiardi

The biggest obstacle for Javascript developers approaching the ClojureScript world, and quite frankly ClojureScript Achilles' heel compared to other JavaScript languages, is undoubtedly npm integration. This is where lumo shines: its fully-fledged Node.js runtime, sprinkled with some V8 snapshot magic is exactly what you need for taking advantage of the relentless work happening in JavaScript tooling land.

One of the latest additions to lumo is the ability to read ClojureScript source files directly from npm packages. This saves you from the canonical wrapping that happens in Cljsjs and, if you are Enterprise, from maintaining both an npm and a Nexus artifact server.

How to use npm packages

Let's say you are working on a ClojureScript Node.js server app and you have just found out how cool and refreshing maintaining state with mount is.

The canonical way would be to specify the dependency in either your deps.edn or project.clj and then call a script to resolve and produce the classpath string that will be fed to lumo --classpath.

Assuming lumo 1.8.0 is installed, this is the way you do it since mount 0.1.12:

$ npm install @tolitius/mount
$ lumo
Lumo 1.8.0
ClojureScript 1.10.238
Node.js v9.10.0
 Docs: (doc function-name-here)
       (find-doc "part-of-name-here")
 Source: (source function-name-here)
 Exit: Control+D or :cljs/quit or exit

cljs.user=> (require '[mount.core :as mount :refer [defstate]])
nil

cljs.user=> (defstate db-connection
       #_=>   :start (println "Starting...")
       #_=>   :stop  (println "Stopping..."))
#'cljs.user/db-connection

cljs.user=> (mount/start)
Starting...
{:started ["#'cljs.user/db-connection"]}

cljs.user=> (mount/stop)
Stopping...
{:stopped ["#'cljs.user/db-connection"]}

Note that no --classpath is necessary and that the code is read from node_modules:

$ ls node_modules/@tolitius/mount
CHANGELOG.md  LICENSE  package.json  README.md  src

How to deploy npm packages

The JavaScript ecosystem has chosen package.json as centralized configuration file and lumo does not deviate from this, again sticking to the principle of least surprise.

In order to publish a ClojureScript package on npm the only requirement is to specify the folder containing the source by using directories:

"directories": {
  "lib": "src"
}

Even if not required, it is good practice to control what goes in the npm tarball by using the files key. Let's look at how macrovich 1 does it:

{
  "name": "macrovich",
  "version": "0.2.1",
  "description": "A set of three macros to ease writing...
  "author": "Christophe Grand",
  "homepage": "https://github.com/cgrand/macrovich",
  "license": "EPL-1.0",
  "directories": {
    "lib": "src"
  },
  "files": [
    "src/*"
  ],
  "keywords": [
    "cljs",
    "cljc",
    "self-host",
    "macro"
  ]
}

That is it. At any given moment you can create a tarball with npm pack. Make sure you do not leak information and happy npm publish-ing.

Links


  1. Macrovich was the first ClojureScript library to be published on npmjs.com, thanks Christophe Grand for bearing with me.