├── samples └── hello │ ├── src │ └── hello.cljs │ ├── resources │ └── public │ │ ├── hello.html │ │ └── hello-dev.html │ ├── project.clj │ └── README.md ├── project.clj ├── README.md └── src └── leiningen └── clojurescript.clj /samples/hello/src/hello.cljs: -------------------------------------------------------------------------------- 1 | (ns hello) 2 | 3 | (defn ^{:export greet} greet [n] 4 | (str "Hello " n)) 5 | -------------------------------------------------------------------------------- /samples/hello/resources/public/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello ClojureScript 4 | 5 | 6 |

Hello ClojureScript!

7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject lein-clojurescript "1.1.1" 2 | :description "leiningen plugin for clojurescript" 3 | :dependencies [[org.clojure/clojure "1.3.0"] 4 | [org.clojure/clojurescript "0.0-971"] 5 | [watcher "0.1.0"] 6 | [clj-stacktrace "0.2.4"] 7 | [fs "0.11.0"] 8 | [conch "0.2.0"]]) 9 | -------------------------------------------------------------------------------- /samples/hello/project.clj: -------------------------------------------------------------------------------- 1 | (defproject hello "1.1.0" 2 | :description "helloworld clojurescript" 3 | :dev-dependencies [[lein-clojurescript "1.1.0"]] 4 | :extra-classpath-dirs ["src"] 5 | :cljs-output-to "resources/public/js/hello.js" 6 | :cljs-output-dir "resources/public/js/out" 7 | :cljs-optimizations :advanced 8 | :cljs-externs ["externs.js"] 9 | :cljs-libs ["js/foobar.js"] 10 | :cljs-foreign-libs [{:file "http://example.com/example.js" 11 | :provides ["example.example"]}] 12 | :cljs-test-cmd ["phantomjs" "test.js"]) 13 | -------------------------------------------------------------------------------- /samples/hello/resources/public/hello-dev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello ClojureScript 4 | 5 | 6 |

Hello ClojureScript!

7 | 8 | 9 | 10 | 13 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /samples/hello/README.md: -------------------------------------------------------------------------------- 1 | # hello 2 | 3 | helloworld for clojurescript using the lein-clojurescript plugin 4 | 5 | ## Usage 6 | 7 | * to setup: lein deps 8 | 9 | * to compile: 10 | 11 | ``` 12 | lein clojurescript 13 | ``` 14 | 15 | view resources/public/hello-dev.html in a browser. 16 | 17 | * to compile in *advanced mode*: 18 | 19 | ``` 20 | lein clojurescript '{:optimizations :advanced}' ;; or specify cljs-optimizations :advanced in project.clj 21 | ``` 22 | 23 | view resources/public/hello.html in a browser. 24 | 25 | ## License 26 | Copyright (C) 2011 justin barton 27 | Distributed under the Eclipse Public License, the same as Clojure. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lein-clojurescript 2 | 3 | [leiningen](https://github.com/technomancy/leiningen) plugin for clojurescript compilation. 4 | 5 | NOTE! as of 1.0.1-SNAPSHOT lein-clojurescript works with lein 1.6.1.1 6 | (thanks to Felix H. Dahlke) 7 | 8 | See clojure.org for clojurescript details. 9 | See code.google.com/closure for google closure details. 10 | See github.com/technomancy/leiningen for lein details. 11 | 12 | ## Usage 13 | 14 | ``` 15 | lein plugin install lein-clojurescript 1.1.0 16 | ``` 17 | 18 | Or in your project.clj add a dev-dependency 19 | ``` 20 | :dev-dependencies [[lein-clojurescript "1.1.0"] ...] 21 | ``` 22 | 23 | To compile clojurescript files in src: 24 | ``` 25 | lein clojurescript 26 | ``` 27 | 28 | To start the compile with a clean output directory and output file: 29 | ``` 30 | lein clojurescript fresh 31 | ``` 32 | 33 | To clean without compiling: 34 | ``` 35 | lein clojurescript clean 36 | ``` 37 | 38 | To watch the sources and recompile when they change: 39 | ``` 40 | lein clojurescript watch 41 | ``` 42 | 43 | To run a test command after compiling (defined by :test-cmd): 44 | ``` 45 | lein clojurescript test 46 | ``` 47 | 48 | Combine some of the above: 49 | ``` 50 | lein clojurescript fresh watch test 51 | ``` 52 | 53 | If you'd like the plugin to hook into the normal compile add to the hooks list: 54 | ``` 55 | :hooks [leiningen.clojurescript ...] 56 | ``` 57 | 58 | To compile clojurescript along with a normal compile: 59 | ``` 60 | lein compile 61 | ``` 62 | 63 | Compile with advanced mode: 64 | ``` 65 | lein compile '{:optimizations :advanced}' 66 | ``` 67 | 68 | Additional plugin-specific project.clj settings include: 69 | 70 | ``` 71 | :cljs 72 | {:output-to "output/file.js" 73 | :output-dir "output/dir" 74 | :externs ["externs.js"] 75 | :libs ["path/to/lib.js"] 76 | :foreign-libs [{:file "flib.js" :provides ["flib.core"]}] 77 | :test-cmd ["testcmd" "arg1" "arg2"] 78 | :wrap-output ["(function(){" "}())"] 79 | :src-dir "cljs/src/dir"} 80 | ``` 81 | 82 | `:test-cmd` must be in a format useable by `clojure.java.shell/sh`. E.g. 83 | ``` 84 | :test-cmd ["phantomjs" "tests.js"] 85 | ``` 86 | 87 | `:wrap-output` must be a vector of two strings, which will wrap optimized 88 | output. 89 | ``` 90 | :wrap-output ["(function(){" "}())"] 91 | ``` 92 | 93 | See 94 | for more information about `:externs`, `:libs`, and 95 | `:foreign-libs`. 96 | 97 | Make macro files visible to the compiler by adding `:extra-classpath-dirs 98 | ["path-to-src"]` to the project definition. 99 | 100 | For an example usage see samples/hello/project.clj. 101 | 102 | 103 | 104 | ## Authors 105 | * fhd (Felix H. Dahlke) 106 | * rplevy (Robert Levy) 107 | * mmwaikar (Manoj Waikar) 108 | * bartonj (Justin Barton) 109 | 110 | ## License 111 | Copyright (C) 2011 Justin Barton 112 | Distributed under the Eclipse Public License, the same as Clojure. 113 | -------------------------------------------------------------------------------- /src/leiningen/clojurescript.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:doc "clojurescript leiningen plugin" :author "justin barton"} 2 | leiningen.clojurescript 3 | (:require [clojure.string :as string] 4 | [clojure.java.io :as io] 5 | [robert.hooke :as hooke] 6 | leiningen.compile 7 | fs) 8 | (:use [watcher :only (with-watch-paths)]) 9 | (:import java.util.Date)) 10 | 11 | (defn- clojurescript-arg? [arg] 12 | (-> arg str string/trim seq first (= \{))) 13 | 14 | (defn- clojurescript-file? [filename] 15 | (.endsWith (string/lower-case filename) ".cljs")) 16 | 17 | (def getName (memfn getName)) 18 | 19 | (defn- maybe-test [opts args] 20 | (when-let [cmd (and (some #{"test"} args) 21 | (:test-cmd opts))] 22 | `(do 23 | (println (str "Running `" ~(clojure.string/join " " cmd) "`.")) 24 | (let [proc# (conch.core/proc ~@cmd)] 25 | (future (conch.core/stream-to-out proc# :out)) 26 | (future (conch.core/stream-to-out proc# :err)) 27 | (conch.core/exit-code proc#))))) 28 | 29 | (defn- build-once [source-dir opts args cljsfiles] 30 | `(try 31 | (println "Compiling ...") 32 | (let [starttime# (.getTime (Date.))] 33 | (cljs.closure/build ~source-dir ~opts) 34 | (println (format "Compiled %d files to %s/ and '%s' (took %d ms)." 35 | ~(count cljsfiles) 36 | ~(:output-dir opts) 37 | ~(:output-to opts) 38 | (- (.getTime (Date.)) starttime#)))) 39 | (System/exit (or ~(maybe-test opts args) 0)) 40 | (catch Throwable e# 41 | (clj-stacktrace.repl/pst+ e#) 42 | (System/exit 1)))) 43 | 44 | (defn- build-loop [source-dirs opts args] 45 | `(let [events?# (atom true)] 46 | (future 47 | (watcher/with-watch-paths [~@source-dirs] 48 | (fn [ignored#] (reset! events?# true)) 49 | :recursive)) 50 | (while true 51 | (if @events?# 52 | (do 53 | (println "Compiling ...") 54 | (try 55 | (cljs.closure/build ~(first source-dirs) ~opts) 56 | ~(maybe-test opts args) 57 | (catch Throwable e# 58 | (clj-stacktrace.repl/pst+ e#))) 59 | (println "Watching ...") 60 | (Thread/sleep 500) 61 | (reset! events?# false)) 62 | (Thread/sleep 100))))) 63 | 64 | (defn- build [project source-dirs opts args cljsfiles] 65 | (binding [leiningen.compile/*skip-auto-compile* true] 66 | (leiningen.compile/eval-in-project 67 | project 68 | (if (some #{"watch"} args) 69 | (build-loop source-dirs opts args) 70 | (build-once (first source-dirs) opts args cljsfiles)) 71 | nil 72 | nil 73 | '(require 'cljs.closure 74 | 'clj-stacktrace.repl 75 | 'conch.core 76 | 'clojure.string 77 | 'watcher)))) 78 | 79 | (defn clojurescript 80 | "lein-clojurescript: Compiles clojurescript (.cljs) files in src to google 81 | closure compatible javascript (.js) files. 82 | Can use as a standalone task or can hook into the normal compile task. 83 | Uses project name or group for outputfile. Accepts the following commandline 84 | arguments: 85 | 86 | watch monitor sources and recompile when they change. 87 | test run (apply conch.core/proc (:cljs-test-cmd project)) after each 88 | compile. 89 | fresh remove output files before doing anything else. 90 | clean remove output files and do nothing else (ignores other args). 91 | { } an option map to pass to cljs.closure/build. 92 | 93 | examples: lein clojurescript 94 | lein clojurescript watch 95 | lein clojurescript fresh '{:output-dir \"myout\" }' 96 | lein compile '{:output-dir \"myout\" \\ 97 | :output-to \"bla.js\" \\ 98 | :optimizations :advanced}'" 99 | [project & args] 100 | (let [outputfile (str (or (:name project) (:group project)) ".js") 101 | opts (apply merge (:cljs project) 102 | (map read-string (filter clojurescript-arg? args))) 103 | src-dir (or (:src-dir opts) "src") 104 | test-dir (or (:test-dir opts) "test") 105 | source-dirs (if (some #{"test"} args) 106 | [test-dir src-dir] 107 | [src-dir]) 108 | starttime (.getTime (Date.))] 109 | (when (some #{"clean" "fresh"} args) 110 | (println (str "Removing '" (:output-dir opts) 111 | "' and '" (:output-to opts) "' ...")) 112 | (fs/delete (:output-to opts)) 113 | (fs/deltree (:output-dir opts))) 114 | (when-not (some #{"clean"} args) 115 | (if-let [cljsfiles (seq (filter (comp clojurescript-file? getName) 116 | (apply concat (map #(file-seq (io/file %)) source-dirs))))] 117 | (do 118 | (build project source-dirs opts args cljsfiles) 119 | (when-let [[x y] (and (:optimizations opts) 120 | (:wrap-output opts))] 121 | (spit (:output-to opts) 122 | (str x (slurp (:output-to opts)) y)))) 123 | (do 124 | (println "No cljs files found.") 125 | 1))))) 126 | 127 | (defn compile-clojurescript-hook [task & args] 128 | (let [project (first args) 129 | js-args (filter clojurescript-arg? (rest args)) 130 | clj-args (remove clojurescript-arg? (rest args))] 131 | (apply clojurescript (cons project js-args)) 132 | (when (or (contains? project :aot) (seq clj-args)) 133 | (apply task (cons project clj-args))))) 134 | 135 | (hooke/add-hook #'leiningen.compile/compile compile-clojurescript-hook) 136 | --------------------------------------------------------------------------------