April 5, 2020

💎 Stealing with deps.edn

This builds on a previous post about a template repo I made. The goal was to make it easier for me and others to start a project on this stack. The ideal state wasn't a repo that needs manual setup. The ideal state is one command that generates a ready to run project. This post will describe how I made an npm module called create-expo-cljs-app.

The Target

First I found an example to work from. Create CLJS App is exactly what I wanted to make for expo cljs projects. Once I grokked the codebase I was set. Clojure open source projects are the only projects I've ever been able to dive into an understand without someone guiding me through the codebase. I think that's a merit of clojure itself -- it's easy to read.

I'm going to refer to the concept this project relies on as an npm create kit. An npm create kit seems to be a convention for creating npm modules that generate projects.

To start the project must be named create-<something>. Then it needs a couple of specific things in its package.json. Most important is a bin key with a script that is keyed to the same name as the project. Like so:

  "bin": {
    "create-cljs-app": "./bin/create-cljs-app.js"

A files key scopes any other necessary files in the create kit. Like so:

  "files": [

This is all the create kit convention needs to run your project given any of the commands for npx yarn or npm. Here are some examples of those commands.

The Heist

It's up to the create kit to actually generate the project in some way. The create-cljs-app project has a simple entry bin script.

const { create } = require('../dist/lib');
const [, , projectPath = ''] = process.argv;
return create(process.cwd(), projectPath);

It calls one create function in a cljs namespace that does everything. Some things include checking for dependencies and writing messages to the console. It took awhile but I realized that I could include this repo with deps.edn and reuse most of the code.

The first change was to add deps.edn to the original create kit. That was easy with shadow-cljs.

Then I extended that create function to take a map of optional options.You can see those changes on this branch of my fork.

My create kit project includes the create-cljs-app as a dependency. It has basically the same bin entrypoint:

const { create } = require('../dist/lib');
const [, , projectPath = ''] = process.argv;
return create(process.cwd(), projectPath);

The only cljs I needed to make a new project was some functions to check for dependencies and a custom message to display at the end.

(defn create [cwd path]
  (cca-lib/create cwd path {:done-msg       done-msg
                            :get-commands   get-commands
                            :has-extras?    (fn [] (has-binary-on-PATH? "expo"))
                            :extras-warning expo-cli-warning}))

(def exports #js {:create create})

The whole file is only about 50 lines.

Loose Ends

One gotcha I ran into was needing to install the Javacsript dependencies of the project I was including. It doesn't seem like deps.edn can manage javascript dependencies.

Locally I tested with something like:

node ~/create-expo-cljs-app/bin/create-expo-cljs-app.js my-app

Once it was working all I had to do was setup an npm account and publish.

Now creating a project is as simple as yarn create expo-cljs-app my-app. I wish this had existed when I started trying to make my first expo cljs app.

Tags: cljs expo shadow-cljs create-kit npm