├── .gitignore ├── COPYING ├── README.md ├── bin ├── bump-versions.sh ├── deploy.sh ├── release.sh └── test-install.sh ├── doc ├── CROSSOVERS.md ├── MIGRATING-TO-0.1.x.md ├── RELEASE-NOTES.md ├── REPL.md └── TESTING.md ├── example-projects ├── advanced │ ├── README.md │ ├── phantom │ │ ├── repl.js │ │ └── unit-test.js │ ├── project.clj │ ├── resources │ │ └── private │ │ │ └── html │ │ │ ├── naked.html │ │ │ └── unit-test.html │ ├── src-clj │ │ └── example │ │ │ ├── macros.clj │ │ │ ├── routes.clj │ │ │ └── views.clj │ ├── src-cljc │ │ └── example │ │ │ └── shared.cljc │ ├── src-cljs │ │ └── example │ │ │ ├── hello.cljs │ │ │ └── repl.cljs │ └── test-cljs │ │ └── example │ │ ├── test.cljs │ │ └── test │ │ └── hello.cljs ├── none │ ├── .gitignore │ ├── index.html │ ├── project.clj │ └── src │ │ └── none │ │ └── core.cljs └── simple │ ├── README.md │ ├── project.clj │ ├── src-clj │ └── example │ │ ├── routes.clj │ │ └── views.clj │ └── src-cljs │ └── example │ └── hello.cljs ├── plugin ├── project.clj ├── resources │ └── sample.project.clj ├── src │ └── leiningen │ │ ├── cljsbuild.clj │ │ └── cljsbuild │ │ ├── config.clj │ │ ├── jar.clj │ │ ├── subproject.clj │ │ └── util.clj └── test │ └── leiningen │ └── test │ ├── cljsbuild.clj │ └── cljsbuild │ ├── config.clj │ ├── jar.clj │ ├── subproject.clj │ └── util.clj ├── sample.project.clj └── support ├── project.clj ├── src └── cljsbuild │ ├── compiler.clj │ ├── crossover.clj │ ├── repl │ ├── listen.clj │ └── rhino.clj │ ├── test.clj │ └── util.clj └── test └── cljsbuild └── test ├── compiler.clj ├── crossover.clj ├── repl ├── listen.clj └── rhino.clj ├── test.clj └── util.clj /.gitignore: -------------------------------------------------------------------------------- 1 | .lein* 2 | pom.xml 3 | *.asc 4 | lib 5 | classes/ 6 | lein-cljsbuild-*.*.* 7 | cljsbuild-*.*.* 8 | target 9 | plugin/target 10 | support/target 11 | support/output-to 12 | example-projects/*/*.jar 13 | example-projects/simple/resources 14 | example-projects/advanced/resources/public/js 15 | example-projects/advanced/resources/private/js 16 | example-projects/advanced/.crossover-cljs 17 | example-projects/advanced/.repl* 18 | example-projects/*/classes 19 | example-projects/*/lib 20 | example-projects/*/target 21 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Source code distributed under the Eclipse Public License - v 1.0: 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 4 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF 5 | THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and 12 | documentation distributed under this Agreement, and 13 | 14 | b) in the case of each subsequent Contributor: 15 | 16 | i) changes to the Program, and 17 | 18 | ii) additions to the Program; 19 | 20 | where such changes and/or additions to the Program originate from and 21 | are distributed by that particular Contributor. A Contribution 22 | 'originates' from a Contributor if it was added to the Program by such 23 | Contributor itself or anyone acting on such Contributor's 24 | behalf. Contributions do not include additions to the Program which: 25 | (i) are separate modules of software distributed in conjunction with 26 | the Program under their own license agreement, and (ii) are not 27 | derivative works of the Program. 28 | 29 | "Contributor" means any person or entity that distributes the Program. 30 | 31 | "Licensed Patents" mean patent claims licensable by a Contributor 32 | which are necessarily infringed by the use or sale of its Contribution 33 | alone or when combined with the Program. 34 | 35 | "Program" means the Contributions distributed in accordance with this 36 | Agreement. 37 | 38 | "Recipient" means anyone who receives the Program under this 39 | Agreement, including all Contributors. 40 | 41 | 2. GRANT OF RIGHTS 42 | 43 | a) Subject to the terms of this Agreement, each Contributor hereby 44 | grants Recipient a non-exclusive, worldwide, royalty-free copyright 45 | license to reproduce, prepare derivative works of, publicly display, 46 | publicly perform, distribute and sublicense the Contribution of such 47 | Contributor, if any, and such derivative works, in source code and 48 | object code form. 49 | 50 | b) Subject to the terms of this Agreement, each Contributor hereby 51 | grants Recipient a non-exclusive, worldwide, royalty-free patent 52 | license under Licensed Patents to make, use, sell, offer to sell, 53 | import and otherwise transfer the Contribution of such Contributor, if 54 | any, in source code and object code form. This patent license shall 55 | apply to the combination of the Contribution and the Program if, at 56 | the time the Contribution is added by the Contributor, such addition 57 | of the Contribution causes such combination to be covered by the 58 | Licensed Patents. The patent license shall not apply to any other 59 | combinations which include the Contribution. No hardware per se is 60 | licensed hereunder. 61 | 62 | c) Recipient understands that although each Contributor grants the 63 | licenses to its Contributions set forth herein, no assurances are 64 | provided by any Contributor that the Program does not infringe the 65 | patent or other intellectual property rights of any other entity. Each 66 | Contributor disclaims any liability to Recipient for claims brought by 67 | any other entity based on infringement of intellectual property rights 68 | or otherwise. As a condition to exercising the rights and licenses 69 | granted hereunder, each Recipient hereby assumes sole responsibility 70 | to secure any other intellectual property rights needed, if any. For 71 | example, if a third party patent license is required to allow 72 | Recipient to distribute the Program, it is Recipient's responsibility 73 | to acquire that license before distributing the Program. 74 | 75 | d) Each Contributor represents that to its knowledge it has sufficient 76 | copyright rights in its Contribution, if any, to grant the copyright 77 | license set forth in this Agreement. 78 | 79 | 3. REQUIREMENTS 80 | 81 | A Contributor may choose to distribute the Program in object code form 82 | under its own license agreement, provided that: 83 | 84 | a) it complies with the terms and conditions of this Agreement; and 85 | 86 | b) its license agreement: 87 | 88 | i) effectively disclaims on behalf of all Contributors all warranties 89 | and conditions, express and implied, including warranties or 90 | conditions of title and non-infringement, and implied warranties or 91 | conditions of merchantability and fitness for a particular purpose; 92 | 93 | ii) effectively excludes on behalf of all Contributors all liability 94 | for damages, including direct, indirect, special, incidental and 95 | consequential damages, such as lost profits; 96 | 97 | iii) states that any provisions which differ from this Agreement are 98 | offered by that Contributor alone and not by any other party; and 99 | 100 | iv) states that source code for the Program is available from such 101 | Contributor, and informs licensees how to obtain it in a reasonable 102 | manner on or through a medium customarily used for software exchange. 103 | 104 | When the Program is made available in source code form: 105 | 106 | a) it must be made available under this Agreement; and 107 | 108 | b) a copy of this Agreement must be included with each copy of the Program. 109 | 110 | Contributors may not remove or alter any copyright notices contained 111 | within the Program. 112 | 113 | Each Contributor must identify itself as the originator of its 114 | Contribution, if any, in a manner that reasonably allows subsequent 115 | Recipients to identify the originator of the Contribution. 116 | 117 | 4. COMMERCIAL DISTRIBUTION 118 | 119 | Commercial distributors of software may accept certain 120 | responsibilities with respect to end users, business partners and the 121 | like. While this license is intended to facilitate the commercial use 122 | of the Program, the Contributor who includes the Program in a 123 | commercial product offering should do so in a manner which does not 124 | create potential liability for other Contributors. Therefore, if a 125 | Contributor includes the Program in a commercial product offering, 126 | such Contributor ("Commercial Contributor") hereby agrees to defend 127 | and indemnify every other Contributor ("Indemnified Contributor") 128 | against any losses, damages and costs (collectively "Losses") arising 129 | from claims, lawsuits and other legal actions brought by a third party 130 | against the Indemnified Contributor to the extent caused by the acts 131 | or omissions of such Commercial Contributor in connection with its 132 | distribution of the Program in a commercial product offering. The 133 | obligations in this section do not apply to any claims or Losses 134 | relating to any actual or alleged intellectual property 135 | infringement. In order to qualify, an Indemnified Contributor must: a) 136 | promptly notify the Commercial Contributor in writing of such claim, 137 | and b) allow the Commercial Contributor tocontrol, and cooperate with 138 | the Commercial Contributor in, the defense and any related settlement 139 | negotiations. The Indemnified Contributor may participate in any such 140 | claim at its own expense. 141 | 142 | For example, a Contributor might include the Program in a commercial 143 | product offering, Product X. That Contributor is then a Commercial 144 | Contributor. If that Commercial Contributor then makes performance 145 | claims, or offers warranties related to Product X, those performance 146 | claims and warranties are such Commercial Contributor's responsibility 147 | alone. Under this section, the Commercial Contributor would have to 148 | defend claims against the other Contributors related to those 149 | performance claims and warranties, and if a court requires any other 150 | Contributor to pay any damages as a result, the Commercial Contributor 151 | must pay those damages. 152 | 153 | 5. NO WARRANTY 154 | 155 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS 156 | PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 157 | KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY 158 | WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY 159 | OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely 160 | responsible for determining the appropriateness of using and 161 | distributing the Program and assumes all risks associated with its 162 | exercise of rights under this Agreement , including but not limited to 163 | the risks and costs of program errors, compliance with applicable 164 | laws, damage to or loss of data, programs or equipment, and 165 | unavailability or interruption of operations. 166 | 167 | 6. DISCLAIMER OF LIABILITY 168 | 169 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR 170 | ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, 171 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING 172 | WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF 173 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 174 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR 175 | DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED 176 | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 177 | 178 | 7. GENERAL 179 | 180 | If any provision of this Agreement is invalid or unenforceable under 181 | applicable law, it shall not affect the validity or enforceability of 182 | the remainder of the terms of this Agreement, and without further 183 | action by the parties hereto, such provision shall be reformed to the 184 | minimum extent necessary to make such provision valid and enforceable. 185 | 186 | If Recipient institutes patent litigation against any entity 187 | (including a cross-claim or counterclaim in a lawsuit) alleging that 188 | the Program itself (excluding combinations of the Program with other 189 | software or hardware) infringes such Recipient's patent(s), then such 190 | Recipient's rights granted under Section 2(b) shall terminate as of 191 | the date such litigation is filed. 192 | 193 | All Recipient's rights under this Agreement shall terminate if it 194 | fails to comply with any of the material terms or conditions of this 195 | Agreement and does not cure such failure in a reasonable period of 196 | time after becoming aware of such noncompliance. If all Recipient's 197 | rights under this Agreement terminate, Recipient agrees to cease use 198 | and distribution of the Program as soon as reasonably 199 | practicable. However, Recipient's obligations under this Agreement and 200 | any licenses granted by Recipient relating to the Program shall 201 | continue and survive. 202 | 203 | Everyone is permitted to copy and distribute copies of this Agreement, 204 | but in order to avoid inconsistency the Agreement is copyrighted and 205 | may only be modified in the following manner. The Agreement Steward 206 | reserves the right to publish new versions (including revisions) of 207 | this Agreement from time to time. No one other than the Agreement 208 | Steward has the right to modify this Agreement. The Eclipse Foundation 209 | is the initial Agreement Steward. The Eclipse Foundation may assign 210 | the responsibility to serve as the Agreement Steward to a suitable 211 | separate entity. Each new version of the Agreement will be given a 212 | distinguishing version number. The Program (including Contributions) 213 | may always be distributed subject to the version of the Agreement 214 | under which it was received. In addition, after a new version of the 215 | Agreement is published, Contributor may elect to distribute the 216 | Program (including its Contributions) under the new version. Except as 217 | expressly stated in Sections 2(a) and 2(b) above, Recipient receives 218 | no rights or licenses to the intellectual property of any Contributor 219 | under this Agreement, whether expressly, by implication, estoppel or 220 | otherwise. All rights in the Program not expressly granted under this 221 | Agreement are reserved. 222 | 223 | This Agreement is governed by the laws of the State of Washington and 224 | the intellectual property laws of the United States of America. No 225 | party to this Agreement will bring a legal action under this Agreement 226 | more than one year after the cause of action arose. Each party waives 227 | its rights to a jury trial in any resulting litigation. 228 | 229 | 230 | 231 | Images distributed under the Creative Commons Attribution + ShareAlike 232 | License version 3.0: 233 | 234 | THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS 235 | CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS 236 | PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE 237 | WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS 238 | PROHIBITED. 239 | 240 | BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND 241 | AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS 242 | LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU 243 | THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH 244 | TERMS AND CONDITIONS. 245 | 246 | 1. Definitions 247 | 248 | "Adaptation" means a work based upon the Work, or upon the Work 249 | and other pre-existing works, such as a translation, adaptation, 250 | derivative work, arrangement of music or other alterations of a 251 | literary or artistic work, or phonogram or performance and 252 | includes cinematographic adaptations or any other form in which 253 | the Work may be recast, transformed, or adapted including in any 254 | form recognizably derived from the original, except that a work 255 | that constitutes a Collection will not be considered an Adaptation 256 | for the purpose of this License. For the avoidance of doubt, where 257 | the Work is a musical work, performance or phonogram, the 258 | synchronization of the Work in timed-relation with a moving image 259 | ("synching") will be considered an Adaptation for the purpose of 260 | this License. 261 | 262 | "Collection" means a collection of literary or artistic works, 263 | such as encyclopedias and anthologies, or performances, phonograms 264 | or broadcasts, or other works or subject matter other than works 265 | listed in Section 1(f) below, which, by reason of the selection 266 | and arrangement of their contents, constitute intellectual 267 | creations, in which the Work is included in its entirety in 268 | unmodified form along with one or more other contributions, each 269 | constituting separate and independent works in themselves, which 270 | together are assembled into a collective whole. A work that 271 | constitutes a Collection will not be considered an Adaptation (as 272 | defined below) for the purposes of this License. 273 | 274 | "Creative Commons Compatible License" means a license that is 275 | listed at http://creativecommons.org/compatiblelicenses that has 276 | been approved by Creative Commons as being essentially equivalent 277 | to this License, including, at a minimum, because that license: 278 | (i) contains terms that have the same purpose, meaning and effect 279 | as the License Elements of this License; and, (ii) explicitly 280 | permits the relicensing of adaptations of works made available 281 | under that license under this License or a Creative Commons 282 | jurisdiction license with the same License Elements as this 283 | License. 284 | 285 | "Distribute" means to make available to the public the original 286 | and copies of the Work or Adaptation, as appropriate, through sale 287 | or other transfer of ownership. 288 | 289 | "License Elements" means the following high-level license 290 | attributes as selected by Licensor and indicated in the title of 291 | this License: Attribution, ShareAlike. 292 | 293 | "Licensor" means the individual, individuals, entity or entities 294 | that offer(s) the Work under the terms of this License. 295 | 296 | "Original Author" means, in the case of a literary or artistic 297 | work, the individual, individuals, entity or entities who created 298 | the Work or if no individual or entity can be identified, the 299 | publisher; and in addition (i) in the case of a performance the 300 | actors, singers, musicians, dancers, and other persons who act, 301 | sing, deliver, declaim, play in, interpret or otherwise perform 302 | literary or artistic works or expressions of folklore; (ii) in the 303 | case of a phonogram the producer being the person or legal entity 304 | who first fixes the sounds of a performance or other sounds; and, 305 | (iii) in the case of broadcasts, the organization that transmits 306 | the broadcast. 307 | 308 | "Work" means the literary and/or artistic work offered under the 309 | terms of this License including without limitation any production 310 | in the literary, scientific and artistic domain, whatever may be 311 | the mode or form of its expression including digital form, such as 312 | a book, pamphlet and other writing; a lecture, address, sermon or 313 | other work of the same nature; a dramatic or dramatico-musical 314 | work; a choreographic work or entertainment in dumb show; a 315 | musical composition with or without words; a cinematographic work 316 | to which are assimilated works expressed by a process analogous to 317 | cinematography; a work of drawing, painting, architecture, 318 | sculpture, engraving or lithography; a photographic work to which 319 | are assimilated works expressed by a process analogous to 320 | photography; a work of applied art; an illustration, map, plan, 321 | sketch or three-dimensional work relative to geography, 322 | topography, architecture or science; a performance; a broadcast; a 323 | phonogram; a compilation of data to the extent it is protected as 324 | a copyrightable work; or a work performed by a variety or circus 325 | performer to the extent it is not otherwise considered a literary 326 | or artistic work. 327 | 328 | "You" means an individual or entity exercising rights under this 329 | License who has not previously violated the terms of this License 330 | with respect to the Work, or who has received express permission 331 | from the Licensor to exercise rights under this License despite a 332 | previous violation. 333 | 334 | "Publicly Perform" means to perform public recitations of the Work 335 | and to communicate to the public those public recitations, by any 336 | means or process, including by wire or wireless means or public 337 | digital performances; to make available to the public Works in 338 | such a way that members of the public may access these Works from 339 | a place and at a place individually chosen by them; to perform the 340 | Work to the public by any means or process and the communication 341 | to the public of the performances of the Work, including by public 342 | digital performance; to broadcast and rebroadcast the Work by any 343 | means including signs, sounds or images. 344 | 345 | "Reproduce" means to make copies of the Work by any means 346 | including without limitation by sound or visual recordings and the 347 | right of fixation and reproducing fixations of the Work, including 348 | storage of a protected performance or phonogram in digital form or 349 | other electronic medium. 350 | 351 | 2. Fair Dealing Rights. Nothing in this License is intended to reduce, 352 | limit, or restrict any uses free from copyright or rights arising from 353 | limitations or exceptions that are provided for in connection with the 354 | copyright protection under copyright law or other applicable laws. 355 | 356 | 3. License Grant. Subject to the terms and conditions of this License, 357 | Licensor hereby grants You a worldwide, royalty-free, non-exclusive, 358 | perpetual (for the duration of the applicable copyright) license to 359 | exercise the rights in the Work as stated below: 360 | 361 | to Reproduce the Work, to incorporate the Work into one or more 362 | Collections, and to Reproduce the Work as incorporated in the 363 | Collections; 364 | 365 | to create and Reproduce Adaptations provided that any such 366 | Adaptation, including any translation in any medium, takes 367 | reasonable steps to clearly label, demarcate or otherwise identify 368 | that changes were made to the original Work. For example, a 369 | translation could be marked "The original work was translated from 370 | English to Spanish," or a modification could indicate "The 371 | original work has been modified."; 372 | 373 | to Distribute and Publicly Perform the Work including as 374 | incorporated in Collections; and, 375 | 376 | to Distribute and Publicly Perform Adaptations. 377 | 378 | For the avoidance of doubt: 379 | 380 | Non-waivable Compulsory License Schemes. In those 381 | jurisdictions in which the right to collect royalties through 382 | any statutory or compulsory licensing scheme cannot be waived, 383 | the Licensor reserves the exclusive right to collect such 384 | royalties for any exercise by You of the rights granted under 385 | this License; 386 | 387 | Waivable Compulsory License Schemes. In those jurisdictions in 388 | which the right to collect royalties through any statutory or 389 | compulsory licensing scheme can be waived, the Licensor waives 390 | the exclusive right to collect such royalties for any exercise 391 | by You of the rights granted under this License; and, 392 | 393 | Voluntary License Schemes. The Licensor waives the right to 394 | collect royalties, whether individually or, in the event that 395 | the Licensor is a member of a collecting society that 396 | administers voluntary licensing schemes, via that society, 397 | from any exercise by You of the rights granted under this 398 | License. 399 | 400 | The above rights may be exercised in all media and formats whether now 401 | known or hereafter devised. The above rights include the right to make 402 | such modifications as are technically necessary to exercise the rights 403 | in other media and formats. Subject to Section 8(f), all rights not 404 | expressly granted by Licensor are hereby reserved. 405 | 406 | 4. Restrictions. The license granted in Section 3 above is expressly 407 | made subject to and limited by the following restrictions: 408 | 409 | You may Distribute or Publicly Perform the Work only under the 410 | terms of this License. You must include a copy of, or the Uniform 411 | Resource Identifier (URI) for, this License with every copy of the 412 | Work You Distribute or Publicly Perform. You may not offer or 413 | impose any terms on the Work that restrict the terms of this 414 | License or the ability of the recipient of the Work to exercise 415 | the rights granted to that recipient under the terms of the 416 | License. You may not sublicense the Work. You must keep intact all 417 | notices that refer to this License and to the disclaimer of 418 | warranties with every copy of the Work You Distribute or Publicly 419 | Perform. When You Distribute or Publicly Perform the Work, You may 420 | not impose any effective technological measures on the Work that 421 | restrict the ability of a recipient of the Work from You to 422 | exercise the rights granted to that recipient under the terms of 423 | the License. This Section 4(a) applies to the Work as incorporated 424 | in a Collection, but this does not require the Collection apart 425 | from the Work itself to be made subject to the terms of this 426 | License. If You create a Collection, upon notice from any Licensor 427 | You must, to the extent practicable, remove from the Collection 428 | any credit as required by Section 4(c), as requested. If You 429 | create an Adaptation, upon notice from any Licensor You must, to 430 | the extent practicable, remove from the Adaptation any credit as 431 | required by Section 4(c), as requested. 432 | 433 | You may Distribute or Publicly Perform an Adaptation only under 434 | the terms of: (i) this License; (ii) a later version of this 435 | License with the same License Elements as this License; (iii) a 436 | Creative Commons jurisdiction license (either this or a later 437 | license version) that contains the same License Elements as this 438 | License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative 439 | Commons Compatible License. If you license the Adaptation under 440 | one of the licenses mentioned in (iv), you must comply with the 441 | terms of that license. If you license the Adaptation under the 442 | terms of any of the licenses mentioned in (i), (ii) or (iii) (the 443 | "Applicable License"), you must comply with the terms of the 444 | Applicable License generally and the following provisions: (I) You 445 | must include a copy of, or the URI for, the Applicable License 446 | with every copy of each Adaptation You Distribute or Publicly 447 | Perform; (II) You may not offer or impose any terms on the 448 | Adaptation that restrict the terms of the Applicable License or 449 | the ability of the recipient of the Adaptation to exercise the 450 | rights granted to that recipient under the terms of the Applicable 451 | License; (III) You must keep intact all notices that refer to the 452 | Applicable License and to the disclaimer of warranties with every 453 | copy of the Work as included in the Adaptation You Distribute or 454 | Publicly Perform; (IV) when You Distribute or Publicly Perform the 455 | Adaptation, You may not impose any effective technological 456 | measures on the Adaptation that restrict the ability of a 457 | recipient of the Adaptation from You to exercise the rights 458 | granted to that recipient under the terms of the Applicable 459 | License. This Section 4(b) applies to the Adaptation as 460 | incorporated in a Collection, but this does not require the 461 | Collection apart from the Adaptation itself to be made subject to 462 | the terms of the Applicable License. 463 | 464 | If You Distribute, or Publicly Perform the Work or any Adaptations 465 | or Collections, You must, unless a request has been made pursuant 466 | to Section 4(a), keep intact all copyright notices for the Work 467 | and provide, reasonable to the medium or means You are utilizing: 468 | (i) the name of the Original Author (or pseudonym, if applicable) 469 | if supplied, and/or if the Original Author and/or Licensor 470 | designate another party or parties (e.g., a sponsor institute, 471 | publishing entity, journal) for attribution ("Attribution 472 | Parties") in Licensor's copyright notice, terms of service or by 473 | other reasonable means, the name of such party or parties; (ii) 474 | the title of the Work if supplied; (iii) to the extent reasonably 475 | practicable, the URI, if any, that Licensor specifies to be 476 | associated with the Work, unless such URI does not refer to the 477 | copyright notice or licensing information for the Work; and (iv) , 478 | consistent with Ssection 3(b), in the case of an Adaptation, a 479 | credit identifying the use of the Work in the Adaptation (e.g., 480 | "French translation of the Work by Original Author," or 481 | "Screenplay based on original Work by Original Author"). The 482 | credit required by this Section 4(c) may be implemented in any 483 | reasonable manner; provided, however, that in the case of a 484 | Adaptation or Collection, at a minimum such credit will appear, if 485 | a credit for all contributing authors of the Adaptation or 486 | Collection appears, then as part of these credits and in a manner 487 | at least as prominent as the credits for the other contributing 488 | authors. For the avoidance of doubt, You may only use the credit 489 | required by this Section for the purpose of attribution in the 490 | manner set out above and, by exercising Your rights under this 491 | License, You may not implicitly or explicitly assert or imply any 492 | connection with, sponsorship or endorsement by the Original 493 | Author, Licensor and/or Attribution Parties, as appropriate, of 494 | You or Your use of the Work, without the separate, express prior 495 | written permission of the Original Author, Licensor and/or 496 | Attribution Parties. 497 | 498 | Except as otherwise agreed in writing by the Licensor or as may be 499 | otherwise permitted by applicable law, if You Reproduce, 500 | Distribute or Publicly Perform the Work either by itself or as 501 | part of any Adaptations or Collections, You must not distort, 502 | mutilate, modify or take other derogatory action in relation to 503 | the Work which would be prejudicial to the Original Author's honor 504 | or reputation. Licensor agrees that in those jurisdictions 505 | (e.g. Japan), in which any exercise of the right granted in 506 | Section 3(b) of this License (the right to make Adaptations) would 507 | be deemed to be a distortion, mutilation, modification or other 508 | derogatory action prejudicial to the Original Author's honor and 509 | reputation, the Licensor will waive or not assert, as appropriate, 510 | this Section, to the fullest extent permitted by the applicable 511 | national law, to enable You to reasonably exercise Your right 512 | under Section 3(b) of this License (right to make Adaptations) but 513 | not otherwise. 514 | 515 | 5. Representations, Warranties and Disclaimer 516 | 517 | UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, 518 | LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR 519 | WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, 520 | STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF 521 | TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, 522 | NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, 523 | OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT 524 | DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED 525 | WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. 526 | 527 | 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY 528 | APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY 529 | LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR 530 | EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, 531 | EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 532 | 533 | 7. Termination 534 | 535 | This License and the rights granted hereunder will terminate 536 | automatically upon any breach by You of the terms of this 537 | License. Individuals or entities who have received Adaptations or 538 | Collections from You under this License, however, will not have 539 | their licenses terminated provided such individuals or entities 540 | remain in full compliance with those licenses. Sections 1, 2, 5, 541 | 6, 7, and 8 will survive any termination of this License. 542 | 543 | Subject to the above terms and conditions, the license granted 544 | here is perpetual (for the duration of the applicable copyright in 545 | the Work). Notwithstanding the above, Licensor reserves the right 546 | to release the Work under different license terms or to stop 547 | distributing the Work at any time; provided, however that any such 548 | election will not serve to withdraw this License (or any other 549 | license that has been, or is required to be, granted under the 550 | terms of this License), and this License will continue in full 551 | force and effect unless terminated as stated above. 552 | 553 | 8. Miscellaneous 554 | 555 | Each time You Distribute or Publicly Perform the Work or a 556 | Collection, the Licensor offers to the recipient a license to the 557 | Work on the same terms and conditions as the license granted to 558 | You under this License. 559 | 560 | Each time You Distribute or Publicly Perform an Adaptation, 561 | Licensor offers to the recipient a license to the original Work on 562 | the same terms and conditions as the license granted to You under 563 | this License. 564 | 565 | If any provision of this License is invalid or unenforceable under 566 | applicable law, it shall not affect the validity or enforceability 567 | of the remainder of the terms of this License, and without further 568 | action by the parties to this agreement, such provision shall be 569 | reformed to the minimum extent necessary to make such provision 570 | valid and enforceable. 571 | 572 | No term or provision of this License shall be deemed waived and no 573 | breach consented to unless such waiver or consent shall be in 574 | writing and signed by the party to be charged with such waiver or 575 | consent. 576 | 577 | This License constitutes the entire agreement between the parties 578 | with respect to the Work licensed here. There are no 579 | understandings, agreements or representations with respect to the 580 | Work not specified here. Licensor shall not be bound by any 581 | additional provisions that may appear in any communication from 582 | You. This License may not be modified without the mutual written 583 | agreement of the Licensor and You. 584 | 585 | The rights granted under, and the subject matter referenced, in 586 | this License were drafted utilizing the terminology of the Berne 587 | Convention for the Protection of Literary and Artistic Works (as 588 | amended on September 28, 1979), the Rome Convention of 1961, the 589 | WIPO Copyright Treaty of 1996, the WIPO Performances and 590 | Phonograms Treaty of 1996 and the Universal Copyright Convention 591 | (as revised on July 24, 1971). These rights and subject matter 592 | take effect in the relevant jurisdiction in which the License 593 | terms are sought to be enforced according to the corresponding 594 | provisions of the implementation of those treaty provisions in the 595 | applicable national law. If the standard suite of rights granted 596 | under applicable copyright law includes additional rights not 597 | granted under this License, such additional rights are deemed to 598 | be included in the License; this License is not intended to 599 | restrict the license of any rights under applicable law. 600 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lein-cljsbuild 2 | 3 | #### Latest version: 4 | [![Clojars Project](https://clojars.org/lein-cljsbuild/latest-version.svg)](http://clojars.org/lein-cljsbuild) 5 | 6 | This is a Leiningen plugin that makes it quick and easy to automatically compile 7 | your ClojureScript code into Javascript whenever you modify it. It's simple 8 | to install and allows you to configure the ClojureScript compiler from within your 9 | `project.clj` file. 10 | 11 | Beyond basic compiler support, lein-cljsbuild can optionally help with a few other things: 12 | 13 | * [Launching REPLs for interactive development](https://github.com/emezeske/lein-cljsbuild/blob/1.1.8/doc/REPL.md) 14 | * [Launching ClojureScript tests](https://github.com/emezeske/lein-cljsbuild/blob/1.1.8/doc/TESTING.md) 15 | 16 | The latest version of lein-cljsbuild is `1.1.8`. 17 | [See the release notes here.](https://github.com/emezeske/lein-cljsbuild/blob/1.1.8/doc/RELEASE-NOTES.md) 18 | 19 | *Note that cljsbuild crossovers are deprecated, and will be removed eventually. 20 | You should never use them. Please use either [reader conditionals](http://dev.clojure.org/display/design/Reader+Conditionals) 21 | (available in Clojure >= 1.7.0-beta2 and ClojureScript >= 0.0-3255), or [cljx](http://github.com/lynaghk/cljx) 22 | to target both Clojure and ClojureScript from the same codebase.* 23 | 24 | **If you are using ClojureScript >= 1.7.170 you need to use a lein-cljsbuild version >= 1.1.1.** 25 | 26 | ## Requirements 27 | 28 | The lein-cljsbuild plugin works with 29 | [Leiningen](https://github.com/technomancy/leiningen/blob/master/README.md) 30 | version `2.1.2` or higher. 31 | 32 | ## Installation 33 | 34 | You can install the plugin by adding lein-cljsbuild to your `project.clj` 35 | file in the `:plugins` section: 36 | 37 | ```clj 38 | (defproject lein-cljsbuild-example "1.2.3" 39 | :plugins [[lein-cljsbuild "1.1.8"]]) 40 | ``` 41 | 42 | In addition, _you should add an explicit ClojureScript dependency to your 43 | project using the ClojureScript version you want to use_: 44 | 45 | ```clojure 46 | :dependencies [[org.clojure/clojurescript "1.9.521"]] 47 | ``` 48 | 49 | lein-cljsbuild will add a dependency to your project if it doesn't already 50 | contain one, but that functionality will not remain for long. The latest 51 | version of lein-cljsbuild currently requires a minimum of ClojureScript 52 | `0.0-3211`. 53 | 54 | ## Just Give Me a Damned Example Already! 55 | 56 | See the 57 | [example-projects](https://github.com/emezeske/lein-cljsbuild/blob/1.1.8/example-projects) 58 | directory for a couple of simple examples of how to use lein-cljsbuild. The 59 | [simple project](https://github.com/emezeske/lein-cljsbuild/blob/1.1.8/example-projects/simple) 60 | shows a dead-simple "compile only" configuration, which is a good place to start. The 61 | [advanced project](https://github.com/emezeske/lein-cljsbuild/blob/1.1.8/example-projects/advanced) 62 | contains examples of how to use the extended features of the plugin. 63 | 64 | For an exhaustive list of all options supported by lein-cljsbuild, see the 65 | [sample.project.clj](https://github.com/emezeske/lein-cljsbuild/blob/1.1.8/sample.project.clj) 66 | file. For a list of options that can be passed to the ClojureScript compiler have a look at the 67 | [ClojureScript Compiler Options site](https://clojurescript.org/reference/compiler-options). 68 | 69 | ## Basic Configuration 70 | 71 | The lein-cljsbuild configuration is specified under the `:cljsbuild` section 72 | of your `project.clj` file. A simple project might look like this: 73 | 74 | ```clj 75 | (defproject lein-cljsbuild-example "1.2.3" 76 | :plugins [[lein-cljsbuild "1.1.8"]] 77 | :cljsbuild { 78 | :builds [{ 79 | ; The path to the top-level ClojureScript source directory: 80 | :source-paths ["src-cljs"] 81 | ; The standard ClojureScript compiler options: 82 | ; (See the ClojureScript compiler documentation for details.) 83 | :compiler { 84 | :output-to "war/javascripts/main.js" ; default: target/cljsbuild-main.js 85 | :optimizations :whitespace 86 | :pretty-print true}}]}) 87 | ``` 88 | ## Basic Usage 89 | 90 | Once the plugin is installed, you can build the ClojureScript once: 91 | 92 | $ lein cljsbuild once 93 | 94 | Or you can have lein-cljsbuild watch your source files for changes and 95 | automatically rebuild them. This is recommended for development, as it 96 | avoids the time-consuming JVM startup for each build: 97 | 98 | $ lein cljsbuild auto 99 | 100 | Assuming you have configured cljsbuild to emit compiler output to one of 101 | Leiningen's `:clean-targets` (which includes `./target` by default), running 102 | `lein clean` will delete all of the JavaScript and ClojureScript files that lein-cljsbuild 103 | generates during compilation. 104 | 105 | ### Color-coded output on Windows 106 | 107 | Colors are a big deal when reading ClojureScript compiler output, but Windows 108 | consoles don't support ANSI color codes. This limitation is commonly corrected by 109 | installing ANSICON: 110 | 111 | 1. Download and unzip [ANSICON](https://github.com/downloads/adoxa/ansicon/ansi160.zip) anywhere. 112 | 2. Open a command prompt (Run as administrator). 113 | 3. Navigate to the unzipped folder. 114 | 4. cd x86 or x64 (depending on whether you have 32-bit or 64-bit machine, respectively) 115 | 5. Run `ansicon -i` to install. 116 | 117 | Afterwards, you should get colored output from all future console sessions that use ANSI color codes. 118 | 119 | ## Hooks 120 | 121 | Some common lein-cljsbuild tasks can hook into the main Leiningen tasks 122 | to enable ClojureScript support in each of them. The following tasks are 123 | supported: 124 | 125 | $ lein compile 126 | $ lein test 127 | $ lein jar 128 | 129 | To enable ClojureScript support for these tasks, add the following entry to 130 | your project configuration: 131 | 132 | ```clj 133 | :hooks [leiningen.cljsbuild] 134 | ``` 135 | 136 | Note that by default the `lein jar` task does *not* package your ClojureScript 137 | code in the JAR file. This feature needs to be explicitly enabled by adding 138 | the following entry to each of the `:builds` that you want included in the 139 | JAR file. `lein uberjar` derives its behavior from `lein jar` and will include 140 | the ClojureScript as well if enabled. 141 | 142 | ```clj 143 | :jar true 144 | ``` 145 | 146 | > **Debug Note:** There is a known issue (#366) where the `lein uberjar` task fails 147 | > to build when using hooks and a cljsbuild configuration within an `:uberjar` 148 | > profile. Instead of hooks, you can use `:prep-tasks` as an alternative: 149 | > 150 | >```clojure 151 | >:prep-tasks ["compile" ["cljsbuild" "once"]] 152 | >``` 153 | 154 | ## Multiple Build Configurations 155 | 156 | If the `:builds` sequence contains more than one map lein-cljsbuild 157 | will treat each map as a separate ClojureScript compiler configuration, 158 | and will build all of them in parallel: 159 | 160 | ```clj 161 | (defproject lein-cljsbuild-example "1.2.3" 162 | :plugins [[lein-cljsbuild "1.1.8"]] 163 | :cljsbuild { 164 | :builds [ 165 | {:source-paths ["src-cljs-main"] 166 | :compiler {:output-to "main.js"}} 167 | {:source-paths ["src-cljs-other"] 168 | :compiler {:output-to "other.js"}}]}) 169 | ``` 170 | 171 | This is extremely convenient for doing library development in ClojureScript. 172 | This allows cljsbuild to compile in all four optimization levels at once, for 173 | easier testing, or to compile a test suite alongside the library code. 174 | 175 | You can optionally assign an ID to a build configuration and build 176 | only that one: 177 | 178 | ```clj 179 | (defproject lein-cljsbuild-example "1.2.3" 180 | :plugins [[lein-cljsbuild "1.1.8"]] 181 | :cljsbuild { 182 | :builds [ 183 | {:source-paths ["src-cljs-main"] 184 | :compiler {:output-to "main.js"}} 185 | {:id "other" 186 | :source-paths ["src-cljs-other"] 187 | :compiler {:output-to "other.js"}}]}) 188 | ``` 189 | 190 | $ lein cljsbuild auto other 191 | 192 | If you want IDs for all of your build configurations, you can specify 193 | them as a map instead of a vector: 194 | 195 | ```clj 196 | (defproject lein-cljsbuild-example "1.2.3" 197 | :plugins [[lein-cljsbuild "1.1.8"]] 198 | :cljsbuild { 199 | :builds { 200 | :main 201 | {:source-paths ["src-cljs-main"] 202 | :compiler {:output-to "main.js"}} 203 | :other 204 | {:source-paths ["src-cljs-other"] 205 | :compiler {:output-to "other.js"}}}}) 206 | ``` 207 | 208 | You can also build multiple configurations at once: 209 | 210 | $ lein cljsbuild auto main other 211 | 212 | See the 213 | [example-projects/advanced](https://github.com/emezeske/lein-cljsbuild/blob/1.1.8/example-projects/advanced) 214 | directory for a working example of a project that uses this feature. 215 | 216 | ## REPL Support 217 | 218 | Lein-cljsbuild has built-in support for launching ClojureScript REPLs in a variety 219 | of ways. See the 220 | [REPL documentation](https://github.com/emezeske/lein-cljsbuild/blob/1.1.8/doc/REPL.md) 221 | for more details. 222 | 223 | ## Testing Support 224 | 225 | Lein-cljsbuild has built-in support for running external ClojureScript test processes. See the 226 | [testing documentation](https://github.com/emezeske/lein-cljsbuild/blob/1.1.8/doc/TESTING.md) 227 | for more details. 228 | 229 | ## Extended Configuration 230 | 231 | ### Custom warning handlers 232 | 233 | You can place custom warning handlers for the ClojureScript compiler under the `:warning-handlers` key. The value should be a vector of either 1.) fully-qualified symbols that resolve to your custom handler, or 2.) anonymous functions that will get eval'd at project build-time. 234 | 235 | ```clj 236 | (defproject lein-cljsbuild-example "1.2.3" 237 | :plugins [[lein-cljsbuild "1.0.4"]] 238 | :cljsbuild { 239 | :builds {:id "example" 240 | :compiler {} 241 | :warning-handlers [my.ns/custom-warning-handler ;; Fully-qualified symbol 242 | ;; Custom function (to be evaluated at project build-time) 243 | (fn [warning-type env extra] 244 | (when-let [s (cljs.analyzer/error-message warning-type extra)] 245 | (binding [*out* *err*] 246 | (cljs.analyzer/message env s))))]}}) 247 | ``` 248 | 249 | 250 | ## ClojureScript Version 251 | 252 | After configuring lein-cljsbuild, `lein deps` will fetch a known-good version of the ClojureScript compiler. 253 | You can use a different version of the compiler via a local clone of the ClojureScript git repository. 254 | See [the wiki](https://github.com/emezeske/lein-cljsbuild/wiki/Using-a-Git-Checkout-of-the-ClojureScript-Compiler) for details. 255 | 256 | ## License 257 | 258 | Source Copyright © Evan Mezeske, 2011-2013. 259 | Released under the Eclipse Public License - v 1.0. 260 | See the file COPYING. 261 | 262 | ## Contributors 263 | 264 | A big thank you to all [contributors](https://github.com/emezeske/lein-cljsbuild/graphs/contributors) who help to make this project better. 265 | -------------------------------------------------------------------------------- /bin/bump-versions.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | 4 | CURRENT_VERSION=$1 5 | NEXT_VERSION=$2 6 | 7 | for f in `find . -name project.clj` README.md `find doc -name '*.md' | grep -v RELEASE-NOTES.md`; do 8 | sed -i '' s/$CURRENT_VERSION/$NEXT_VERSION/ $f 9 | done 10 | -------------------------------------------------------------------------------- /bin/deploy.sh: -------------------------------------------------------------------------------- 1 | 2 | pushd support 3 | lein do clean, deploy clojars 4 | popd 5 | 6 | pushd plugin 7 | lein do clean, deploy clojars 8 | popd 9 | -------------------------------------------------------------------------------- /bin/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Release a specific branch to master. 4 | 5 | set -e 6 | 7 | branch=$1 8 | if [ -z "$1" ]; then 9 | echo "Usage: $0 ." 10 | exit 1 11 | fi 12 | 13 | read -p "Are you sure you want to release $branch to master (y/n)? " -n 1 reply 14 | echo 15 | if [[ ! $reply =~ ^[Yy]$ ]]; then 16 | echo 'Aborting.' 17 | exit 1 18 | fi 19 | 20 | echo "Releasing $branch." 21 | git checkout master 22 | git merge $branch 23 | git push --all 24 | git checkout $branch 25 | git tag -m "Release ${branch}." $branch 26 | git push --tags 27 | git checkout master 28 | git branch -D $branch 29 | git push origin :refs/heads/$branch 30 | 31 | bin/deploy.sh 32 | 33 | -------------------------------------------------------------------------------- /bin/test-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Build the lein-cljsbuild plugin and support JARs, install them 4 | # locally, and test them in the simple and advanced example projects. 5 | # If arguments are specified, only build the example projects they name. 6 | 7 | set -e 8 | 9 | projects="$@" 10 | [ -z "$projects" ] && projects='simple advanced' 11 | project_root=$(dirname $0)/.. 12 | pushd $project_root 13 | 14 | rm -rf ~/.m2/repository/lein-cljsbuild ~/.m2/repository/cljsbuild/ 15 | for d in support plugin; do 16 | pushd $d 17 | lein do clean, install, test 18 | popd 19 | done 20 | for d in $projects; do 21 | pushd example-projects/$d 22 | if [ $d = advanced ]; then 23 | extra_command=', cljsbuild test' 24 | fi 25 | lein do clean, cljsbuild once$extra_command 26 | popd 27 | done 28 | -------------------------------------------------------------------------------- /doc/CROSSOVERS.md: -------------------------------------------------------------------------------- 1 | # Sharing Code Between Clojure >= 1.7.0-beta2 and ClojureScript >= 0.0-3255 2 | 3 | A very simple setup is to 4 | 5 | - put all shared code clojure files in a separate folder (without other cljs 6 | or clj files) 7 | - adapt their namespace accordingly 8 | - change their file ending to cljc and 9 | - refer to the folder in the `:source-paths` settings of both the main project 10 | and the cljsbuild. 11 | 12 | You can then refer to the files via ordinary `require` from cljs and clj files. 13 | 14 | Once you hit the need for platform specific code, please refer 15 | to [reader conditionals](http://dev.clojure.org/display/design/Reader+Conditionals) 16 | 17 | # Sharing Code Between Clojure and ClojureScript (deprecated) 18 | 19 | **cljsbuild crossovers are _deprecated_, and will be removed in v2.x.** 20 | Please use either [reader 21 | conditionals](http://dev.clojure.org/display/design/Reader+Conditionals) 22 | (available in Clojure 23 | >= 1.7.0-beta2 and ClojureScript >= 0.0-3255), or 24 | >[cljx](http://github.com/lynaghk/cljx) to 25 | target both Clojure and ClojureScript from the same codebase. 26 | 27 | Sharing code with lein-cljsbuild is accomplished via the configuration 28 | of "crossovers". A crossover specifies a Clojure namespace, the content 29 | of which should be copied into your ClojureScript project. This can be any 30 | namespace that is available via the Java CLASSPATH, which includes your 31 | project's main :source-paths by default. 32 | 33 | When a crossover namespace is provided by your current project (either via the 34 | main `:source-dir` or one of the `:extra-classpath-dirs` in your project.clj file), 35 | the files that make up that namespace (recursively) will be monitored for changes, 36 | and will be copied to the ClojureScript project whenever modified. 37 | 38 | Crossover namespaces provided by jar files cannot be searched recursively, and 39 | thus must be specified on a per-file basis. They are copied over once, when 40 | lein-cljsbuild begins compilation, and are not monitored for changes. 41 | 42 | Of course, remember that since the namespace will be used by both Clojure 43 | and ClojureScript, it will need to only use the subset of features provided by 44 | both languages. 45 | 46 | Assuming that your top-level directory structure looks something like this: 47 | 48 |
 49 | └── src-clj
 50 |     └── example
 51 |         ├── core.clj
 52 |         ├── something.clj
 53 |         └── crossover
 54 |             ├── some_stuff.clj
 55 |             └── some_other_stuff.clj
 56 | 
57 | 58 | And your `project.clj` file looks like this: 59 | 60 | ```clj 61 | (defproject lein-cljsbuild-example "1.2.3" 62 | :plugins [[lein-cljsbuild "1.1.8"]] 63 | :source-paths ["src-clj"] 64 | :cljsbuild { 65 | ; Each entry in the :crossovers vector describes a Clojure namespace 66 | ; that is meant to be used with the ClojureScript code as well. 67 | ; The files that make up this namespace will be automatically copied 68 | ; into the ClojureScript source path whenever they are modified. 69 | :crossovers [example.crossover] 70 | ; Set the path into which the crossover namespaces will be copied. 71 | :crossover-path "crossover-cljs" 72 | ; Set this to true to allow the :crossover-path to be copied into 73 | ; the JAR file (if hooks are enabled). 74 | :crossover-jar false}) 75 | ``` 76 | 77 | Then lein-cljsbuild would copy files from `src-clj/example/crossover` 78 | to `crossover-cljs`, and you'd end up with this: 79 | 80 |
 81 | ├── src-clj
 82 | │   └── example
 83 | │       ├── a_file.clj
 84 | │       ├── core.clj
 85 | │       └── crossover
 86 | │           ├── some_stuff.clj
 87 | │           └── some_other_stuff.clj
 88 | └── crossover-cljs
 89 |     └── example
 90 |         └── crossover
 91 |             ├── some_stuff.cljs
 92 |             └── some_other_stuff.cljs
 93 | 
94 | 95 | Notice that the files in the `crossover-cljs` directory have had their extensions 96 | modified so that they will be seen by the ClojureScript compiler. The `crossover-cljs` 97 | directory will automatically be added to the classpath for the ClojureScript compiler, 98 | so your other ClojureScript code should be able to reference it via a regular `(ns)` form. 99 | 100 | With this setup, you would probably want to add `crossover-cljs` 101 | to your `.gitignore` file (or equivalent), as its contents are updated automatically 102 | by lein-cljsbuild. 103 | 104 | # Sharing Macros Between Clojure and ClojureScript 105 | 106 | In ClojureScript, macros are still written in Clojure, and can not be written 107 | in the same file as actual ClojureScript code. Also, to use them in a ClojureScript 108 | namespace, they must be required via `:require-macros` rather than the usual `:require`. 109 | 110 | This makes using the crossover feature to share macros between Clojure and ClojureScript 111 | a bit difficult, but lein-cljsbuild has some special constructs to make it possible. 112 | 113 | Three things need to be done to use lein-cljsbuild to share macros. 114 | 115 | ## 1. Keep Macros in Separate Files 116 | 117 | These examples assume that your project uses the `src-clj/example/crossover` 118 | directory, and that all of the macros are in a file called 119 | `src-clj/example/crossover/macros.clj`. 120 | 121 | ## 2. Tell lein-cljsbuild Which Files Contain Macros 122 | 123 | Add this magical comment to any crossover files that contain macros: 124 | 125 | ```clj 126 | ;*CLJSBUILD-MACRO-FILE*; 127 | ``` 128 | 129 | This tells lein-cljsbuild to refrain from copying the `.clj` files 130 | into the `:crossover-path`. This step can be skipped if the 131 | macro file is not included in any of the crossover namespaces. 132 | 133 | ## 3. Use Black Magic to Require Macros Specially 134 | 135 | In any crossover Clojure file, lein-cljsbuild will automatically erase the 136 | following string when copying the Clojure file into the `:crossover-path`: 137 | 138 | ```clj 139 | ;*CLJSBUILD-REMOVE*; 140 | ``` 141 | 142 | This magic can be used to generate a `ns` statement that can successfully 143 | reference a macro namespace from both Clojure and ClojureScript: 144 | 145 | ```clj 146 | (ns example.crossover.some_stuff 147 | (:require;*CLJSBUILD-REMOVE*;-macros 148 | [example.crossover.macros :as macros])) 149 | ``` 150 | 151 | Thus, after removing comments, Clojure will see: 152 | 153 | ```clj 154 | (ns example.crossover.some_stuff 155 | (:require 156 | [example.crossover.macros :as macros])) 157 | ``` 158 | 159 | However, lein-cljsbuild will remove the `;*CLJSBUILD-REMOVE*;` string entirely, 160 | before copying the file. Thus, ClojureScript will see: 161 | 162 | ```clj 163 | (ns example.crossover.some_stuff 164 | (:require-macros 165 | [example.crossover.macros :as macros])) 166 | ``` 167 | 168 | And thus the macros can be shared. 169 | -------------------------------------------------------------------------------- /doc/MIGRATING-TO-0.1.x.md: -------------------------------------------------------------------------------- 1 | # Migrating from 0.0.x to 0.1.x 2 | 3 | The `0.1.x` release of lein-cljsbuild adds several new features that necessitate 4 | some changes to the format of the `:cljsbuild` configuration entry. 5 | 6 | With that said, `0.1.x` **is** backwards-compatible with your existing configuration. 7 | However, it will be complaining loudly about how it's deprecated. This document 8 | explains how to fix that. 9 | 10 | The next major release will drop backwards compatibilty with `0.0.x`, so get 11 | your project updated soon! 12 | 13 | ## The Easy Way 14 | 15 | Before doing anything else, `lein cljsbuild clean`. This will remove old temporary files 16 | that may be renamed in the new version. Most importantly, it will remove any crossover files 17 | that have been copied into place. **This is very important. If you are using crossovers, 18 | skipping this step will cause pain!** 19 | 20 | Once you've updated your project to use `[lein-cljsbuild "0.1.x"]`, run any subcommand, 21 | for instance, `lein cljsbuild once`. The plugin will complain that your configuration 22 | is deprecated. However, it should automatically convert your `:cljsbuild` entry to the new 23 | format, and print it out. 24 | 25 | You should be able to just replace your existing `:cljsbuild` entry with this one and be all set. 26 | 27 | ## The Hard Way 28 | 29 | In case you want to know specifically what has changed: 30 | 31 | * Several plugin-global options have been added. Thus, what used to be the top-level 32 | config has been moved into the `:builds` entry. 33 | 34 | * The `:builds` entry is not allowed to be either a map or a vector, like the `:cljsbuild` 35 | entry used to be. It is always a vector of maps. 36 | 37 | * The `:output-dir` entry for each of the `:compiler` entries must now be unique. Previously, they 38 | could be set to the same directory, which caused a nasty race condition (and failed compiles). 39 | 40 | * `:crossovers` are no longer specified per-build -- they are global to the whole project. 41 | Instead of being copied into a `:source-path`, they're all copied into a single `:crossover-path`. 42 | -------------------------------------------------------------------------------- /doc/RELEASE-NOTES.md: -------------------------------------------------------------------------------- 1 | # Release Notes for lein-cljsbuild 2 | 3 | ## [1.1.8](https://github.com/emezeske/lein-cljsbuild/pulls?utf8=%E2%9C%93&q=is%3Aclosed+is%3Apr+milestone%3A1.1.8) 4 | 5 | * Improved checkout handling when cljsbuild entry is missing [#473](https://github.com/emezeske/lein-cljsbuild/pull/473) 6 | * Add `lein cljsbuild deps` task to install cljsbuild deps to local Maven repo [#497](https://github.com/emezeske/lein-cljsbuild/pull/497) 7 | 8 | ## [1.1.7](https://github.com/emezeske/lein-cljsbuild/pulls?utf8=%E2%9C%93&q=is%3Aclosed+is%3Apr+milestone%3A1.1.7) 9 | 10 | * When no output-dir given use relative target-path instead of absolute target-path. A relative target path is required when building for nodejs [#464](https://github.com/emezeske/lein-cljsbuild/issues/464). 11 | * Use full project map to build subproject. Previously, some project options (e.g. :root and :prep-tasks) weren't included causing problems such as [#465](https://github.com/emezeske/lein-cljsbuild/issues/465) and [#434](https://github.com/emezeske/lein-cljsbuild/issues/434). 12 | * No :output-to default needed when :modules option provided. Fix auto build for modules. [#467](https://github.com/emezeske/lein-cljsbuild/issues/467) 13 | 14 | ## [1.1.6](https://github.com/emezeske/lein-cljsbuild/pulls?utf8=%E2%9C%93&q=is%3Aclosed+is%3Apr+milestone%3A1.1.6) 15 | 16 | * Fix #453: use ProcessBuilder for output redirect (https://github.com/emezeske/lein-cljsbuild/pull/459) 17 | * Fix Markdown issues (https://github.com/emezeske/lein-cljsbuild/pull/460, https://github.com/emezeske/lein-cljsbuild/pull/461) 18 | * Update sample.project.clj doc to reflect default :optimizations value (https://github.com/emezeske/lein-cljsbuild/pull/456) 19 | * Fix #455: use clojure.tools.namespace for reloading (https://github.com/emezeske/lein-cljsbuild/issues/455) 20 | 21 | ## [1.1.5](https://github.com/emezeske/lein-cljsbuild/pulls?utf8=%E2%9C%93&q=is%3Aclosed+is%3Apr+milestone%3A1.1.5) 22 | 23 | * Add support for lein 2.7's `:managed-dependencies` (https://github.com/emezeske/lein-cljsbuild/pull/454) 24 | * Fix: 1.1.4 causes cljsbuild to sometimes compile sources in other build configs (https://github.com/emezeske/lein-cljsbuild/issues/451) 25 | 26 | ## [1.1.4](https://github.com/emezeske/lein-cljsbuild/pulls?utf8=%E2%9C%93&q=is%3Aclosed+is%3Apr+milestone%3A1.1.4) 27 | 28 | * Fix bad ns form with regard to core specs 29 | * Use full project config to create subproject 30 | 31 | ## [1.1.3](https://github.com/emezeske/lein-cljsbuild/pulls?utf8=%E2%9C%93&q=is%3Aclosed+is%3Apr+milestone%3A1.1.3) 32 | 33 | * Remove `System.exit` after compilation 34 | * Remove cljs-compat 35 | 36 | ## [1.1.2](https://github.com/emezeske/lein-cljsbuild/pulls?utf8=%E2%9C%93&q=is%3Aclosed+is%3Apr+milestone%3A1.1.2) 37 | 38 | * Avoid calls to cljs.build.api/src-file->target_file on deps.cljs 39 | 40 | ## [1.1.1](https://github.com/emezeske/lein-cljsbuild/pulls?utf8=%E2%9C%93&q=is%3Aclosed+is%3Apr+milestone%3A1.1.1) 41 | 42 | * Use ClojureScript build API to build project 43 | * Add support for mirrors project attribute 44 | * Update advanced example project to work with PhantomJS 2.0 45 | 46 | ## [1.1.0](https://github.com/emezeske/lein-cljsbuild/issues?q=milestone%3A1.1.0+is%3Aclosed) 47 | 48 | * Eliminated all remaining non-default ClojureScript compiler options 49 | * Changes in Leiningen `checkouts` directories now are taken into account when 50 | triggering builds under `cljsbuild auto` 51 | 52 | ## [1.0.6](https://github.com/emezeske/lein-cljsbuild/issues?q=milestone%3A1.0.6+is%3Aclosed) 53 | 54 | * Added support for 55 | [reader conditionals](http://dev.clojure.org/display/design/Reader+Conditionals) 56 | and the new `.cljc` file type. 57 | * Completely removed the `cljsbuild clean` subtask. 58 | * cljsbuild configurations now have no default `:optimizations` setting beyond 59 | the ClojureScript compiler's own defaults (net effect currently: 60 | `:optimizations :none`, unless specified otherwise in your configs) 61 | 62 | ## [1.0.5](https://github.com/emezeske/lein-cljsbuild/issues?q=milestone%3A1.0.5) 63 | 64 | * Added `:warning-handlers` option so that compiler warnings/errors/messages can 65 | be displayed or potentially fail the build (gh-370, gh-367) 66 | * Stop attempting to reload `data_readers.clj` (gh-360) 67 | * Failing tests now properly cause cljsbuild to exit Leiningen with a non-zero 68 | error code (gh-356, regression in `1.0.4`) 69 | * Fix the version compatibility checks to allow any SNAPSHOT version of 70 | cljsbuild. (gh-346) 71 | 72 | ## [1.0.4](https://github.com/emezeske/lein-cljsbuild/issues?q=milestone%3A1.0.4+is%3Aclosed) 73 | 74 | * Refactoring applied to minimize the amount of rework performed by 75 | `cljsbuild auto`, yielding significant workflow responsiveness improvements 76 | (gh-349, gh-348, gh-345) 77 | * The `cljsbuild clean` subtask is now a no-op. You should use `lein clean` to 78 | remove prior compilation results, and ensure that your output configurations 79 | direct those results into a Leiningen `:clean-target` (which includes 80 | `./target` by default) (gh-297) 81 | 82 | ## [1.0.3](https://github.com/emezeske/lein-cljsbuild/issues?milestone=36&page=1&state=closed) 83 | 84 | * ClojureScript `0.0-2197` is now the default release added to projects that do 85 | not specify a ClojureScript dependency explicitly, and is also the new minimum 86 | required version of ClojureScript. 87 | * Older releases of lein-cljsbuild will only work with ClojureScript versions 88 | `0.0-2014` - `0.0-2173`. 89 | * Fixed a problem where explicitly-specified Clojure and ClojureScript 90 | dependencies were being overridden by lein-cljsbuild's defaults (gh-298) 91 | * The `:local-repo` `project.clj` configuration (if one exists) is now passed on 92 | to subprocesses spawned by lein-cljsbuild (gh-291) 93 | 94 | ## [1.0.2](https://github.com/emezeske/lein-cljsbuild/issues?milestone=35&page=1&state=closed) 95 | 96 | * ClojureScript `0.0-2156` is now the default release added to projects that do 97 | not specify a ClojureScript dependency explicitly. 98 | * Crossovers are now officially deprecated, and will be removed in future 99 | releases. Please use [cljx](http://github.com/lynaghk/cljx) to target both 100 | Clojure and ClojureScript from the same codebase. 101 | * Fixed timestamps applied to compiled output files (gh-281, gh-282) 102 | 103 | ## [1.0.1](https://github.com/emezeske/lein-cljsbuild/issues?milestone=34&page=1&state=closed) 104 | 105 | * Output files are now touched to change their mtime to match when the last 106 | compilation was _started_, so that `cljsbuild auto` will pick up any source 107 | file modifications made during the compile (gh-277) 108 | * `:jvm-opts` defined in `project.clj` is now used in all lein-cljsbuild tasks 109 | (gh-271) 110 | * The order of project dependencies as specified in `project.clj` is now 111 | preserved for the duration of all lein-cljsbuild tasks (gh-268) 112 | 113 | ## 1.0.0 114 | 115 | No changes since `1.0.0-alpha2`. 116 | 117 | ## [1.0.0-alpha2](https://github.com/emezeske/lein-cljsbuild/issues?milestone=33&page=1&state=closed) 118 | 119 | * ClojureScript `0.0-2014` is now the default release added to projects that do 120 | not specify a ClojureScript dependency explicitly. Further, this version of 121 | ClojureScript is now a **minimum requirement**; if upgrading to `0.0-2014` is 122 | not possible for some reason, you must stick with prior revisions of 123 | lein-cljsbuild. (gh-253, gh-264) 124 | * lein-cljsbuild will now halt your build if you are declaring a ClojureScript 125 | dependency that is known to be incompatible with the version of lein-cljsbuild 126 | you are using. (gh-266) 127 | * All tasks are now run with a max Java heap of 256MB. (gh-261) 128 | 129 | ## [1.0.0-alpha1](https://github.com/emezeske/lein-cljsbuild/issues?milestone=29&state=closed) 130 | 131 | * The name of each `:test-command` entry is now printed prior to that test being 132 | run. (gh-244) 133 | * Fixed regression where `cljsbuild auto` would exit on a compilation error 134 | (gh-249) 135 | * Fixed regression where tests would halt on a failing `:test-command`, rather 136 | than proceeding through all. (gh-252) 137 | 138 | ## [0.3.4](https://github.com/emezeske/lein-cljsbuild/issues?milestone=32&state=closed) 139 | 140 | * Added new `sample` subtask that emits the contents of the `sample.project.clj` 141 | file detailing cljsbuild's options (gh-232) 142 | * Hard compilation failures now properly fail the broader Leiningen 143 | invocation (gh-234) 144 | * Any non-string values in a `:test-command` vector now properly cause a failure 145 | (gh-243) 146 | * The `:output-wrapper` ClojureScript compiler option is now defaulted to `true` 147 | if the `:optimizations` option is set to `:advanced` (gh-201) 148 | * Fixed an issue where case-sensitivity of the drive letter on windows prevented 149 | the proper relativization of paths to Clojure files containing macros to be 150 | (re)loaded (gh-240) 151 | * Test runs now properly fail if any `:test-commands` vector contains any 152 | non-string values (gh-243) 153 | 154 | ## [0.3.3](https://github.com/emezeske/lein-cljsbuild/issues?milestone=28&state=closed) 155 | 156 | 1. Changed to use upstream ClojureScript version 0.0-1859. 157 | 2. cljsbuild now warns if you have not explicitly specified a ClojureScript 158 | dependency in your project. (gh-224) 159 | 3. The file scanning associated with `cljsbuild auto` has been improved 160 | significantly, and should now represent a negligible CPU load. (gh-219) 161 | 4. Under `cljsbuild auto`, Clojure files are now only reloaded if they define 162 | macros. (gh-210) 163 | 5. A sane error message is now emitted if you attempt to run a nonexistent 164 | cljsbuild task (gh-215) 165 | 6. Various documentation and example project tweaks. 166 | 167 | ## 0.3.2 168 | 169 | 1. Changed to use upstream ClojureScript version 0.0-1806. 170 | 171 | ## 0.3.1 172 | 173 | 1. Changed to use upstream ClojureScript version 0.0-1803. 174 | 2. Updated the Clojure version used in the plugin to 1.5.1. 175 | 3. This plugin version requires Leiningen version 2.1.2 or higher. 176 | 4. Fix `lein cljsbuild test` so that it exits quickly (i.e. without a 30-second delay). 177 | 5. Hide the Clojure stacktrace when ClojureScript unit tests fail. 178 | 179 | ## 0.3.0 180 | 181 | 1. Dropped support for Leiningen 1.x, now that Leiningen 2.0 is available. **REPEAT: Leiningen 1.x is no longer supported.** 182 | 2. Changed the `:source-path "path"` option to `:source-paths ["path" "path"]`. The new option accepts a vector of paths rather than a single path. **The old singular `:source-path` is now deprecated and will be removed soon.** 183 | 3. Changed all default output paths (e.g. for temporary compiler files, crossover files, and REPL files) to be in the `:target-path` directory. Explicitly configured paths can still be whatever you like. 184 | 4. For compatibility with Leiningen 2.0, `:resource-paths` is now used instead of `:resources-path`. 185 | 5. Fixed a long delay before exiting that could sometimes occur after one-shot tasks (like `cljsbuild once`). 186 | 6. Changes to JavaScript files in `:libs` now trigger rebuilds when using `cljsbuild auto`. 187 | 7. Removed CLOSURE_NO_DEPS notes from example projects, as it is no longer necessary. 188 | 189 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?milestone=25&state=closed) 190 | 191 | ## 0.2.10 192 | 193 | 1. Changed to use upstream ClojureScript version 0.0-1552. 194 | 2. Balanced the parenthesis and square braces in the README correctly. 195 | 3. Added a workaround for an unresolved upstream compiler issue: http://dev.clojure.org/jira/browse/CLJS-418. 196 | 4. Modifications to JavaScript files specified in the :libs compiler option will now cause :builds to be rebuilt. 197 | 198 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?milestone=24&state=closed) 199 | 200 | ## 0.2.9 201 | 202 | 1. Changed to use upstream ClojureScript version 0.0-1513. 203 | 2. Changed to use clj-stacktrace version 0.2.5. 204 | 205 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?milestone=23&state=closed) 206 | 207 | ## 0.2.8 208 | 209 | 1. Fixed a bug where a `RejectedExecutionException` could be thrown if hooks were enabled and the project was run via `lein trampoline`. 210 | 2. Added the ability to set `clojure.core/*assert*` via the `:assert` option in each `:builds` map. 211 | 3. Fixed a bug where if exceptions would not be caught if thrown while automatically reloading a Clojure file (e.g. containing macros). 212 | 4. Changed to use upstream ClojureScript version 0.0-1503. 213 | 214 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?milestone=22&state=closed) 215 | 216 | ## 0.2.7 217 | 218 | 1. Fix a bug introduced in 0.2.6 that broke the `cljsbuild jar` task for Leiningen 2.x. 219 | 2. Alleviate the need for the parent project to specify a Clojure version. 220 | 221 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?milestone=21&state=closed) 222 | 223 | ## 0.2.6 224 | 225 | 1. Updated to support recent preview releases of Leiningen 2. 226 | 227 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?milestone=20&state=closed) 228 | 229 | ## 0.2.5 230 | 231 | 1. Changed to use upstream ClojureScript version 0.0-1450. 232 | 233 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?milestone=19&state=closed) 234 | 235 | ## 0.2.4 236 | 237 | 1. Removed support for `:warn-on-undeclared`, because the compiler itself now supports a `:warnings` option. Use that instead. 238 | 239 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?milestone=18&state=closed) 240 | 241 | ## 0.2.3 242 | 243 | 1. Changed to use upstream ClojureScript version 0.0-1443. 244 | 2. Ignore hidden files in the source and crossover paths (this makes things work better with emacs or other editors that use dotfiles for state). 245 | 246 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?milestone=17&state=closed) 247 | 248 | ## 0.2.2 249 | 250 | 1. Changed to use upstream ClojureScript version 0.0-1424. 251 | 2. Fixed an issue with copying crossover files under Windows. 252 | 253 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?milestone=16&state=closed) 254 | 255 | ## 0.2.1 256 | 257 | 1. Automatically add `closure-js/libs` to `:libs` and `closure-js/externs` to `:externs`. This means that libraries can put their libs and externs in `resources/closure-js/libs/` and `resources/closure-js/externs/`, respectively, and lein-cljsbuild will automatically pick them up. 258 | 259 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?milestone=15&state=closed) 260 | 261 | ## 0.2.0 262 | 263 | Note that the minor version was incremented not due to any major features, but due to the fact that the `:notify-command` option was changed in a backwards-incompatible way. 264 | 265 | 1. The compiler is now run under Clojure 1.4.0. 266 | 2. Added a new `:build` suboption `:incremental`, which determines whether intermediate JavaScript sources are left intact between automatic builds. The old behavior was to delete intermediate files before each build. This slowed things down, but worked around (unisolated) problems with incremental compilation. Incremental builds are now the default, as they appear to work well, but this option allows the old behavior to be selected if necessary for troubleshooting. 267 | 3. The :notify option has been changed such that its argument is consistent with other cljsbuild shell commands. This means that the `%` argument is no longer respected, and the textual result will simply be appended as the last command line argument. Also, `:beep true` no longer has any effect. If either of these features is desired, the recommended solution is a small shell script wrapper. 268 | 4. Clojure source files that reside in the ClojureScript :source-path (as well as crossover macro files) are now monitored for changes. When modified, they will be reloaded, and a build will be triggered. This is useful for ClojureScript projects that use macros. 269 | 5. Multiple builds are now built sequentially instead of in parallel. This is due to the fact that the underlying compiler is no longer thread-safe. 270 | 271 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?milestone=14&state=closed) 272 | 273 | ## 0.1.10 274 | 275 | 1. Changed to use upstream ClojureScript version 0.0-1236. 276 | 277 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?milestone=13&state=closed) 278 | 279 | ## 0.1.9 280 | 281 | 1. Changed to use upstream ClojureScript version 0.0-1211. 282 | 2. Updated example projects to use the latest Clojure, Ring, Compojure, and Hiccup versions. 283 | 284 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?milestone=12&state=closed) 285 | 286 | ## 0.1.8 287 | 288 | 1. Minor fix for compatibility with the latest Leiningen 2 preview. 289 | 290 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?sort=created&direction=desc&state=closed&page=1&milestone=11) 291 | 292 | ## 0.1.7 293 | 294 | 1. The various REPL commands now work when used via Leiningen 2. This should mean that lein-cljsbuild is fully Leiningen-2-compatible. 295 | 2. Raise a descriptive error if the parent project uses Clojure < 1.3. 296 | 3. Ensure that `lein cljsbuild clean` cleans up :stdout and :stderr files for various commands. 297 | 4. Add a comprehensive unit test suite, to hopefully help prevent new releases from breaking things. 298 | 299 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?sort=created&direction=desc&state=closed&page=1&milestone=10) 300 | 301 | ## 0.1.6 302 | 303 | 1. Changed to use upstream ClojureScript version 0.0-1011. This should fix REPL issues. 304 | 305 | [Milestone Details for this Release](https://github.com/emezeske/lein-cljsbuild/issues?sort=created&direction=desc&state=closed&page=1&milestone=9) 306 | -------------------------------------------------------------------------------- /doc/REPL.md: -------------------------------------------------------------------------------- 1 | # REPL Support 2 | 3 | Lein-cljsbuild has built-in support for launching ClojureScript REPLs in a variety 4 | of ways. Note as of Leiningen 1.x, all plugin REPL commands must be launched 5 | via `lein trampoline` to work correctly. 6 | 7 | ## repl-rhino 8 | 9 | The simplest REPL uses Rhino to evaluate the JavaScript that results from compiling 10 | your ClojureScript input. This REPL does not have access to your project's namespaces, 11 | and is not evaluated in a browser context. It is, however, useful for running simple 12 | pure-ClojureScript commands. It is also the only REPL option that does not require 13 | your application to provide additional support code: 14 | 15 | $ lein trampoline cljsbuild repl-rhino 16 | 17 | ## repl-listen 18 | 19 | The next REPL is more sophisticated. With support from your application, it is possible 20 | to use `repl-listen` to run a REPL with access to your application's ClojureScript namespaces. 21 | The REPL may be started as follows: 22 | 23 | $ lein trampoline cljsbuild repl-listen 24 | 25 | This will open a REPL, which will listen on a TCP port for a ClojureScript application 26 | to connect to it. By default, it will listen on port `9000`, although this may be changed 27 | via the `:repl-listen-port` option. Until a connection is received, the REPL will not be 28 | usable. 29 | 30 | From your application, you can connect to the REPL from your Clojurescript code with code such as this: 31 | 32 | ```clj 33 | (ns lein-cljsbuild-example.repl 34 | (:require [clojure.browser.repl :as repl])) 35 | ; Use of "localhost" will only work for local development. 36 | ; Change the port to match the :repl-listen-port. 37 | (repl/connect "http://localhost:9000/repl") 38 | ``` 39 | 40 | For instance, you might include the above call to `repl/connect` in the code for 41 | a particular web page served by your application. So, you would launch your application, 42 | launch `repl-listen`, and then browse to that page. It would then connect to the REPL, 43 | enabling you to execute commands in the context of that page. 44 | 45 | For more information on this approach, see the 46 | [ClojureScript Wiki](https://github.com/clojure/clojurescript/wiki/Quick-Start). 47 | 48 | ## repl-launch 49 | 50 | This is the most sophisticated REPL. Like `repl-listen`, `repl-launch` requires 51 | application support code to function. The difference between `repl-listen` and `repl-launch` 52 | is that the latter may be configured to automatically launch the browser after starting 53 | the REPL. This REPL is launched as follows: 54 | 55 | $ lein trampoline cljsbuild repl-launch 56 | 57 | Of course, this won't work until you've told lein-cljsbuild what to launch. Multiple 58 | launch presets may be created, and thus the `` parameter is used to select 59 | between them. To configure a launch preset, add an entry to the `:repl-launch-commands` map: 60 | 61 | ```clj 62 | (defproject lein-cljsbuild-example "1.2.3" 63 | :plugins [[lein-cljsbuild "1.1.8"]] 64 | :cljsbuild { 65 | :repl-listen-port 9000 66 | :repl-launch-commands 67 | {"my-launch" ["firefox" "-jsconsole" "http://localhost/my-page"]}) 68 | ``` 69 | 70 | With this configuration, the launch preset is identified by `my-launch`. When a REPL 71 | is started with this launch preset, `firefox -jsconsole http://localhost/my-page` 72 | will be run in the background, and presumably the page it loads will connect to the REPL: 73 | 74 | $ lein trampoline cljsbuild repl-launch my-launch 75 | 76 | Note that any additional arguments following the `` will be passed to the 77 | launch command. Thus, with a configuration such as: 78 | 79 | ```clj 80 | :repl-launch-commands 81 | {"my-other-launch" ["firefox" "-jsconsole"]} 82 | ``` 83 | 84 | The target URL could be selected like so: 85 | 86 | $ lein trampoline cljsbuild repl-launch my-other-launch http://localhost/another-page 87 | 88 | By default, your launch command's standard output and error streams will be streamed 89 | to the console. This can be problematic, as it can spam your console session and 90 | disrupt things. Thus, you may want to redirect the command's output to a file. 91 | 92 | If a keyword appears in the command vector, it and all following entries will be 93 | treated as an option map. Currently, the only supported options are 94 | :stdout and :stderr, which allow you to redirect the command's output to files. 95 | 96 | ```clj 97 | :repl-launch-commands 98 | {"my-launch" ["firefox" :stdout "my-stdout.txt" :stderr "my-stderr"]} 99 | ``` 100 | 101 | For more ideas on how to use `repl-launch`, take a look at the 102 | [advanced example project](https://github.com/emezeske/lein-cljsbuild/blob/1.1.8/example-projects/advanced) 103 | It has several examples of useful launch commands, with descriptions in its README. 104 | Note that, in particular, the possibilities with 105 | [PhantomJS](http://www.phantomjs.org) 106 | are very intriguing. 107 | -------------------------------------------------------------------------------- /doc/TESTING.md: -------------------------------------------------------------------------------- 1 | # Testing Support 2 | 3 | Lein-cljsbuild has built-in support for running external ClojureScript test processes. 4 | Test commands are configured via the `:test-commands` map. Each key in this map names 5 | a test command, and each value is a vector representing a shell command to run for that 6 | test: 7 | 8 | ```clj 9 | (defproject lein-cljsbuild-example "1.2.3" 10 | :plugins [[lein-cljsbuild "1.1.8"]] 11 | :cljsbuild { 12 | :test-commands 13 | {"my-test" ["phantomjs" "phantom/unit-test.js" "..."]}) 14 | ``` 15 | 16 | This example creates one test named "my-test", that will run `phantomjs` with a couple 17 | of arguments. The following command will run this test: 18 | 19 | $ lein cljsbuild test my-test 20 | 21 | Alternately, this command will run *all* configured tests: 22 | 23 | $ lein cljsbuild test 24 | 25 | If [hooks](https://github.com/emezeske/lein-cljsbuild#hooks) are enabled, this will also result in the tests being run: 26 | 27 | $ lein test 28 | 29 | ## Testing with PhantomJS 30 | 31 | For ClojureScript code that targets web browsers (as opposed to Node.js), it is often 32 | useful to run unit tests in the context of a web browser. This allows code that has 33 | side-effects (e.g. DOM manipulation) to be tested. 34 | 35 | [PhantomJS](http://www.phantomjs.org) is a headless Webkit-based browser with full 36 | JavaScript support. This means that it can do most anything you would expect a desktop 37 | browser to do, except it does not have a GUI. PhantomJS can be automated via JavaScript, 38 | and thus is convenient to use for running automated tests. 39 | 40 | The [advanced example project](https://github.com/emezeske/lein-cljsbuild/blob/1.1.8/example-projects/advanced) 41 | contains an example of how to use PhantomJS for running ClojureScript tests. There are several 42 | components that come together to make this work: 43 | 44 | 1. A `:builds` entry dedicated to the test code in the `test-cljs` directory. This compiles 45 | the unit tests into JavaScript (so that it they can be run by PhantomJS). 46 | 47 | 2. A 48 | [static HTML page](https://github.com/emezeske/lein-cljsbuild/blob/1.1.8/example-projects/advanced/resources/private/html/unit-test.html) 49 | with a ` 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example-projects/advanced/resources/private/html/unit-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | This is just a dummy HTML file with which to load the unit tests. 4 | This file could be changed to include HTML for the tests to use 5 | during their operation. 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example-projects/advanced/src-clj/example/macros.clj: -------------------------------------------------------------------------------- 1 | (ns example.macros) 2 | 3 | (defmacro reverse-eval [form] 4 | (reverse form)) 5 | -------------------------------------------------------------------------------- /example-projects/advanced/src-clj/example/routes.clj: -------------------------------------------------------------------------------- 1 | (ns example.routes 2 | (:use compojure.core 3 | example.views 4 | [hiccup.middleware :only (wrap-base-url)]) 5 | (:require [compojure.route :as route] 6 | [compojure.handler :as handler] 7 | [compojure.response :as response])) 8 | 9 | (defroutes main-routes 10 | (GET "/" [] (index-page)) 11 | (GET "/repl-demo" [] (repl-demo-page)) 12 | (route/resources "/") 13 | (route/not-found "Page not found")) 14 | 15 | (def app 16 | (-> (handler/site main-routes) 17 | (wrap-base-url))) 18 | -------------------------------------------------------------------------------- /example-projects/advanced/src-clj/example/views.clj: -------------------------------------------------------------------------------- 1 | (ns example.views 2 | (:require 3 | [example.shared :as shared] 4 | [hiccup 5 | [page :refer [html5]] 6 | [element :refer [javascript-tag]] 7 | [page :refer [include-js]]])) 8 | 9 | (defn- run-clojurescript [path init] 10 | (list 11 | (include-js path) 12 | (javascript-tag init))) 13 | 14 | (defn index-page [] 15 | (html5 16 | [:head 17 | [:title (shared/make-example-text)] ] 18 | [:body 19 | [:h1 (shared/make-example-text)] 20 | (run-clojurescript 21 | "/js/main-debug.js" 22 | "example.hello.say_hello()")])) 23 | 24 | (defn repl-demo-page [] 25 | (html5 26 | [:head 27 | [:title "REPL Demo"]] 28 | [:body 29 | [:h1 "REPL Demo"] 30 | [:hr] 31 | "This page is meant to be accessed by running this in one terminal:" 32 | [:pre "lein ring server-headless 3000"] 33 | "And then this in a different terminal:" 34 | [:pre "lein trampoline cljsbuild repl-launch firefox http://localhost:3000/repl-demo"] 35 | [:hr] 36 | "Alternately, you can run:" 37 | [:pre "lein ring server-headless 3000 & 38 | lein trampoline cljsbuild repl-listen"] 39 | "And then browse to this page manually."] 40 | [:hr] 41 | [:h2 {:id "fun"} "Try some fun REPL commands!"] 42 | [:pre "> (js/alert \"Hello!\") 43 | > (.log js/console (reduce + [1 2 3 4 5])) 44 | > (load-namespace 'goog.dom) 45 | > (goog.dom.setTextContent (goog.dom.getElement \"fun\") \"I changed something....\") "] 46 | (run-clojurescript 47 | "/js/main-debug.js" 48 | "example.repl.connect()"))) 49 | -------------------------------------------------------------------------------- /example-projects/advanced/src-cljc/example/shared.cljc: -------------------------------------------------------------------------------- 1 | (ns example.shared 2 | #?(:clj (:require 3 | [example.macros :as macros])) 4 | #?(:cljs (:require-macros [example.macros :as macros]))) 5 | 6 | (defn make-example-text [] 7 | (macros/reverse-eval 8 | ("code" "shared " "from the " "Hello " str))) 9 | -------------------------------------------------------------------------------- /example-projects/advanced/src-cljs/example/hello.cljs: -------------------------------------------------------------------------------- 1 | (ns example.hello 2 | (:require 3 | [example.shared :as shared])) 4 | 5 | (defn ^:export say-hello [] 6 | (js/alert (shared/make-example-text))) 7 | 8 | (defn add-some-numbers [& numbers] 9 | (apply + numbers)) 10 | -------------------------------------------------------------------------------- /example-projects/advanced/src-cljs/example/repl.cljs: -------------------------------------------------------------------------------- 1 | (ns example.repl 2 | (:require 3 | [clojure.browser.repl :as repl])) 4 | 5 | (defn ^:export connect [] 6 | (repl/connect "http://localhost:9000/repl")) 7 | -------------------------------------------------------------------------------- /example-projects/advanced/test-cljs/example/test.cljs: -------------------------------------------------------------------------------- 1 | (ns example.test 2 | (:require [example.test.hello :as hello])) 3 | 4 | (def success 0) 5 | 6 | (defn ^:export run [] 7 | (.log js/console "Example test started.") 8 | (hello/run) 9 | success) 10 | -------------------------------------------------------------------------------- /example-projects/advanced/test-cljs/example/test/hello.cljs: -------------------------------------------------------------------------------- 1 | (ns example.test.hello 2 | (:use [example.hello :only [add-some-numbers]])) 3 | 4 | (defn run [] 5 | (assert (= (add-some-numbers 2 2) 4)) 6 | (assert (= (add-some-numbers 1 2 3) 6)) 7 | (assert (= (add-some-numbers 4 5 6) 15))) 8 | -------------------------------------------------------------------------------- /example-projects/none/.gitignore: -------------------------------------------------------------------------------- 1 | pom.xml 2 | *jar 3 | /lib/ 4 | /classes/ 5 | /out/ 6 | /target/ 7 | .lein-deps-sum 8 | .lein-repl-history 9 | .lein-plugins/ 10 | -------------------------------------------------------------------------------- /example-projects/none/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example-projects/none/project.clj: -------------------------------------------------------------------------------- 1 | (defproject none "0.1.0-SNAPSHOT" 2 | :description "FIXME: write this!" 3 | :url "http://example.com/FIXME" 4 | 5 | :dependencies [[org.clojure/clojure "1.8.0"] 6 | [org.clojure/clojurescript "1.9.521"]] 7 | 8 | :plugins [[lein-cljsbuild "1.1.8"]] 9 | 10 | :source-paths ["src"] 11 | 12 | :cljsbuild { 13 | :builds [{:id "none" 14 | :source-paths ["src"] 15 | :compiler { 16 | ;; Outputs main file as none.js in current directory 17 | ;; This file mainly consists of code loading other files 18 | :output-to "none.js" 19 | ;; Where all the other files are stored. This folder must 20 | ;; be accessible from your web page, as it will be loaded 21 | ;; from JavaScript 22 | :output-dir "out" 23 | ;; The :none option is much faster than the other ones, 24 | ;; and is the only one to provide correct srouce-maps. 25 | :optimizations :none 26 | ;; source-maps are used by the browser to show the 27 | ;; ClojureScript code in the debugger 28 | :source-map true}}]}) 29 | -------------------------------------------------------------------------------- /example-projects/none/src/none/core.cljs: -------------------------------------------------------------------------------- 1 | (ns none.core) 2 | 3 | (enable-console-print!) 4 | 5 | (println "Hello world!") 6 | -------------------------------------------------------------------------------- /example-projects/simple/README.md: -------------------------------------------------------------------------------- 1 | # cljsbuild-example-simple 2 | 3 | This is an example web application that uses [lein-cljsbuild][1], 4 | [Ring][2], and [Compojure][3]. It demonstrates the use of 5 | lein-cljsbuild to build ClojureScript into JavaScript. 6 | 7 | To play around with this example project, you will first need 8 | [Leiningen][4] installed. 9 | 10 | ## Running the App 11 | 12 | Set up and start the server like this: 13 | 14 | $ cd example-projects/simple 15 | $ lein cljsbuild once 16 | $ lein ring server-headless 3000 17 | 18 | Now, point your web browser at `http://localhost:3000`, and see the web app in action! 19 | 20 | [1]: https://github.com/emezeske/lein-cljsbuild 21 | [2]: https://github.com/ring-clojure/ring 22 | [3]: https://github.com/weavejester/compojure 23 | [4]: https://github.com/technomancy/leiningen 24 | -------------------------------------------------------------------------------- /example-projects/simple/project.clj: -------------------------------------------------------------------------------- 1 | (defproject cljsbuild-example-simple "1.1.8" 2 | :description "A simple example of how to use lein-cljsbuild" 3 | :source-paths ["src-clj"] 4 | :dependencies [[org.clojure/clojure "1.8.0"] 5 | [org.clojure/clojurescript "1.9.521" 6 | :exclusions [org.apache.ant/ant]] 7 | [compojure "1.1.6"] 8 | [hiccup "1.0.4"]] 9 | :plugins [[lein-cljsbuild "1.1.8"] 10 | [lein-ring "0.8.7"]] 11 | :cljsbuild { 12 | :builds [{:source-paths ["src-cljs"] 13 | :compiler {:output-to "resources/public/js/main.js" 14 | :optimizations :whitespace 15 | :pretty-print true}}]} 16 | :ring {:handler example.routes/app}) 17 | -------------------------------------------------------------------------------- /example-projects/simple/src-clj/example/routes.clj: -------------------------------------------------------------------------------- 1 | (ns example.routes 2 | (:use compojure.core 3 | example.views 4 | [hiccup.middleware :only (wrap-base-url)]) 5 | (:require [compojure.route :as route] 6 | [compojure.handler :as handler] 7 | [compojure.response :as response])) 8 | 9 | (defroutes main-routes 10 | (GET "/" [] (index-page)) 11 | (route/resources "/") 12 | (route/not-found "Page not found")) 13 | 14 | (def app 15 | (-> (handler/site main-routes) 16 | (wrap-base-url))) 17 | -------------------------------------------------------------------------------- /example-projects/simple/src-clj/example/views.clj: -------------------------------------------------------------------------------- 1 | (ns example.views 2 | (:require 3 | [hiccup 4 | [page :refer [html5]] 5 | [page :refer [include-js]]])) 6 | 7 | (defn index-page [] 8 | (html5 9 | [:head 10 | [:title "Hello World"] 11 | (include-js "/js/main.js")] 12 | [:body 13 | [:h1 "Hello World"]])) 14 | -------------------------------------------------------------------------------- /example-projects/simple/src-cljs/example/hello.cljs: -------------------------------------------------------------------------------- 1 | (ns example.hello) 2 | 3 | (js/alert "Hello from ClojureScript!") 4 | -------------------------------------------------------------------------------- /plugin/project.clj: -------------------------------------------------------------------------------- 1 | (defproject lein-cljsbuild "1.1.8" 2 | :description "ClojureScript Autobuilder Plugin" 3 | :url "http://github.com/emezeske/lein-cljsbuild" 4 | :license 5 | {:name "Eclipse Public License - v 1.0" 6 | :url "http://www.eclipse.org/legal/epl-v10.html" 7 | :distribution :repo} 8 | :dependencies [[fs "1.1.2"]] 9 | :profiles { 10 | :dev { 11 | :dependencies [ 12 | [midje "1.6.3"] 13 | [cljsbuild "1.1.8"]] 14 | :plugins [[lein-midje "3.1.3"]]}} 15 | :eval-in-leiningen true) 16 | -------------------------------------------------------------------------------- /plugin/resources/sample.project.clj: -------------------------------------------------------------------------------- 1 | ../../sample.project.clj -------------------------------------------------------------------------------- /plugin/src/leiningen/cljsbuild.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.cljsbuild 2 | "Compile ClojureScript source into a JavaScript file." 3 | (:refer-clojure :exclude [test]) 4 | (:require 5 | [fs.core :as fs] 6 | [leiningen.cljsbuild.config :as config] 7 | [leiningen.cljsbuild.jar :as jar] 8 | [leiningen.cljsbuild.subproject :as subproject] 9 | [leiningen.compile :as lcompile] 10 | [leiningen.core.eval :as leval] 11 | [leiningen.core.project :as lproject] 12 | [leiningen.core.main :as lmain] 13 | [leiningen.help :as lhelp] 14 | [leiningen.jar :as ljar] 15 | [leiningen.test :as ltest] 16 | [leiningen.trampoline :as ltrampoline] 17 | [robert.hooke :as hooke] 18 | [clojure.java.io :as io] 19 | [clojure.string :as string])) 20 | 21 | (def ^:private repl-output-path "repl") 22 | 23 | (def ^:dynamic *suppress-exit?* false) 24 | 25 | (defn- exit 26 | ([code] 27 | (if-not *suppress-exit?* 28 | `(System/exit ~code) 29 | (when-not (zero? code) 30 | `(throw (ex-info "Suppress exit" {:exit-code ~code}))))) 31 | ([] (exit 0))) 32 | 33 | (defn- run-local-project [project crossover-path builds requires form] 34 | (leval/eval-in-project (subproject/make-subproject project crossover-path builds) 35 | `(try 36 | ~form 37 | (catch cljsbuild.test.TestsFailedException e# 38 | ; Do not print stack trace on test failure 39 | ~(exit 1)) 40 | (catch Exception e# 41 | (do 42 | (.printStackTrace e#) 43 | ~(exit 1)))) 44 | requires)) 45 | 46 | (defn- read-dependency-project [project-file] 47 | (when (fs/exists? project-file) 48 | (let [project (fs/absolute-path project-file)] 49 | (try 50 | (lproject/read project) 51 | (catch Exception e 52 | (throw (Exception. (format "Problem loading %s" project) e))))))) 53 | 54 | (defn- read-checkouts 55 | [project] 56 | (let [checkouts-dir (io/file (:root project) "checkouts")] 57 | (for [dep (fs/list-dir checkouts-dir) 58 | :let [project-file (io/file checkouts-dir dep "project.clj") 59 | checkout-project (read-dependency-project project-file)] 60 | :when checkout-project] 61 | checkout-project))) 62 | 63 | (defn- walk-checkouts [root-project f] 64 | (loop [[project & rest] (read-checkouts root-project) 65 | walk-results []] 66 | (if project 67 | (recur (concat (read-checkouts project) rest) 68 | (concat (f project) walk-results)) 69 | walk-results))) 70 | 71 | (defn- checkout-cljs-paths [project] 72 | (walk-checkouts 73 | project 74 | (fn [checkout] 75 | (if-let [{:keys [builds]} (config/extract-options checkout)] 76 | (for [build builds 77 | path (:source-paths build)] 78 | (fs/absolute-path (io/file (:root checkout) path))))))) 79 | 80 | (defn- run-compiler [project {:keys [crossover-path crossovers builds]} build-ids watch?] 81 | (doseq [build-id build-ids] 82 | (if (empty? (filter #(= (:id %) build-id) builds)) 83 | (throw (Exception. (str "Unknown build identifier: " build-id))))) 84 | (println (if watch? "Watching for changes before compiling ClojureScript..." "Compiling ClojureScript...")) 85 | ; If crossover-path does not exist before eval-in-project is called, 86 | ; the files it contains won't be classloadable, for some reason. 87 | (when (not-empty crossovers) 88 | (println "\033[31mWARNING: lein-cljsbuild crossovers are deprecated, and will be removed in future versions. See https://github.com/emezeske/lein-cljsbuild/blob/master/doc/CROSSOVERS.md for details.\033[0m") 89 | (fs/mkdirs crossover-path)) 90 | (let [filtered-builds (if (empty? build-ids) 91 | builds 92 | (filter #(some #{(:id %)} build-ids) builds)) 93 | parsed-builds (map config/parse-notify-command filtered-builds) 94 | checkout-cljs-paths (checkout-cljs-paths project)] 95 | (doseq [build parsed-builds] 96 | (config/warn-unsupported-warn-on-undeclared build) 97 | (config/warn-unsupported-notify-command build)) 98 | (run-local-project project crossover-path parsed-builds 99 | '(require 'cljsbuild.compiler 'cljsbuild.crossover 'cljsbuild.util) 100 | `(do 101 | (letfn [(copy-crossovers# [] 102 | (cljsbuild.crossover/copy-crossovers 103 | ~crossover-path 104 | '~crossovers))] 105 | (copy-crossovers#) 106 | (when ~watch? 107 | (cljsbuild.util/once-every-bg 1000 "copying crossovers" copy-crossovers#)) 108 | (let [crossover-macro-paths# (cljsbuild.crossover/crossover-macro-paths '~crossovers) 109 | builds# (for [opts# '~parsed-builds] 110 | [opts# (cljs.env/default-compiler-env (:compiler opts#))])] 111 | ;; Prep the environments 112 | (doseq [[build# compiler-env#] builds#] 113 | ;; Require any ns if necessary 114 | (doseq [handler# (:warning-handlers build#)] 115 | (when (symbol? handler#) 116 | (let [[n# sym#] (string/split (str handler#) #"/")] 117 | (assert (and n# sym#) 118 | (str "Symbols for :warning-handlers must be fully-qualified, " (pr-str handler#) " is missing namespace.")) 119 | (when (and n# sym#) 120 | (require (symbol n#))))))) 121 | (loop [dependency-mtimes# (repeat (count builds#) {})] 122 | (let [builds-mtimes# (map vector builds# dependency-mtimes#) 123 | new-dependency-mtimes# 124 | (doall 125 | (for [[[build# compiler-env#] mtimes#] builds-mtimes#] 126 | (cljs.analyzer/with-warning-handlers 127 | (if-let [handlers# (:warning-handlers build#)] 128 | ;; Prep the warning handlers via eval and 129 | ;; resolve if custom, otherwise default to 130 | ;; built-in warning handlers 131 | (mapv (fn [handler#] 132 | ;; Resolve symbols to their fns 133 | (if (symbol? handler#) 134 | (resolve handler#) 135 | handler#)) (eval handlers#)) 136 | cljs.analyzer/*cljs-warning-handlers*) 137 | (binding [cljs.env/*compiler* compiler-env#] 138 | (cljsbuild.compiler/run-compiler 139 | (:source-paths build#) 140 | '~checkout-cljs-paths 141 | ~crossover-path 142 | crossover-macro-paths# 143 | (:compiler build#) 144 | (:parsed-notify-command build#) 145 | (:incremental build#) 146 | (:assert build#) 147 | mtimes# 148 | ~watch?)))))] 149 | (when ~watch? 150 | (Thread/sleep 100) 151 | (recur new-dependency-mtimes#)))))))))) 152 | 153 | (defn- run-tests [project {:keys [test-commands crossover-path builds]} args] 154 | (when (> (count args) 1) 155 | (lmain/abort "Only expected zero or one arguments.")) 156 | (when (and (= (count args) 1) (not (get test-commands (first args)))) 157 | (lmain/abort (format "No such test name: %s - valid names are: %s" 158 | (first args) 159 | (apply str (interpose ", " (keys test-commands)))))) 160 | (let [selected-tests (if (empty? args) 161 | (seq test-commands) 162 | [[(first args) (test-commands (first args))]]) 163 | parsed-tests (map (fn [[test-name test-cmd]] 164 | [test-name (config/parse-shell-command test-cmd)]) 165 | selected-tests)] 166 | (doseq [[_ test-command] test-commands] 167 | (when-not (every? string? test-command) 168 | (lmain/abort 169 | "Invalid :test-command, contains non-string value:" test-command))) 170 | (when (seq parsed-tests) 171 | (when (empty? (:shell (second (first parsed-tests)))) 172 | (println (str "Could not locate test command " (first args) ".")) 173 | (lmain/abort)) 174 | (run-local-project project crossover-path builds 175 | '(require 'cljsbuild.test) 176 | `(cljsbuild.test/run-tests '~parsed-tests))))) 177 | 178 | (defmacro require-trampoline [& forms] 179 | `(if ltrampoline/*trampoline?* 180 | (do ~@forms) 181 | (do 182 | (println "REPL subcommands must be run via \"lein trampoline cljsbuild \".") 183 | (lmain/abort)))) 184 | 185 | (defn- deps 186 | "Downloads internal `lein-cljsbuild` dependencies" 187 | [project] 188 | (println "Downloading cljsbuild dependencies..") 189 | (leval/eval-in-project 190 | (subproject/make-subproject project nil nil) 191 | `(println "Done."))) 192 | 193 | (defn- once 194 | "Compile the ClojureScript project once." 195 | [project options build-ids] 196 | (run-compiler project options build-ids false)) 197 | 198 | (defn- auto 199 | "Automatically recompile when files are modified." 200 | [project options build-ids] 201 | (run-compiler project options build-ids true)) 202 | 203 | (defn- test 204 | "Run ClojureScript tests." 205 | [project options args] 206 | (run-compiler project options nil false) 207 | (run-tests project options args)) 208 | 209 | (defn- repl-listen 210 | "Run a REPL that will listen for incoming connections." 211 | [project {:keys [crossover-path builds repl-listen-port]}] 212 | (require-trampoline 213 | (println (str "Running ClojureScript REPL, listening on port " repl-listen-port ".")) 214 | (run-local-project project crossover-path builds 215 | '(require 'cljsbuild.repl.listen) 216 | `(cljsbuild.repl.listen/run-repl-listen 217 | ~repl-listen-port 218 | ~repl-output-path)))) 219 | 220 | (defn- repl-launch 221 | "Run a REPL and launch a custom command to connect to it." 222 | [project {:keys [crossover-path builds repl-listen-port repl-launch-commands]} args] 223 | (require-trampoline 224 | (when (< (count args) 1) 225 | (throw (Exception. "Must supply a launch command identifier."))) 226 | (let [launch-name (first args) 227 | command-args (rest args) 228 | command-base (repl-launch-commands launch-name)] 229 | (when (nil? command-base) 230 | (throw (Exception. (str "Unknown REPL launch command: " launch-name)))) 231 | (let [parsed (config/parse-shell-command command-base) 232 | shell (concat (:shell parsed) command-args) 233 | command (assoc parsed :shell shell)] 234 | (println "Running ClojureScript REPL and launching command:" shell) 235 | (run-local-project project crossover-path builds 236 | '(require 'cljsbuild.repl.listen) 237 | `(cljsbuild.repl.listen/run-repl-launch 238 | ~repl-listen-port 239 | ~repl-output-path 240 | '~command)))))) 241 | 242 | (defn- repl-rhino 243 | "Run a Rhino-based REPL." 244 | [project {:keys [crossover-path builds]}] 245 | (require-trampoline 246 | (println "Running Rhino-based ClojureScript REPL.") 247 | (run-local-project project crossover-path builds 248 | '(require 'cljsbuild.repl.rhino) 249 | `(cljsbuild.repl.rhino/run-repl-rhino)))) 250 | 251 | (defn- sample 252 | "Display a sample project.clj." 253 | [] 254 | (-> (io/resource "sample.project.clj") slurp println)) 255 | 256 | (defn cljsbuild 257 | "Run the cljsbuild plugin." 258 | {:help-arglists '([once auto test repl-listen repl-launch repl-rhino sample]) 259 | :subtasks [#'once #'auto #'test #'repl-listen #'repl-launch #'repl-rhino #'sample]} 260 | ([project] 261 | (println 262 | (lhelp/help-for "cljsbuild")) 263 | (lmain/abort)) 264 | ([project subtask & args] 265 | (let [options (config/extract-options project)] 266 | (case subtask 267 | "deps" (deps project) 268 | "once" (once project options args) 269 | "auto" (auto project options args) 270 | "test" (test project options args) 271 | "repl-listen" (repl-listen project options) 272 | "repl-launch" (repl-launch project options args) 273 | "repl-rhino" (repl-rhino project options) 274 | "sample" (sample) 275 | (do 276 | (println 277 | "Subtask" (str \" subtask \") "not found." 278 | (lhelp/subtask-help-for *ns* #'cljsbuild)) 279 | (lmain/abort)))))) 280 | 281 | (defn compile-hook [task & args] 282 | (apply task args) 283 | (run-compiler (first args) (config/extract-options (first args)) nil false)) 284 | 285 | (defn test-hook [task & args] 286 | (apply task args) 287 | (run-tests (first args) (config/extract-options (first args)) [])) 288 | 289 | (defn jar-hook [task & [project out-file filespecs]] 290 | (apply task [project out-file (concat filespecs (jar/get-filespecs project))])) 291 | 292 | (defn activate 293 | "Set up hooks for the plugin. Eventually, this can be changed to just hook, 294 | and people won't have to specify :hooks in their project.clj files anymore." 295 | [] 296 | (hooke/add-hook #'lcompile/compile #'compile-hook) 297 | (hooke/add-hook #'ltest/test #'test-hook) 298 | (hooke/add-hook #'ljar/write-jar #'jar-hook)) 299 | -------------------------------------------------------------------------------- /plugin/src/leiningen/cljsbuild/config.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.cljsbuild.config 2 | "Utilities for parsing the cljsbuild config." 3 | (:require 4 | [clojure.pprint :as pprint] 5 | [leiningen.cljsbuild.util :as util])) 6 | 7 | (defn in-target-path [target-path subpath] 8 | (str target-path "/cljsbuild-" subpath)) 9 | 10 | (defn- compiler-output-dir-base [target-path] 11 | (in-target-path target-path "compiler-")) 12 | 13 | (def compiler-global-dirs 14 | {:libs "closure-js/libs" 15 | :externs "closure-js/externs"}) 16 | 17 | (defn- default-global-options [target-path] 18 | {:repl-launch-commands {} 19 | :repl-listen-port 9000 20 | :test-commands {} 21 | :crossover-path (in-target-path target-path "crossover") 22 | :crossover-jar false 23 | :crossovers []}) 24 | 25 | (defn- default-compiler-options [target-path] 26 | {:externs [] 27 | :libs []}) 28 | 29 | (defn- default-build-options [target-path] 30 | {:source-paths ["src-cljs"] 31 | :jar false 32 | :notify-command nil 33 | :incremental true 34 | :assert true 35 | :compiler (default-compiler-options target-path)}) 36 | 37 | (defn convert-builds-map [options] 38 | (update-in options [:builds] 39 | #(if (map? %) 40 | (for [[id build] %] 41 | (assoc build :id (name id))) 42 | %))) 43 | 44 | (defn- backwards-compat-builds [options] 45 | (cond 46 | (and (map? options) (some #{:compiler :source-path :source-paths} (keys options))) 47 | {:builds [options]} 48 | (vector? options) 49 | {:builds options} 50 | :else 51 | options)) 52 | 53 | (defn- backwards-compat-source-path [{:keys [builds] :as options}] 54 | (assoc options :builds 55 | (for [build builds] 56 | (if-let [source-path (:source-path build)] 57 | (dissoc 58 | (assoc build :source-paths 59 | (vec (concat (:source-paths build) [source-path]))) 60 | :source-path) 61 | build)))) 62 | 63 | (defn- backwards-compat-crossovers [{:keys [builds crossovers] :as options}] 64 | (let [all-crossovers (->> builds 65 | (mapcat :crossovers) 66 | (concat crossovers) 67 | (distinct) 68 | (vec)) 69 | no-crossovers (assoc options 70 | :builds (vec (map #(dissoc % :crossovers) builds)))] 71 | (if (empty? all-crossovers) 72 | no-crossovers 73 | (assoc no-crossovers 74 | :crossovers all-crossovers)))) 75 | 76 | (defn backwards-compat [options] 77 | (-> options 78 | backwards-compat-builds 79 | backwards-compat-source-path 80 | backwards-compat-crossovers)) 81 | 82 | (defn- warn-deprecated [options] 83 | (letfn [(delim [] (println (apply str (take 80 (repeat "-")))))] 84 | (delim) 85 | (println 86 | (str 87 | "WARNING: your :cljsbuild configuration is in a deprecated format. It has been\n" 88 | "automatically converted it to the new format, which will be printed below.\n" 89 | "It is recommended that you update your :cljsbuild configuration ASAP.")) 90 | (delim) 91 | (println ":cljsbuild") 92 | (pprint/pprint options) 93 | (delim) 94 | (println 95 | (str 96 | "See https://github.com/emezeske/lein-cljsbuild/blob/master/README.md\n" 97 | "for details on the new format.")) 98 | (delim) 99 | options)) 100 | 101 | (declare deep-merge-item) 102 | 103 | (defn- deep-merge [& ms] 104 | (apply merge-with deep-merge-item ms)) 105 | 106 | (defn- deep-merge-item [a b] 107 | (if (and (map? a) (map? b)) 108 | (deep-merge a b) 109 | b)) 110 | 111 | (defn set-default-build-options [target-path options] 112 | (let [options (deep-merge (default-build-options target-path) options)] 113 | (if (or (get-in options [:compiler :output-to]) 114 | (get-in options [:compiler :modules])) 115 | options 116 | (assoc-in options [:compiler :output-to] (in-target-path target-path "main.js"))))) 117 | 118 | (defn- set-default-output-dirs [target-path options] 119 | (let [output-dir-key [:compiler :output-dir] 120 | relative-target-path (when target-path 121 | (util/relative-path (util/get-working-dir) target-path)) 122 | builds 123 | (for [[build counter] (map vector (:builds options) (range))] 124 | (if (get-in build output-dir-key) 125 | build 126 | (assoc-in build output-dir-key 127 | (str (compiler-output-dir-base relative-target-path) counter))))] 128 | (if (or (empty? builds) 129 | (apply distinct? (map #(get-in % output-dir-key) builds))) 130 | (assoc options :builds builds) 131 | (throw (Exception. (str "All " output-dir-key " options must be distinct.")))))) 132 | 133 | (defn set-default-options [target-path options] 134 | (set-default-output-dirs target-path 135 | (deep-merge (default-global-options target-path) 136 | (assoc options :builds 137 | (map #(set-default-build-options target-path %) (:builds options)))))) 138 | 139 | (defn set-build-global-dirs [build] 140 | (reduce (fn [build [k v]] (update-in build [:compiler k] conj v)) 141 | build 142 | compiler-global-dirs)) 143 | 144 | (defn set-compiler-global-dirs [options] 145 | (assoc options :builds 146 | (map set-build-global-dirs (:builds options)))) 147 | 148 | (defn- normalize-options 149 | "Sets default options and accounts for backwards compatibility." 150 | [target-path options] 151 | (let [convert (convert-builds-map options) 152 | compat (backwards-compat convert)] 153 | (when (and options (not= convert compat)) 154 | (warn-deprecated compat)) 155 | (->> compat 156 | (set-default-options target-path) 157 | set-compiler-global-dirs))) 158 | 159 | (defn parse-shell-command [raw] 160 | (let [[shell tail] (split-with (comp not keyword?) raw) 161 | options (apply hash-map tail)] 162 | (merge {:shell shell} options))) 163 | 164 | (defn parse-notify-command [build] 165 | (assoc build :parsed-notify-command 166 | (parse-shell-command (:notify-command build)))) 167 | 168 | (defn warn-unsupported-warn-on-undeclared [build] 169 | (when (contains? build :warn-on-undeclared) 170 | (println "WARNING: the :warn-on-undeclared option is no longer available.") 171 | (println "Set \":warnings true\" in your :compiler options instead."))) 172 | 173 | (defn warn-unsupported-notify-command [build] 174 | (when (or (first (filter #(= "%" %) (:shell (:parsed-notify-command build)))) 175 | (:beep (:parsed-notify-command build))) 176 | (println "WARNING: the :notify-command option no longer accepts the \"%\" or :beep options.") 177 | (println "See https://github.com/emezeske/lein-cljsbuild/blob/master/doc/RELEASE-NOTES.md for details."))) 178 | 179 | (defn extract-options 180 | "Given a project, returns a seq of cljsbuild option maps." 181 | [project] 182 | (when (nil? (:cljsbuild project)) 183 | (println "WARNING: no :cljsbuild entry found in" (:name project) "project definition.")) 184 | (let [raw-options (:cljsbuild project)] 185 | (normalize-options (:target-path project) raw-options))) 186 | -------------------------------------------------------------------------------- /plugin/src/leiningen/cljsbuild/jar.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.cljsbuild.jar 2 | "Utilities for the cljsbuild jar hook." 3 | (:require 4 | [clojure.java.io :as io] 5 | [fs.core :as fs] 6 | [leiningen.cljsbuild.config :as config] 7 | [leiningen.cljsbuild.util :as util])) 8 | 9 | (defn file-bytes 10 | "Reads a file into a byte array" 11 | [filename] 12 | (let [file (io/file filename)] 13 | (with-open [input (java.io.FileInputStream. file)] 14 | (let [data (byte-array (.length file))] 15 | (.read input data) 16 | data)))) 17 | 18 | (defn join-paths [& paths] 19 | (apply str (interpose "/" paths))) 20 | 21 | (defn canonical-path [path] 22 | (.getCanonicalPath (io/file path))) 23 | 24 | ;; The reason we return a :bytes filespec is that it's the only way of 25 | ;; specifying a file's destination path inside the jar and is contents 26 | ;; independently. Obviously this presents issues if there are any very 27 | ;; large files - this should be fixable in leiningen 2.0. 28 | (defn path-filespecs 29 | "Given a path, returns a seq of filespecs representing files on the path." 30 | [path] 31 | (let [filenames (apply concat 32 | (for [[root _ filenames] (fs/iterate-dir path)] 33 | (for [file filenames] 34 | (join-paths root file))))] 35 | (for [filename filenames] 36 | {:type :bytes 37 | :path (util/relative-path (canonical-path path) (canonical-path filename)) 38 | :bytes (file-bytes filename)}))) 39 | 40 | (defn get-filespecs 41 | "Returns a seq of filespecs for cljs dirs (as passed to leiningen.jar/write-jar)" 42 | [project] 43 | (let [options (config/extract-options project) 44 | builds (:builds options) 45 | build-paths (mapcat :source-paths (filter :jar builds)) 46 | paths (if (:crossover-jar options) 47 | (conj build-paths (:crossover-path options)) 48 | build-paths)] 49 | (mapcat path-filespecs paths))) 50 | -------------------------------------------------------------------------------- /plugin/src/leiningen/cljsbuild/subproject.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.cljsbuild.subproject 2 | "Utilities for running cljsbuild in a subproject" 3 | (:require 4 | [leiningen.core.main :as lmain] 5 | [clojure.java.io :refer (resource)] 6 | [clojure.string :as string] 7 | [leiningen.core.classpath :as classpath])) 8 | 9 | (def cljsbuild-version 10 | (let [[_ coords version] 11 | (-> (or (resource "META-INF/leiningen/lein-cljsbuild/lein-cljsbuild/project.clj") 12 | ; this should only ever come into play when testing cljsbuild itself 13 | "project.clj") 14 | slurp 15 | read-string)] 16 | (assert (= coords 'lein-cljsbuild) 17 | (str "Something very wrong, could not find lein-cljsbuild's project.clj, actually found: " 18 | coords)) 19 | (assert (string? version) 20 | (str "Something went wrong, version of lein-cljsbuild is not a string: " 21 | version)) 22 | version)) 23 | 24 | (def required-clojure-version "1.5.1") 25 | 26 | (def cljsbuild-dependencies 27 | [['cljsbuild cljsbuild-version] 28 | ['org.clojure/clojure required-clojure-version]]) 29 | 30 | (defn- numeric-version [v] 31 | (map #(Integer. %) (re-seq #"\d+" (first (string/split v #"-" 2))))) 32 | 33 | (defn- version-satisfies? [v1 v2] 34 | (let [v1 (numeric-version v1) 35 | v2 (numeric-version v2)] 36 | (loop [versions (map vector v1 v2) 37 | [seg1 seg2] (first versions)] 38 | (cond (empty? versions) true 39 | (= seg1 seg2) (recur (rest versions) (first (rest versions))) 40 | (> seg1 seg2) true 41 | (< seg1 seg2) false)))) 42 | 43 | (defn check-clojure-version [project-dependencies] 44 | (if-let [clojure-dependency ('org.clojure/clojure project-dependencies)] 45 | (let [version (first clojure-dependency)] 46 | (when (not (version-satisfies? version required-clojure-version)) 47 | (lmain/abort (str "The ClojureScript compiler requires Clojure version >= " 48 | required-clojure-version)))))) 49 | 50 | (defn dependency-map [dependency-vec] 51 | (apply array-map (mapcat (juxt first rest) dependency-vec))) 52 | 53 | (defn array-map-assoc 54 | "As assoc, except that it preserves the ArrayMap's ordering. If the key is not 55 | already in the array map, it is put at the end of the array map." 56 | [amap k v] 57 | (apply array-map (apply concat (concat (seq amap) [[k v]])))) 58 | 59 | (defn merge-dependencies [project-dependencies] 60 | (let [project (dependency-map project-dependencies) 61 | desired-cljs-version ('org.clojure/clojurescript project) 62 | cljsbuild (dependency-map cljsbuild-dependencies)] 63 | (check-clojure-version project) 64 | (when-not desired-cljs-version 65 | (println "\033[33mWARNING: It appears your project does not contain a ClojureScript" 66 | "dependency. One will be provided for you by lein-cljsbuild, but it" 67 | "is strongly recommended that you add your own. You can find a list" 68 | "of all ClojureScript releases here:") 69 | (println "http://search.maven.org/#search|gav|1|g%3A%22org.clojure%22%20AND%20a%3A%22clojurescript%22") 70 | (println "\033[0m")) 71 | (->> (reduce-kv array-map-assoc cljsbuild project) 72 | (map (fn [[k v]] (vec (cons k v))))))) 73 | 74 | (defn- get-deps-from-project 75 | [project] 76 | ;; This checks to see if the user is running lein 2.7 or greater, and, if so, 77 | ;; merges in the dependency versions from the `:managed-dependencies` vector. 78 | (if-let [merge-versions-from-managed-coords (resolve 'leiningen.core.classpath/merge-versions-from-managed-coords)] 79 | (merge-versions-from-managed-coords 80 | (get project :dependencies) 81 | (get project :managed-dependencies)) 82 | (get project :dependencies))) 83 | 84 | (defn make-subproject [project crossover-path builds] 85 | (let [deps (get-deps-from-project project) 86 | remove-compile-task (fn [tasks] 87 | (remove (fn [task] 88 | (= task "compile")) 89 | tasks))] 90 | (with-meta 91 | ;; Leiningen sets javac and compile as default prep-tasks. We need to remove 92 | ;; the compile task, so it doesn't trigger the compile hook for all builds. 93 | ;; https://github.com/emezeske/lein-cljsbuild/issues/451 94 | (merge (update project :prep-tasks remove-compile-task) 95 | {:local-repo-classpath true 96 | :dependencies (merge-dependencies deps) 97 | :source-paths (concat 98 | (:source-paths project) 99 | (mapcat :source-paths builds) 100 | (filter some? [crossover-path]))}) 101 | (meta project)))) 102 | -------------------------------------------------------------------------------- /plugin/src/leiningen/cljsbuild/util.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.cljsbuild.util 2 | (:require 3 | [clojure.string :as s])) 4 | 5 | (defn relative-path 6 | "Given two normalized path strings, returns a path string of the second 7 | relative to the first. Otherwise returns the second path." 8 | [parent child] 9 | (let [relative (s/replace child parent "")] 10 | (if (= child relative) 11 | child 12 | (s/replace relative #"^[\\/]" "")))) 13 | 14 | (defn get-working-dir 15 | "Returns the full path to the working directory, which is the directory the 16 | build was started in." 17 | [] 18 | (str (System/getProperty "user.dir"))) 19 | -------------------------------------------------------------------------------- /plugin/test/leiningen/test/cljsbuild.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.cljsbuild 2 | (:use 3 | leiningen.cljsbuild 4 | midje.sweet) 5 | (:require 6 | [leiningen.trampoline :as ltrampoline] 7 | [leiningen.cljsbuild.config :as config] 8 | [leiningen.cljsbuild.jar :as jar] 9 | [leiningen.cljsbuild.subproject :as subproject] 10 | [leiningen.core.main :as lmain] 11 | [leiningen.core.eval :as leval] 12 | [leiningen.core.project :as lproject] 13 | [fs.core :as fs] 14 | [clojure.java.io :as io] 15 | cljsbuild.crossover 16 | cljsbuild.util 17 | cljsbuild.compiler 18 | cljsbuild.test 19 | cljsbuild.repl.listen 20 | cljsbuild.repl.rhino)) 21 | 22 | (def repl-listen-port 10000) 23 | (def repl-launch-command-id "launch-id") 24 | (def repl-launch-command ["launch-me"]) 25 | (def test-command-id "test-id") 26 | (def test-command ["test-me"]) 27 | (def crossover-path "target/crossover-path") 28 | (def crossovers ['test.crossover]) 29 | (def crossover-macros [{:absolute "/root/stuff/test/crossover.clj" 30 | :classpath "test/crossover.clj"}]) 31 | 32 | (def build-id "build-id") 33 | (def source-paths ["source-path-a" "source-path-b"]) 34 | (def incremental? false) 35 | (def assert? false) 36 | (def watch? false) 37 | 38 | (def compiler 39 | {:output-to "target/output-to" 40 | :output-dir "target/output-dir" 41 | :warnings false 42 | :optimizations :advanced 43 | :pretty-print false}) 44 | 45 | (def builds 46 | (list 47 | {:id build-id 48 | :source-paths source-paths 49 | :jar true 50 | :notify-command ["notify"] 51 | :incremental incremental? 52 | :assert assert? 53 | :compiler compiler})) 54 | 55 | (def parsed-builds 56 | (map config/set-build-global-dirs 57 | (map config/parse-notify-command builds))) 58 | 59 | (def parsed-compiler 60 | (:compiler (first parsed-builds))) 61 | 62 | (def project-dir "/project") 63 | (def checkouts-dir (io/file project-dir "checkouts")) 64 | 65 | (def project 66 | {:dependencies [['org.clojure/clojure "1.5.1"]] 67 | :root project-dir 68 | :cljsbuild 69 | {:repl-listen-port repl-listen-port 70 | :repl-launch-commands {repl-launch-command-id repl-launch-command} 71 | :test-commands {test-command-id test-command} 72 | :crossover-path crossover-path 73 | :crossover-jar true 74 | :crossovers crossovers 75 | :builds builds}}) 76 | 77 | (defn hook-success [& args] 78 | nil) 79 | 80 | (defn hook-failure [& args] 81 | (throw (Exception. "Dummy hook failed."))) 82 | 83 | (defn eval-locally [_ body _] 84 | (eval body)) 85 | 86 | (background 87 | (around 88 | :facts (with-redefs [leval/eval-in-project eval-locally 89 | cljsbuild.util/once-every-bg (fn [_# _# _#] nil)] 90 | (binding [*suppress-exit?* true 91 | lmain/*exit-process?* false] 92 | ?form)))) 93 | 94 | (fact "fail when no arguments present" 95 | (cljsbuild project) => (throws Exception)) 96 | 97 | (fact "once/auto call eval-in-project with the right args" 98 | (doseq [command ["once" "auto"]] 99 | (cljsbuild project command) => nil 100 | (provided 101 | (leval/eval-in-project 102 | (subproject/make-subproject project crossover-path parsed-builds) 103 | anything 104 | anything) => nil :times 1)) ) 105 | 106 | (fact "once task calls the compiler correctly" 107 | (doseq [extra-args [[] [build-id]]] 108 | (apply cljsbuild project "once" extra-args) => nil 109 | (provided 110 | (fs/list-dir checkouts-dir) => nil, :times 1 111 | (cljsbuild.crossover/crossover-macro-paths 112 | crossovers) => crossover-macros :times 1 113 | (cljsbuild.crossover/copy-crossovers 114 | crossover-path 115 | crossovers) => nil :times 1 116 | (cljsbuild.compiler/run-compiler 117 | source-paths 118 | [] 119 | crossover-path 120 | crossover-macros 121 | parsed-compiler 122 | anything 123 | incremental? 124 | assert? 125 | {} 126 | watch?) => nil :times 1))) 127 | 128 | (fact "bad build IDs are detected" 129 | (cljsbuild project "once" "wrong-build-id") => (throws Exception)) 130 | 131 | (fact "compile-hook calls through to the compiler when task succeeds" 132 | (compile-hook hook-success project) => nil 133 | (provided 134 | (fs/list-dir checkouts-dir) => nil, :times 1 135 | (cljsbuild.crossover/crossover-macro-paths 136 | crossovers) => crossover-macros :times 1 137 | (cljsbuild.crossover/copy-crossovers 138 | crossover-path 139 | crossovers) => nil :times 1 140 | (cljsbuild.compiler/run-compiler 141 | source-paths 142 | [] 143 | crossover-path 144 | crossover-macros 145 | parsed-compiler 146 | anything 147 | incremental? 148 | assert? 149 | {} 150 | watch?) => nil :times 1)) 151 | 152 | (fact "checkouts path are recognized and passed correctly" 153 | (let [checkout-a-dir (io/file checkouts-dir "lib-a") 154 | checkout-a-project (io/file checkout-a-dir "project.clj") 155 | checkout-a-checkouts (io/file checkout-a-dir "checkouts") 156 | 157 | checkout-b-dir (io/file checkouts-dir "lib-b") 158 | checkout-b-project (io/file checkout-b-dir "project.clj") 159 | checkout-b-checkouts (io/file checkout-b-dir "checkouts")] 160 | 161 | (cljsbuild project "once") => nil 162 | (provided 163 | (fs/list-dir checkouts-dir) => ["lib-a" "lib-b"], :times 1 164 | (fs/list-dir checkout-a-checkouts) => nil, :times 1 165 | (fs/list-dir checkout-b-checkouts) => nil, :times 1 166 | (fs/exists? checkout-a-project) => true, :times 1 167 | (fs/exists? checkout-b-project) => true, :times 1 168 | 169 | (lproject/read "/project/checkouts/lib-a/project.clj") => 170 | (assoc project :root (fs/absolute-path checkout-a-dir)), :times 1 171 | (lproject/read "/project/checkouts/lib-b/project.clj") => 172 | (assoc project :root (fs/absolute-path checkout-b-dir)), :times 1 173 | 174 | (cljsbuild.crossover/crossover-macro-paths 175 | crossovers) => crossover-macros :times 1 176 | (cljsbuild.crossover/copy-crossovers 177 | crossover-path 178 | crossovers) => nil :times 1 179 | (cljsbuild.compiler/run-compiler 180 | source-paths 181 | ["/project/checkouts/lib-b/source-path-a" 182 | "/project/checkouts/lib-b/source-path-b" 183 | "/project/checkouts/lib-a/source-path-a" 184 | "/project/checkouts/lib-a/source-path-b"] 185 | crossover-path 186 | crossover-macros 187 | parsed-compiler 188 | anything 189 | incremental? 190 | assert? 191 | {} 192 | watch?) => nil :times 1))) 193 | 194 | (fact "compile-hook does not call through to the compiler when task fails" 195 | (compile-hook hook-failure project) => (throws Exception) 196 | (provided 197 | (fs/list-dir checkouts-dir) => nil, :times 0 198 | (cljsbuild.crossover/crossover-macro-paths 199 | anything) => nil :times 0 200 | (cljsbuild.crossover/copy-crossovers 201 | anything 202 | anything) => nil :times 0 203 | (cljsbuild.compiler/run-compiler 204 | anything 205 | anything 206 | anything 207 | anything 208 | anything 209 | anything 210 | anything 211 | anything 212 | anything 213 | anything) => nil :times 0)) 214 | 215 | ; NOTE: This let has to be outside the fact, because against-background does not 216 | ; like being directly inside a let. 217 | (let [parsed-commands [[test-command-id (config/parse-shell-command test-command)]]] 218 | (fact "tests work correctly" 219 | (cljsbuild project "test") => nil 220 | (cljsbuild project "test" test-command-id) => nil 221 | (test-hook hook-success project) => nil 222 | (test-hook hook-failure project) => (throws Exception) 223 | (against-background 224 | (fs/list-dir checkouts-dir) => nil, :times 1 225 | (cljsbuild.crossover/crossover-macro-paths 226 | crossovers) => crossover-macros :times 0 227 | (cljsbuild.crossover/copy-crossovers 228 | crossover-path 229 | crossovers) => nil :times 1 230 | (cljsbuild.compiler/run-compiler 231 | source-paths 232 | [] 233 | crossover-path 234 | crossover-macros 235 | parsed-compiler 236 | anything 237 | incremental? 238 | assert? 239 | {} 240 | watch?) => nil :times 1 241 | (cljsbuild.test/run-tests parsed-commands) => nil :times 1))) 242 | 243 | (against-background [(around :checks 244 | (binding [ltrampoline/*trampoline?* true] 245 | ?form))] 246 | 247 | (fact "repl-listen calls run-repl-listen" 248 | (cljsbuild project "repl-listen") => nil 249 | (provided 250 | (cljsbuild.repl.listen/run-repl-listen repl-listen-port anything) => nil :times 1)) 251 | 252 | (fact "repl-launch with no ID fails" 253 | (cljsbuild project "repl-launch") => (throws Exception)) 254 | 255 | (fact "repl-launch with bad ID fails" 256 | (cljsbuild project "repl-launch" "wrong-repl-launch-id") => (throws Exception)) 257 | 258 | (fact "repl-launch calls run-repl-launch" 259 | (let [parsed-command (config/parse-shell-command repl-launch-command)] 260 | (cljsbuild project "repl-launch" repl-launch-command-id) => nil 261 | (provided 262 | (cljsbuild.repl.listen/run-repl-launch 263 | repl-listen-port 264 | anything 265 | parsed-command) => nil :times 1))) 266 | 267 | (fact "repl-rhino calls run-repl-rhino" 268 | (cljsbuild project "repl-rhino") => nil 269 | (provided 270 | (cljsbuild.repl.rhino/run-repl-rhino) => nil :times 1))) 271 | 272 | (unfinished jar-task) 273 | 274 | (fact "jar-hook calls through to get-filespecs" 275 | (let [out-file "out" 276 | input-filespecs [{:type :bytes :path "/i/j" :bytes "fake-1"}] 277 | project-filespecs [{:type :bytes :path "/a/b" :bytes "fake-2"}] 278 | all-filespecs (concat input-filespecs project-filespecs)] 279 | (jar-hook jar-task project out-file input-filespecs) => nil 280 | (provided 281 | (jar-task project out-file all-filespecs) => nil 282 | (jar/get-filespecs project) => project-filespecs))) 283 | -------------------------------------------------------------------------------- /plugin/test/leiningen/test/cljsbuild/config.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.cljsbuild.config 2 | (:use 3 | leiningen.cljsbuild.config 4 | midje.sweet)) 5 | 6 | (fact 7 | (let [config-0-0-x-early {:source-path "a" 8 | :compiler {:output-to "hello.js"}} 9 | config-0-0-x-late [{:source-path "a" 10 | :compiler {:output-to "hello.js"}}] 11 | config-backwards {:builds 12 | [{:source-paths ["a"] 13 | :compiler {:output-to "hello.js"}}]}] 14 | (backwards-compat config-0-0-x-early) => config-backwards 15 | (backwards-compat config-0-0-x-late) => config-backwards)) 16 | 17 | (fact 18 | (let [config-vec {:builds [{:id "a"} {:id "b"}]} 19 | config-map {:builds {:a {} :b {}}}] 20 | (convert-builds-map config-vec) => config-vec 21 | (convert-builds-map config-map) => config-vec)) 22 | 23 | (fact 24 | (parse-shell-command []) => {:shell []} 25 | (parse-shell-command ["a"]) => {:shell ["a"]} 26 | (parse-shell-command ["a" "b" "c" :x 1 :y 2]) => {:shell ["a" "b" "c"] :x 1 :y 2}) 27 | 28 | (def target-path "target") 29 | 30 | (def config-in 31 | {:repl-launch-commands {:a ["a"]} 32 | :repl-listen-port 10000 33 | :test-commands {:b ["b"]} 34 | :crossover-path "c" 35 | :crossover-jar true 36 | :crossovers ["d" "e"] 37 | :builds 38 | '({:source-paths ["f"] 39 | :jar true 40 | :notify-command ["g"] 41 | :incremental false 42 | :assert false 43 | :compiler 44 | {:output-to "h" 45 | :output-dir "i" 46 | :warnings false 47 | :libs ["j"] 48 | :externs ["k"] 49 | :optimizations :advanced 50 | :pretty-print false}})}) 51 | 52 | (fact "custom settings are not overwritten by defaults" 53 | (set-default-options target-path config-in) => config-in) 54 | 55 | (fact "missing settings have defaults provided" 56 | (doseq [option (keys config-in)] 57 | (set-default-options target-path (dissoc config-in option)) => (contains {option anything}))) 58 | 59 | (defn- get-build [config] 60 | (first (:builds config))) 61 | 62 | (defn- default-build-option [config build option] 63 | (set-default-options target-path 64 | (assoc config :builds 65 | (list (dissoc build option))))) 66 | 67 | (fact "missing build settings have defaults provided" 68 | (let [build (get-build config-in)] 69 | (doseq [build-option (keys build)] 70 | (let [defaulted (default-build-option config-in build build-option)] 71 | (get-build defaulted) => (contains {build-option anything}))))) 72 | 73 | (def config-out (set-compiler-global-dirs config-in)) 74 | 75 | (fact 76 | (extract-options {:cljsbuild config-in}) => config-out) 77 | 78 | (def modules-config {:compiler {:externs [] 79 | :libs [] 80 | :modules {:front {:output-to "lib/assets/cljs/cljs-front.js" 81 | :entries #{"hb.front.core"}} 82 | :extranet {:output-to "lib/assets/cljs/cljs-extranet.js" 83 | :entries #{"hb.core"}}}} 84 | :assert true 85 | :incremental true 86 | :jar false 87 | :notify-command nil 88 | :source-paths ["src-cljs"]}) 89 | 90 | (fact "don't set :output-to option when :modules option is provided" 91 | (set-default-build-options target-path modules-config) => modules-config) 92 | -------------------------------------------------------------------------------- /plugin/test/leiningen/test/cljsbuild/jar.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.cljsbuild.jar 2 | (:use 3 | leiningen.cljsbuild.jar 4 | midje.sweet) 5 | (:require 6 | [fs.core :as fs])) 7 | 8 | (defn- make-bytes [s] 9 | byte-array (map (comp byte int) s)) 10 | 11 | (fact 12 | (let [root "/a/b" 13 | basename-1 "file1" 14 | basename-2 "file2" 15 | path-1 (join-paths root basename-1) 16 | path-2 (join-paths root basename-2) 17 | bytes-1 (make-bytes "some-bytes") 18 | bytes-2 (make-bytes "other-bytes")] 19 | (path-filespecs root) => [{:type :bytes 20 | :path basename-1 21 | :bytes bytes-1} 22 | {:type :bytes 23 | :path basename-2 24 | :bytes bytes-2}] 25 | (provided 26 | (fs/iterate-dir root) => [[root [] [basename-1 basename-2]]] :times 1 27 | (file-bytes path-1) => bytes-1 :times 1 28 | (file-bytes path-2) => bytes-2 :times 1))) 29 | 30 | (defn make-jar-directory [root basename contents] 31 | {:root (join-paths root basename) 32 | :filespec 33 | {:type :bytes 34 | :path basename 35 | :bytes (make-bytes contents)}}) 36 | 37 | (defn make-jar-directories [names] 38 | (let [directories (for [n (map name names)] 39 | (make-jar-directory 40 | (join-paths "/root" n) 41 | (str "file-" n) 42 | (str "contents-" n)))] 43 | (into {} (map vector names directories)))) 44 | 45 | (fact 46 | (let [entries (make-jar-directories [:a :b :c]) 47 | project {:cljsbuild 48 | {:crossover-path (get-in entries [:a :root]) 49 | :crossover-jar true 50 | :builds 51 | [{:source-paths [(get-in entries [:b :root])] 52 | :jar true} 53 | {:source-paths [(get-in entries [:c :root])] 54 | :jar true} 55 | {:source-paths ["not-in-jar"]}]}} 56 | root-a (get-in entries [:a :root]) 57 | root-b (get-in entries [:b :root]) 58 | root-c (get-in entries [:c :root])] 59 | (get-filespecs project) => (just (set (map :filespec (vals entries)))) 60 | (provided 61 | (path-filespecs root-a) => [(get-in entries [:a :filespec])] :times 1 62 | (path-filespecs root-b) => [(get-in entries [:b :filespec])] :times 1 63 | (path-filespecs root-c) => [(get-in entries [:c :filespec])] :times 1) 64 | (let [entries (dissoc entries :a) 65 | project (assoc-in project [:cljsbuild :crossover-jar] false)] 66 | (get-filespecs project) => (just (set (map :filespec (vals entries)))) 67 | (provided 68 | (path-filespecs root-b) => [(get-in entries [:b :filespec])] :times 1 69 | (path-filespecs root-c) => [(get-in entries [:c :filespec])] :times 1)))) 70 | -------------------------------------------------------------------------------- /plugin/test/leiningen/test/cljsbuild/subproject.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.cljsbuild.subproject 2 | (:require 3 | [leiningen.core.main :as lmain]) 4 | (:use 5 | leiningen.cljsbuild.subproject 6 | midje.sweet)) 7 | 8 | (background 9 | (around 10 | :facts (binding [lmain/*exit-process?* false] 11 | ?form))) 12 | 13 | (fact 14 | (check-clojure-version {}) => nil 15 | (check-clojure-version {'org.clojure/clojure [required-clojure-version]}) => nil 16 | (check-clojure-version {'org.clojure/clojure ["1.3.0"]}) => (throws Exception) 17 | (check-clojure-version {'org.clojure/clojure ["1.2.0"]}) => (throws Exception)) 18 | 19 | (def clojure-dependency ['org.clojure/clojure required-clojure-version]) 20 | 21 | (fact 22 | (let [original [clojure-dependency ['a "1"] ['b "2"]] 23 | merged (conj original ['cljsbuild cljsbuild-version])] 24 | (merge-dependencies original) => (just merged :in-any-order)) 25 | 26 | (let [original [['a "1"] ['b "2"]] 27 | merged (concat original [clojure-dependency ['cljsbuild cljsbuild-version]])] 28 | (merge-dependencies original) => (just merged :in-any-order))) 29 | 30 | (def lein-crossover ".crossovers") 31 | (def lein-build-source-paths ["src-cljs-a"]) 32 | (def lein-source-paths ["src-clj"]) 33 | (def lein-extra-classpath-dirs ["a" "b"]) 34 | (def lein-dependencies [clojure-dependency ['a "1"]]) 35 | (def expected-dependencies (conj lein-dependencies ['cljsbuild cljsbuild-version])) 36 | (def lein-repositories ["repository"]) 37 | (def lein-builds [{:source-paths lein-build-source-paths}]) 38 | 39 | (def lein-metadata {:test-metadata "testing 1 2 3"}) 40 | (def lein-eval-in :trampoline) 41 | (def lein-resource-paths "resources") 42 | (def lein-project 43 | (with-meta 44 | {:dependencies lein-dependencies 45 | :source-paths (concat lein-source-paths lein-extra-classpath-dirs) 46 | :repositories lein-repositories 47 | :eval-in lein-eval-in 48 | :resource-paths lein-resource-paths} 49 | lein-metadata)) 50 | 51 | (fact 52 | (let [subproject (make-subproject lein-project lein-crossover lein-builds)] 53 | (meta subproject) => lein-metadata 54 | (doseq [dir (concat lein-extra-classpath-dirs lein-source-paths lein-build-source-paths [lein-crossover])] 55 | (:source-paths subproject) => (contains dir)) 56 | (:local-repo-classpath subproject) 57 | (:repositories subproject) => lein-repositories 58 | (:eval-in subproject) => lein-eval-in 59 | (:resource-paths subproject) => lein-resource-paths 60 | (:dependencies subproject) => (just expected-dependencies :in-any-order))) 61 | -------------------------------------------------------------------------------- /plugin/test/leiningen/test/cljsbuild/util.clj: -------------------------------------------------------------------------------- 1 | (ns leiningen.test.cljsbuild.util 2 | (:use 3 | leiningen.cljsbuild.util 4 | midje.sweet)) 5 | 6 | (fact 7 | (relative-path "/" "/a") => "a" 8 | (relative-path "/a/b/c" "/a/b/c/d/e") => "d/e" 9 | (relative-path "" "a") => "a" 10 | (relative-path "/a/b/c" "/a/b") => "/a/b") 11 | -------------------------------------------------------------------------------- /sample.project.clj: -------------------------------------------------------------------------------- 1 | ; This is an annotated example of the lein-cljsbuild options that 2 | ; may be set in a project.clj file. It is a fairly contrived example 3 | ; in order to cover all options exhaustively; it shouldn't be considered 4 | ; a representative configuration. For complete, working examples, look in 5 | ; the example-projects/ folder. 6 | 7 | (defproject org.example/sample "1.0.0-SNAPSHOT" 8 | ; Your project must use Clojure 1.4 or above to support 9 | ; ClojureScript compilation. 10 | :dependencies [[org.clojure/clojure "1.7.0"] 11 | ; Your project should specify its own dependency on 12 | ; ClojureScript 13 | [org.clojure/clojurescript "1.7.170" 14 | :exclusions [org.apache.ant/ant]]] 15 | ; Your project should plugin-depend on lein-cljsbuild, to ensure that 16 | ; the right version of the plugin is installed. 17 | :plugins [[lein-cljsbuild "1.1.1"]] 18 | ; The standard Leiningen :source-paths option is used by lein-cljsbuild 19 | ; to determine the source directory from which crossover files will 20 | ; be copied. Leiningen defaults to ["src"]. 21 | :source-paths ["src-clj"] 22 | ; This is required for lein-cljsbuild to hook into the default Leningen 23 | ; tasks, e.g. the "lein compile" and "lein jar" tasks. 24 | :hooks [leiningen.cljsbuild] 25 | ; All lein-cljsbuild-specific configuration is under the :cljsbuild key. 26 | :cljsbuild { 27 | ; When using a ClojureScript REPL, this option controls what port 28 | ; it listens on for a browser to connect to. Defaults to 9000. 29 | :repl-listen-port 9000 30 | ; The keys in this map identify repl-launch commands. The values are 31 | ; sequences representing shell commands like [command, arg1, arg2, ...]. 32 | ; Defaults to the empty map. 33 | :repl-launch-commands 34 | {"firefox" ["firefox"] 35 | "firefox-naked" ["firefox" "resources/public/html/naked.html"] 36 | "phantom" ["phantomjs" "phantom/page-repl.js"] 37 | ; If a keyword appears in the command vector, it and all following 38 | ; entries will be treated as an option map. Currently, the only 39 | ; supported options are :stdout and :stderr, which allow you to 40 | ; redirect the command's output to files. 41 | "phantom-naked" ["phantomjs" 42 | "phantom/page-repl.js" 43 | "resources/public/html/naked.html" 44 | :stdout ".repl-phantom-naked-out" 45 | :stderr ".repl-phantom-naked-err"]} 46 | ; The keys in this map identify test commands. The values are sequences 47 | ; representing shell commands like [command, arg1, arg2, ...]. Note that 48 | ; the :stdout and :stderr options work here as well. 49 | ; Defaults to the empty map. 50 | :test-commands 51 | {"unit" ["phantomjs" "phantom/unit-test.js" "resources/private/html/unit-test.html"]} 52 | ; A list of namespaces that should be copied from the Clojure classpath into 53 | ; the :crossover-path, with some changes for ClojureScript compatibility. See 54 | ; doc/CROSSOVERS.md for more details. Defaults to the empty vector []. 55 | :crossovers [example.crossover] 56 | ; The directory into which the :crossovers namespaces should be copied. 57 | ; Defaults to "target/cljsbuild-crossover". 58 | :crossover-path "target/my-crossovers" 59 | ; If hooks are enabled, this flag determines whether files from :crossover-path 60 | ; are added to the JAR file created by "lein jar". 61 | :crossover-jar true 62 | ; The :builds option should be set to a sequence of maps. Each 63 | ; map will be treated as a separate, independent, ClojureScript 64 | ; compiler configuration. 65 | :builds { 66 | :main { 67 | ; The paths under which lein-cljsbuild will look for ClojureScript 68 | ; files to compile. Defaults to ["src-cljs"]. 69 | :source-paths ["src-cljs"] 70 | ; If hooks are enabled, this flag determines whether files from these 71 | ; :source-paths are added to the JAR file created by "lein jar". 72 | :jar true 73 | ; If a notify-command is specified, it will be called when compilation succeeds 74 | ; or fails, and a textual description of what happened will be appended as the 75 | ; last argument to the command. If a more complex command needs to be constructed, 76 | ; the recommendation is to write a small shell script wrapper. 77 | ; Defaults to nil (disabled). 78 | :notify-command ["growlnotify" "-m"] 79 | ; Determines whether the temporary JavaScript files will be left in place between 80 | ; automatic builds. Leaving them in place speeds up compilation because things can 81 | ; be built incrementally. This probably shouldn't be disabled except for troubleshooting. 82 | ; Defaults to true. 83 | :incremental true 84 | ; Determines whether assertions are enabled for the ClojureScript code. Technically, this 85 | ; binds clojure.core/*assert*, which is respected by the ClojureScript compiler. 86 | ; Defaults to true. 87 | :assert true 88 | ; The :compiler options are passed directly to the ClojureScript compiler. 89 | :compiler { 90 | ; The path to the JavaScript file that will be output. 91 | ; Defaults to "target/cljsbuild-main.js". 92 | :output-to "resources/public/js/main.js" 93 | ; This flag will turn on compiler warnings for references to 94 | ; undeclared vars, wrong function call arities, etc. Defaults to true. 95 | :warnings true 96 | ; The optimization level. May be :none, :whitespace, :simple, or :advanced. 97 | ; :none is the recommended setting for development, while :advanced is the 98 | ; recommended setting for production, unless something prevents it (incompatible 99 | ; external library, bug, etc.). 100 | ; :none requires manual code loading and hence a separate HTML from the other options. 101 | ; Defaults to :none. 102 | :optimizations :whitespace 103 | ; This flag will cause all (assert x) calls to be removed during compilation 104 | ; Useful for production. Default is always false even in advanced compilation. 105 | ; Does NOT specify goog.asserts.ENABLE_ASSERTS which is different and used by 106 | ; the closure library. 107 | :elide-asserts true 108 | ; Determines whether the JavaScript output will be tabulated in 109 | ; a human-readable manner. Defaults to true. 110 | :pretty-print true 111 | ; Determines whether comments will be output in the JavaScript that 112 | ; can be used to determine the original source of the compiled code. 113 | ; Defaults to false. 114 | :print-input-delimiter false 115 | ; If targeting nodejs add this line. Takes no other options at the moment. 116 | :target :nodejs 117 | ; See 118 | ; https://github.com/clojure/clojurescript/wiki/Source-maps 119 | :source-map "resources/public/js/main.js.map" 120 | ; Sets the output directory for temporary files used during 121 | ; compilation. Must be unique among all :builds. Defaults to 122 | ; "target/cljsbuild-compiler-X" (where X is a unique integer). 123 | :output-dir "target/my-compiler-output-" 124 | ; Wrap the JavaScript output in (function(){...};)() to avoid clobbering globals. 125 | ; Defaults to true when using advanced compilation, false otherwise. 126 | :output-wrapper false 127 | ; Configure externs files for external libraries. 128 | ; Defaults to the empty vector []. 129 | ; For this entry, and those below, you can find a very good explanation at: 130 | ; http://lukevanderhart.com/2011/09/30/using-javascript-and-clojurescript.html 131 | :externs ["jquery-externs.js"] 132 | ; Adds dependencies on external libraries. Note that files in these directories will be 133 | ; watched and a rebuild will occur if they are modified. 134 | ; Defaults to the empty vector []. 135 | :libs ["closure/library/third_party/closure"] 136 | ; Adds dependencies on foreign libraries. Be sure that the url returns a HTTP Code 200 137 | ; Defaults to the empty vector []. 138 | :foreign-libs [{:file "http://example.com/remote.js" 139 | :provides ["my.example"]}] 140 | ; Prepends the contents of the given files to each output file. 141 | ; Defaults to the empty vector []. 142 | :preamble ["license.js"] 143 | ; Configure the input and output languages for the closure library. 144 | ; May be :ecmascript3, ecmascript5, or ecmascript5-strict. 145 | ; Defaults to ecmascript3. 146 | :language-in :ecmascript5 147 | :language-out :ecmascript5 148 | ; Configure warnings generated by the Closure compiler. 149 | :closure-warnings {:externs-validation :off}}}}}) 150 | -------------------------------------------------------------------------------- /support/project.clj: -------------------------------------------------------------------------------- 1 | (defproject cljsbuild "1.1.8" 2 | :description "ClojureScript Autobuilder" 3 | :url "http://github.com/emezeske/lein-cljsbuild" 4 | :license 5 | {:name "Eclipse Public License - v 1.0" 6 | :url "http://www.eclipse.org/legal/epl-v10.html" 7 | :distribution :repo} 8 | :dependencies 9 | [[org.clojure/clojure "1.5.1"] 10 | [org.clojure/clojurescript "0.0-3211" 11 | :exclusions [org.apache.ant/ant]] 12 | [fs "1.1.2"] 13 | [clj-stacktrace "0.2.5"] 14 | [org.clojure/tools.namespace "0.2.11"]] 15 | :aot [cljsbuild.test] 16 | :profiles { 17 | :dev { 18 | :dependencies [[midje "1.6.3"]] 19 | :plugins [[lein-midje "3.1.3"]]}}) 20 | -------------------------------------------------------------------------------- /support/src/cljsbuild/compiler.clj: -------------------------------------------------------------------------------- 1 | (ns cljsbuild.compiler 2 | (:use 3 | [clojure.pprint] 4 | [clj-stacktrace.repl :only [pst+]]) 5 | (:require 6 | [cljsbuild.util :as util] 7 | [cljs.build.api :as bapi] 8 | [clojure.string :as string] 9 | [clojure.java.io :as io] 10 | [fs.core :as fs] 11 | [clojure.tools.namespace 12 | [track :as track] 13 | [dir :as dir] 14 | [reload :as reload]])) 15 | 16 | (defonce refresh-tracker (track/tracker)) 17 | 18 | (def reset-color "\u001b[0m") 19 | (def foreground-red "\u001b[31m") 20 | (def foreground-green "\u001b[32m") 21 | 22 | (defn- colorizer [c] 23 | (fn [& args] 24 | (str c (apply str args) reset-color))) 25 | 26 | (def red (colorizer foreground-red)) 27 | (def green (colorizer foreground-green)) 28 | 29 | (defn- elapsed [started-at] 30 | (let [elapsed-us (- (System/currentTimeMillis) started-at)] 31 | (with-precision 2 32 | (str (/ (double elapsed-us) 1000) " seconds")))) 33 | 34 | (defn- notify-cljs [command message colorizer] 35 | (when (seq (:shell command)) 36 | (try 37 | (util/sh (update-in command [:shell] (fn [old] (concat old [message])))) 38 | (catch Throwable e 39 | (println (red "Error running :notify-command:")) 40 | (pst+ e)))) 41 | (println (colorizer message))) 42 | 43 | (defn get-output-files [compiler-options] 44 | (if-let [output-file (:output-to compiler-options)] 45 | [output-file] 46 | (into [] (map :output-to (->> compiler-options :modules vals))))) 47 | 48 | (defn- compile-cljs [cljs-paths compiler-options notify-command incremental? assert? watching?] 49 | (let [output-files (get-output-files compiler-options) 50 | output-files-parent (map fs/parent output-files)] 51 | (println (str "Compiling " (pr-str output-files) " from " (pr-str cljs-paths) "...")) 52 | (flush) 53 | (when (not incremental?) 54 | (fs/delete-dir (:output-dir compiler-options))) 55 | (doseq [output-file-parent output-files-parent] 56 | (when output-file-parent 57 | (fs/mkdirs output-file-parent))) 58 | (let [started-at (System/currentTimeMillis)] 59 | (try 60 | (binding [*assert* assert?] 61 | (bapi/build (apply bapi/inputs cljs-paths) compiler-options)) 62 | (doseq [output-file output-files] 63 | (fs/touch output-file started-at)) 64 | (notify-cljs 65 | notify-command 66 | (str "Successfully compiled " (pr-str output-files) " in " (elapsed started-at) ".") green) 67 | (catch Throwable e 68 | (notify-cljs 69 | notify-command 70 | (str "Compiling " (pr-str output-files) " failed.") red) 71 | (if watching? 72 | (pst+ e) 73 | (throw e))))))) 74 | 75 | (defn- get-mtimes [paths] 76 | (into {} 77 | (map (fn [path] [path (fs/mod-time path)]) paths))) 78 | 79 | (defn- list-modified [output-mtime dependency-mtimes] 80 | (reduce (fn [modified [path mtime]] 81 | (if (< output-mtime mtime) 82 | (conj modified path) 83 | modified)) 84 | [] 85 | dependency-mtimes)) 86 | 87 | (defn get-oldest-mtime [output-files] 88 | (apply min (map (fn [output-file] 89 | (if (fs/exists? output-file) 90 | (fs/mod-time output-file) 91 | 0)) 92 | output-files))) 93 | 94 | (defn- drop-extension [path] 95 | (let [i (.lastIndexOf path ".")] 96 | (if (pos? i) 97 | (subs path 0 i) 98 | path))) 99 | 100 | (defn- relativize [parent path] 101 | (let [path (.getCanonicalPath (fs/file path)) 102 | parent (.getCanonicalPath (fs/file parent))] 103 | (if (.startsWith path parent) 104 | (subs path (count parent)) 105 | path))) 106 | 107 | (def additional-file-extensions 108 | (try 109 | (apply #'read-string [{:read-cond :allow} "#?(:clj 5 :default nil)"]) 110 | #{"cljc"} 111 | (catch Throwable t 112 | #{}))) 113 | 114 | (defn reload-clojure [cljs-files paths compiler-options notify-command] 115 | ;; touch all cljs target files so that cljsc/build will rebuild them 116 | (doseq [cljs-file cljs-files] 117 | (let [target-file (bapi/src-file->target-file (io/file cljs-file) compiler-options)] 118 | (if (.exists target-file) 119 | (.setLastModified target-file 5000)))) 120 | 121 | ;; reload Clojure files 122 | (alter-var-root #'refresh-tracker #(apply dir/scan % paths)) 123 | (alter-var-root #'refresh-tracker reload/track-reload) 124 | (when-let [e (::reload/error refresh-tracker)] 125 | (notify-cljs notify-command 126 | (str "Reloading Clojure file \"" (::reload/error-ns refresh-tracker) "\" failed.") red) 127 | (pst+ e))) 128 | 129 | (defn run-compiler [cljs-paths checkout-paths crossover-path crossover-macro-paths 130 | compiler-options notify-command incremental? 131 | assert? last-dependency-mtimes watching?] 132 | (let [compiler-options (merge {:output-wrapper (= :advanced (:optimizations compiler-options))} 133 | compiler-options) 134 | output-files (get-output-files compiler-options) 135 | lib-paths (:libs compiler-options) 136 | output-mtime (get-oldest-mtime output-files) 137 | macro-files (map :absolute crossover-macro-paths) 138 | macro-classpath-files (into {} (map vector macro-files (map :classpath crossover-macro-paths))) 139 | clj-files (mapcat (fn [cljs-path] 140 | (util/find-files cljs-path (conj additional-file-extensions "clj"))) 141 | (concat cljs-paths checkout-paths)) 142 | cljs-files (->> (concat cljs-paths checkout-paths [crossover-path]) 143 | (mapcat #(util/find-files % (conj additional-file-extensions "cljs"))) 144 | (remove #(contains? cljs.compiler/cljs-reserved-file-names (.getName (io/file %))))) 145 | js-files (let [output-dir-str 146 | (.getAbsolutePath (io/file (:output-dir compiler-options)))] 147 | (->> lib-paths 148 | (mapcat #(util/find-files % #{"js"})) 149 | ; Don't include js files in output-dir or our output file itself, 150 | ; both possible if :libs is set to [""] (a cljs compiler workaround to 151 | ; load all libraries without enumerating them, see 152 | ; http://dev.clojure.org/jira/browse/CLJS-526) 153 | (remove #(.startsWith ^String % output-dir-str)) 154 | (remove #(.endsWith ^String % (:output-to compiler-options))))) 155 | macro-mtimes (get-mtimes macro-files) 156 | clj-mtimes (get-mtimes clj-files) 157 | cljs-mtimes (get-mtimes cljs-files) 158 | js-mtimes (get-mtimes js-files) 159 | dependency-mtimes (merge macro-mtimes clj-mtimes cljs-mtimes js-mtimes)] 160 | (when (not= last-dependency-mtimes dependency-mtimes) 161 | (let [macro-modified (list-modified output-mtime macro-mtimes) 162 | clj-modified (list-modified output-mtime clj-mtimes) 163 | cljs-modified (list-modified output-mtime cljs-mtimes) 164 | js-modified (list-modified output-mtime js-mtimes)] 165 | (when (or (seq macro-modified) (seq clj-modified)) 166 | (reload-clojure cljs-files (concat macro-modified clj-modified) compiler-options notify-command)) 167 | (when (or (seq macro-modified) (seq clj-modified) (seq cljs-modified) (seq js-modified)) 168 | (compile-cljs cljs-paths compiler-options notify-command incremental? assert? watching?)))) 169 | dependency-mtimes)) 170 | -------------------------------------------------------------------------------- /support/src/cljsbuild/crossover.clj: -------------------------------------------------------------------------------- 1 | (ns cljsbuild.crossover 2 | (:use 3 | [clojure.java.io :only [as-url resource]]) 4 | (:require 5 | [cljsbuild.util :as util] 6 | [clojure.string :as string] 7 | [fs.core :as fs]) 8 | (:import 9 | java.io.File 10 | java.net.URLDecoder)) 11 | 12 | (defn- is-macro-file? [file] 13 | (not (neg? (.indexOf (slurp file) ";*CLJSBUILD-MACRO-FILE*;")))) 14 | 15 | ; There is a little bit of madness here to share macros between Clojure 16 | ; and ClojureScript. The latter needs a (:require-macros ...) whereas the 17 | ; former just wants (:require ...). Thus, we have a ;*CLJSBUILD-REMOVE*; 18 | ; conditional comment to allow different code to be used for ClojureScript files. 19 | (defn- filtered-crossover-file [file] 20 | (str 21 | "; DO NOT EDIT THIS FILE! IT WAS AUTOMATICALLY GENERATED BY\n" 22 | "; lein-cljsbuild FROM THE FOLLOWING SOURCE FILE:\n" 23 | "; " file "\n\n" 24 | (string/replace (slurp file) ";*CLJSBUILD-REMOVE*;" ""))) 25 | 26 | (defn get-path-safe 27 | "Pull a local file path out of a resource URL. Without this, it's possible to end up 28 | with weird paths like /C:/x/y/z on Windows, which are troublesome to deal with." 29 | [url] 30 | (-> url 31 | .getPath 32 | (URLDecoder/decode "utf-8") 33 | File. 34 | .getPath)) 35 | 36 | (defn- crossover-to [crossover-path [from-parent from-resource]] 37 | (let [subpath (string/replace-first 38 | (fs/absolute-path (get-path-safe from-resource)) 39 | (fs/absolute-path from-parent) "") 40 | to-file (fs/normalized-path 41 | (util/join-paths (fs/absolute-path crossover-path) subpath))] 42 | (string/replace to-file #"\.clj$" ".cljs"))) 43 | 44 | (defn- recurse-resource-dir [dir] 45 | (when dir 46 | ; We can't determine the contents of a jar dir. Thus, crossover files 47 | ; in jars cannot be specified recursively; they have to be named file 48 | ; by file. 49 | (if (= (.getProtocol dir) "file") 50 | (let [files (util/find-files (get-path-safe dir) #{"clj"})] 51 | (map #(as-url (str "file:" %)) files)) 52 | [dir]))) 53 | 54 | (defn- truncate-url-path [url n] 55 | (if url 56 | (let [uri-path (get-path-safe url)] 57 | (subs uri-path 0 (- (count uri-path) n))) 58 | nil)) 59 | 60 | (defn- ns-to-path [ns] 61 | (let [underscored (string/replace (str ns) #"-" "_")] 62 | (apply util/join-paths 63 | (string/split underscored #"\.")))) 64 | 65 | (defn- find-crossover [crossover macros?] 66 | (let [ns-path (ns-to-path crossover) 67 | as-dir (resource ns-path) 68 | dir-parent (truncate-url-path as-dir (count ns-path)) 69 | recurse-dirs (recurse-resource-dir as-dir) 70 | ns-file-path (str ns-path ".clj") 71 | as-file (resource ns-file-path) 72 | file-parent (truncate-url-path as-file (count ns-file-path)) 73 | all-resources (conj 74 | (map vector (repeat dir-parent) recurse-dirs) 75 | [file-parent as-file]) 76 | all-resources (remove 77 | (comp nil? second) 78 | all-resources) 79 | keep-wanted (if macros? filter remove) 80 | resources (keep-wanted 81 | (comp is-macro-file? second) 82 | all-resources)] 83 | (when (empty? all-resources) 84 | (println "WARNING: Unable to find crossover: " crossover)) 85 | resources)) 86 | 87 | (defn find-crossovers [crossovers macros?] 88 | (distinct 89 | (mapcat #(find-crossover % macros?) crossovers))) 90 | 91 | (defn crossover-macro-paths [crossovers] 92 | (let [macro-paths (find-crossovers crossovers true) 93 | macro-files (remove #(not= (.getProtocol (second %)) "file") macro-paths)] 94 | (map (fn [[parent file]] 95 | (let [file-path (get-path-safe file) 96 | classpath-path (string/replace-first file-path parent "")] 97 | {:absolute (fs/absolute-path file-path) 98 | :classpath classpath-path})) 99 | macro-files))) 100 | 101 | (defn crossover-needs-update? [from-resource to-file] 102 | (or 103 | (not (fs/exists? to-file)) 104 | (and 105 | ; We can't determine the mtime for jar resources; they'll just 106 | ; be copied once and that's it. 107 | (= "file" (.getProtocol from-resource)) 108 | (> (fs/mod-time (get-path-safe from-resource)) (fs/mod-time to-file))))) 109 | 110 | (defn write-crossover 111 | "Write a temp file and atomically rename to the real file 112 | to prevent the compiler from reading a half-written file." 113 | [from-resource to-file] 114 | (let [temp-file (str to-file ".tmp")] 115 | (spit temp-file (filtered-crossover-file from-resource)) 116 | (when-not (fs/rename temp-file to-file) 117 | ; On Windows, if the destination file exists, attempting to replace 118 | ; it by renaming a different file will fail. 119 | (do (fs/delete to-file) 120 | (fs/rename temp-file to-file))) 121 | ; Mark the file as read-only, to hopefully warn the user not to modify it. 122 | (fs/chmod "-w" to-file))) 123 | 124 | (defn copy-crossovers [crossover-path crossovers] 125 | (let [from-resources (find-crossovers crossovers false) 126 | to-files (map (partial crossover-to crossover-path) from-resources)] 127 | (doseq [dir (distinct (map fs/parent to-files))] 128 | (fs/mkdirs dir)) 129 | (doseq [[[_ from-resource] to-file] (zipmap from-resources to-files)] 130 | (when (crossover-needs-update? from-resource to-file) 131 | (write-crossover from-resource to-file))))) 132 | -------------------------------------------------------------------------------- /support/src/cljsbuild/repl/listen.clj: -------------------------------------------------------------------------------- 1 | (ns cljsbuild.repl.listen 2 | (:require 3 | [cljs.repl :as repl] 4 | [cljs.repl.browser :as browser] 5 | [cljsbuild.util :as util] 6 | [clojure.string :as string])) 7 | 8 | (defn run-repl-listen [port output-dir] 9 | (let [env (browser/repl-env :port (Integer. port) :working-dir output-dir)] 10 | (repl/repl env))) 11 | 12 | (defn delayed-process-start [command] 13 | (future 14 | (try 15 | ; TODO: Poll the REPL to see if it's ready before starting 16 | ; the process, instead of sleeping. 17 | (util/sleep 5000) 18 | (util/process-start command) 19 | (catch Exception e 20 | (println "Error in background process: " e) 21 | (throw e))))) 22 | 23 | (defn delayed-process-wait [process] 24 | (let [p @process] 25 | ((:kill p)) 26 | ((:wait p)))) 27 | 28 | (defn run-repl-launch [port output-dir command] 29 | (let [process (delayed-process-start command)] 30 | (try 31 | (run-repl-listen port output-dir) 32 | (finally 33 | (delayed-process-wait process))))) 34 | -------------------------------------------------------------------------------- /support/src/cljsbuild/repl/rhino.clj: -------------------------------------------------------------------------------- 1 | (ns cljsbuild.repl.rhino 2 | (:require 3 | [cljs.repl :as repl] 4 | [cljs.repl.rhino :as rhino])) 5 | 6 | (defn run-repl-rhino [] 7 | (let [env (rhino/repl-env)] 8 | (repl/repl env))) 9 | -------------------------------------------------------------------------------- /support/src/cljsbuild/test.clj: -------------------------------------------------------------------------------- 1 | (ns cljsbuild.test 2 | (:require 3 | [cljsbuild.util :as util]) 4 | (:import 5 | [java.lang Exception])) 6 | 7 | (gen-class :name cljsbuild.test.TestsFailedException 8 | :extends java.lang.Exception) 9 | 10 | (defmacro dofor [seq-exprs body-expr] 11 | `(doall (for ~seq-exprs ~body-expr))) 12 | 13 | (defn run-tests [test-commands] 14 | (let [success (every? #(= % 0) 15 | (dofor [[test-name test-command] test-commands] 16 | (do 17 | (println "Running ClojureScript test:" test-name) 18 | (util/sh test-command))))] 19 | (when (not success) 20 | (throw (cljsbuild.test.TestsFailedException. "Test failed."))))) 21 | -------------------------------------------------------------------------------- /support/src/cljsbuild/util.clj: -------------------------------------------------------------------------------- 1 | (ns cljsbuild.util 2 | (:require 3 | [clojure.java.io :as io] 4 | [clojure.string :as string] 5 | [fs.core :as fs]) 6 | (:import 7 | (java.io File OutputStreamWriter) 8 | (java.lang ProcessBuilder$Redirect) 9 | (java.util List))) 10 | 11 | (defn join-paths [& paths] 12 | (apply str (interpose "/" paths))) 13 | 14 | (defn- filter-by-ext [types files] 15 | (let [ext #(nth (re-matches #".+\.([^\.]+)$" %) 1)] 16 | (filter #(types (ext %)) files))) 17 | 18 | (defn find-files [dir types] 19 | ; not using fs because it's slow for listing directories; 40ms vs 1ms for 20 | ; ~typical `cljsbuild auto` scanning 21 | (letfn [(files-in-dir [^File dir] 22 | (let [fs (.listFiles dir)] 23 | (->> (.listFiles dir) 24 | (remove #(.isHidden ^File %)) 25 | (mapcat #(if (.isFile ^File %) 26 | [%] 27 | (files-in-dir %))))))] 28 | (->> (files-in-dir (io/file dir)) 29 | (map #(.getAbsolutePath ^File %)) 30 | (filter-by-ext types)))) 31 | 32 | (defn sleep [ms] 33 | (Thread/sleep ms)) 34 | 35 | (defn once-every 36 | ([ms desc f keep-going] 37 | (while (keep-going) 38 | (try 39 | (f) 40 | (catch Exception e 41 | (println (str "Error " desc ": " e)))) 42 | (sleep ms))) 43 | ([ms desc f] 44 | (once-every ms desc f (fn [] true)))) 45 | 46 | (defn once-every-bg [& args] 47 | (future 48 | (apply once-every args))) 49 | 50 | (defn process-start [{:keys [shell stdout stderr]}] 51 | (let [process (-> (ProcessBuilder. ^List shell) 52 | (.redirectOutput (if stdout (ProcessBuilder$Redirect/to stdout) ProcessBuilder$Redirect/INHERIT)) 53 | (.redirectError (if stderr (ProcessBuilder$Redirect/to stderr) ProcessBuilder$Redirect/INHERIT)) 54 | (.start))] 55 | {:kill (fn [] 56 | (.destroy process)) 57 | :wait (fn [] 58 | (.waitFor process) 59 | (.exitValue process))})) 60 | 61 | (defn sh [command] 62 | (let [process (process-start command)] 63 | ((:wait process)))) 64 | -------------------------------------------------------------------------------- /support/test/cljsbuild/test/compiler.clj: -------------------------------------------------------------------------------- 1 | (ns cljsbuild.test.compiler 2 | (:use 3 | cljsbuild.compiler 4 | midje.sweet) 5 | (:require 6 | [cljsbuild.util :as util] 7 | [clojure.java.io :as io] 8 | [cljs.build.api :as bapi] 9 | [fs.core :as fs])) 10 | 11 | (def cljs-path-a "src-cljs-a") 12 | (def cljs-file-a (str cljs-path-a "/file-a.cljs")) 13 | (def cljs-path-b "src-cljs-b") 14 | (def cljs-file-b (str cljs-path-b "/file-b.cljs")) 15 | (def cljs-paths [cljs-path-a cljs-path-b]) 16 | 17 | (def cljs-checkout-path-a "checkouts/dep-a/cljs-src") 18 | (def cljs-checkout-file-a "checkouts/dep-a/cljs-src/file-a.cljs") 19 | (def cljs-checkout-path-b "checkouts/dep-b/cljs-src") 20 | (def cljs-checkout-file-b "checkouts/dep-b/cljs-src/file-b.cljs") 21 | (def checkout-paths [cljs-checkout-path-a cljs-checkout-path-b]) 22 | 23 | (def crossover-path "crossovers") 24 | (def crossover-file (str crossover-path "/file-c.cljs")) 25 | (def crossover-macro-absolute "/a/b/crossovers/macros.clj") 26 | (def crossover-macro-classpath "crossovers/macros.clj") 27 | (def crossover-macro-paths [{:absolute crossover-macro-absolute 28 | :classpath crossover-macro-classpath}]) 29 | (def output-to "output-to") 30 | (def output-to-2 "output-to-2") 31 | (def compiler-options 32 | {:output-to output-to 33 | :output-dir "output-dir" 34 | :optimizations :advanced 35 | :pretty-print false}) 36 | (def compiler-options-with-defaults 37 | (assoc compiler-options :output-wrapper true)) 38 | (def notify-command {:shell ["a" "b"] :test "c"}) 39 | (def assert? false) 40 | (def incremental? true) 41 | (def mtime 1234) 42 | (def mtime-2 5678) 43 | 44 | ; TODO: We really need more tests here, particularly for the crossover/clojure reloading stuff. 45 | 46 | (fact "run-compiler calls cljs/build correctly" 47 | (run-compiler 48 | cljs-paths 49 | checkout-paths 50 | crossover-path 51 | crossover-macro-paths 52 | compiler-options 53 | notify-command 54 | incremental? 55 | assert? 56 | {} 57 | false) => (just {cljs-file-a mtime 58 | cljs-file-b mtime 59 | cljs-checkout-file-a mtime 60 | cljs-checkout-file-b mtime 61 | crossover-file mtime 62 | crossover-macro-absolute mtime}) 63 | (provided 64 | (fs/exists? output-to) => false :times 1 65 | (util/find-files cljs-path-a #{"clj"}) => [] :times 1 66 | (util/find-files cljs-path-b #{"clj"}) => [] :times 1 67 | (util/find-files cljs-path-a #{"cljs"}) => [cljs-file-a] :times 1 68 | (util/find-files cljs-path-b #{"cljs"}) => [cljs-file-b] :times 1 69 | (util/find-files cljs-checkout-path-a #{"clj"}) => [] :times 1 70 | (util/find-files cljs-checkout-path-b #{"clj"}) => [] :times 1 71 | (util/find-files cljs-checkout-path-a #{"cljs"}) => [cljs-checkout-file-a] :times 1 72 | (util/find-files cljs-checkout-path-b #{"cljs"}) => [cljs-checkout-file-b] :times 1 73 | (util/find-files crossover-path #{"cljs"}) => [crossover-file] :times 1 74 | (util/sh anything) => nil :times 1 75 | (fs/mod-time cljs-file-a) => mtime :times 1 76 | (fs/mod-time cljs-file-b) => mtime :times 1 77 | (fs/mod-time cljs-checkout-file-a) => mtime :times 1 78 | (fs/mod-time cljs-checkout-file-b) => mtime :times 1 79 | (fs/mod-time crossover-file) => mtime :times 1 80 | (fs/mod-time crossover-macro-absolute) => mtime :times 1 81 | (fs/mkdirs anything) => nil 82 | (reload-clojure [cljs-file-a 83 | cljs-file-b 84 | cljs-checkout-file-a 85 | cljs-checkout-file-b 86 | crossover-file] 87 | [crossover-macro-absolute] compiler-options-with-defaults notify-command) => nil :times 1 88 | ; bapi/inputs returns different instance each time and it doesn't provide equals method 89 | (bapi/build 90 | (as-checker #(and (instance? cljs.closure.Compilable %) (instance? cljs.closure.Inputs %))) 91 | compiler-options-with-defaults) 92 | => nil :times 1)) 93 | 94 | (fact "returns oldest modified time" 95 | (get-oldest-mtime [cljs-file-a cljs-file-b]) => mtime 96 | (provided 97 | (fs/exists? cljs-file-a) => true 98 | (fs/mod-time cljs-file-a) => mtime 99 | (fs/exists? cljs-file-b) => true 100 | (fs/mod-time cljs-file-b) => mtime-2)) 101 | 102 | (fact "should get output file provided through output-to option" 103 | (get-output-files {:output-to output-to}) => [output-to]) 104 | 105 | (fact "should get output files provided through modules option" 106 | (get-output-files {:modules {:front {:output-to output-to 107 | :entries #{"hb.front.core"}} 108 | :extranet {:output-to output-to-2 109 | :entries #{"hb.core"}}}}) => [output-to output-to-2]) 110 | 111 | (fact "should return no output files when output-to and modules options not provided" 112 | (get-output-files {}) => []) 113 | -------------------------------------------------------------------------------- /support/test/cljsbuild/test/crossover.clj: -------------------------------------------------------------------------------- 1 | (ns cljsbuild.test.crossover 2 | (:use 3 | cljsbuild.crossover 4 | midje.sweet) 5 | (:require 6 | [clojure.java.io :as io] 7 | [fs.core :as fs]) 8 | (:import java.net.URL)) 9 | 10 | (def crossover-path "/project/crossovers") 11 | (def crossovers '[a a.a a.b]) 12 | 13 | (fact 14 | (copy-crossovers crossover-path crossovers) => nil 15 | (provided 16 | (find-crossovers crossovers false) => 17 | [["/project/src" (io/file "/project/src/a.clj")] 18 | ["/project/src" (io/file "/project/src/a/a.clj")] 19 | ["/project/src" (io/file "/project/src/a/b.clj")]] :times 1 20 | (fs/mkdirs anything) => nil 21 | (crossover-needs-update? anything anything) => true 22 | (write-crossover anything anything) => nil :times 3)) 23 | 24 | (def cljsbuild-remove ";*CLJSBUILD-REMOVE*;") 25 | (def clojurescript-source 26 | (str 27 | "(ns a) 28 | " cljsbuild-remove " 29 | (def a 5)")) 30 | 31 | (defn- tempfile [] 32 | (doto (fs/temp-file) .deleteOnExit)) 33 | 34 | (let [from-resource (tempfile) 35 | to-file (tempfile)] 36 | (spit from-resource clojurescript-source) 37 | (fact "crossover files are copied/edited correctly" 38 | (write-crossover from-resource to-file) => anything 39 | (.indexOf (slurp to-file) cljsbuild-remove) => -1)) 40 | 41 | (let [windows-index (-> "os.name" System/getProperty .toLowerCase (.indexOf "win")) 42 | expected-path (if (not= windows-index -1) "C:/x/y/z" "/C:/x/y/z")] 43 | (fact "Paths are pulled out of URLs correctly." 44 | (get-path-safe (URL. "file:/C:/x/y/z")) => expected-path)) 45 | 46 | ; TODO: It would be nice to test more of the crossover features, but they 47 | ; are pretty heavily dependent on interop and resources, which makes 48 | ; that difficult. 49 | -------------------------------------------------------------------------------- /support/test/cljsbuild/test/repl/listen.clj: -------------------------------------------------------------------------------- 1 | (ns cljsbuild.test.repl.listen 2 | (:use 3 | cljsbuild.repl.listen 4 | midje.sweet) 5 | (:require 6 | [cljs.repl :as repl] 7 | [cljs.repl.browser :as browser] 8 | [cljsbuild.util :as util])) 9 | 10 | (def port (Integer. 1234)) 11 | (def output-dir "output-dir") 12 | (def command {:shell ["command"]}) 13 | 14 | (fact 15 | (run-repl-listen port output-dir) => nil 16 | 17 | (run-repl-launch port output-dir command) => nil 18 | (provided 19 | (delayed-process-start command) => (future {:kill (fn [] nil) :wait (fn [] nil)})) 20 | 21 | (against-background 22 | (browser/repl-env :port port :working-dir output-dir) => {} :times 1 23 | (repl/repl {}) => nil :times 1)) 24 | -------------------------------------------------------------------------------- /support/test/cljsbuild/test/repl/rhino.clj: -------------------------------------------------------------------------------- 1 | (ns cljsbuild.test.repl.rhino 2 | (:use 3 | cljsbuild.repl.rhino 4 | midje.sweet) 5 | (:require 6 | [cljs.repl :as repl] 7 | [cljs.repl.rhino :as rhino])) 8 | 9 | (fact 10 | (run-repl-rhino) => nil 11 | (provided 12 | (rhino/repl-env) => {} :times 1 13 | (repl/repl {}) => nil :times 1)) 14 | -------------------------------------------------------------------------------- /support/test/cljsbuild/test/test.clj: -------------------------------------------------------------------------------- 1 | (ns cljsbuild.test.test 2 | (:use 3 | cljsbuild.test 4 | midje.sweet) 5 | (:require 6 | [cljsbuild.util :as util])) 7 | 8 | (fact 9 | (let [command-1 {:shell ["command1"]} 10 | command-2 {:shell ["command2"]} 11 | command-3 {:shell ["command3"]} 12 | commands [["command1" command-1] ["command2" command-2] ["command3" command-3]]] 13 | (run-tests commands) => nil 14 | (provided 15 | (util/sh command-1) => 0 :times 1 16 | (util/sh command-2) => 0 :times 1 17 | (util/sh command-3) => 0 :times 1))) 18 | 19 | (fact 20 | (let [command-1 {:shell ["command1"]} 21 | command-2 {:shell ["command2"]} 22 | commands [["command1" command-1] ["command2" command-2]]] 23 | (run-tests commands) => (throws cljsbuild.test.TestsFailedException) 24 | (provided 25 | (util/sh command-1) => 0 :times 1 26 | (util/sh command-2) => 1 :times 1))) 27 | -------------------------------------------------------------------------------- /support/test/cljsbuild/test/util.clj: -------------------------------------------------------------------------------- 1 | (ns cljsbuild.test.util 2 | (:use 3 | cljsbuild.util 4 | midje.sweet) 5 | (:require 6 | [clojure.java.io :as io] 7 | [fs.core :as fs])) 8 | 9 | (fact 10 | (join-paths) => "" 11 | (join-paths "a") => "a" 12 | (join-paths "a" "b" "c") => "a/b/c") 13 | 14 | (fact 15 | (let [files ["a.a" "b.b" "c.c" "d.d" "e.d" "f.d" "1/2/3/4/5.e"] 16 | filter-by-ext #'cljsbuild.util/filter-by-ext] 17 | (filter-by-ext #{"q" "r" "s"} files) => [] 18 | (filter-by-ext #{"a"} files) => ["a.a"] 19 | (filter-by-ext #{"d"} files) => ["d.d" "e.d" "f.d"] 20 | (filter-by-ext #{"e"} files) => ["1/2/3/4/5.e"] 21 | (filter-by-ext #{"a" "b" "c" "d" "e"} files) => files)) 22 | 23 | (defn- path-ends-with? [exts path] 24 | (some #(.endsWith path %) exts)) 25 | 26 | (fact (find-files ".." #{"md"}) => #(every? (partial path-ends-with? #{".md"}) %)) 27 | 28 | (unfinished call-once-every) 29 | (unfinished keep-going) 30 | 31 | (fact 32 | (once-every 1000 "description" call-once-every keep-going) => nil 33 | (provided 34 | (keep-going) =streams=> [true true true false] :times 4 35 | (call-once-every) => nil :times 3 36 | (sleep 1000) => nil :times 3)) 37 | 38 | ; TODO: It would be nice to test process-start, but it does a lot of Java 39 | ; interop so I'm not sure how to go about that just yet. Maybe if 40 | ; it was switched to using "conch" instead of raw interop it would 41 | ; be easier to test? 42 | 43 | (fact 44 | (let [command {:shell ["command" "arg1" "arg2"]}] 45 | (sh command) => 0 46 | (provided 47 | (process-start command) => {:wait (fn [] 0)} :times 1))) 48 | --------------------------------------------------------------------------------