Skip to content

Instantly share code, notes, and snippets.

@Deraen

Deraen/dep-graph.clj

Last active Aug 29, 2015
Embed
What would you like to do?
ClojureScript output file dependency graph
; Use case:
; We reload the files automatically in browser when they are changed.
; To load the files in correct order we need to know their dependencies.
(require '[cljs.build.api :refer :all]
'[cljs.analyzer.api :as ana-api]
'[clojure.java.io :as io]
'[cljs.util :as util])
(def state (ana-api/empty-state))
(defn rm [f]
(when (.exists f)
(when (.isDirectory f)
(doseq [n (seq (.list f))]
(rm (io/file f n))))
(io/delete-file f)))
(rm (io/file "out"))
(build "samples/hello/src" {:compiler {:main "hello.core"}} state)
;;
;; Current version
;;
(def dep-graph
(let [cljs-nses (:cljs.compiler/compiled-cljs @state)
js-nses (reduce-kv (fn [xs k v]
(assoc xs (:file v) v))
{}
(:js-dependency-index @state))
all-nses (reduce-kv (fn [xs k v]
(reduce #(assoc %1 (str %2) (str k)) xs (:provides v)))
{}
(merge js-nses cljs-nses))]
(->> cljs-nses
(reduce-kv (fn [xs k v]
(assoc xs k (set (keep (comp all-nses str) (:requires v)))))
{}))))
; =>
; {"/home/juho/Source/clojurescript/out/hello/foo/bar.js" #{},
; "/home/juho/Source/clojurescript/out/hello/core.js" #{"/home/juho/Source/clojurescript/out/cljs/reader.js" "/home/juho/Source/clojurescript/out/hello/foo/bar.js"},
; "/home/juho/Source/clojurescript/out/cljs/reader.js" #{"goog/string/stringbuffer.js" "goog/string/string.js"}}
(def b (let [m "/home/juho/Source/clojurescript/out/hello/core.js"]
(reverse (cons m (util/topo-sort m dep-graph)))))
; =>
; ("goog/string/string.js" "goog/string/stringbuffer.js" "/home/juho/Source/clojurescript/out/hello/foo/bar.js" "/home/juho/Source/clojurescript/out/cljs/reader.js" "/home/juho/Source/clojurescript/out/hello/core.js")
;;
;; Using current Cljs api
;;
; Note: Google closure files wont change so their ordering isn't interesting.
; Note: What about foreign libs? They could be interesting.
(defn ns->path [x]
(.getPath (target-file-for-cljs-ns x)))
(def dep-graph-2
(->> (ana-api/all-ns state)
(reduce (fn [acc n]
(let [p (ns->path n)]
(->> (cljs-ns-dependents state n)
(map ns->path)
(reduce (fn [acc x]
(update-in acc [x] (fnil conj #{}) p))
(update-in acc [p] #(or % #{}))))))
{})))
; =>
; {"out/hello/foo/bar.js" #{},
; "out/hello/core.js" ("out/cljs/reader.js" "out/hello/foo/bar.js"),
; "out/cljs/core.js" #{},
; "out/cljs/reader.js" #{},
; "out/cljs/user.js" #{}}
;;
;; Proposal: ns-dependencies
;;
(defn ns-dependencies
"Given a namespace as a symbol..."
([ns] (ns-dependencies env/*compiler* ns))
([state ns]
(vals (:requires (ana-api/find-ns state ns)))))
(def dep-graph-3
(let [all-ns (ana-api/all-ns state)
all-ns-set (set all-ns)]
(->> all-ns
(reduce (fn [acc n]
(assoc acc (ns->path n) (->> (ns-dependencies state n)
(keep all-ns-set)
(map ns->path)
(set))))
{}))))
; =>
; {"out/cljs/user.js" #{},
; "out/cljs/core.js" #{},
; "out/hello/foo/bar.js" #{},
; "out/hello/core.js" #{"out/hello/foo/bar.js" "out/cljs/reader.js"},
; "out/cljs/reader.js" #{}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment