├── 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 |
--------------------------------------------------------------------------------