├── .gitignore ├── CHANGELOG.md ├── README.md ├── boot.properties ├── build.boot └── src └── cljsjs ├── boot_cljsjs └── packaging.clj └── impl ├── closure.clj ├── decompress.clj └── download.clj /.gitignore: -------------------------------------------------------------------------------- 1 | .boot 2 | target 3 | .nrepl-* 4 | gpg.edn 5 | out/ 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.10.5 (2020-01-31) 2 | 3 | - Update ClojureScript dependency for `validate-libs` task, should improve 4 | Java 11 compatibility 5 | 6 | ## 0.10.4 (2019-08-08) 7 | 8 | - Drop Javax use for Java 10+ compatibility 9 | 10 | ## 0.10.3 (2018-10-26) 11 | 12 | - Fix `:global-exports` 13 | 14 | ## 0.10.2 (2018-10-26) 15 | 16 | - ~~Force `:global-exports` keys and values as strings if they use `/`.~~ 17 | - Update asset-minfier ([#54](https://github.com/cljsjs/boot-cljsjs/pull/54)) 18 | 19 | ## 0.10.1 (2018-09-18) 20 | 21 | - Try to account Windows file paths in `validate-checksum` default regex 22 | - Mention Windows file paths in docstrings and regex parameters 23 | 24 | ## 0.10.0 (2017-12-12) 25 | 26 | - New `deps-cljs` options `foreign-libs` and `externs` allow creating deps.cljs 27 | with multiple entries. Regex and string formatting allows dynamically matching 28 | and creating entries. Check cljsjs/highlight for example. 29 | - Added `with-files` utility 30 | - Added `run-commands` task, can be used to run npm, Node and other tools 31 | 32 | ## 0.9.0 (2017-11-22) 33 | 34 | - Add `validate-libs` task which runs the foreign-libs and externs through 35 | ClojureScript and Closure compiler, to ensure they are correct 36 | - Improve `validate-checksum` default patterns 37 | 38 | ## 0.8.2 (2017-11-09) 39 | 40 | - Improve `validate-checksum` default patterns, and sort 41 | checksum map to keep the content deterministic 42 | 43 | ## 0.8.1 (2017-09-28) 44 | 45 | - Fix `decompress` with `tgz` files 46 | 47 | ## 0.8.0 (2017-09-28) 48 | 49 | - Add new `validate-checksums` task 50 | - Deprecate `package` `:checksum` option 51 | 52 | ## 0.7.3 (2017-09-28) 53 | 54 | - Pretty print deps.cljs 55 | - Print deps.cljs content to console 56 | 57 | ## 0.7.2 (2017-09-14) 58 | 59 | - Add `:target` option to download task 60 | 61 | ## 0.7.1 (2017-09-06) 62 | 63 | - Fix 64 | 65 | ## 0.7.0 (2017-09-03) 66 | 67 | **[compare](https://github.com/cljsjs/boot-cljsjs/compare/0.6.0...0.7.0)** 68 | 69 | - Adds support for providing `:provides` and `:global-exports` for `deps.cljs` 70 | 71 | ## 0.6.0 (1.2.2017) 72 | 73 | **[compare](https://github.com/cljsjs/boot-cljsjs/compare/0.5.2...0.6.0)** 74 | 75 | - Support single file archives in `decompress` ([#44](https://github.com/cljsjs/boot-cljsjs/issues/44)) 76 | - Remove `cljsjs.boot-cljsjs` namespace, including `from-cljsjs`, `from-jars` and 77 | `from-webjars` tasks. Alternatives: 78 | - [Using non JS Clsjs assets](https://github.com/cljsjs/packages/wiki/Non-JS-Assets) 79 | - [ring-webjars](https://github.com/weavejester/ring-webjars) 80 | - [ring-cljsjs](https://github.com/Deraen/ring-cljsjs) 81 | - Boot `sift` task 82 | 83 | ## 0.5.2 (22.6.2016) 84 | 85 | - Updated Clj-http to 2.2.0 86 | 87 | ## 0.5.1 (18.1.2016) 88 | 89 | - added `lang` option to `minify` task to allow users to set input language for to-be-minified JS 90 | 91 | ## 0.4.8 (12.5.2015) 92 | 93 | - Added general decompression task 94 | - Uses apache commons compress and should support all the most used 95 | compression and archive formats 96 | 97 | ## 0.4.7 (20.3.2015) 98 | 99 | - Use `clj-http` to download files (knows how to handle HTTP redirects) 100 | 101 | ## 0.4.0 (6.1.2015) 102 | 103 | - Removed deprecated js-import task 104 | - Added packaging-ns which provides download-task to be used when creating 105 | new cljsjs libraries. 106 | 107 | ## 0.3.1 (5.1.2015) 108 | 109 | - Main namespace is now `cljsjs.boot-cljsjs` 110 | - Removed `:package` flag 111 | - It's responsibility of other tasks to add files to resource set if needed. 112 | - E.g. boot-cljs should add .inc.js files to resource set when doing `:optimizations :none` build. 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # boot-cljsjs [![Clojars Project](https://img.shields.io/clojars/v/cljsjs/boot-cljsjs.svg)](https://clojars.org/cljsjs/boot-cljsjs) 2 | 3 | CLJSJS logo 5 | 6 | This project provides tasks to help packaging of libraries for Cljsjs. 7 | 8 | ## Using libraries 9 | 10 | Please refer to the [packages][cljsjs-packages] project for documentation: 11 | 12 | - [Creating Packages](https://github.com/cljsjs/packages/wiki/Creating-Packages) 13 | - [Updating Packages](https://github.com/cljsjs/packages/wiki/Updating-packages) 14 | 15 | # License 16 | 17 | Copyright © 2014 Martin Klepsch and Juho Teperi 18 | 19 | Distributed under the Eclipse Public License, the same as Clojure. 20 | 21 | [boot]: https://github.com/boot-clj/boot 22 | [cljsjs-packages]: https://github.com/cljsjs/packages 23 | [packaging-ns]: src/cljsjs/boot_cljsjs/packaging.clj 24 | [main-ns]: src/cljsjs/boot_cljsjs.clj 25 | [boot-cljs]: https://github.com/adzerk/boot-cljs 26 | [cljsjs-react]: https://github.com/cljsjs/packages/tree/master/react 27 | -------------------------------------------------------------------------------- /boot.properties: -------------------------------------------------------------------------------- 1 | #http://boot-clj.com 2 | #Thu Dec 24 12:31:47 EET 2015 3 | BOOT_VERSION=2.8.2 4 | -------------------------------------------------------------------------------- /build.boot: -------------------------------------------------------------------------------- 1 | (set-env! 2 | :resource-paths #{"src"} 3 | ;; Note: these deps are just for testing locally, running repl etc. 4 | ;; All Boot task also declare their depdendencies in the cljsjs.boot-cljsjs.packaging 5 | ;; namespace, so remember to update those also! 6 | :dependencies '[[org.apache.commons/commons-compress "1.14" :scope "test"] 7 | [clj-http "3.7.0" :scope "test"] 8 | [org.clojure/clojurescript "1.10.238" :scope "test"] 9 | [cljsjs/react-dom "16.1.0-0" :scope "test"] 10 | ;; Conflicts with cljs 11 | #_[asset-minifier "0.2.6" :scope "test" :exclusions []]]) 12 | 13 | (def +version+ "0.10.5") 14 | 15 | (task-options! 16 | pom {:project 'cljsjs/boot-cljsjs 17 | :version +version+ 18 | :description "Tooling to package and deploy Javascript libraries for Clojurescript projects" 19 | :url "https://github.com/cljsjs/boot-cljsjs" 20 | :scm {:url "https://github.com/cljsjs/boot-cljsjs"} 21 | :license {"EPL" "http://www.eclipse.org/legal/epl-v10.html"}}) 22 | 23 | (deftask build [] 24 | (comp 25 | (pom) 26 | (jar) 27 | (install))) 28 | 29 | (deftask dev [] 30 | (comp 31 | (watch) 32 | (build) 33 | (repl :server true))) 34 | 35 | (deftask deploy [] 36 | (comp 37 | (build) 38 | (push :repo "clojars" :gpg-sign (not (.endsWith +version+ "-SNAPSHOT"))))) 39 | -------------------------------------------------------------------------------- /src/cljsjs/boot_cljsjs/packaging.clj: -------------------------------------------------------------------------------- 1 | (ns cljsjs.boot-cljsjs.packaging 2 | {:boot/export-tasks true} 3 | (:require [boot.core :as c] 4 | [boot.pod :as pod] 5 | [boot.util :as util] 6 | [boot.task.built-in :as tasks] 7 | [clojure.edn :as edn] 8 | [clojure.java.io :as io] 9 | [clojure.pprint :as pprint] 10 | [clojure.string :as string]) 11 | (:import [java.security DigestInputStream MessageDigest] 12 | [java.util.zip ZipFile])) 13 | 14 | (defn- realize-input-stream! [s] 15 | (loop [c (.read s)] 16 | (if-not (neg? c) 17 | (recur (.read s))))) 18 | 19 | (defn hex-string [ba] 20 | (->> ba 21 | (map (partial format "%02X")) 22 | (string/join ""))) 23 | 24 | (defn- message-digest->str [^MessageDigest message-digest] 25 | (-> message-digest 26 | (.digest) 27 | (hex-string))) 28 | 29 | (comment 30 | (String/format "%02X" (into-array '(10))) 31 | (DatatypeConverter/printHexBinary (byte-array '(1 1 1 50 255))) 32 | (hex-string (byte-array '(1 1 1 50 255)))) 33 | 34 | (def checksum-deprecated-message (atom false)) 35 | 36 | (c/deftask checksum 37 | [s sum FILENAME=CHECKSUM {str str} "Check the md5 checksum of file against md5"] 38 | (c/with-pre-wrap fileset 39 | (swap! checksum-deprecated-message 40 | (fn [x] 41 | (when-not x 42 | (util/warn (str "Download :checksum option is deprecated. Instead use validate-checksums task as the " 43 | "last task in the package pipeline.\n"))) 44 | true)) 45 | (doseq [f (c/ls fileset) 46 | :let [path (c/tmp-path f)]] 47 | (when-let [checksum (some-> (get sum path) string/upper-case)] 48 | (with-open [is (io/input-stream (c/tmp-file f)) 49 | dis (DigestInputStream. is (MessageDigest/getInstance "MD5"))] 50 | (realize-input-stream! dis) 51 | (let [real (message-digest->str (.getMessageDigest dis))] 52 | (if (not= checksum real) 53 | (throw (IllegalStateException. (format "Checksum of file %s in not %s but %s" path checksum real)))))))) 54 | fileset)) 55 | 56 | (c/deftask unzip 57 | [p paths PATH #{str} "Paths in fileset to unzip"] 58 | (let [tmp (c/tmp-dir!)] 59 | (c/with-pre-wrap fileset 60 | (let [archives (filter (comp paths c/tmp-path) (c/ls fileset))] 61 | (doseq [archive archives 62 | :let [zipfile (ZipFile. (c/tmp-file archive)) 63 | entries (->> (.entries zipfile) 64 | enumeration-seq 65 | (remove #(.isDirectory %)))]] 66 | (util/info "Extracting %d files\n" (count entries)) 67 | (doseq [entry entries 68 | :let [target (io/file tmp (.getName entry))]] 69 | (io/make-parents target) 70 | (with-open [is (.getInputStream zipfile entry) ] 71 | (io/copy is target)))) 72 | (-> fileset (c/rm archives) (c/add-resource tmp) c/commit!))))) 73 | 74 | (def decompress-deps '[[org.apache.commons/commons-compress "1.14"]]) 75 | 76 | (c/deftask decompress 77 | [p paths PATH #{str} "Paths in fileset to untar" 78 | f compression-format FORMAT str "Compression format" 79 | F archive-format FORMAT str "Archive format"] 80 | (let [tmp (c/tmp-dir!) 81 | pod (future (pod/make-pod (-> (c/get-env) (update-in [:dependencies] into decompress-deps))))] 82 | (c/with-pre-wrap fileset 83 | (let [archives (filter (comp paths c/tmp-path) (c/ls fileset))] 84 | (doseq [archive archives] 85 | (pod/with-call-in @pod 86 | (cljsjs.impl.decompress/decompress-file ~(.getPath (c/tmp-file archive)) ~(.getPath tmp) 87 | {:compression-format ~compression-format 88 | :archive-format ~archive-format}))) 89 | (-> fileset (c/rm archives) (c/add-resource tmp) c/commit!))))) 90 | 91 | (def download-deps '[[clj-http "3.7.0"]]) 92 | 93 | (c/deftask download 94 | [u url URL str "The url to download" 95 | n name NAME str "Optional name for target file" 96 | c checksum CHECKSUM str "Optional MD5 checksum of downloaded file" 97 | x unzip bool "Unzip the downloaded file" 98 | X decompress bool "Decompress the archive (tar, zip, gzip, bzip...)" 99 | f compression-format FORMAT str "Manually set format for decompression (e.g. lzma can't be autodetected)." 100 | F archive-format FORMAT str "Manually set format for archive" 101 | t target PATH str "Move the downloaded file to this path"] 102 | (let [tmp (c/tmp-dir!) 103 | pod (future (pod/make-pod (-> (c/get-env) (update-in [:dependencies] into download-deps)))) 104 | fname (or name (last (string/split url #"/")))] 105 | (cond-> 106 | (c/with-pre-wrap fileset 107 | (util/info "Downloading %s\n" fname) 108 | (pod/with-call-in @pod 109 | (cljsjs.impl.download/download ~url ~(.getPath tmp) ~fname)) 110 | (-> fileset (c/add-resource tmp) c/commit!)) 111 | checksum (comp (cljsjs.boot-cljsjs.packaging/checksum :sum {fname checksum})) 112 | unzip (comp (cljsjs.boot-cljsjs.packaging/unzip :paths #{fname})) 113 | decompress (comp (cljsjs.boot-cljsjs.packaging/decompress :paths #{fname} :compression-format compression-format :archive-format archive-format)) 114 | target (comp (tasks/sift :move {(re-pattern fname) target}))))) 115 | 116 | (defn- build-legacy-deps-cljs [in-files name provides requires global-exports no-externs] 117 | (let [regular (first (c/by-ext [".inc.js"] (c/not-by-ext [".min.inc.js"] in-files))) 118 | minified (first (c/by-ext [".min.inc.js"] in-files)) 119 | externs (c/by-ext [".ext.js"] in-files)] 120 | 121 | (assert (or (seq provides) name) "Either list of provides or a name has to be provided.") 122 | (assert regular "No .inc.js file found!") 123 | 124 | (if-not no-externs 125 | (assert (first externs) "No .ext.js file(s) found!")) 126 | 127 | (let [base-lib {:file (c/tmp-path regular) 128 | :provides (or provides [name])} 129 | lib (cond-> base-lib 130 | requires (assoc :requires requires) 131 | minified (assoc :file-min (c/tmp-path minified)) 132 | global-exports (assoc :global-exports global-exports))] 133 | (merge {:foreign-libs [lib]} 134 | (if (seq externs) 135 | {:externs (mapv c/tmp-path externs)}))))) 136 | 137 | (defn- update-provides [provides matches] 138 | (into (empty provides) 139 | (map (fn [p] 140 | (apply format p matches)) 141 | provides))) 142 | 143 | (defn- update-global-exports [global-exports matches] 144 | (letfn [(replace-matches [v matches] 145 | (if (symbol? v) 146 | (if (namespace v) 147 | (symbol (apply format (namespace v) matches) 148 | (apply format (name v) matches)) 149 | (symbol (apply format (name v) matches))) 150 | (apply format v matches)))] 151 | (into (empty global-exports) 152 | (map (fn [[k v]] 153 | [(replace-matches k matches) 154 | (replace-matches v matches)]) 155 | global-exports)))) 156 | 157 | (comment 158 | (update-provides ["cljsjs.hello.%s"] ["foo"]) 159 | (update-provides ["%s" "cljsjs.hello.%2$s"] ["foo" "bar"]) 160 | ;; Keep the type 161 | ;; Strings needed for cases with multiple / 162 | (update-global-exports {"hljs/languages/%1$s" 'hljs.%1$s} ["fi"]) 163 | ;; For symbols both namespace and name is formatted 164 | (update-global-exports {'react-dom.%1$s/server.%1$s 'hljs.%1$s} ["fi"])) 165 | 166 | (defn- build-deps-cljs [in-files foreign-libs externs] 167 | (let [foreign-libs (mapcat (fn [{:keys [file file-min] :as lib}] 168 | (let [files (if file (c/by-re [file] in-files)) 169 | files-min (if file-min (c/by-re [file-min] in-files))] 170 | (assert (or (= (count files) (count files-min)) 171 | (not file) (not file-min)) 172 | "If both :file and :file-min are provided, they have to match the same number of files.") 173 | (map (fn [matched-file matched-file-min] 174 | (let [[_ & matches] (if matched-file 175 | (re-find file (c/tmp-path matched-file)) 176 | (re-find file-min (c/tmp-path matched-file-min)))] 177 | (cond-> lib 178 | (seq matches) (update :provides update-provides matches) 179 | (seq matches) (update :global-exports update-global-exports matches) 180 | matched-file (assoc :file (c/tmp-path matched-file)) 181 | matched-file-min (assoc :file-min (c/tmp-path matched-file-min))))) 182 | (if file files (repeat nil)) 183 | (if file-min files-min (repeat nil))))) 184 | foreign-libs) 185 | externs (mapcat (fn [re] 186 | (c/by-re [re] in-files)) 187 | externs)] 188 | (merge {:foreign-libs (vec foreign-libs)} 189 | (if (seq externs) 190 | {:externs (mapv c/tmp-path externs)})))) 191 | 192 | (c/deftask deps-cljs 193 | "Creates deps.cljs file based on \"template\" given, i.e. list of foreign-lib 194 | maps and extern paths. When :file, :file-min or :externs paths are regex, 195 | the pattern is used to match files in the fileset. 196 | If :file and :file-min match groups are used to format :provides names, and generate 197 | multiple entries if pattern matches many files. 198 | All files matched by :externs patterns are included. 199 | Check e.g. cljsjs/highlight for example. 200 | 201 | Note that you should take system directory separator char into account in 202 | `foreign-libs` `:file` and `:file-min` regex, i.e. use `[/\\]` instead of just `/`. 203 | 204 | Legacy version: single foreign lib can be declared using given name, 205 | provides, requires, global-exports and no-externs options. 206 | The first .inc.js file is passed as :file, similarily .min.inc.js 207 | is passed as :file-min. Files ending in .ext.js are passed as :externs." 208 | [f foreign-libs FOREIGN-LIBS edn "Template for foreign-lib entries" 209 | e externs EXTERNS edn "Extern paths" 210 | 211 | ;; Legacy options 212 | n name NAME str "Name for provided foreign lib" 213 | 214 | p provides PROV [str] "Modules provided by this lib" 215 | R requires REQ [str] "Modules required by this lib" 216 | g global-exports GLOBAL {sym sym} "" 217 | E no-externs bool "No externs are provided"] 218 | (let [tmp (c/tmp-dir!) 219 | deps-file (io/file tmp "deps.cljs") 220 | legacy-opts? (and (nil? foreign-libs) (nil? externs))] 221 | 222 | (assert (or (and (nil? provides) (nil? requires) (nil? global-exports) (nil? no-externs)) 223 | (and (nil? foreign-libs) (nil? externs))) 224 | "Use only foreign-libs and externs options, or the legacy options, not both.") 225 | 226 | (c/with-pre-wrap fileset 227 | (let [in-files (c/input-files fileset) 228 | data (if legacy-opts? 229 | (build-legacy-deps-cljs in-files name provides requires global-exports no-externs) 230 | (build-deps-cljs in-files foreign-libs externs)) 231 | s (with-out-str (pprint/pprint data))] 232 | (util/info (str "deps.cljs:\n" s)) 233 | (spit deps-file s) 234 | (-> fileset 235 | (c/add-resource tmp) 236 | c/commit!))))) 237 | 238 | (defn minifier-pod [] 239 | (pod/make-pod (assoc-in (c/get-env) [:dependencies] '[[asset-minifier "0.2.6"]]))) 240 | 241 | (c/deftask minify 242 | "Minifies .js and .css files based on their file extension 243 | 244 | Note that you should take system directory separator char into account in 245 | `in` regex, i.e. use `[/\\]` instead of just `/`. 246 | 247 | NOTE: potentially slow when called with watch or multiple times" 248 | [i in INPUT str "Path to file to be compressed" 249 | o out OUTPUT str "Path to where compressed file should be saved" 250 | l lang-in LANGUAGE_IN kw "Language of the input javascript file. Default value is ecmascript6" 251 | L lang-out LANGUAGE_OUT kw "Language of the input javascript file. Default value is ecmascript5"] 252 | (assert in "Path to input file required") 253 | (assert out "Path to output file required") 254 | (let [tmp (c/tmp-dir!) 255 | out-file (io/file tmp out) 256 | min-pod (minifier-pod)] 257 | (c/with-pre-wrap fileset 258 | (let [in-files (c/input-files fileset) 259 | in-file (c/tmp-file (first (c/by-re [(re-pattern in)] in-files))) 260 | in-path (.getPath in-file) 261 | out-path (.getPath out-file) 262 | lang-out (or lang-out lang-in)] 263 | (util/info "Minifying %s\n" (.getName in-file)) 264 | (io/make-parents out-file) 265 | (cond 266 | (. in-path (endsWith "js")) 267 | (pod/with-eval-in min-pod 268 | (require 'asset-minifier.core) 269 | (asset-minifier.core/minify-js ~in-path ~out-path (cond-> {} 270 | ~lang-in (assoc :language-in ~lang-in) 271 | ~lang-out (assoc :language-out ~lang-out)))) 272 | (. in-path (endsWith "css")) 273 | (pod/with-eval-in min-pod 274 | (require 'asset-minifier.core) 275 | (asset-minifier.core/minify-css ~in-path ~out-path))) 276 | (-> fileset 277 | (c/add-resource tmp) 278 | c/commit!))))) 279 | 280 | (c/deftask replace-content 281 | "Replaces portion of a file matching some pattern with some value. 282 | 283 | Note that you should take system directory separator char into account in 284 | `in` regex, i.e. use `[/\\]` instead of just `/`." 285 | [i in INPUT str "Path to file to be modified" 286 | m match MATCH regex "Pattern to match" 287 | v value VALUE str "Value to replace with" 288 | o out OUTPUT str "Path to where modified file should be saved"] 289 | (assert in "Path to input file required") 290 | (let [tmp (c/tmp-dir!) 291 | out-file (io/file tmp (or out in))] 292 | (c/with-pre-wrap fileset 293 | (let [in-files (c/input-files fileset) 294 | in-file (c/tmp-file (first (c/by-re [(re-pattern in)] in-files))) 295 | in-path (.getPath in-file)] 296 | (util/info "Replacing content of %s\n" (.getName in-file)) 297 | (io/make-parents out-file) 298 | (spit out-file (string/replace (slurp in-file) match value)) 299 | (-> fileset 300 | (c/add-resource tmp) 301 | c/commit!))))) 302 | 303 | (def checksum-re #"^cljsjs[/\\].*\.inc\.js$") 304 | 305 | (comment 306 | (re-matches checksum-re "cljsjs/foo/common/foo.inc.js") 307 | (re-matches checksum-re "cljsjs/foo/common/modules/foo.inc.js") 308 | (re-matches checksum-re "cljsjs/foo/common/foo.ext.js") 309 | (re-matches checksum-re "cljsjs/common/foo.inc.js") 310 | (re-matches checksum-re "cljsjs\\common\\foo.inc.js") 311 | ) 312 | 313 | (c/deftask validate-checksums 314 | "Checks files (by default Cljsjs JS files) 315 | against `boot-cljsjs-checksums.edn` files in 316 | working directory, if it exists. If there are differences, 317 | asks the user to validate changes, or in CI, throw error. 318 | New checksum are written to the file. 319 | 320 | Default pattern to check is \"^cljsjs[/\\].*\\.inc\\.js$\". 321 | 322 | Note that you should take system directory separator char into account in 323 | `patterns` regex, i.e. use `[/\\]` instead of just `/`. 324 | 325 | The checksum file should be commited to git." 326 | [_ patterns PATTERN [regex] "File patterns to check the checksums for"] 327 | (let [patterns (if (seq patterns) 328 | patterns 329 | [checksum-re])] 330 | (fn [next-handler] 331 | (fn [fileset] 332 | (let [files (->> fileset 333 | c/input-files 334 | (c/by-re patterns)) 335 | checksums-file (io/file "boot-cljsjs-checksums.edn") 336 | current-checksums (if (.exists checksums-file) 337 | (edn/read-string (slurp checksums-file))) 338 | new-checksums (reduce (fn [m f] 339 | (let [checksum (with-open [is (io/input-stream (c/tmp-file f)) 340 | dis (DigestInputStream. is (MessageDigest/getInstance "MD5"))] 341 | (realize-input-stream! dis) 342 | (message-digest->str (.getMessageDigest dis)))] 343 | (assoc m (c/tmp-path f) checksum))) 344 | (sorted-map) 345 | files) 346 | ci? (= "true" (System/getenv "CIRCLECI"))] 347 | (if (and current-checksums (not= current-checksums new-checksums)) 348 | (do 349 | (util/info (str "\nCurrent checksums:\n" (with-out-str (pprint/pprint current-checksums) "\n"))) 350 | (util/info (str "\nNew checksums:\n" (with-out-str (pprint/pprint new-checksums)) "\n")) 351 | (if-not ci? 352 | (util/warn "Checksums have changed, update? [yn] ")) 353 | (let [answer (and (not ci?) (.readLine (System/console)))] 354 | (if (not= "y" answer) 355 | (throw (ex-info "Checksums do not match" {}))))) 356 | (util/info "Checksums match\n")) 357 | (if (not= current-checksums new-checksums) 358 | (util/warn "Checksum file boot-cljsjs-checksums.edn updated, please commit this file to Git.\n")) 359 | (spit checksums-file (with-out-str (pprint/pprint new-checksums))) 360 | (next-handler fileset)))))) 361 | 362 | (defn cljs-pod [] 363 | (pod/make-pod (-> (c/get-env) 364 | (update-in [:dependencies] into '[[org.clojure/clojurescript "1.10.238"]]) 365 | (assoc :resource-paths #{} 366 | :directories #{})))) 367 | 368 | (c/deftask validate-libs 369 | [] 370 | (let [pod (cljs-pod)] 371 | (fn [next-handler] 372 | (fn [fileset] 373 | (util/info "Running externs and foreign-libs through Closure to validate them...\n") 374 | 375 | ;; React and other multi package builds probably have conflicting deps.cljs files, 376 | ;; conflicts can be avoided by building classpath manually, no directories, 377 | ;; just the built jars in addition to dependencies to the classpath. 378 | (let [jars (->> fileset 379 | (c/output-files) 380 | (c/by-ext [".jar"]))] 381 | (assert (seq jars) "Validate-libs needs to be run after the jar has been built.") 382 | (doseq [jar jars] 383 | (pod/with-call-in pod 384 | (boot.pod/add-classpath ~(.getPath (c/tmp-file jar)))))) 385 | 386 | (pod/with-call-in pod 387 | (cljsjs.impl.closure/validate-externs!)) 388 | 389 | (next-handler fileset))))) 390 | 391 | (c/deftask validate 392 | [] 393 | (comp 394 | (validate-libs) 395 | (validate-checksums))) 396 | 397 | ;; TODO: Should eventually be included in boot.core 398 | (defn with-files 399 | "Runs middleware with filtered fileset and merges the result back into complete fileset." 400 | [p middleware] 401 | (fn [next-handler] 402 | (fn [fileset] 403 | (let [merge-fileset-handler (fn [fileset'] 404 | (next-handler (c/commit! (assoc fileset :tree (merge (:tree fileset) (:tree fileset')))))) 405 | handler (middleware merge-fileset-handler) 406 | fileset (assoc fileset :tree (reduce-kv 407 | (fn [tree path x] 408 | (if (p x) 409 | (assoc tree path x) 410 | tree)) 411 | (empty (:tree fileset)) 412 | (:tree fileset)))] 413 | (handler fileset))))) 414 | 415 | (c/deftask run-commands 416 | "Runs given commands with fileset checked out as working directory, and commits the working directory to fileset 417 | after running commands." 418 | [c commands COMMAND edn "Commands"] 419 | (let [tmp (c/tmp-dir!)] 420 | (c/with-pre-wrap fileset 421 | (doseq [f (->> fileset c/input-files) 422 | :let [target (io/file tmp (c/tmp-path f))]] 423 | (io/make-parents target) 424 | (io/copy (c/tmp-file f) target)) 425 | (binding [util/*sh-dir* (str tmp)] 426 | (doseq [command commands] 427 | ((apply util/sh command)))) 428 | (-> fileset (c/add-resource tmp) c/commit!)))) 429 | -------------------------------------------------------------------------------- /src/cljsjs/impl/closure.clj: -------------------------------------------------------------------------------- 1 | (ns cljsjs.impl.closure 2 | (:require [cljs.closure :as closure] 3 | [boot.util :as util] 4 | [clojure.pprint :as pprint])) 5 | 6 | (defn validate-externs! [] 7 | (let [deps (closure/get-upstream-deps*)] 8 | (util/info (str "\nFound libs and externs:\n" (with-out-str (pprint/pprint deps)) "\n")) 9 | (closure/build '[(def ^:export hello "OK")] 10 | {:optimizations :advanced 11 | :closure-warnings {:check-types :warning}}))) 12 | 13 | (comment 14 | (validate-externs!)) 15 | -------------------------------------------------------------------------------- /src/cljsjs/impl/decompress.clj: -------------------------------------------------------------------------------- 1 | (ns cljsjs.impl.decompress 2 | (:require [boot.util :as util] 3 | [clojure.java.io :as io]) 4 | (:import [org.apache.commons.compress.compressors CompressorStreamFactory FileNameUtil ] 5 | [org.apache.commons.compress.archivers ArchiveStreamFactory ArchiveInputStream])) 6 | 7 | (def file-name-util (delay (FileNameUtil. {".gz" "" 8 | ".xz" "" 9 | ".bzip2" "" 10 | ".bz2" "" 11 | ".tgz" ".tar" 12 | ".tbz2" ".tar" 13 | ".tlz" ".tar"} 14 | ""))) 15 | 16 | (defn not-decompressable? [e] 17 | (re-find #"No Compressor found for the stream signature\." (.getMessage e))) 18 | 19 | (defn try-decompress-stream [is & [{:keys [format]}]] 20 | (try 21 | (cond-> (CompressorStreamFactory.) 22 | format (.createCompressorInputStream format is true) ;; for concatenated bzip2, gzip and xz 23 | (not format) (.createCompressorInputStream is)) 24 | (catch Exception e 25 | (if (not-decompressable? e) 26 | is 27 | (throw e))))) 28 | 29 | (defn mark-not-supported? [e] 30 | (re-find #"Mark is not supported\." (.getMessage e))) 31 | 32 | (defn unpackage-stream [is & [{:keys [format]}]] 33 | (try 34 | (cond-> (ArchiveStreamFactory.) 35 | format (.createArchiveInputStream format is) 36 | (not format) (.createArchiveInputStream is)) 37 | (catch Exception e 38 | (if (mark-not-supported? e) 39 | is 40 | (throw e))))) 41 | 42 | (defn decompress-file [in out-dir & [{:keys [compression-format archive-format]}]] 43 | (with-open [is (-> (io/input-stream in) 44 | (try-decompress-stream {:format compression-format}) 45 | (unpackage-stream {:format archive-format}))] 46 | (if (instance? ArchiveInputStream is) 47 | (let [count (loop [i 0 48 | entry (.getNextEntry is)] 49 | (if entry 50 | (if-not (.isDirectory entry) 51 | (let [target (io/file out-dir (.getName entry))] 52 | (io/make-parents target) 53 | ;; After .getNextEntry the stream points to the specific archive entry 54 | (io/copy is target) 55 | (recur (inc i) (.getNextEntry is))) 56 | (recur i (.getNextEntry is))) 57 | i))] 58 | (util/info (format "Extracted %d files\n" count))) 59 | (let [target (io/file out-dir (.getUncompressedFilename @file-name-util (.getName (io/file in))))] 60 | (io/make-parents target) 61 | (io/copy is target) 62 | (util/info (format "Extracted 1 file\n")))))) 63 | -------------------------------------------------------------------------------- /src/cljsjs/impl/download.clj: -------------------------------------------------------------------------------- 1 | (ns cljsjs.impl.download 2 | (:require [clojure.java.io :as io] 3 | [clj-http.client :as http])) 4 | 5 | (defn download [url out-dir fname] 6 | (let [target (io/file out-dir fname)] 7 | (io/make-parents target) 8 | (with-open [is (:body (http/get url {:as :stream}))] 9 | (io/copy is target)))) 10 | --------------------------------------------------------------------------------