|
161 | 161 | (defmethod to-url String [s] (to-url (io/file s)))
|
162 | 162 |
|
163 | 163 | (defprotocol IJavaScript
|
| 164 | + (-foreign? [this] "Whether the Javascript represents a foreign |
| 165 | + library (a js file that not have any goog.provide statement") |
164 | 166 | (-url [this] "The URL where this JavaScript is located. Returns nil
|
165 | 167 | when JavaScript exists in memory only.")
|
166 | 168 | (-provides [this] "A list of namespaces that this JavaScript provides.")
|
|
170 | 172 | (extend-protocol IJavaScript
|
171 | 173 |
|
172 | 174 | String
|
| 175 | + (-foreign? [this] false) |
173 | 176 | (-url [this] nil)
|
174 | 177 | (-provides [this] (:provides (parse-js-ns (string/split-lines this))))
|
175 | 178 | (-requires [this] (:requires (parse-js-ns (string/split-lines this))))
|
176 | 179 | (-source [this] this)
|
177 | 180 |
|
178 | 181 | clojure.lang.IPersistentMap
|
| 182 | + (-foreign? [this] (:foreign this)) |
179 | 183 | (-url [this] (or (:url this)
|
180 | 184 | (to-url (:file this))))
|
181 | 185 | (-provides [this] (map name (:provides this)))
|
|
184 | 188 | s
|
185 | 189 | (slurp (io/reader (-url this))))))
|
186 | 190 |
|
187 |
| -(defrecord JavaScriptFile [^URL url provides requires] |
| 191 | +(defrecord JavaScriptFile [foreign ^URL url provides requires] |
188 | 192 | IJavaScript
|
| 193 | + (-foreign? [this] foreign) |
189 | 194 | (-url [this] url)
|
190 | 195 | (-provides [this] provides)
|
191 | 196 | (-requires [this] requires)
|
192 | 197 | (-source [this] (slurp (io/reader url))))
|
193 | 198 |
|
194 |
| -(defn javascript-file [^URL url provides requires] |
195 |
| - (JavaScriptFile. url (map name provides) (map name requires))) |
| 199 | +(defn javascript-file [foreign ^URL url provides requires] |
| 200 | + (JavaScriptFile. foreign url (map name provides) (map name requires))) |
196 | 201 |
|
197 | 202 | (defn map->javascript-file [m]
|
198 |
| - (javascript-file (to-url (:file m)) |
| 203 | + (javascript-file (:foreign m) |
| 204 | + (to-url (:file m)) |
199 | 205 | (:provides m)
|
200 | 206 | (:requires m)))
|
201 | 207 |
|
|
371 | 377 | ;; need for closurebuilder. cljs dependencies will be compiled as
|
372 | 378 | ;; needed.
|
373 | 379 |
|
| 380 | +(defn find-url |
| 381 | + "Given a string, returns a URL. Attempts to resolve as a classpath-relative |
| 382 | + path, then as a path relative to the working directory or a URL string" |
| 383 | + [path-or-url] |
| 384 | + (or (io/resource path-or-url) |
| 385 | + (try (io/as-url path-or-url) |
| 386 | + (catch java.net.MalformedURLException e |
| 387 | + false)) |
| 388 | + (io/as-url (io/as-file path-or-url)))) |
| 389 | + |
| 390 | +(defn load-foreign-library* |
| 391 | + "Given a library spec (a map containing the keys :file |
| 392 | + and :provides), returns a map containing :provides, :requires, :file |
| 393 | + and :url" |
| 394 | + [lib-spec] |
| 395 | + (merge lib-spec {:foreign true |
| 396 | + :requires nil |
| 397 | + :url (find-url (:file lib-spec))})) |
| 398 | + |
| 399 | +(def load-foreign-library (memoize load-foreign-library*)) |
| 400 | + |
374 | 401 | (defn load-library*
|
375 | 402 | "Given a path to a JavaScript library, which is a directory
|
376 | 403 | containing Javascript files, return a list of maps
|
|
386 | 413 |
|
387 | 414 | (def load-library (memoize load-library*))
|
388 | 415 |
|
389 |
| -(defn library-dependencies [{:keys [libs]}] |
390 |
| - (mapcat load-library libs)) |
| 416 | +(defn library-dependencies [{:keys [libs foreign-libs]}] |
| 417 | + (concat |
| 418 | + (mapcat load-library libs) |
| 419 | + (map load-foreign-library foreign-libs))) |
391 | 420 |
|
392 | 421 | (comment
|
393 | 422 | ;; load one library
|
394 | 423 | (load-library* "closure/library/third_party/closure")
|
395 | 424 | ;; load all library dependencies
|
396 | 425 | (library-dependencies {:libs ["closure/library/third_party/closure"]})
|
397 |
| - ) |
| 426 | + (library-dependencies {:foreign-libs [{:file "http://example.com/remote.js" |
| 427 | + :provides ["my.example"]}]}) |
| 428 | + (library-dependencies {:foreign-libs [{:file "local/file.js" |
| 429 | + :provides ["my.example"]}]}) |
| 430 | + (library-dependencies {:foreign-libs [{:file "cljs/nodejs_externs.js" |
| 431 | + :provides ["my.example"]}]})) |
398 | 432 |
|
399 | 433 | (defn goog-dependencies*
|
400 | 434 | "Create an index of Google dependencies by namespace and file name."
|
|
414 | 448 |
|
415 | 449 | (def goog-dependencies (memoize goog-dependencies*))
|
416 | 450 |
|
| 451 | + |
417 | 452 | (defn js-dependency-index
|
418 | 453 | "Returns the index for all JavaScript dependencies. Lookup by
|
419 | 454 | namespace or file name."
|
|
501 | 536 | (let [requires (mapcat -requires inputs)
|
502 | 537 | required-cljs (cljs-dependencies opts requires)
|
503 | 538 | required-js (js-dependencies opts (set (concat (mapcat -requires required-cljs) requires)))]
|
504 |
| - (concat (map #(-> (javascript-file (or (:url %) (io/resource (:file %))) |
| 539 | + (concat (map #(-> (javascript-file (:foreign %) |
| 540 | + (or (:url %) (io/resource (:file %))) |
505 | 541 | (:provides %)
|
506 | 542 | (:requires %))
|
507 | 543 | (assoc :group (:group %))) required-js)
|
|
520 | 556 | (str "goog.provide('test.app');\n"
|
521 | 557 | "goog.require('goog.array');\n"
|
522 | 558 | "goog.require('goog.dom.query');"))
|
| 559 | + ;; add dependencies with foreign lib |
| 560 | + (add-dependencies {:foreign-libs [{:file "samples/hello/src/hello/core.cljs" |
| 561 | + :provides ["example.lib"]}]} |
| 562 | + (str "goog.provide('test.app');\n" |
| 563 | + "goog.require('example.lib');\n")) |
523 | 564 | ;; add dependencies to a JavaScriptFile record
|
524 |
| - (add-dependencies {} (javascript-file (to-url "samples/hello/src/hello/core.cljs") |
525 |
| - ["hello.core"] |
526 |
| - ["goog.array"])) |
| 565 | + (add-dependencies {} (javascript-file false |
| 566 | + (to-url "samples/hello/src/hello/core.cljs") |
| 567 | + ["hello.core"] |
| 568 | + ["goog.array"])) |
527 | 569 | )
|
528 | 570 |
|
529 | 571 | ;; Optimize
|
|
539 | 581 |
|
540 | 582 | (defmethod javascript-name JavaScriptFile [js] (javascript-name (-url js)))
|
541 | 583 |
|
| 584 | +(defn build-provides |
| 585 | + "Given a vector of provides, builds required goog.provide statements" |
| 586 | + [provides] |
| 587 | + (apply str (map #(str "goog.provide('" % "');\n") provides))) |
| 588 | + |
| 589 | + |
542 | 590 | (defmethod js-source-file JavaScriptFile [_ js]
|
543 |
| - (when-let [url (-url js)] |
544 |
| - (js-source-file (javascript-name url) (io/input-stream url)))) |
| 591 | + (when-let [url (-url js)] |
| 592 | + (js-source-file (javascript-name url) |
| 593 | + (if (-foreign? js) |
| 594 | + (str (build-provides (-provides js)) (slurp url)) |
| 595 | + (io/input-stream url))))) |
545 | 596 |
|
546 | 597 | (defn optimize
|
547 | 598 | "Use the Closure Compiler to optimize one or more JavaScript files."
|
|
0 commit comments