-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathcore.clj
98 lines (89 loc) · 3.62 KB
/
core.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
(ns garden-watcher.core
(:require [clojure.java.io :as io]
[clojure.java.classpath :as cp]
[clojure.string :as str]
[com.stuartsierra.component :as component]
[garden.core :refer [css]]
[hawk.core :as hawk])
(:import java.io.File
java.nio.file.Paths
java.nio.file.Files))
(defn- ns-file-name
"Copied from clojure.tools.namespace.move because it's private there."
[sym]
(str (-> (name sym)
(str/replace "-" "_")
(str/replace "." File/separator))
".clj"))
(defn- file-on-classpath
"Given a relative path to a source file, find it on the classpath, returning a
fully qualified java.io.File "
[path]
(->> (cp/classpath)
(map #(io/file % path))
(filter #(.exists %))
first))
(defn- select-ns-path
"Given a list of namespace names (symbols) and a path (string), transforms the
path so it's relative to the classpath"
[namespaces file]
(let [ns-paths (map ns-file-name namespaces)]
(first (filter #(.endsWith file %) ns-paths))))
(defn- file->ns
"Given a list of namespace names (symbols) and a path (string), return the
namespace name that corresponds with the path name"
[namespaces path]
(first (filter #(.endsWith path (ns-file-name %)) namespaces)))
(defn- reload-and-compile!
"Reload the given path, then find all vars with a :garden metadata in the
corresponding namespace, and compile those to CSS. The target path is either
defined in the :garden metadata as :output-to, or it's derived from the var
name as resources/public/css/<name>.css"
[namespaces path]
(when-let [ns (file->ns namespaces path)]
(require ns :reload)
(doseq [[sym var] (ns-publics ns)]
(when-let [garden-meta (-> var meta :garden)]
(let [garden-meta (if (map? garden-meta) garden-meta {})]
(let [target (:output-to garden-meta (str "resources/public/css/" sym ".css"))]
(println (str "Garden: compiling #'" ns "/" sym))
(io/make-parents target)
(css (assoc garden-meta :output-to target) @var)))))))
(defn- garden-reloader-handler [namespaces]
(fn [_ctx event]
(when (= (:kind event) :modify)
(when-let [ns-path (select-ns-path namespaces (str (:file event)))]
(reload-and-compile! namespaces ns-path)))))
(defn compile-garden-namespaces
"Given a list of namespaces (seq of symbol), reloads the namespaces, finds all
syms with a :garden metadata key, and compiles them to CSS."
[namespaces]
(run! #(reload-and-compile! namespaces %)
(map ns-file-name namespaces)))
(defrecord GardenWatcherComponent [namespaces]
component/Lifecycle
(start [this]
(if (:garden-watcher-hawk this)
(do
(println "Garden: watcher already running.")
this)
(let [paths (map (comp file-on-classpath ns-file-name) namespaces)
handler (garden-reloader-handler namespaces)]
(compile-garden-namespaces namespaces)
(println "Garden: watching" (str/join ", " paths))
(assoc this :garden-watcher-hawk (hawk/watch! [{:paths paths
:handler handler}])))))
(stop [this]
(if-let [hawk (:garden-watcher-hawk this)]
(do
(hawk/stop! hawk)
(println "Garden: stopped watching namespaces.")
(dissoc this :garden-watcher-hawk))
(do
(println "Garden: watcher not running")
this))))
(defn new-garden-watcher
"Create a new Sierra Component that watches the given namespaces for changes,
and upon change compiles any symbols with a :garden metadata key to CSS."
[namespaces]
(->GardenWatcherComponent namespaces))