Browse files

Added support for google closure compilation of foreign libraries

  • Loading branch information...
1 parent b216d0d commit 96b38e2c951ef07b397e9d42a8214ab59895f9f6 @levand levand committed Sep 28, 2011
Showing with 64 additions and 13 deletions.
  1. +64 −13 src/clj/cljs/closure.clj
@@ -161,6 +161,8 @@
(defmethod to-url String [s] (to-url (io/file s)))
(defprotocol IJavaScript
+ (-foreign? [this] "Whether the Javascript represents a foreign
+ library (a js file that not have any goog.provide statement")
(-url [this] "The URL where this JavaScript is located. Returns nil
when JavaScript exists in memory only.")
(-provides [this] "A list of namespaces that this JavaScript provides.")
@@ -170,12 +172,14 @@
(extend-protocol IJavaScript
+ (-foreign? [this] false)
(-url [this] nil)
(-provides [this] (:provides (parse-js-ns (string/split-lines this))))
(-requires [this] (:requires (parse-js-ns (string/split-lines this))))
(-source [this] this)
+ (-foreign? [this] (:foreign this))
(-url [this] (or (:url this)
(to-url (:file this))))
(-provides [this] (map name (:provides this)))
@@ -184,18 +188,20 @@
(slurp (io/reader (-url this))))))
-(defrecord JavaScriptFile [^URL url provides requires]
+(defrecord JavaScriptFile [foreign ^URL url provides requires]
+ (-foreign? [this] foreign)
(-url [this] url)
(-provides [this] provides)
(-requires [this] requires)
(-source [this] (slurp (io/reader url))))
-(defn javascript-file [^URL url provides requires]
- (JavaScriptFile. url (map name provides) (map name requires)))
+(defn javascript-file [foreign ^URL url provides requires]
+ (JavaScriptFile. foreign url (map name provides) (map name requires)))
(defn map->javascript-file [m]
- (javascript-file (to-url (:file m))
+ (javascript-file (:foreign m)
+ (to-url (:file m))
(:provides m)
(:requires m)))
@@ -371,6 +377,27 @@
;; need for closurebuilder. cljs dependencies will be compiled as
;; needed.
+(defn find-url
+ "Given a string, returns a URL. Attempts to resolve as a classpath-relative
+ path, then as a path relative to the working directory or a URL string"
+ [path-or-url]
+ (or (io/resource path-or-url)
+ (try (io/as-url path-or-url)
+ (catch e
+ false))
+ (io/as-url (io/as-file path-or-url))))
+(defn load-foreign-library*
+ "Given a library spec (a map containing the keys :file
+ and :provides), returns a map containing :provides, :requires, :file
+ and :url"
+ [lib-spec]
+ (merge lib-spec {:foreign true
+ :requires nil
+ :url (find-url (:file lib-spec))}))
+(def load-foreign-library (memoize load-foreign-library*))
(defn load-library*
"Given a path to a JavaScript library, which is a directory
containing Javascript files, return a list of maps
@@ -386,15 +413,22 @@
(def load-library (memoize load-library*))
-(defn library-dependencies [{:keys [libs]}]
- (mapcat load-library libs))
+(defn library-dependencies [{:keys [libs foreign-libs]}]
+ (concat
+ (mapcat load-library libs)
+ (map load-foreign-library foreign-libs)))
;; load one library
(load-library* "closure/library/third_party/closure")
;; load all library dependencies
(library-dependencies {:libs ["closure/library/third_party/closure"]})
- )
+ (library-dependencies {:foreign-libs [{:file ""
+ :provides ["my.example"]}]})
+ (library-dependencies {:foreign-libs [{:file "local/file.js"
+ :provides ["my.example"]}]})
+ (library-dependencies {:foreign-libs [{:file "cljs/nodejs_externs.js"
+ :provides ["my.example"]}]}))
(defn goog-dependencies*
"Create an index of Google dependencies by namespace and file name."
@@ -414,6 +448,7 @@
(def goog-dependencies (memoize goog-dependencies*))
(defn js-dependency-index
"Returns the index for all JavaScript dependencies. Lookup by
namespace or file name."
@@ -501,7 +536,8 @@
(let [requires (mapcat -requires inputs)
required-cljs (cljs-dependencies opts requires)
required-js (js-dependencies opts (set (concat (mapcat -requires required-cljs) requires)))]
- (concat (map #(-> (javascript-file (or (:url %) (io/resource (:file %)))
+ (concat (map #(-> (javascript-file (:foreign %)
+ (or (:url %) (io/resource (:file %)))
(:provides %)
(:requires %))
(assoc :group (:group %))) required-js)
@@ -520,10 +556,16 @@
(str "goog.provide('');\n"
+ ;; add dependencies with foreign lib
+ (add-dependencies {:foreign-libs [{:file "samples/hello/src/hello/core.cljs"
+ :provides ["example.lib"]}]}
+ (str "goog.provide('');\n"
+ "goog.require('example.lib');\n"))
;; add dependencies to a JavaScriptFile record
- (add-dependencies {} (javascript-file (to-url "samples/hello/src/hello/core.cljs")
- ["hello.core"]
- ["goog.array"]))
+ (add-dependencies {} (javascript-file false
+ (to-url "samples/hello/src/hello/core.cljs")
+ ["hello.core"]
+ ["goog.array"]))
;; Optimize
@@ -539,9 +581,18 @@
(defmethod javascript-name JavaScriptFile [js] (javascript-name (-url js)))
+(defn build-provides
+ "Given a vector of provides, builds required goog.provide statements"
+ [provides]
+ (apply str (map #(str "goog.provide('" % "');\n") provides)))
(defmethod js-source-file JavaScriptFile [_ js]
- (when-let [url (-url js)]
- (js-source-file (javascript-name url) (io/input-stream url))))
+ (when-let [url (-url js)]
+ (js-source-file (javascript-name url)
+ (if (-foreign? js)
+ (str (build-provides (-provides js)) (slurp url))
+ (io/input-stream url)))))
(defn optimize
"Use the Closure Compiler to optimize one or more JavaScript files."

0 comments on commit 96b38e2

Please sign in to comment.