|
47 | 47 | [clojure.data.json :as json] |
48 | 48 | [clojure.tools.reader :as reader] |
49 | 49 | [clojure.tools.reader.reader-types :as readers]) |
50 | | - (:import [java.io File BufferedInputStream StringWriter] |
| 50 | + (:import [java.lang ProcessBuilder] |
| 51 | + [java.io File BufferedInputStream BufferedReader |
| 52 | + Writer InputStreamReader IOException StringWriter] |
51 | 53 | [java.net URL] |
52 | 54 | [java.util.logging Level] |
53 | 55 | [java.util List Random] |
|
342 | 344 | (doseq [next (seq warnings)] |
343 | 345 | (println "WARNING:" (.toString ^JSError next))))) |
344 | 346 |
|
345 | | - |
346 | 347 | ;; Protocols for IJavaScript and Compilable |
347 | 348 | ;; ======================================== |
348 | 349 |
|
|
353 | 354 | (-source-map [this] "Return the CLJS compiler generated JS source mapping")) |
354 | 355 |
|
355 | 356 | (extend-protocol deps/IJavaScript |
356 | | - |
| 357 | + |
357 | 358 | String |
358 | 359 | (-foreign? [this] false) |
359 | 360 | (-closure-lib? [this] false) |
|
362 | 363 | (-provides [this] (:provides (deps/parse-js-ns (string/split-lines this)))) |
363 | 364 | (-requires [this] (:requires (deps/parse-js-ns (string/split-lines this)))) |
364 | 365 | (-source [this] this) |
365 | | - |
| 366 | + |
366 | 367 | clojure.lang.IPersistentMap |
367 | 368 | (-foreign? [this] (:foreign this)) |
368 | 369 | (-closure-lib? [this] (:closure-lib this)) |
|
481 | 482 | returns a JavaScriptFile. In either case the return value satisfies |
482 | 483 | IJavaScript." |
483 | 484 | [^File file {:keys [output-file] :as opts}] |
484 | | - (if output-file |
| 485 | + (if output-file |
485 | 486 | (let [out-file (io/file (util/output-directory opts) output-file)] |
486 | 487 | (compiled-file (comp/compile-file file out-file opts))) |
487 | 488 | (let [path (.getPath ^File file)] |
|
567 | 568 | (case (.getProtocol this) |
568 | 569 | "file" (-find-sources (io/file this) opts) |
569 | 570 | "jar" (find-jar-sources this opts))) |
570 | | - |
| 571 | + |
571 | 572 | clojure.lang.PersistentList |
572 | 573 | (-compile [this opts] |
573 | 574 | (compile-form-seq [this])) |
574 | 575 | (-find-sources [this opts] |
575 | 576 | [(ana/parse-ns [this] opts)]) |
576 | | - |
| 577 | + |
577 | 578 | String |
578 | 579 | (-compile [this opts] (-compile (io/file this) opts)) |
579 | 580 | (-find-sources [this opts] (-find-sources (io/file this) opts)) |
580 | | - |
| 581 | + |
581 | 582 | clojure.lang.PersistentVector |
582 | 583 | (-compile [this opts] (compile-form-seq this)) |
583 | 584 | (-find-sources [this opts] |
|
1339 | 1340 |
|
1340 | 1341 | ;; optimize a ClojureScript form |
1341 | 1342 | (optimize {:optimizations :simple} (-compile '(def x 3) {})) |
1342 | | - |
| 1343 | + |
1343 | 1344 | ;; optimize a project |
1344 | 1345 | (println (->> (-compile "samples/hello/src" {}) |
1345 | 1346 | (apply add-dependencies {}) |
|
1730 | 1731 | (output-deps-file opts disk-sources)))) |
1731 | 1732 |
|
1732 | 1733 | (comment |
1733 | | - |
| 1734 | + |
1734 | 1735 | ;; output unoptimized alone |
1735 | 1736 | (output-unoptimized {} "goog.provide('test');\ngoog.require('cljs.core');\nalert('hello');\n") |
1736 | 1737 | ;; output unoptimized with all dependencies |
|
1916 | 1917 | [lib])))] |
1917 | 1918 | (into [] (mapcat expand-lib* libs)))) |
1918 | 1919 |
|
| 1920 | +(declare index-node-modules) |
| 1921 | + |
1919 | 1922 | (defn add-implicit-options |
1920 | 1923 | [{:keys [optimizations output-dir] |
1921 | 1924 | :or {optimizations :none |
1922 | 1925 | output-dir "out"} |
1923 | 1926 | :as opts}] |
1924 | | - (let [opts (cond-> (update opts :foreign-libs expand-libs) |
| 1927 | + (let [opts (cond-> (update opts :foreign-libs |
| 1928 | + (fn [libs] |
| 1929 | + (into [] |
| 1930 | + (util/distinct-merge-by :file |
| 1931 | + (index-node-modules opts) |
| 1932 | + (expand-libs libs))))) |
1925 | 1933 | (:closure-defines opts) |
1926 | 1934 | (assoc :closure-defines |
1927 | 1935 | (into {} |
|
1966 | 1974 | (nil? (:closure-module-roots opts)) |
1967 | 1975 | (assoc :closure-module-roots [])))) |
1968 | 1976 |
|
| 1977 | +(defn- alive? [proc] |
| 1978 | + (try (.exitValue proc) false (catch IllegalThreadStateException _ true))) |
| 1979 | + |
| 1980 | +(defn- pipe [^Process proc in ^Writer out] |
| 1981 | + ;; we really do want system-default encoding here |
| 1982 | + (with-open [^java.io.Reader in (-> in InputStreamReader. BufferedReader.)] |
| 1983 | + (loop [buf (char-array 1024)] |
| 1984 | + (when (alive? proc) |
| 1985 | + (try |
| 1986 | + (let [len (.read in buf)] |
| 1987 | + (when-not (neg? len) |
| 1988 | + (.write out buf 0 len) |
| 1989 | + (.flush out))) |
| 1990 | + (catch IOException e |
| 1991 | + (when (and (alive? proc) (not (.contains (.getMessage e) "Stream closed"))) |
| 1992 | + (.printStackTrace e *err*)))) |
| 1993 | + (recur buf))))) |
| 1994 | + |
| 1995 | +(defn maybe-install-node-deps! |
| 1996 | + [{:keys [npm-deps verbose] :as opts}] |
| 1997 | + (if-not (empty? npm-deps) |
| 1998 | + (do |
| 1999 | + (when (or ana/*verbose* verbose) |
| 2000 | + (util/debug-prn "Installing Node.js dependencies")) |
| 2001 | + (let [proc (-> (ProcessBuilder. |
| 2002 | + (into ["npm" "install" "module-deps"] |
| 2003 | + (map (fn [[dep version]] (str (name dep) "@" version))) |
| 2004 | + npm-deps)) |
| 2005 | + .start) |
| 2006 | + is (.getInputStream proc) |
| 2007 | + iw (StringWriter. (* 16 1024 1024)) |
| 2008 | + es (.getErrorStream proc) |
| 2009 | + ew (StringWriter. (* 1024 1024)) |
| 2010 | + _ (do (.start |
| 2011 | + (Thread. |
| 2012 | + (bound-fn [] (pipe proc is iw)))) |
| 2013 | + (.start |
| 2014 | + (Thread. |
| 2015 | + (bound-fn [] (pipe proc es ew))))) |
| 2016 | + err (.waitFor proc)] |
| 2017 | + (when (and (not (zero? err)) (not (.isAlive proc))) |
| 2018 | + (println (str ew))) |
| 2019 | + opts)) |
| 2020 | + opts)) |
| 2021 | + |
| 2022 | +(defn node-module-deps |
| 2023 | + "EXPERIMENTAL: return the foreign libs entries as computed by running |
| 2024 | + the module-deps package on the supplied JavaScript entry point. Assumes |
| 2025 | + that the module-deps NPM package is either locally or globally installed." |
| 2026 | + ([entry] |
| 2027 | + (node-module-deps entry |
| 2028 | + (when env/*compiler* |
| 2029 | + (:options @env/*compiler*)))) |
| 2030 | + ([{:keys [file]} {:keys [target] :as opts}] |
| 2031 | + (let [code (-> (slurp (io/resource "cljs/module_deps.js")) |
| 2032 | + (string/replace "JS_FILE" file) |
| 2033 | + (string/replace "CLJS_TARGET" (str "" (when target (name target))))) |
| 2034 | + proc (-> (ProcessBuilder. |
| 2035 | + ["node" "--eval" code]) |
| 2036 | + .start) |
| 2037 | + is (.getInputStream proc) |
| 2038 | + iw (StringWriter. (* 16 1024 1024)) |
| 2039 | + es (.getErrorStream proc) |
| 2040 | + ew (StringWriter. (* 1024 1024)) |
| 2041 | + _ (do (.start |
| 2042 | + (Thread. |
| 2043 | + (bound-fn [] (pipe proc is iw)))) |
| 2044 | + (.start |
| 2045 | + (Thread. |
| 2046 | + (bound-fn [] (pipe proc es ew))))) |
| 2047 | + err (.waitFor proc)] |
| 2048 | + (if (zero? err) |
| 2049 | + (into [] |
| 2050 | + (map (fn [{:strs [file provides]}] file |
| 2051 | + (merge |
| 2052 | + {:file file |
| 2053 | + :module-type :commonjs} |
| 2054 | + (when provides |
| 2055 | + {:provides provides})))) |
| 2056 | + (next (json/read-str (str iw)))) |
| 2057 | + (do |
| 2058 | + (when-not (.isAlive proc) |
| 2059 | + (println (str ew))) |
| 2060 | + []))))) |
| 2061 | + |
| 2062 | +(defn node-inputs |
| 2063 | + "EXPERIMENTAL: return the foreign libs entries as computed by running |
| 2064 | + the module-deps package on the supplied JavaScript entry points. Assumes |
| 2065 | + that the module-deps NPM packages is either locally or globally installed." |
| 2066 | + ([entries] |
| 2067 | + (node-inputs entries |
| 2068 | + (when env/*compiler* |
| 2069 | + (:options @env/*compiler*)))) |
| 2070 | + ([entries opts] |
| 2071 | + (into [] (distinct (mapcat #(node-module-deps % opts) entries))))) |
| 2072 | + |
| 2073 | +(defn index-node-modules |
| 2074 | + ([] |
| 2075 | + (index-node-modules |
| 2076 | + (when env/*compiler* |
| 2077 | + (:options @env/*compiler*)))) |
| 2078 | + ([{:keys [npm-deps] :as opts}] |
| 2079 | + (let [node-modules (io/file "node_modules")] |
| 2080 | + (when (and (.exists node-modules) (.isDirectory node-modules)) |
| 2081 | + (let [modules (map name (keys npm-deps)) |
| 2082 | + deps-file (io/file (str (util/output-directory opts) File/separator |
| 2083 | + "cljs$node_modules.js"))] |
| 2084 | + (util/mkdirs deps-file) |
| 2085 | + (with-open [w (io/writer deps-file)] |
| 2086 | + (run! #(.write w (str "require('" % "');\n")) modules)) |
| 2087 | + (node-inputs [{:file (.getAbsolutePath deps-file)}])))))) |
| 2088 | + |
1969 | 2089 | (defn process-js-modules |
1970 | 2090 | "Given the current compiler options, converts JavaScript modules to Google |
1971 | 2091 | Closure modules and writes them to disk. Adds mapping from original module |
|
2067 | 2187 | (env/with-compiler-env compiler-env |
2068 | 2188 | (let [compiler-stats (:compiler-stats opts) |
2069 | 2189 | all-opts (-> opts |
| 2190 | + maybe-install-node-deps! |
2070 | 2191 | add-implicit-options |
2071 | 2192 | process-js-modules)] |
2072 | 2193 | (check-output-to opts) |
|
0 commit comments