Smart Clojure/ClojureScript code sharing
Pull request Compare This branch is 1 commit ahead, 106 commits behind lynaghk:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


           | $$                
  /$$$$$$$ | $$    /$$   /$$   /$$
 /$$_____/ | $$   |__/  |  $$ /$$/
| $$       | $$    /$$   \  $$$$/ 
| $$       | $$   | $$    >$$  $$ 
|  $$$$$$$ | $$   | $$   /$$/\  $$
 \_______/ |__/   | $$  |__/  \__/
             /$$  | $$          
            |  $$$$$$/  Your code is, like, data, bro.        

Cljx is a Lein plugin that emits Clojure and ClojureScript code from a single metadata-annotated codebase.

To use it, add it to your project.clj:

:plugins [[com.keminglabs/cljx "0.2.0"]]
:cljx {:builds [{:source-paths ["src/cljx"]
                 :output-path ".generated/clj"
                 :rules cljx.rules/clj-rules}

                {:source-paths ["src/cljx"]
                 :output-path ".generated/cljs"
                 :extension "cljs"
                 :include-meta true
                 :rules cljx.rules/cljs-rules}]}

Can be run "once" or "auto", in which case it will watch all source-paths for changes to .cljx files. Defaults to "once".


:hooks [cljx.hooks]

to automatically run cljx before cutting a JAR.

The included clj and cljs rule sets will remove forms marked with platform-specific metadata and rename protocols as appropriate. E.g., the .cljx source containing

^:clj (ns c2.maths
        (:use [c2.macros :only [combine-with]]))
^:cljs (ns c2.maths
         (:use-macros [c2.macros :only [combine-with]]))

(defn ^:clj sin [x] (Math/sin x))
(defn ^:cljs sin [x] (.sin js/Math x))

  (invoke [_ x] (inc x)))

will, when run through cljx.rules/cljs-rules, yield:

(ns c2.maths
  (:use-macros [c2.macros :only [combine-with]]))

(defn sin [x] (.sin js/Math x))

  (invoke [_ x] (inc x)))

The value associated with :rules should be a symbol naming a var containing the rules to use for that build. cljx.rules/cljs-rules and cljx.rules/clj-rules are provided as a convenience, but you can extend those (or replace them entirely). For example, a namespace on your classpath like this defines some rules:

(ns my.rules
  (:require [kibit.rules.util :refer (compile-rule defrules)]))

(defrules rules
  [(+ ?x 1) (inc ?x)]
  [(- ?x 1) (dec ?x)])

Now you can use those rules in a cljx build like so:

:rules my.rules/rules

The var's namespace will be automatically loaded by cljx (i.e. no need to do so manually via the :injections key in your project.clj).

Forms that are converted into :cljx.core/exclude will be excluded from the output. See Kibit for more info on writing rules, and C2 for a project that uses .cljx heavily.

Clojure is a hosted language

Cljx does not try to hide implementation differences between host platforms. Clojure has ints, floats, longs, &c., ClojureScript has number; Clojure regular expressions act differently than ClojureScript regular expressions, because they are different.

Cljx only tries to unify Clojure/ClojureScript abstractions when it makes sense. E.g., converting clojure.lang.IFn into IFn when generating ClojureScript.


Emacs users, want syntax highlighting? Add to your emacs config: (add-to-list 'auto-mode-alist '("\\.cljx\\'" . clojure-mode)).


  • CLJS: Remove docstrings from namespaces.


@jonase & @ohpauleez for kibit @swannodette for core.logic