├── .gitignore ├── CHANGES.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── boot.properties ├── build.boot ├── circle.yml ├── docs ├── cljs.edn.md ├── compiler-options.md └── integration-with-other-tasks.md ├── src └── adzerk │ ├── boot_cljs.clj │ └── boot_cljs │ ├── impl.clj │ ├── js_deps.clj │ ├── middleware.clj │ └── util.clj └── test ├── adzerk ├── boot_cljs │ ├── impl_test.clj │ ├── middleware_test.clj │ └── util_test.clj └── boot_cljs_test.clj └── demo ├── core.cljs ├── index.cljs.edn ├── index.html ├── other.cljs ├── other.cljs.edn └── other.html /.gitignore: -------------------------------------------------------------------------------- 1 | ### BOOT ####################################################################### 2 | 3 | /.boot/ 4 | 5 | ### LEININGEN ################################################################## 6 | 7 | .lein-* 8 | 9 | ### MAVEN (include pom.xml in /base) ########################################### 10 | 11 | *.jar 12 | *.war 13 | pom.xml 14 | pom.xml.asc 15 | 16 | ### NREPL ###################################################################### 17 | 18 | .repl-* 19 | .nrepl-* 20 | 21 | ### JAVA ####################################################################### 22 | 23 | /hs_err_pid*.log 24 | 25 | ### OSX ######################################################################## 26 | 27 | .DS_Store 28 | 29 | ### EMACS ###################################################################### 30 | 31 | [#]*[#] 32 | 33 | ### VIM ######################################################################## 34 | 35 | *.swn 36 | *.swo 37 | *.swp 38 | 39 | ### PROJECT #################################################################### 40 | 41 | /zzz/ 42 | /target/ 43 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## 2.1.5 (2018-10-25) 2 | 3 | - Try handling exceptions in a way that serialization errors are less likely 4 | - Handle exceptions where ex-data can't be serialized 5 | - This should print real exceptions for problematic cases, but the exceptions won't be 6 | formatted perfectly. 7 | 8 | ## 2.1.4 (2017-09-25) 9 | 10 | - Fix setting `:modules` `:output-to` automatically ([#174](https://github.com/boot-clj/boot-cljs/issues/174)) 11 | - Fix using Node packages and native requires with Node.js ([#175](https://github.com/boot-clj/boot-cljs/issues/175)) 12 | - Uses `java.util.Base64` instead of `javax.xml.bind.DatatypeConverter`, to support Java 9 ([#178](https://github.com/boot-clj/boot-cljs/issues/178)) 13 | - Breaks Java 1.7 14 | - Somehow fixes NPE when using `:infer-externs` (not sure what caused this...) ([#172](https://github.com/boot-clj/boot-cljs/issues/172)) 15 | 16 | **[compare](https://github.com/adzerk-oss/boot-cljs/compare/2.1.3...master)** 17 | 18 | ## 2.1.3 (2017-08-29) 19 | 20 | - Fix `:source-map` option logic to allow disabling the option when doing optimized build. 21 | - If `:source-map` is string (i.e. path), use value as is, instead of setting new value based on `:output-to`. 22 | 23 | **[compare](https://github.com/adzerk-oss/boot-cljs/compare/2.1.2...2.1.3)** 24 | 25 | ## 2.1.2 (2017-08-17) 26 | 27 | - Fix missing exception message 28 | - Handle new exception location format 29 | - Escape `%` in exception messages if using Boot <2.7.2 ([Boot fix](https://github.com/boot-clj/boot/commit/c07b08751ad195e6e6349cb7c2f826c95a8e8186)) 30 | 31 | **[compare](https://github.com/adzerk-oss/boot-cljs/compare/2.1.1...2.1.2)** 32 | 33 | ## 2.1.1 (29.7.2017) 34 | 35 | - Fix `:modules` `:output-to` handling 36 | 37 | **[compare](https://github.com/adzerk-oss/boot-cljs/compare/2.1.0...2.1.1)** 38 | 39 | ## 2.1.0 (29.7.2017) 40 | 41 | - Require `:main` namespace in our shim ns requires ([#139](https://github.com/boot-clj/boot-cljs/issues/139)) 42 | - Don't set `:source-map-path` automatically ([#157](https://github.com/boot-clj/boot-cljs/pull/157), [#136](https://github.com/boot-clj/boot-cljs/issues/136)) 43 | - Use relative path of `.cljs.edn` for asset-path instead of name of the file ([#156](https://github.com/boot-clj/boot-cljs/pull/156), [#152](https://github.com/boot-clj/boot-cljs/issues/152)) 44 | - Use provided `:output-dir` and `:output-to` and set default values based on path of `.cljs.edn`, like previously ([#104](https://github.com/boot-clj/boot-cljs/issues/104)) 45 | - Drop support for `.cljs.edn` `:modules` option (with warning) and instead handle `:output-to` paths in 46 | `:modules` under `:compiler-options` 47 | - If project dependencies have changed, add new dependencies to Boot-cljs pods ([#95](https://github.com/boot-clj/boot-cljs/issues/95)) 48 | - E.g. `(boot.core/set-env! :dependencies #(conj % '[com.andrewmcveigh/cljs-time "0.4.0"]))` 49 | can be used to add `cljs-time` dependency and Boot-cljs will find this without restarting 50 | the whole Boot process. 51 | - Add validation for Boot-cljs `.cljs.edn` options ([#148](https://github.com/boot-clj/boot-cljs/issues/148)) 52 | - During `:advanced` compilation set the `:output-wrapper` option to `true`. 53 | This is not the ClojureScript compiler default but more friendly for regular use. 54 | This makes `boot-cljs` behave similar to `lein-cljsbuild` as well. ([#64](https://github.com/boot-clj/boot-cljs/issues/64)) 55 | 56 | **[compare](https://github.com/adzerk-oss/boot-cljs/compare/2.0.0...2.1.0)** 57 | 58 | ## 2.0.0 (10.3.2017) 59 | 60 | - Stop following ClojureScript compiler version number 61 | - Boot-cljs uses the [public compiler API](https://github.com/clojure/clojurescript/blob/master/src/main/clojure/cljs/build/api.clj) 62 | - The API is static and should not change between versions 63 | - ClojureScript compiler versions since [1.7.28](https://github.com/clojure/clojurescript/blob/master/changes.md#1728) 64 | which included change [CLJS-1367](https://github.com/clojure/clojurescript/commit/9e662c3ea5b9f536f303e9084415e988bf5d0878), should work. 65 | - Expose ClojureScript compiler options in `.cljs.edn` Boot fileset metadata for 66 | others tasks (e.g. boot-reload) 67 | - Hide stacktraces for Cljs compiler exceptions (reader exception, analysis error) 68 | 69 | **[compare](https://github.com/adzerk-oss/boot-cljs/compare/1.7.228-2...2.0.0)** 70 | 71 | ## 1.7.228-2 (18.10.2016) 72 | 73 | **[compare](https://github.com/adzerk-oss/boot-cljs/compare/1.7.228-1...1.7.228-2)** 74 | 75 | - Changed the order compiler-options are merged. Task-options are now merged on top of 76 | options from .cljs.edn. This allows overriding options per build task invocation. 77 | 78 | ## 1.7.228-1 (11.1.2016) 79 | 80 | **[compare](https://github.com/adzerk-oss/boot-cljs/compare/1.7.228-0...1.7.228-1)** 81 | 82 | - Fixed optimization none builds ([#115](https://github.com/adzerk-oss/boot-cljs/pull/115)) 83 | 84 | ## 1.7.228-0 (10.1.2016) 85 | 86 | **[compare](https://github.com/adzerk-oss/boot-cljs/compare/1.7.170-3...1.7.228-0)** 87 | 88 | - Update default ClojureScript release to 1.7.228 89 | - Improved exception messages in some cases. ([#110](https://github.com/adzerk-oss/boot-cljs/issues/110)) 90 | - Added *experimental* support for Closure Modules using `:modules` option on 91 | `.cljs.edn` files. ([#113](https://github.com/adzerk-oss/boot-cljs/issues/113)) 92 | 93 | ## 1.7.170-3 (8.11.2015) 94 | 95 | **[compare](https://github.com/adzerk-oss/boot-cljs/compare/1.7.170-2...1.7.170-3)** 96 | 97 | - Fixed critical bug introduced in the previous release 98 | - Second compilation broke the main shim 99 | 100 | ## 1.7.170-2 (8.11.2015) 101 | 102 | **[compare](https://github.com/adzerk-oss/boot-cljs/compare/1.7.170-1...1.7.170-2)** 103 | 104 | - Read `.cljs.edn` always instead of only once 105 | - This allows editing `:require` and `:compiler-options` in without 106 | restarting the build process. 107 | 108 | ## 1.7.170-1 (6.11.2015) 109 | 110 | **[compare](https://github.com/adzerk-oss/boot-cljs/compare/1.7.166-1...1.7.170-1)** 111 | 112 | - Update to match latest ClojureScript release 113 | 114 | ## 1.7.166-1 (4.11.2015) 115 | 116 | - Removed workaround related to recompile-dependents which is made unncessary 117 | by [CLJS-1437](https://github.com/clojure/clojurescript/commit/409d1eca4fcf776be1f7b28f759d6f36f7a83ec8). 118 | - Fixed source-map options when `optimizations` is not explicitly set. 119 | 120 | ## 1.7.48-6 (16.10.2015) 121 | 122 | - Fixed problem where default main (no `.cljs.edn` files) tried to compile 123 | `deps.cljs` files in the fileset. 124 | 125 | ## 1.7.48-5 (4.10.2015) 126 | 127 | - Warning messages now display file path without tmp-dir path 128 | - Adds information about warnings and exceptions to fileset so that 129 | [boot-reload](https://github.com/adzerk-oss/boot-reload) can use the 130 | information to display HUD. 131 | - Cljs exceptions are now always rethrown, this is to allow other tasks to 132 | catch the exceptions and to see when build failed 133 | 134 | ## 1.7.48-4 (19.9.2015) 135 | 136 | - Improved ClojureScript dependency check logic 137 | 1. If ClojureScript dependency is specific in build.boot, use that 138 | 2. If no dependency is found but ClojureScript is available in classpath, 139 | display a warning 140 | 3. If no dependency is found and ClojureScript is not available, add dependency 141 | 142 | ## 1.7.48-3 (24.8.2015) 143 | 144 | - **BREAKING**: Changed the way `:ids` option selects the files. Instead of looking only at the basename of file, it looks at the paths. E.g. to use `src/js/main.cljs.edn` use `--ids js/main` instead of just `--ids main`. 145 | - Fixed advanced optimization caused by generated main namespace not being on 146 | classpath 147 | - Fixed where advanced optimization was broken with multiple builds 148 | [#92](https://github.com/adzerk-oss/boot-cljs/pull/92). 149 | 150 | ## 1.7.48-2 (20.8.2015) 151 | 152 | - Display reader exceptions in pretty format 153 | - Fix [#89](https://github.com/adzerk-oss/boot-cljs/issues/89). 154 | 155 | ## 1.7.48-1 (20.8.2015) 156 | 157 | - Fix Cljs version assertion 158 | - Add Cljs output files as resources so they'll be available in classpath 159 | 160 | ## 1.7.48-0 (20.8.2015) 161 | 162 | - Support reloading macro namespaces 163 | - Use ClojureScript API 164 | - Builds specified by `.cljs.edn` files are now run parallel in separated 165 | environments. 166 | - Add `:ids` option to select used `.cljs.edn` files 167 | - Bug fixes 168 | 169 | ## 0.0-3308-0 (13.6.2015) 170 | 171 | - Updated to latest ClojureScript version 172 | 173 | ## 0.0-3269-4 (13.6.2015) 174 | 175 | - Instead automatically adding or updating Clojure 1.7 dependency, 176 | display a warning and link to [Boot wiki](https://github.com/boot-clj/boot/wiki/Setting-Clojure-version) 177 | page about setting the Clojure version. 178 | 179 | ## 0.0-3269-3 (13.6.2015) 180 | 181 | - Broken release 182 | 183 | ## 0.0-3269-2 (30.5.2015) 184 | 185 | - Path->js fix fir cljc namespaces 186 | 187 | ## 0.0-3269-1 (23.5.2015) 188 | 189 | - Automatically add Clojure 1.7 dependency to Cljs pod if project 190 | is not yet using it. 191 | 192 | ## 0.0-3269-0 (17.5.2015) 193 | 194 | - **Probably breaks stuff**: Updated to latest ClojureScript compiler 195 | - **Might break stuff**: Uses shim created by cljs compiler 196 | - **Breaking**: Removed \*.inc.js, \*.ext.js, \*.lib.js handling 197 | - Most of use cases are covered by [cljsjs](http://cljsjs.github.io/) 198 | - If you still need to add local js files to the build, you can add deps.cljs 199 | file to your local project 200 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | Contributions are welcome. 4 | 5 | Please file bug reports and feature requests to https://github.com/adzerk-oss/boot-cljs/issues. 6 | 7 | ## Hacking 8 | 9 | Check [development guide](https://github.com/adzerk-oss/boot-cljs/wiki/Development) on wiki for how 10 | to build and test boot-cljs locally. 11 | 12 | ## Making changes 13 | 14 | * Fork the repository on Github 15 | * Create a topic branch from where you want to base your work (usually the master branch) 16 | * Check the formatting rules from existing code (no trailing whitepace, mostly default indentation) 17 | * Ensure any new code is well-tested, and if possible, any issue fixed is covered by one or more new tests 18 | * Verify that all tests pass using ```boot run-tests``` and test the changes manually as our test suite is not very comprehesive 19 | * Push your code to your fork of the repository 20 | * Make a Pull Request 21 | 22 | ## Commit messages 23 | 24 | 1. Separate subject from body with a blank line 25 | 2. Limit the subject line to 50 characters 26 | 3. Capitalize the subject line 27 | 4. Do not end the subject line with a period 28 | 5. Use the imperative mood in the subject line 29 | - "Add x", "Fix y", "Support z", "Remove x" 30 | 6. Wrap the body at 72 characters 31 | 7. Use the body to explain what and why vs. how 32 | 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor tocontrol, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of Washington and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # boot-cljs [![Clojars Project](https://img.shields.io/clojars/v/adzerk/boot-cljs.svg)](https://clojars.org/adzerk/boot-cljs) [![Circle CI](https://circleci.com/gh/boot-clj/boot-cljs.svg?style=shield)](https://circleci.com/gh/boot-clj/boot-cljs) [![Downloads](https://jarkeeper.com/adzerk/boot-cljs/downloads.svg)](https://jarkeeper.com/adzerk/boot-cljs) 2 | 3 | [Boot](http://boot-clj.com/) task to compile ClojureScript applications. 4 | 5 | * Provides the `cljs` task for compiling ClojureScript to JavaScript 6 | * Requirements 7 | * Supports [ClojureScript versions](https://github.com/clojure/clojurescript/blob/master/changes.md) since 1.7.28 8 | * Boot version 2.6.0 but 2.7.0 is recommended for better error reporting 9 | * Java 8+ 10 | * Docs 11 | * [.cljs.edn files](docs/cljs.edn.md) 12 | * [Compiler options](docs/compiler-options.md), Boot-cljs modified and automatically sets some options 13 | * **Related projects:** [boot-reload](https://github.com/adzerk-oss/boot-reload) and [boot-cljs-repl](https://github.com/adzerk-oss/boot-cljs-repl) 14 | 15 | ## Quick start 16 | 17 | Add ClojureScript and `boot-cljs` to your `build.boot` dependencies and `require` the namespace: 18 | 19 | ```clj 20 | (set-env! :dependencies '[[adzerk/boot-cljs "X.Y.Z" :scope "test"]]) 21 | (require '[adzerk.boot-cljs :refer [cljs]]) 22 | ``` 23 | 24 | You can see the options available on the command line: 25 | 26 | ```bash 27 | boot cljs --help 28 | ``` 29 | 30 | Or the same in the REPL: 31 | 32 | ```clj 33 | boot.user=> (doc cljs) 34 | ``` 35 | 36 | ### Further Reading 37 | 38 | - [boot-cljs-example](https://github.com/adzerk/boot-cljs-example) - An example project with a local web server, CLJS REPL, and live-reload. 39 | - [Saapas example project](https://github.com/Deraen/saapas) - Opinionated example project for Boot. 40 | - [Tenzing project template](https://github.com/martinklepsch/tenzing) - ClojureScript application template. 41 | - [Modern ClojureScript](https://github.com/magomimmo/modern-cljs) - Series of tutorials for ClojureScript. Uses Boot. 42 | 43 | 44 | ## License 45 | 46 | Copyright © 2014 Adzerk
47 | Copyright © 2015-2017 Juho Teperi 48 | 49 | Distributed under the Eclipse Public License either version 1.0 or (at 50 | your option) any later version. 51 | -------------------------------------------------------------------------------- /boot.properties: -------------------------------------------------------------------------------- 1 | #https://github.com/boot-clj/boot 2 | #Wed Jul 08 20:50:42 EEST 2015 3 | BOOT_CLOJURE_VERSION=1.8.0 4 | BOOT_VERSION=2.7.1 5 | -------------------------------------------------------------------------------- /build.boot: -------------------------------------------------------------------------------- 1 | (set-env! 2 | :resource-paths #{"src"} 3 | :source-paths #{"test"} 4 | :dependencies '[[org.clojure/clojure "1.8.0" :scope "provided"] 5 | [adzerk/boot-test "1.1.0" :scope "test"] 6 | [pandeiro/boot-http "0.7.0" :scope "test"] 7 | [org.clojure/clojurescript "1.7.228" :scope "test"] 8 | [ns-tracker "0.3.1" :scope "test"]]) 9 | 10 | (require '[adzerk.boot-test] 11 | '[adzerk.boot-cljs :refer [cljs]] 12 | '[pandeiro.boot-http :refer [serve]]) 13 | 14 | (def +version+ "2.1.5") 15 | 16 | (task-options! 17 | pom {:project 'adzerk/boot-cljs 18 | :version +version+ 19 | :description "Boot task to compile ClojureScript applications." 20 | :url "https://github.com/adzerk/boot-cljs" 21 | :scm {:url "https://github.com/adzerk/boot-cljs"} 22 | :license {"EPL" "http://www.eclipse.org/legal/epl-v10.html"}}) 23 | 24 | (ns-unmap *ns* 'test) 25 | 26 | (deftask test 27 | [O optimizations LEVEL kw "Compiler optimization level." 28 | j junit-output-to JUNIT str "Test report destination."] 29 | (comp (serve) 30 | (cljs :optimizations (or optimizations :whitespace)) 31 | (adzerk.boot-test/test 32 | :namespaces #{'adzerk.boot-cljs-test 'adzerk.boot-cljs.util-test} 33 | :junit-output-to junit-output-to))) 34 | 35 | (deftask build [] 36 | (comp 37 | (pom) 38 | (jar) 39 | (install))) 40 | 41 | (deftask dev [] 42 | (comp 43 | (watch) 44 | (build) 45 | (repl :server true))) 46 | 47 | (deftask deploy [] 48 | (comp 49 | (build) 50 | (push :repo "clojars" :gpg-sign (not (.endsWith +version+ "-SNAPSHOT"))))) 51 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | java: 3 | version: oraclejdk8 4 | environment: 5 | _JAVA_OPTIONS: "-Xms512m -Xmx1024m" 6 | dependencies: 7 | cache_directories: 8 | - "~/.boot/cache/lib" 9 | - "~/.boot/cache/bin" 10 | pre: 11 | - curl -L https://github.com/boot-clj/boot-bin/releases/download/latest/boot.sh -o ~/bin/boot 12 | - chmod +x ~/bin/boot 13 | override: 14 | - boot show -d 15 | test: 16 | override: 17 | - boot test -j junit 18 | - mkdir -p $CIRCLE_TEST_REPORTS/junit/ 19 | - find . -type f -regex ".*/target/junit/.*xml" -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \; 20 | -------------------------------------------------------------------------------- /docs/cljs.edn.md: -------------------------------------------------------------------------------- 1 | # `.cljs.edn` files 2 | 3 | `.cljs.edn` files are configuration files for Boot-cljs which allow options 4 | being set in a separate file instead of in `build.boot` task call. 5 | 6 | There are several benefits to providing configuration in separate files: 7 | 8 | Using separate file is that the options can be changed 9 | without restarting the whole Boot process. 10 | 11 | It is possible to calculate some options based on the 12 | relative path of `.cljs.edn` file inside the fileset. If user doesn't 13 | provide `:output-dir` or `:output-to` options, the values are set up using 14 | relative path of the `.cljs.edn` file. 15 | 16 | Perhaps the most important feature of `.cljs.edn` files is that other tasks 17 | can modify this configuration. For example Boot-reload and Boot-cljs-repl 18 | use this set up their code using `:require`. 19 | 20 | Boot-cljs is able to run several Cljs compiler parallel if multiple 21 | `.cljs.edn` files are present. 22 | 23 | > NOTE: This might change. 24 | > Cljs compiler can now work parallel itself, if `:parallel-build` option is enabled. 25 | > For cases there the application should be split to several JS files, `:modules` 26 | > is probably better fit as that way the shared code is not duplicated to every 27 | > output file. 28 | > For cases where separate builds, like app and tests, are needed, it might be 29 | > simpler to just run two cljs tasks. 30 | 31 | ## `:compiler-options` 32 | 33 | [Compiler options](./compiler-options.md) can be provided here. 34 | 35 | ## `:require` 36 | 37 | Collection of namespaces to require in Boot-cljs generated main namespace. 38 | 39 | ## `:init-fns` 40 | 41 | Collection of functions (namespaced symbols) to call from Boot-cljs main namespace, 42 | called after all the `:require` namespaces are loaded. 43 | Namespaces of these symbols are automatically added to `:require`. 44 | -------------------------------------------------------------------------------- /docs/compiler-options.md: -------------------------------------------------------------------------------- 1 | # Boot-cljs compiler options 2 | 3 | Boot-cljs does some special handling with some of [ClojureScript compiler options](https://clojurescript.org/reference/compiler-options), 4 | these cases are either related to Boot temp dir and fileset architecture, or to 5 | support some additional features of Boot-cljs. 6 | 7 | Example of first case is `:output-to` and `:output-dir`. These filepaths need to 8 | be under temp directory managed by Boot-cljs. 9 | 10 | Example of second case is `:main` namespace. Boot-cljs writes it's own main namespace 11 | because we need to support multiple `:require` namespaces from `.cljs.edn` files. This 12 | is important because other tools ([boot-reload](https://github.com/adzerk-oss/boot-reload)) 13 | use `:require` to initialize themselves. 14 | 15 | ## Merge order 16 | 17 | 1. `:compiler-options` in `.cljs.edn` file 18 | 2. `:compiler-options` task option 19 | 3. `:optimizations` and `:source-map` task options 20 | 4. options automatically set by boot-cljs (`:output-dir`, `:output-to`, `:main`) 21 | 22 | TODO: add support for merging `:closure-defines` and other map options [#135](https://github.com/boot-clj/boot-cljs/issues/135) 23 | 24 | ## `:output-dir` & `:output-to` 25 | 26 | If these options are set, path of Boot-cljs temp dir is prepended into these. 27 | 28 | If values are not set, default value is created based on relative path of the `.cljs.edn` file. 29 | 30 | - `resources/js/main.cljs.edn` 31 | - `:resource-paths #{"resources"}` 32 | - relative path of the file inside fileset is `js/main.cljs.edn` 33 | - `:output-dir` is set to `js/main.out` 34 | - `:output-to` is set to `js/main.js` 35 | 36 | ## `:main` 37 | 38 | This value is always set to namespace generated by Boot-cljs, e.g. `boot.cljs.main31649`, 39 | where the number is generated with `gensym`. 40 | 41 | If user provides `:main` option, the namespace is appended to `:require` `.cljs.edn` 42 | option, and is thus required by the Boot-cljs main namespace. This should usually 43 | work nearly the same as without Boot-cljs main namespace, but this keeps the support 44 | for Boot-reload and others to use `:require`. 45 | 46 | Note: It would be possible for Boot-reload to use `:preloads` instead of `:require`, but 47 | that would change the loading order and cause problems with current implementation 48 | of Boot-reload (check [#143](https://github.com/boot-clj/boot-cljs/pull/143) for some 49 | information.) 50 | 51 | ## `:source-map` 52 | 53 | This option can be provided task option or as compiler-option. If `:optimizations` is 54 | not `:none` and `:modules` is not used, `:source-map` is automatically set to 55 | `:output-to` with `.map` prepended. 56 | 57 | ## `:modules` 58 | 59 | Should work similar to `:output-to`. If user provides a value, it is prepended with 60 | path to Boot-cljs temp dir, else default value is created based on relative path of `.cljs.edn` 61 | file and name of the module. 62 | 63 | ## `:output-wrapper` 64 | 65 | When doing `:optimization :advanced` build, if `:output-wrapper` is `nil` it is set enabled. This follows Lein-cljsbuild. 66 | 67 | See more: [#64](https://github.com/boot-clj/boot-cljs/issues/64) 68 | -------------------------------------------------------------------------------- /docs/integration-with-other-tasks.md: -------------------------------------------------------------------------------- 1 | # Integration with other tasks 2 | 3 | ## `.cljs.edn` 4 | 5 | Other tasks can modify the Cljs build options by modifying `.cljs.edn` files 6 | in the fileset. For example boot-reload and boot-cljs-repl add their 7 | client namespaces to `:require` vector in `.cljs.edn` files. 8 | 9 | Examples: 10 | 11 | - https://github.com/adzerk-oss/boot-reload/blob/master/src/adzerk/boot_reload.clj#L84-L89 12 | - https://github.com/adzerk-oss/boot-reload/blob/master/src/adzerk/boot_reload.clj#L138-L148 13 | 14 | ## Without `.cljs.edn` files 15 | 16 | FIXME: 17 | 18 | If tasks don't find `.cljs.edn` files, they don't currently have way to 19 | define which namespaces to include in build. This works for now, 20 | as Boot-cljs will create default `.cljs.edn` file, which has `:require` 21 | in any `.cljs` files in fileset. However, this causes the main file to 22 | require all the namespaces, even those that are unused. 23 | 24 | ## Related 25 | 26 | - [Boot-reload HUD messages](https://github.com/adzerk-oss/boot-reload/blob/master/doc/hud-messages.md) 27 | -------------------------------------------------------------------------------- /src/adzerk/boot_cljs.clj: -------------------------------------------------------------------------------- 1 | (ns adzerk.boot-cljs 2 | {:boot/export-tasks true} 3 | (:refer-clojure :exclude [compile]) 4 | (:require [adzerk.boot-cljs.js-deps :as deps] 5 | [adzerk.boot-cljs.middleware :as wrap] 6 | [adzerk.boot-cljs.util :as util] 7 | [boot.core :as core] 8 | [boot.pod :as pod] 9 | [boot.file :as file] 10 | [boot.util :refer [dbug info warn guard]] 11 | [clojure.java.io :as io] 12 | [clojure.pprint :as pp] 13 | [clojure.string :as string]) 14 | (:import [java.util.concurrent ExecutionException])) 15 | 16 | (def cljs-version "1.7.228") 17 | 18 | (defn- cljs-dependency [] 19 | (let [proj-deps (core/get-env :dependencies) 20 | cljs-dep? (first (filter (comp #{'org.clojure/clojurescript} first) proj-deps)) 21 | cljs-exists? (io/resource "cljs/build/api.clj")] 22 | (cond 23 | ; org.clojure/clojurescript in project (non-transitive) deps - do nothing 24 | cljs-dep? nil 25 | ; cljs.core on classpath, org.clojure/clojurescript not in project deps 26 | cljs-exists? (do (warn "WARNING: No ClojureScript in project dependencies but ClojureScript was found in classpath. Adding direct dependency is advised.\n") nil) 27 | ; no cljs on classpath, no project dep, add cljs dep to pod 28 | :else ['org.clojure/clojurescript cljs-version]))) 29 | 30 | (def ^:private deps 31 | "ClojureScript dependency to load in the pod if 32 | none is provided via project" 33 | (delay (filter identity [(cljs-dependency) 34 | ['ns-tracker "0.3.0"]]))) 35 | 36 | (def ^:private QUALIFIERS 37 | "Order map for well-known Clojure version qualifiers." 38 | { "alpha" 0 "beta" 1 "rc" 2 "" 3}) 39 | 40 | (defn- assert-clojure-version! 41 | "Warn user if Clojure 1.7 or greater is not found" 42 | [pod] 43 | (let [{:keys [major minor incremental qualifier]} (pod/with-eval-in pod *clojure-version*) 44 | [qualifier-part1 qualifier-part2] (if-let [[_ w d] (re-find #"(\w+)(\d+)" (or qualifier ""))] 45 | [(get QUALIFIERS w) (Integer/parseInt d)] 46 | [3 0])] 47 | (when-not (>= (compare [major minor incremental qualifier-part1 qualifier-part2] [1 7 0 3 0]) 0) 48 | (warn "ClojureScript requires Clojure 1.7 or greater.\nSee https://github.com/boot-clj/boot/wiki/Setting-Clojure-version.\n")))) 49 | 50 | (defn- read-cljs-edn 51 | [tmp-file] 52 | (let [file (core/tmp-file tmp-file) 53 | path (core/tmp-path tmp-file)] 54 | ;; FIXME: Better to use edn/read-string instead? 55 | (assoc (read-string (slurp file)) 56 | :path (.getPath file) 57 | :rel-path path 58 | :id (string/replace (.getName file) #"\.cljs\.edn$" "")))) 59 | 60 | (defn- compile 61 | "Given a compiler context and a pod, compiles CLJS accordingly. Returns a 62 | seq of all compiled JS files known to the CLJS compiler in dependency order, 63 | as paths relative to the :output-to compiled JS file." 64 | [{:keys [tmp-src tmp-out main opts] :as ctx} macro-changes pod] 65 | (let [{:keys [output-dir]} opts 66 | rel-path #(.getPath (file/relative-to tmp-out %))] 67 | (pod/with-call-in pod 68 | (adzerk.boot-cljs.impl/reload-macros!)) 69 | (pod/with-call-in pod 70 | (adzerk.boot-cljs.impl/backdate-macro-dependants! ~output-dir ~macro-changes)) 71 | (let [{:keys [warnings exception] :as result} 72 | (pod/with-call-in pod 73 | (adzerk.boot-cljs.impl/compile-cljs ~(.getPath tmp-src) ~opts))] 74 | (swap! core/*warnings* + (or (count warnings) 0)) 75 | (when exception 76 | (throw (util/deserialize-object exception))) 77 | (-> result 78 | (update-in [:dep-order] #(->> (conj % (:output-to opts)) (map rel-path))) 79 | (assoc :opts opts))))) 80 | 81 | (defn- macro-files-changed 82 | [diff] 83 | (->> (core/input-files diff) 84 | (core/by-ext [".clj" ".cljc"]) 85 | (map core/tmp-path))) 86 | 87 | (defn main-files [fileset ids] 88 | (let [re-pat #(re-pattern (str "^\\Q" % "\\E\\.cljs\\.edn$")) 89 | select (if (seq ids) 90 | #(core/by-re (map re-pat ids) %) 91 | #(core/by-ext [".cljs.edn"] %))] 92 | (->> fileset 93 | core/input-files 94 | select 95 | (sort-by :path)))) 96 | 97 | (defn- new-env [tmp-src] 98 | (-> (core/get-env) 99 | (update-in [:dependencies] into @deps) 100 | (update-in [:directories] conj (.getPath tmp-src))) ) 101 | 102 | (defn- make-compiler 103 | [cljs-edn-content] 104 | (let [tmp-src (core/tmp-dir!) 105 | env (new-env tmp-src)] 106 | {:initial-ctx {:tmp-src tmp-src 107 | :tmp-out (core/tmp-dir!) 108 | :main-ns-name (name (gensym "main"))} 109 | :pod (future 110 | (doto (pod/make-pod env) 111 | (assert-clojure-version!)))})) 112 | 113 | (defn assert-cljs-edn! 114 | "Validate boot-cljs specific .cljs.edn options. 115 | 116 | Check https://github.com/boot-clj/boot-cljs/blob/master/docs/cljs.edn.md 117 | for documentation about options." 118 | [{:keys [main] :as ctx}] 119 | (assert (and (every? (fn [v] 120 | (and (symbol? v) (not (namespace v)))) 121 | (:require main))) 122 | (str "Every .cljs.edn :require item should be a symbol referring to a namespace, " 123 | "i.e. symbol should only have a name:\n" 124 | (:require main))) 125 | 126 | (assert (and (every? (fn [v] 127 | (and (symbol? v) (and (namespace v) (name v)))) 128 | (:init-fns main))) 129 | (str "Every .cljs.edn :init-fns item should a be symbol referring to a function, " 130 | "i.e. symbol should have both a namespace and a name:\n" 131 | (:init-fns main))) 132 | 133 | ctx) 134 | 135 | (defn- compile-1 136 | [compilers task-opts macro-changes write-main? {:keys [path] :as cljs-edn} deps-changed?] 137 | (let [cljs-edn-content (read-cljs-edn cljs-edn) 138 | _ (swap! compilers (fn [compilers] 139 | (if (contains? compilers path) 140 | compilers 141 | (assoc compilers path (make-compiler cljs-edn-content))))) 142 | 143 | {:keys [pod initial-ctx]} (get @compilers path) 144 | ctx (-> initial-ctx 145 | (assoc :main cljs-edn-content) 146 | (assert-cljs-edn!) 147 | (wrap/compiler-options task-opts) 148 | (wrap/main write-main?) 149 | (wrap/modules) 150 | wrap/source-map 151 | wrap/non-standard-defaults) 152 | tmp (:tmp-out initial-ctx) 153 | out (.getPath (file/relative-to tmp (-> ctx :opts :output-to)))] 154 | 155 | (when deps-changed? 156 | (pod/add-dependencies-in @pod (new-env (:tmp-src initial-ctx)))) 157 | 158 | (info "• %s\n" out) 159 | (dbug "CLJS options:\n%s\n" (with-out-str (pp/pprint (:opts ctx)))) 160 | (future (compile ctx macro-changes @pod)))) 161 | 162 | (core/deftask ^:private default-main 163 | "Private task. 164 | 165 | If no .cljs.edn exists with given id, creates one. This default .cljs.edn file 166 | will :require all CLJS namespaces found in the fileset." 167 | [i ids IDS #{str} ""] 168 | (let [tmp-main (core/tmp-dir!)] 169 | (core/with-pre-wrap fileset 170 | (core/empty-dir! tmp-main) 171 | (if (seq (main-files fileset ids)) 172 | fileset 173 | (let [remove-deps-cljs #(core/by-name ["deps.cljs"] % true) 174 | cljs (->> fileset core/input-files (core/by-ext [".cljs" ".cljc"]) remove-deps-cljs (sort-by :path)) 175 | out-main (str (or (first ids) "main") ".cljs.edn") 176 | out-file (io/file tmp-main out-main)] 177 | (info "Writing %s...\n" (.getName out-file)) 178 | (doto out-file 179 | (io/make-parents) 180 | (spit {:require (mapv (comp symbol util/path->ns core/tmp-path) cljs)})) 181 | (-> fileset (core/add-source tmp-main) core/commit!)))))) 182 | 183 | (defn- remove-previous-cljs-output 184 | [fileset] 185 | (core/rm fileset (filter ::output (core/ls fileset)))) 186 | 187 | (defn- cljs-output-meta 188 | [dir] 189 | (-> (file-seq dir) 190 | (->> (filter #(.isFile %)) 191 | (map #(.getPath (file/relative-to dir %)))) 192 | (zipmap (repeat {::output true})))) 193 | 194 | (defn- cljs-warnings-meta 195 | [cljs-edns results] 196 | (zipmap (map :path cljs-edns) 197 | (map (fn [r] {::warnings (:warnings r)}) 198 | results))) 199 | 200 | (defn- cljs-opts-meta 201 | [cljs-edns results] 202 | (zipmap (map :path cljs-edns) 203 | (map (fn [r] {::opts (:opts r)}) 204 | results))) 205 | 206 | (core/deftask cljs 207 | "Compile ClojureScript applications. 208 | 209 | Multiple builds can be compiled parallel. To define builds use .cljs.edn 210 | files. ID of build is the name of .cljs.edn file without the extension. 211 | To compile only specific builds, use ids option to select .cljs.edn files 212 | by name. Output files of build will be put below id.out folder in fileset. 213 | 214 | If no .cljs.edn files exists, default one is created. It will depend on 215 | all .cljs files in fileset. 216 | 217 | Available --optimizations levels (default 'none'): 218 | 219 | * none No optimizations. Bypass the Closure compiler completely. 220 | * whitespace Remove comments, unnecessary whitespace, and punctuation. 221 | * simple Whitespace + local variable and function parameter renaming. 222 | * advanced Simple + aggressive renaming, inlining, dead code elimination. 223 | 224 | Source maps can be enabled via the --source-map flag. This provides what the 225 | browser needs to map locations in the compiled JavaScript to the corresponding 226 | locations in the original ClojureScript source files. 227 | 228 | The --compiler-options option can be used to set any other options that should 229 | be passed to the Clojurescript compiler. A full list of options can be found 230 | here: https://github.com/clojure/clojurescript/wiki/Compiler-Options. 231 | 232 | Cljs compiler options are merged in this order: 233 | 234 | 1. :compiler-options in .cljs.edn file 235 | 2. :compiler-options task option 236 | 3. :optimizations and :source-map task options 237 | 4. options automatically set by boot-cljs (:output-dir, :output-to, :main)" 238 | 239 | [i ids IDS #{str} "" 240 | O optimizations LEVEL kw "The optimization level." 241 | s source-map bool "Create source maps for compiled JS." 242 | c compiler-options OPTS edn "Options to pass to the Clojurescript compiler."] 243 | 244 | (let [tmp-result (core/tmp-dir!) 245 | compilers (atom {}) 246 | prev (atom nil) 247 | prev-deps (atom (core/get-env :dependencies))] 248 | (comp 249 | (default-main :ids ids) 250 | (core/with-pre-wrap fileset 251 | (info "Compiling ClojureScript...\n") 252 | ;; If there are any output files from other instances of the cljs 253 | ;; task in the pipeline we need to remove them from the classpath 254 | ;; or the cljs compiler will try to use them during compilation. 255 | (-> fileset remove-previous-cljs-output core/commit!) 256 | (let [diff (core/fileset-diff @prev fileset) 257 | macro-changes (macro-files-changed diff) 258 | cljs-edns (main-files fileset ids) 259 | changed-cljs-edns (->> diff core/input-files (core/by-ext [".cljs.edn"]) set) 260 | 261 | ;; Check if the dependencies have changed since last time 262 | new-deps (core/get-env :dependencies) 263 | deps-changed? (not= @prev-deps new-deps) 264 | _ (when deps-changed? 265 | (info "Project dependencies have changed, updating Boot-cljs pods...\n")) 266 | _ (reset! prev-deps new-deps) 267 | 268 | ;; Force realization to start compilation 269 | futures (doall (map (fn [cljs-edn] 270 | (let [write-main? (contains? changed-cljs-edns cljs-edn)] 271 | (compile-1 compilers *opts* macro-changes write-main? cljs-edn deps-changed?))) 272 | cljs-edns)) 273 | ;; Wait for all compilations to finish 274 | ;; Remove unnecessary layer of cause stack added by futures 275 | results (try (doall (map deref futures)) 276 | (catch ExecutionException e 277 | (throw (.getCause e)))) 278 | ;; Since each build has its own :output-dir we don't need to do 279 | ;; anything special to merge dependency ordering of files across 280 | ;; builds. Each :output-to js file will depend only on compiled 281 | ;; namespaces in its own :output-dir, including goog/base.js etc. 282 | dep-order (reduce into [] (map :dep-order results))] 283 | (reset! prev fileset) 284 | (->> (vals @compilers) 285 | (map #(get-in % [:initial-ctx :tmp-out])) 286 | (apply core/sync! tmp-result)) 287 | (-> fileset 288 | (core/add-resource tmp-result) 289 | ;; Add warnings to .cljs.edn files metadata so other tasks 290 | ;; can use this information and report it to users 291 | (core/add-meta (cljs-warnings-meta cljs-edns results)) 292 | (core/add-meta (cljs-opts-meta cljs-edns results)) 293 | ;; Add metadata to mark the output of this task so subsequent 294 | ;; instances of the cljs task can remove them before compiling. 295 | (core/add-meta (cljs-output-meta tmp-result)) 296 | (core/add-meta (deps/compiled fileset dep-order)) 297 | core/commit!)))))) 298 | -------------------------------------------------------------------------------- /src/adzerk/boot_cljs/impl.clj: -------------------------------------------------------------------------------- 1 | (ns adzerk.boot-cljs.impl 2 | (:require [boot.file :as file] 3 | [boot.kahnsort :as kahn] 4 | [boot.pod :as pod] 5 | [boot.util :as butil] 6 | [clojure.string :as str] 7 | [clojure.edn :as edn] 8 | [cljs.analyzer :as ana] 9 | [cljs.env :as env] 10 | [cljs.analyzer.api :as ana-api :refer [warning-enabled?]] 11 | [cljs.build.api :as build-api :refer [build inputs target-file-for-cljs-ns]] 12 | [clojure.java.io :as io] 13 | [adzerk.boot-cljs.util :as util] 14 | [ns-tracker.core :refer [ns-tracker]])) 15 | 16 | ; Because this ns is loaded in pod, it's private to one cljs task. 17 | ; Compiler env is a atom. 18 | (def ^:private stored-env (atom nil)) 19 | 20 | (defn ns-dependencies 21 | "Given a namespace as a symbol return list of namespaces required by the namespace." 22 | ; ([ns] (ns-dependencies env/*compiler* ns)) 23 | ([state ns] 24 | (vals (:requires (ana-api/find-ns state ns))))) 25 | 26 | (defn cljs-dependency-graph [state] 27 | (let [all-ns (ana-api/all-ns state) 28 | all-ns-set (set all-ns)] 29 | (->> all-ns 30 | (reduce (fn [acc n] 31 | (assoc acc n (->> (ns-dependencies state n) 32 | (keep all-ns-set) 33 | (set)))) 34 | {})))) 35 | 36 | (defn dep-order [env opts] 37 | (->> (cljs-dependency-graph env) 38 | (kahn/topo-sort) 39 | reverse 40 | (map #(.getPath (target-file-for-cljs-ns % (:output-dir opts)))) 41 | vec)) 42 | 43 | ;; Checks if https://github.com/boot-clj/boot/commit/c07b08751ad195e6e6349cb7c2f826c95a8e8186 44 | ;; is present. 45 | ;; The change will preserve %% as % are escapted before format, and will add newline. 46 | (def new-boot? 47 | (try 48 | (let [message (with-out-str 49 | (binding [*err* *out*] 50 | (butil/print-ex (ex-info "%%s" {:boot.util/omit-stacktrace? true}))))] 51 | (and message 52 | (.contains message "%%") 53 | (.contains message "\n"))) 54 | (catch Throwable _ 55 | false))) 56 | 57 | (defn- ensure-ends-in-newline [s] 58 | (if s 59 | (if (.endsWith s "\n") 60 | s 61 | (str s "\n")))) 62 | 63 | (defn- escape-format-string [s] 64 | (str/replace s #"%" "%%")) 65 | 66 | (defn handle-ex 67 | "Rethrows an interesting exception if possible or the original exception. 68 | 69 | Exception is interesting when it has either type or tag ex-data set by cljs compiler. 70 | 71 | If ex-data has file property, it is changed to contain path in original source-paths. 72 | Exceptino message is rewriten to contain fixed path." 73 | [e source-paths dirs] 74 | (let [{:keys [type tag file line] :as data} (util/merge-cause-ex-data e) 75 | column (or (:col data) (:column data)) 76 | 77 | ; Get the message without location info 78 | message (util/last-cause-message e) 79 | ; Sometimes filepath is a URI 80 | file (if file (str/replace file #"^file:" "")) 81 | real-file (util/find-original-path source-paths dirs file) 82 | ; Replace tmpdir path with real file path 83 | message (if (and message file real-file) 84 | (.replace message file real-file) 85 | message) 86 | file real-file 87 | ; Add file info if message doesn't contain it but it is available 88 | message (if (and message file (not (str/includes? message file))) 89 | (str 90 | (if file (str file " ")) 91 | (if (or line column) 92 | (str "[" 93 | (if line (str "line " line)) 94 | (if column (str ", col " column)) 95 | "] ")) 96 | message) 97 | message) 98 | ;; E.g. (defmacro foo [] (+ nil nil)) will cause exception where 99 | ;; message is only " at line ..." 100 | message (if (str/starts-with? message " at line ") 101 | (str "Compilation failed" message) 102 | message) 103 | ;; NOTE: This probably matches any errors from macro compilation 104 | cljs-error? (or (= :reader-exception type) 105 | (= :cljs/analysis-error tag))] 106 | 107 | (ex-info 108 | ;; For now, when Boot shows the message with omit-stacktrace, no newline is automatically appended 109 | (if new-boot? 110 | message 111 | (-> message ensure-ends-in-newline escape-format-string)) 112 | (if cljs-error? 113 | ;; Skip ex-data if it won't even be shown. This will avoid some serialization errors, 114 | ;; as compiler or spec errors from macros only really need the message anyway. 115 | {:boot.util/omit-stacktrace? true} 116 | (-> data 117 | (assoc :from :boot-cljs) 118 | (cond-> 119 | file (assoc :file file))))))) 120 | 121 | (defn compile-cljs 122 | "Given a seq of directories containing CLJS source files and compiler options 123 | opts, compiles the CLJS to produce JS files." 124 | [input-path {:keys [optimizations] :as opts}] 125 | ;; So directories need to be passed to cljs compiler when compiling in dev 126 | ;; or there are stale namespace problems with tests. However, if compiling 127 | ;; with optimizations other than :none adding directories will break the 128 | ;; build and defeat tree shaking and :main option. 129 | (let [dirs (:directories pod/env) 130 | ; Includes also some tmp-dirs passed to this pod, but shouldn't matter 131 | source-paths (concat (:source-paths pod/env) (:resource-paths pod/env)) 132 | warnings (atom []) 133 | handler (fn [warning-type env extra] 134 | (when (warning-enabled? warning-type) 135 | (when-let [s (ana/error-message warning-type extra)] 136 | (let [path (if (= (-> env :ns :name) 'cljs.core) 137 | "cljs/core.cljs" 138 | (util/find-original-path source-paths dirs ana/*cljs-file*)) 139 | warning-data {:line (:line env) 140 | :column (:column env) 141 | :ns (-> env :ns :name) 142 | :file path 143 | :type warning-type 144 | :message s 145 | ;; :fn-deprecated warning contains the whole 146 | ;; analyze result as extra data, and :env value 147 | ;; of that is unprintable. 148 | :extra (cond-> extra 149 | (:fexpr extra) (update :fexpr dissoc :env))}] 150 | (butil/warn (str "WARNING: " 151 | s 152 | (if (:line env) 153 | (str " at line " (:line env) " " path) 154 | (when path 155 | (str " in file " path))) 156 | "\n")) 157 | (butil/dbug* "%s\n" (butil/pp-str warning-data)) 158 | (swap! warnings conj warning-data)))))] 159 | ;; ana-api/empty-state doesn't take options, so need to use this instead 160 | (swap! stored-env #(or % (env/default-compiler-env opts))) 161 | (try 162 | (build 163 | (inputs input-path) 164 | (assoc opts :warning-handlers [handler]) 165 | @stored-env) 166 | {:warnings @warnings 167 | :dep-order (dep-order @stored-env opts)} 168 | (catch Exception e 169 | (let [ex (handle-ex e source-paths dirs)] 170 | (try 171 | {:exception (util/serialize-object ex)} 172 | (catch Exception e 173 | (butil/warn "Exception couldn't be serialized properly: %s, rethrowing original exception but it won't formatted perfectly. Please report this bug.\n" (.getMessage e)) 174 | (throw ex)))))))) 175 | 176 | 177 | (def tracker (atom nil)) 178 | 179 | (defn reload-macros! [] 180 | (let [dirs (:directories pod/env)] 181 | (when (nil? @tracker) 182 | (reset! tracker (ns-tracker (vec dirs)))) 183 | ; Reload only namespaces which are already loaded 184 | ; As opposed to :reload-all, ns-tracker only reloads namespaces which are really changed. 185 | (doseq [s (filter find-ns (@tracker))] 186 | (butil/dbug "Reload macro ns: %s\n" s) 187 | (require s :reload)))) 188 | 189 | (defn backdate-macro-dependants! 190 | [output-dir changed-files] 191 | (if @stored-env 192 | (doseq [cljs-ns (->> changed-files 193 | (map (comp symbol util/path->ns)) 194 | (build-api/cljs-dependents-for-macro-namespaces @stored-env))] 195 | ; broken 196 | ; (build-api/mark-cljs-ns-for-recompile! cljs-ns output-dir) 197 | (let [f (build-api/target-file-for-cljs-ns cljs-ns output-dir)] 198 | (when (.exists f) 199 | (butil/dbug "Backdate macro dependant cljs ns: %s\n" cljs-ns) 200 | (.setLastModified f 5000)))))) 201 | -------------------------------------------------------------------------------- /src/adzerk/boot_cljs/js_deps.clj: -------------------------------------------------------------------------------- 1 | (ns adzerk.boot-cljs.js-deps) 2 | 3 | (defn- max* [& args] (apply max (or (seq args) [0]))) 4 | 5 | (defn- start-dep-order 6 | "Returns the next dependency order index, one greater than the maximum dep 7 | order index in the given metadata map (path => {:dependency-order n})." 8 | [maps] 9 | (->> maps (keep :dependency-order) (apply max*) inc)) 10 | 11 | (defn compiled 12 | "Given a dep order metadata map (path => {:dependency-order n}) and a seq of 13 | fileset paths in dependency order, creates a new metadata map adding entries 14 | for the paths such that the paths depend on the preexisting entries." 15 | [dep-order-meta paths] 16 | (let [start (->> dep-order-meta vals start-dep-order)] 17 | (->> paths 18 | (map-indexed (fn [a b] [b {:dependency-order (+ start a)}])) 19 | (into dep-order-meta)))) 20 | 21 | (defn analyzed 22 | [analysis-meta] 23 | (reduce-kv (fn [xs k v] (assoc xs k {:cljs-analysis v})) {} analysis-meta)) 24 | -------------------------------------------------------------------------------- /src/adzerk/boot_cljs/middleware.clj: -------------------------------------------------------------------------------- 1 | (ns adzerk.boot-cljs.middleware 2 | (:require [adzerk.boot-cljs.util :as util] 3 | [boot.file :as file] 4 | [boot.util :as butil] 5 | [clojure.java.io :as io] 6 | [clojure.string :as string])) 7 | 8 | ;; helpers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 9 | 10 | (defn- main-ns-forms 11 | "Given a namespace symbol, ns-sym, a sorted set of namespace symbols this 12 | namespace will :require, requires, and a vector of namespace-qualified 13 | symbols, init-fns, constructs the forms for a namespace named ns-sym that 14 | :requires the namespaces in requires and calls the init-fns with no arguments 15 | at the top level." 16 | [ns-sym requires init-fns] 17 | (apply list 18 | (list 'ns ns-sym (apply list :require requires)) 19 | (map list init-fns))) 20 | 21 | (defn- format-ns-forms 22 | "Given a sequence of forms, formats them nicely as a string." 23 | [forms] 24 | (->> forms (map pr-str) (string/join "\n\n") (format "%s\n"))) 25 | 26 | (defn- cljs-edn-path->output-dir-path 27 | [cljs-edn-path] 28 | (str (.replaceAll cljs-edn-path "\\.cljs\\.edn$" "") ".out")) 29 | 30 | (defn- cljs-edn-path->js-path 31 | [cljs-edn-path] 32 | (str (.replaceAll cljs-edn-path "\\.cljs\\.edn$" "") ".js")) 33 | 34 | ;; middleware ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 35 | 36 | (defn compiler-options 37 | [{:keys [opts main] :as ctx} 38 | {:keys [compiler-options] :as task-options}] 39 | (assoc ctx :opts (merge (:compiler-options main) 40 | compiler-options 41 | (select-keys task-options [:optimizations :source-map])))) 42 | 43 | (defn set-option [ctx k value] 44 | (when-let [current-value (get-in ctx [:opts k])] 45 | (butil/warn "WARNING: Replacing ClojureScript compiler option %s with automatically set value.\n" k)) 46 | (assoc-in ctx [:opts k] value)) 47 | 48 | (defn main 49 | "Middleware to create the CLJS namespace for the build's .cljs.edn file and 50 | set the compiler :output-to option accordingly. The :output-to will be derived 51 | from the path of the .cljs.edn file (e.g. foo/bar.cljs.edn will produce the 52 | foo.bar CLJS namespace with output to foo/bar.js)." 53 | [{:keys [tmp-src tmp-out main main-ns-name] :as ctx} write-main?] 54 | (let [out-rel-path (if-let [output-dir (:output-dir (:opts ctx))] 55 | output-dir 56 | (cljs-edn-path->output-dir-path (:rel-path main))) 57 | asset-path out-rel-path 58 | out-file (io/file tmp-out out-rel-path) 59 | out-path (.getPath out-file) 60 | js-path (if-let [output-to (:output-to (:opts ctx))] 61 | (util/path tmp-out output-to) 62 | (util/path tmp-out (cljs-edn-path->js-path (:rel-path main)))) 63 | 64 | cljs-path (util/path "boot" "cljs" (str main-ns-name ".cljs")) 65 | cljs-file (io/file tmp-src cljs-path) 66 | cljs-ns (symbol (util/path->ns cljs-path)) 67 | init-fns (:init-fns main) 68 | requires (into (sorted-set) (:require main)) 69 | ;; We set out own shim ns as :main, but if user provides :main, 70 | ;; include that in our shim ns requires 71 | requires (if-let [main (:main (:opts ctx))] 72 | (conj requires (symbol main)) 73 | requires) 74 | init-nss (into requires (->> init-fns (keep namespace) (map symbol)))] 75 | (.mkdirs out-file) 76 | (when write-main? 77 | (doto cljs-file 78 | (io/make-parents) 79 | (spit (format-ns-forms (main-ns-forms cljs-ns init-nss init-fns))))) 80 | (-> ctx 81 | (update-in [:opts :asset-path] #(if % % asset-path)) 82 | (assoc-in [:opts :output-dir] out-path) 83 | (assoc-in [:opts :output-to] js-path) 84 | (assoc-in [:opts :main] cljs-ns)))) 85 | 86 | (defn modules 87 | "Updates :modules :output-to paths under :compiler-options 88 | 89 | If module declaration has :output-to, the path is prepended with 90 | path to Boot-cljs temp directory. 91 | 92 | If :output-to is not set, default value is created based on 93 | the temp directory, relative path of the .cljs.edn file and module name." 94 | [{:keys [tmp-out main] :as ctx}] 95 | (when (:modules main) 96 | (butil/warn "WARNING: .cljs.edn :modules option is no longer supported, please set :modules under :compiler-options.\n")) 97 | (if (contains? (:opts ctx) :modules) 98 | (update-in ctx [:opts :modules] 99 | (fn [modules] 100 | (into (empty modules) 101 | (map (fn [[k v]] 102 | [k (if-let [output-to (:output-to v)] 103 | (assoc v :output-to (util/path tmp-out output-to)) 104 | (assoc v :output-to (util/path (-> ctx :opts :output-dir) (str (name k) ".js"))))]) 105 | modules)))) 106 | ctx)) 107 | 108 | (defn source-map 109 | "Middleware to configure source map related CLJS compiler options." 110 | [{:keys [opts] :as ctx}] 111 | (if (contains? opts :source-map) 112 | (let [optimizations (:optimizations opts :none) 113 | ; Under :none optimizations only true and false are valid values 114 | ; Same if :modules is used. 115 | ; https://github.com/clojure/clojurescript/wiki/Compiler-Options#source-map 116 | sm (if (or (= optimizations :none) (:modules opts)) 117 | (boolean (:source-map opts)) 118 | ;; If path is already provided, use that 119 | (if (string? (:source-map opts)) 120 | (:source-map opts) 121 | ;; If non-path is provided, generate path, if the given value is truthy 122 | (and (:source-map opts) (-> opts :output-to (str ".map")))))] 123 | (assoc-in ctx [:opts :source-map] sm)) 124 | ctx)) 125 | 126 | (defn non-standard-defaults 127 | "Sets defaults differing from the ones provided by the ClojureScript compiler." 128 | [{:keys [opts] :as ctx}] 129 | (let [advanced? (= :advanced (:optimizations opts))] 130 | (cond-> ctx 131 | (and advanced? (nil? (:output-wrapper opts))) 132 | (assoc-in [:opts :output-wrapper] true)))) 133 | -------------------------------------------------------------------------------- /src/adzerk/boot_cljs/util.clj: -------------------------------------------------------------------------------- 1 | (ns adzerk.boot-cljs.util 2 | (:require [clojure.java.io :as io] 3 | [clojure.string :as string] 4 | [boot.file :as file]) 5 | (:import [java.util Base64] 6 | [java.io ObjectInputStream ObjectOutputStream ByteArrayOutputStream ByteArrayInputStream])) 7 | 8 | (defn path->js 9 | "Given a path to a CLJS namespace source file, returns the corresponding 10 | Google Closure namespace name for goog.provide() or goog.require()." 11 | [path] 12 | (-> path 13 | (string/replace #"\.clj([s|c])?$" "") 14 | (string/replace #"[/\\]" "."))) 15 | 16 | (defn path->ns 17 | "Given a path to a CLJS namespace source file, returns the corresponding 18 | CLJS namespace name." 19 | [path] 20 | (-> (path->js path) (string/replace #"_" "-"))) 21 | 22 | (defn get-name 23 | [path-or-file] 24 | (-> path-or-file io/file .getName)) 25 | 26 | (defn path [& parts] 27 | (.getPath (apply io/file parts))) 28 | 29 | (defn find-relative-path [dirs filepath] 30 | (if-let [file (io/file filepath)] 31 | (let [parent (->> dirs 32 | (map io/file) 33 | (some (fn [x] (if (file/parent? x file) x))))] 34 | (if parent (.getPath (file/relative-to parent file)))))) 35 | 36 | (defn find-original-path [source-paths dirs filepath] 37 | (if-let [rel-path (find-relative-path dirs filepath)] 38 | (or (some (fn [source-path] 39 | (let [f (io/file source-path rel-path)] 40 | (if (.exists f) 41 | (.getPath f)))) 42 | source-paths) 43 | rel-path) 44 | filepath)) 45 | 46 | ;; 47 | ;; Object serialization 48 | ;; 49 | 50 | (defn serialize-object 51 | "Serialize given Object to String using Object Streams and encode the bytes 52 | as Base64 string." 53 | [e] 54 | (with-open [bos (ByteArrayOutputStream.) 55 | out (ObjectOutputStream. bos)] 56 | (.writeObject out e) 57 | (.encodeToString (Base64/getEncoder) (.toByteArray bos)))) 58 | 59 | (defn deserialize-object 60 | "Deserialize given Base64 encoding string using Object Streams and return the 61 | Object." 62 | [ba] 63 | (with-open [bis (ByteArrayInputStream. (.decode (Base64/getDecoder) ba)) 64 | in (ObjectInputStream. bis)] 65 | (.readObject in))) 66 | 67 | (defn merge-cause-ex-data 68 | "Merges ex-data from all exceptions in cause stack. First value for a key is 69 | used." 70 | [ex] 71 | (loop [ex ex 72 | data {}] 73 | (if-let [c (.getCause ex)] 74 | (recur c (merge (ex-data ex) data)) 75 | (merge (ex-data ex) data)))) 76 | 77 | (defn last-cause [ex] 78 | (loop [ex ex] 79 | (if (.getCause ex) 80 | (recur (.getCause ex)) 81 | ex))) 82 | 83 | (defn last-cause-message [ex] 84 | (loop [ex ex 85 | last-msg (.getMessage ex)] 86 | (if-let [c (.getCause ex)] 87 | (recur c (or (.getMessage c) last-msg)) 88 | last-msg))) 89 | -------------------------------------------------------------------------------- /test/adzerk/boot_cljs/impl_test.clj: -------------------------------------------------------------------------------- 1 | (ns adzerk.boot-cljs.impl-test 2 | (:require [clojure.test :refer :all] 3 | [adzerk.boot-cljs.impl :refer :all] 4 | [clojure.java.io :as io] 5 | [clojure.string :as string] 6 | [boot.from.io.aviso.exception :as pretty] 7 | [boot.from.io.aviso.ansi :as ansi] 8 | [boot.util :as util :refer [*verbosity* fail]])) 9 | 10 | (defn delete-dir [dir] 11 | (doseq [f (file-seq dir) 12 | :when (.isFile f)] 13 | (.delete f)) 14 | (doseq [f (file-seq dir) 15 | :when (.isDirectory f)] 16 | (.delete f)) 17 | (.delete dir)) 18 | 19 | (deftest handle-ex-test 20 | (with-redefs [adzerk.boot-cljs.impl/new-boot? true] 21 | (let [source-dir (io/file "boot-cljs-handle-ex-test") 22 | core-cljs-file (io/file source-dir "frontend" "core.cljs")] 23 | (delete-dir source-dir) 24 | 25 | ;; Setup mock source for finding the original source path 26 | (io/make-parents core-cljs-file) 27 | (spit core-cljs-file "") 28 | 29 | (testing "old error format" 30 | (let [e (handle-ex (ex-info "Unmatched delimiter )" 31 | {:type :reader-exception 32 | :file "/home/juho/.boot/cache/tmp/home/juho/Source/saapas/obh/uanrg/frontend/core.cljs" 33 | :line 30 34 | :column 67}) 35 | [(.getPath source-dir)] 36 | ["/home/juho/.boot/cache/tmp/home/juho/Source/saapas/obh/uanrg"])] 37 | (is (= "boot-cljs-handle-ex-test/frontend/core.cljs [line 30, col 67] Unmatched delimiter )" (.getMessage e))) 38 | (is (= "boot-cljs-handle-ex-test/frontend/core.cljs" (:file (ex-data e)))))) 39 | 40 | (testing "new error format" 41 | (let [e (handle-ex (ex-info "/home/juho/.boot/cache/tmp/home/juho/Source/saapas/obh/uanrg/frontend/core.cljs [line 30, col 67] Unmatched delimiter )." 42 | {:type :reader-exception 43 | :ex-kind :reader-error 44 | :file "/home/juho/.boot/cache/tmp/home/juho/Source/saapas/obh/uanrg/frontend/core.cljs" 45 | :line 30 46 | :col 67}) 47 | [(.getPath source-dir)] 48 | ["/home/juho/.boot/cache/tmp/home/juho/Source/saapas/obh/uanrg"])] 49 | (is (= "boot-cljs-handle-ex-test/frontend/core.cljs [line 30, col 67] Unmatched delimiter )." (.getMessage e))) 50 | (is (= "boot-cljs-handle-ex-test/frontend/core.cljs" (:file (ex-data e)))))) 51 | 52 | (delete-dir source-dir)))) 53 | 54 | ;; From 2.7.1 55 | (defn old-print-ex 56 | [ex] 57 | (cond 58 | (= 0 @*verbosity*) nil 59 | (::omit-stacktrace? (ex-data ex)) (fail (.getMessage ex)) 60 | (= 1 @*verbosity*) (pretty/write-exception *err* ex nil) 61 | (= 2 @*verbosity*) (pretty/write-exception *err* ex {:filter nil}) 62 | :else (binding [*out* *err*] (.printStackTrace ex)))) 63 | 64 | ;; From 2.7.2 65 | (defn- ensure-ends-in-newline [s] 66 | (if s 67 | (if (.endsWith s "\n") 68 | s 69 | (str s "\n")))) 70 | 71 | (defn- escape-format-string [s] 72 | (string/replace s #"%" "%%")) 73 | 74 | (defn new-print-ex 75 | [ex] 76 | (cond 77 | (= 0 @*verbosity*) nil 78 | (::omit-stacktrace? (ex-data ex)) (fail (-> (.getMessage ex) escape-format-string ensure-ends-in-newline)) 79 | (= 1 @*verbosity*) (pretty/write-exception *err* ex nil) 80 | (= 2 @*verbosity*) (pretty/write-exception *err* ex {:filter nil}) 81 | :else (binding [*out* *err*] (.printStackTrace ex)))) 82 | 83 | (deftest ex-message-print-test 84 | (is (= "Missing symbol foo%asd\n" 85 | (ansi/strip-ansi 86 | (with-out-str 87 | (binding [*err* *out*] 88 | (old-print-ex (ex-info "Missing symbol foo%%asd\n" {::omit-stacktrace? true}))))))) 89 | 90 | (is (= "Missing symbol foo%asd\n" 91 | (ansi/strip-ansi 92 | (with-out-str 93 | (binding [*err* *out*] 94 | (new-print-ex (ex-info "Missing symbol foo%asd" {::omit-stacktrace? true})))))))) 95 | -------------------------------------------------------------------------------- /test/adzerk/boot_cljs/middleware_test.clj: -------------------------------------------------------------------------------- 1 | (ns adzerk.boot-cljs.middleware-test 2 | (:require [clojure.test :refer :all] 3 | [adzerk.boot-cljs.middleware :refer :all] 4 | [clojure.java.io :as io] 5 | [clojure.stacktrace :as st] 6 | [clojure.string :as string])) 7 | 8 | (deftest source-map-test 9 | (is (= {:opts {:source-map true}} 10 | (source-map {:opts {:source-map true}}))) 11 | 12 | (is (= {:opts {:source-map true 13 | :optimizations :none}} 14 | (source-map {:opts {:source-map true 15 | :optimizations :none}}))) 16 | 17 | (testing "source-map boolean is replaced with path when doing optimized build" 18 | (is (= {:opts {:source-map "main.js.map" 19 | :output-to "main.js" 20 | :optimizations :advanced}} 21 | (source-map {:opts {:source-map true 22 | :optimizations :advanced 23 | :output-to "main.js"}})))) 24 | 25 | (testing "if source-map disabled, not path added for optimized builds" 26 | (is (= {:opts {:source-map false 27 | :output-to "main.js" 28 | :optimizations :advanced}} 29 | (source-map {:opts {:source-map false 30 | :optimizations :advanced 31 | :output-to "main.js"}}))) 32 | 33 | (is (= {:opts {:source-map false 34 | :output-to "main.js" 35 | :optimizations :simple}} 36 | (source-map {:opts {:source-map false 37 | :optimizations :simple 38 | :output-to "main.js"}})))) 39 | 40 | (testing "if source-map path is provided, use that" 41 | (is (= {:opts {:source-map "hello.js.map" 42 | :output-to "main.js" 43 | :optimizations :simple}} 44 | (source-map {:opts {:source-map "hello.js.map" 45 | :optimizations :simple 46 | :output-to "main.js"}}))))) 47 | -------------------------------------------------------------------------------- /test/adzerk/boot_cljs/util_test.clj: -------------------------------------------------------------------------------- 1 | (ns adzerk.boot-cljs.util-test 2 | (:require [clojure.test :refer :all] 3 | [adzerk.boot-cljs.util :refer :all] 4 | [clojure.java.io :as io] 5 | [clojure.stacktrace :as st] 6 | [clojure.string :as string])) 7 | 8 | (deftest find-relative-path-test 9 | (is (= "src/foo/bar.clj" (find-relative-path ["/foo" "/bar"] "/foo/src/foo/bar.clj"))) 10 | (is (= nil (find-relative-path ["/foo" "/bar"] nil)))) 11 | 12 | (def pwd (.getAbsolutePath (io/file ""))) 13 | 14 | (deftest find-original-path-test 15 | (testing "file doesn't exist in source-paths - returns relative path" 16 | (is (= "foo/bar.clj" (find-original-path ["src" "test"] ["/foo" "/bar"] "/foo/foo/bar.clj")))) 17 | (testing "existing file - returns path with original source-path" 18 | (is (= "src/adzerk/boot_cljs.clj" (find-original-path ["src" "test"] ["/foo" "/bar"] "/foo/adzerk/boot_cljs.clj"))))) 19 | 20 | (deftest basic-exception-test 21 | (let [original (Exception. "foo") 22 | a (deserialize-object (serialize-object original))] 23 | (is (= "foo" (.getMessage a))))) 24 | 25 | (deftest ex-data-test 26 | (let [original (ex-info "foo" {:a 1}) 27 | a (deserialize-object (serialize-object original))] 28 | (is (= "foo" (.getMessage a))) 29 | (is (= {:a 1} (ex-data a))))) 30 | 31 | (deftest cause-test 32 | (let [original (Exception. "foo" (Exception. "bar")) 33 | a (deserialize-object (serialize-object original))] 34 | (is (= "bar" (.getMessage (.getCause a)))))) 35 | 36 | (defn drop-first-line [s] 37 | (-> s 38 | (string/split #"\n") 39 | rest 40 | (->> (string/join "\n")))) 41 | 42 | (deftest stack-trace-test 43 | (let [original (Exception. "foo") 44 | a (deserialize-object (serialize-object original))] 45 | (is (= (with-out-str (st/print-stack-trace original)) 46 | (with-out-str (st/print-stack-trace a)))))) 47 | 48 | (deftest merge-cause-ex-data-test 49 | (is (= {:a 1 :b 2} (merge-cause-ex-data (ex-info "a" {:a 1} (ex-info "b" {:b 2})))))) 50 | 51 | (deftest last-cause-message-test 52 | (is (= "a" (last-cause-message (Exception. "a")))) 53 | (is (= "b" (last-cause-message (Exception. "a" (Exception. "b"))))) 54 | (is (= "c" (last-cause-message (Exception. "a" (Exception. "b" (Exception. "c")))))) 55 | (is (= "b" (last-cause-message (Exception. "a" (Exception. "b" (Exception.)))))) 56 | (is (= "b" (last-cause-message (Exception. "a" (Exception. (Exception. "b")))))) 57 | ) 58 | -------------------------------------------------------------------------------- /test/adzerk/boot_cljs_test.clj: -------------------------------------------------------------------------------- 1 | (ns adzerk.boot-cljs-test 2 | (:require 3 | [boot.pod :as pod] 4 | [clojure.test :refer :all])) 5 | 6 | (def pod-env 7 | '{:dependencies [[net.sourceforge.htmlunit/htmlunit "2.18"]]}) 8 | 9 | (def hunit-pod 10 | (delay (doto (pod/make-pod pod-env) 11 | (pod/with-eval-in 12 | (import 13 | [java.util.logging Logger Level] 14 | [com.gargoylesoftware.htmlunit WebClient]) 15 | (-> (Logger/getLogger "com.gargoylesoftware") 16 | (.setLevel (Level/-OFF))) 17 | (def client (WebClient.)))))) 18 | 19 | (defn hunit-page 20 | [path] 21 | (pod/with-eval-in @hunit-pod 22 | (.asText (.getPage client ~(format "http://localhost:3000%s" path))))) 23 | 24 | (deftest a-test 25 | (testing "Compiling with .cljs.edn" 26 | (is (= "test passed" (hunit-page "/demo/index.html"))))) 27 | -------------------------------------------------------------------------------- /test/demo/core.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.core) 2 | 3 | (.appendChild (.-body js/document) (.createTextNode js/document "test passed")) 4 | -------------------------------------------------------------------------------- /test/demo/index.cljs.edn: -------------------------------------------------------------------------------- 1 | {:require [demo.core]} 2 | -------------------------------------------------------------------------------- /test/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/demo/other.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.other) 2 | 3 | (.appendChild (.-body js/document) (.createTextNode js/document "test passed")) 4 | -------------------------------------------------------------------------------- /test/demo/other.cljs.edn: -------------------------------------------------------------------------------- 1 | {:require [demo.other]} 2 | -------------------------------------------------------------------------------- /test/demo/other.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | --------------------------------------------------------------------------------