├── .gitignore ├── README.md ├── cljs-watch ├── project.clj ├── src └── cljs_watch │ └── core.clj └── test └── cljs_watch └── test └── core.clj /.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | *jar 3 | /lib/ 4 | /classes/ 5 | .lein-failures 6 | .lein-deps-sum 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CLJS watcher 2 | 3 | A simple shell script that watches for changes to CLJS files and recompiles them for you. 4 | 5 | ## Usage 6 | 7 | You must have ClojureScript already setup and have $CLOJURESCRIPT_HOME correctly set. 8 | 9 | Put `cljs-watch` on your $PATH (such as in /usr/local/bin) and then simply run it from your project root: 10 | 11 | ```bash 12 | #without options it watches the src/ directory 13 | cljs-watch 14 | 15 | #it can also take a directory and compile options 16 | cljs-watch cljs-src/ '{:optimizations :none :output-to "test.js"}' 17 | ``` 18 | 19 | ## Notes 20 | * the default output-to is set to `resources/public/cljs/bootstrap.js` 21 | * it will add the local `lib/` to your classpath when you run it, allowing you to have other cljs deps in that folder 22 | * to add custom macros, you can use create a folder called `cljs-macros/` from the root directory and add your macros there. You can also put macros in `CLOJURESCRIPT_HOME/lib/` to have them globally available. 23 | 24 | ## License 25 | 26 | Copyright (C) 2011 Chris Granger 27 | 28 | Distributed under the Eclipse Public License, the same as Clojure. 29 | -------------------------------------------------------------------------------- /cljs-watch: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CLJS_LIB=$CLOJURESCRIPT_HOME/lib/ 4 | CLJS_LIB_JARS=$CLOJURESCRIPT_HOME/lib/* 5 | CLJS_SRC=$CLOJURESCRIPT_HOME/src/clj 6 | CLJS_CLJS=$CLOJURESCRIPT_HOME/src/cljs 7 | LOCAL_LIB_JARS=lib/* 8 | LOCAL_LIB=lib/ 9 | LOCAL_MACROS=cljs-macros/ 10 | 11 | exec java -server -cp "${CLJS_LIB}:${CLJS_LIB_JARS}:${CLJS_SRC}:${CLJS_CLJS}:${LOCAL_LIB}:${LOCAL_LIB_JARS}:${LOCAL_MACROS}" clojure.main -e \ 12 | " 13 | (use '[clojure.java.io :only [file]]) 14 | (require '[cljs.closure :as cljsc]) 15 | 16 | (do 17 | (import '[java.util Calendar]) 18 | (import '[java.text SimpleDateFormat]) 19 | 20 | (defn text-timestamp [] 21 | (let [c (Calendar/getInstance) 22 | f (SimpleDateFormat. \"HH:mm:ss\")] 23 | (.format f (.getTime c)))) 24 | 25 | (def default-opts {:optimizations :simple 26 | :pretty-print true 27 | :output-dir \"resources/public/cljs/\" 28 | :output-to \"resources/public/cljs/bootstrap.js\"}) 29 | 30 | (def ANSI-CODES 31 | {:reset \"[0m\" 32 | :default \"[39m\" 33 | :white \"[37m\" 34 | :black \"[30m\" 35 | :red \"[31m\" 36 | :green \"[32m\" 37 | :blue \"[34m\" 38 | :yellow \"[33m\" 39 | :magenta \"[35m\" 40 | :cyan \"[36m\" 41 | }) 42 | 43 | (defn ansi 44 | [code] 45 | (str \u001b (get ANSI-CODES code (:reset ANSI-CODES)))) 46 | 47 | (defn style 48 | [s & codes] 49 | (str (apply str (map ansi codes)) s (ansi :reset))) 50 | 51 | (def last-compiled (atom 0)) 52 | 53 | (defn ext-filter [coll ext] 54 | (filter (fn [f] 55 | (let [fname (.getName f) 56 | fext (subs fname (inc (.lastIndexOf fname \".\")))] 57 | (and (not= \. (first fname)) (.isFile f) (= fext ext)))) 58 | coll)) 59 | 60 | (defn find-cljs [dir] 61 | (let [dir-files (-> dir file file-seq)] 62 | (ext-filter dir-files \"cljs\"))) 63 | 64 | (defn compile-cljs [src-dir opts] 65 | (try 66 | (cljsc/build src-dir opts) 67 | (catch Throwable e 68 | (.printStackTrace e))) 69 | (reset! last-compiled (System/currentTimeMillis))) 70 | 71 | (defn newer? [f] 72 | (let [last-modified (.lastModified f)] 73 | (> last-modified @last-compiled))) 74 | 75 | (defn files-updated? [dir] 76 | (some newer? (find-cljs dir))) 77 | 78 | (defn watcher-print [& text] 79 | (print (style (str (text-timestamp) \" :: watcher :: \") :magenta)) 80 | (apply print text) 81 | (flush)) 82 | 83 | (defn status-print [text] 84 | (print \" \" (style text :green) \"\n\") 85 | (flush)) 86 | 87 | (defn transform-cl-args 88 | [args] 89 | (let [source (first args) 90 | opts-string (apply str (interpose \" \" (rest args))) 91 | options (when (> (count opts-string) 1) 92 | (try (read-string opts-string) 93 | (catch Exception e (println e))))] 94 | {:source source :options options})) 95 | 96 | (let [{:keys [source options]} (transform-cl-args *command-line-args*) 97 | src-dir (or source \"src/\") 98 | opts (merge default-opts options)] 99 | (watcher-print \"Building ClojureScript files in ::\" src-dir) 100 | (compile-cljs src-dir opts) 101 | (status-print \"[done]\") 102 | (watcher-print \"Waiting for changes\n\") 103 | (while true 104 | (Thread/sleep 1000) 105 | (when (files-updated? src-dir) 106 | (watcher-print \"Compiling updated files...\") 107 | (compile-cljs src-dir opts) 108 | (status-print \"[done]\"))))) 109 | " $1 $* 110 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject cljs-watch "1.0.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :main cljs-watch.core 4 | :dependencies [[org.clojure/clojure "1.3.0-beta1"] 5 | [cljs-compiler-jar "0.1.0-SNAPSHOT"]]) 6 | -------------------------------------------------------------------------------- /src/cljs_watch/core.clj: -------------------------------------------------------------------------------- 1 | (use '[clojure.java.io :only [file]]) 2 | (require '[cljs.closure :as cljsc]) 3 | 4 | ;;wrap everything in a do to prevent the ouput, as a file 5 | ;;this is unnecessary, but from clojure.main -e it makes a 6 | ;;difference 7 | (do 8 | (import '[java.util Calendar]) 9 | (import '[java.text SimpleDateFormat]) 10 | 11 | (defn text-timestamp [] 12 | (let [c (Calendar/getInstance) 13 | f (SimpleDateFormat. "HH:mm:ss")] 14 | (.format f (.getTime c)))) 15 | 16 | (def default-opts {:optimizations :simple 17 | :pretty-print true 18 | :output-dir "resources/public/cljs/" 19 | :output-to "resources/public/cljs/bootstrap.js"}) 20 | 21 | (def ANSI-CODES 22 | {:reset "[0m" 23 | :default "[39m" 24 | :white "[37m" 25 | :black "[30m" 26 | :red "[31m" 27 | :green "[32m" 28 | :blue "[34m" 29 | :yellow "[33m" 30 | :magenta "[35m" 31 | :cyan "[36m" 32 | }) 33 | 34 | (defn ansi 35 | [code] 36 | (str \u001b (get ANSI-CODES code (:reset ANSI-CODES)))) 37 | 38 | (defn style 39 | [s & codes] 40 | (str (apply str (map ansi codes)) s (ansi :reset))) 41 | 42 | (def last-compiled (atom 0)) 43 | 44 | (defn ext-filter [coll ext] 45 | (filter (fn [f] 46 | (let [fname (.getName f) 47 | fext (subs fname (inc (.lastIndexOf fname ".")))] 48 | (and (not= \. (first fname)) (.isFile f) (= fext ext)))) 49 | coll)) 50 | 51 | (defn find-cljs [dir] 52 | (let [dir-files (-> dir file file-seq)] 53 | (ext-filter dir-files "cljs"))) 54 | 55 | (defn compile-cljs [src-dir opts] 56 | (try 57 | (cljsc/build src-dir opts) 58 | (catch Throwable e 59 | (.printStackTrace e))) 60 | (reset! last-compiled (System/currentTimeMillis))) 61 | 62 | (defn newer? [f] 63 | (let [last-modified (.lastModified f)] 64 | (> last-modified @last-compiled))) 65 | 66 | (defn files-updated? [dir] 67 | (some newer? (find-cljs dir))) 68 | 69 | (defn watcher-print [& text] 70 | (print (style (str (text-timestamp) " :: watcher :: ") :magenta)) 71 | (apply print text) 72 | (flush)) 73 | 74 | (defn status-print [text] 75 | (print " " (style text :green) "\n") 76 | (flush)) 77 | 78 | (defn transform-cl-args 79 | [args] 80 | (let [source (first args) 81 | opts-string (apply str (interpose " " (rest args))) 82 | options (when (> (count opts-string) 1) 83 | (try (read-string opts-string) 84 | (catch Exception e (println e))))] 85 | {:source source :options options})) 86 | 87 | (let [{:keys [source options]} (transform-cl-args *command-line-args*) 88 | src-dir (or source "src/") 89 | opts (merge default-opts options)] 90 | (.mkdirs (file (:output-dir opts))) 91 | (watcher-print "Building ClojureScript files in ::" src-dir) 92 | (compile-cljs src-dir opts) 93 | (status-print "[done]") 94 | (watcher-print "Waiting for changes\n") 95 | (while true 96 | (Thread/sleep 1000) 97 | (when (files-updated? src-dir) 98 | (watcher-print "Compiling updated files...") 99 | (compile-cljs src-dir opts) 100 | (status-print "[done]"))))) 101 | -------------------------------------------------------------------------------- /test/cljs_watch/test/core.clj: -------------------------------------------------------------------------------- 1 | (ns cljs-watch.test.core 2 | (:use [cljs-watch.core]) 3 | (:use [clojure.test])) 4 | 5 | (deftest replace-me ;; FIXME: write 6 | (is false "No tests have been written.")) 7 | --------------------------------------------------------------------------------