Fork me on GitHub

Overview

lein-dalap is a source code transformation tool that facilitates the creation and maintenance of libraries that work in Clojure and Clojurescript.

lein-dalap allows you to maintain code that works in both the JVM and in the browser, without forking your code and without relying on cljsbuild crossovers.

lein-dalap is inspired by cljx, a leiningen plugin that transforms input source files with a .cljx extension and special meta-data markup into .clj and .cljs output. In contrast with cljx, lein-dalap's input files are plain .clj files and only the .cljs files are auto-generated. It is also simpler to specify custom transformation rules at the project level.

What version of lein-dalap does this guide cover?

This guide covers lein-dalap version 0.1.0

Supported Clojure Versions

Currently this tool has only been tested with Clojure 1.4 and leiningen 2

Adding lein-dalap to your project

You will require leiningen in order to use this plugin, you'll need to add it to the plugin list of your project

(defproject some-project

;; ...

:plugins [[com.birdseye-sw/lein-dalap "0.1.0"]]

;;...

)

Executing lein-dalap transformation command

lein-dalap offers a similar interface to lein-cljsbuild. There are three main sub-tasks:

  • lein dalap auto transforms your clj files to cljs files as soon as it detects a change in one of them
  • lein dalap once transforms your clj files to cljs only once
  • lein dalap clean removes all files generated by lein-dalap

Specifying which files you want to transform to cljs

lein-dalap expects you to specify every Clojure source file that you would like to transform to Clojurescript in a top-level file named dalap_rules.clj.

This file needs to be in the root of your project (where your project.clj is). Specify a map of file specs (the keys) and the transformation rules to use (the values of the map). A file spec is a 2 element vector of [input-path output-path]. Transformation rules are css-like selector+transformer pairs and are interpreted using dalap. Specify them in a vector of pairs. Leave the vector empty if you only need the default lein-dalap rules.

{
  ["src/clj/util.clj" "src/cljs/util.clj"]
  ;; ^ the file-spec [source-clj-file target-cljs-file]

  ;; followed by the transformation rules for util.clj:
  [
  ;; Rule 1
  JavaClass 
  js_class ;; replace all the JavaClass symbols with js_class on cljs

  ;; Rule 2
  ;; You may also use functions as selectors and transformers.
  ;; Wrap selector functions in `dalap/when' and transformer
  ;; functions in `dalap/transform'. The following is a default rule:
  (dalap/when (has-meta? :cljs)) 
  (dalap/transform (replace-with-meta :cljs))
  ;;
  ;; from clojure:
  ;; (^{:cljs '-invoke} invoke [args] ...)
  ;; To clojurescript:
  ;; (-invoke [args] ...)
  ]
}

Add a file-spec for each of the files you want to transform to cljs.

Default Transformation Rules

JVM Types to Javascript Types

By default, lein-dalap transforms some core Clojure/Java type symbols to their JS equivalents. For example

(extend-protocol IMyProtocol
  java.lang.String
  (my-fn [s] ...)

  java.lang.Object
  (my-fn [s] ...)

becomes the following Clojurescript

(extend-protocol IMyProtocol
  string
  (my-fn [s] ...)

  default
  (my-fn [s] ...))

See rules.clj in the source for all the defaults. Please note, the default JVM types specified in rules.clj will only be transformed if they are fully qualified symbols: java.lang.Object not Object.

The :cljs meta tag

If you want to replace any form in your Clojure source with an alternate Clojurescript form, mark the form with :cljs meta tag containing the quoted replacement form. For example

^{:cljs
  '(ns project.test.util-tests
    (:require [project.util :as utils])
    (:require-macros [buster-cljs.macros :refer [deftest describe it is]]))
(ns project.test.util-tests
   (:require [buster-cljs.clojure :refer [deftest describe it is]
             [project.util :as utils])

The :clj meta tag

To completely remove a form in the Clojurescript output, mark it with the ^:clj meta tag.

(defn my-fn []
  ^:clj
  (println "hello world")
  ...)

The (println "hello world") will be dropped in the Clojurescript output.

The ignore reader macro with :cljs at the start

You may specify a form that will only be available in Clojurescript while keeping it a valid Clojure form by using the #_(:cljs form) syntax

#_(:cljs (initialize-buster))

The Clojure reader will drop it and it will translated into the following Clojurescript

(do
  (initialize-buster))

Integrating with lein-cljsbuild

Given that you will most likely be running cljsbuild alongside this plugin you can automatically run lein-dalap when you are running cljsbuild auto or once command, just by adding a :hooks to your project.clj file

 (defproject my-project
   ;; ...
   :hooks [leiningen.dalap]
   ;; ...
   )

Wrapping Up

Congratulations, you now know how to do the most common operations with lein-dalap, let's review what we just learned:

  • Use the lein dalap auto|once|clean commands.
  • Create a new dalap_rules.clj specifying the files you want to transform.
  • Specify basic custom transformation rules for specific input files.
  • Integrate with lein-cljsbuild.

What to read next

You may want to check out the dalap project. It is also being used to generate HTML from Clojure forms with the same css-like rule transformation system in lein-dalap.