├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── appveyor.yml ├── build.gradle ├── docs ├── faq.md ├── grunt.md ├── gulp.md ├── installing.md └── node.md ├── examples ├── README.md ├── build.gradle ├── settings.gradle ├── simple-grunt │ ├── Gruntfile.js │ ├── README.md │ ├── build.gradle │ └── package.json ├── simple-gulp │ ├── Gulpfile.js │ ├── README.md │ ├── build.gradle │ └── package.json ├── simple-node │ ├── README.md │ ├── build.gradle │ ├── package.json │ └── src │ │ └── node │ │ └── index.js └── simple-yarn │ ├── README.md │ ├── build.gradle │ ├── package.json │ ├── src │ └── node │ │ └── index.js │ └── yarn.lock ├── gradle.properties ├── gradle ├── additional-artifacts.gradle ├── buildscript.gradle ├── coverage.gradle ├── publishing.gradle ├── travis-ci.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── integTest ├── groovy │ └── com │ │ └── moowork │ │ └── gradle │ │ ├── AbstractIntegTest.groovy │ │ ├── grunt │ │ ├── GruntInstall_integTest.groovy │ │ ├── GruntRule_integTest.groovy │ │ └── Grunt_integTest.groovy │ │ ├── gulp │ │ ├── GulpInstall_integTest.groovy │ │ ├── GulpRule_integTest.groovy │ │ └── Gulp_integTest.groovy │ │ └── node │ │ ├── npm │ │ ├── NpmInstall_integTest.groovy │ │ └── NpmRule_integTest.groovy │ │ ├── task │ │ ├── Node_integTest.groovy │ │ └── Setup_integTest.groovy │ │ └── yarn │ │ ├── YarnInstall_integTest.groovy │ │ └── YarnRule_integTest.groovy └── resources │ └── fixtures │ ├── npm-missing │ └── package.json │ ├── npm-present │ ├── npm-shrinkwrap.json │ └── package.json │ └── yarn │ └── package.json ├── main ├── groovy │ └── com │ │ └── moowork │ │ └── gradle │ │ ├── grunt │ │ ├── GruntExtension.groovy │ │ ├── GruntInstallTask.groovy │ │ ├── GruntPlugin.groovy │ │ └── GruntTask.groovy │ │ ├── gulp │ │ ├── GulpExtension.groovy │ │ ├── GulpInstallTask.groovy │ │ ├── GulpPlugin.groovy │ │ └── GulpTask.groovy │ │ └── node │ │ ├── NodeExtension.groovy │ │ ├── NodePlugin.groovy │ │ ├── exec │ │ ├── ExecRunner.groovy │ │ └── NodeExecRunner.groovy │ │ ├── npm │ │ ├── NpmExecRunner.groovy │ │ ├── NpmInstallTask.groovy │ │ ├── NpmSetupTask.groovy │ │ └── NpmTask.groovy │ │ ├── task │ │ ├── NodeTask.groovy │ │ └── SetupTask.groovy │ │ ├── util │ │ └── PlatformHelper.groovy │ │ ├── variant │ │ ├── Variant.groovy │ │ └── VariantBuilder.groovy │ │ └── yarn │ │ ├── YarnExecRunner.groovy │ │ ├── YarnInstallTask.groovy │ │ ├── YarnSetupTask.groovy │ │ └── YarnTask.groovy └── resources │ └── META-INF │ └── gradle-plugins │ ├── com.moowork.grunt.properties │ ├── com.moowork.gulp.properties │ └── com.moowork.node.properties └── test └── groovy └── com └── moowork └── gradle ├── AbstractProjectTest.groovy ├── grunt ├── AbstractTaskTest.groovy ├── GruntPluginTest.groovy └── GruntTaskTest.groovy ├── gulp ├── AbstractTaskTest.groovy ├── GulpPluginTest.groovy └── GulpTaskTest.groovy └── node ├── NodeExtensionTest.groovy ├── NodePluginTest.groovy ├── npm ├── NpmSetupTaskTest.groovy └── NpmTaskTest.groovy ├── task ├── AbstractTaskTest.groovy └── NodeTaskTest.groovy ├── util └── PlatformHelperTest.groovy ├── variant └── VariantBuilderTest.groovy └── yarn ├── YarnSetupTaskTest.groovy └── YarnTaskTest.groovy /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build/ 3 | .gradle/ 4 | *.iws 5 | *.ids 6 | *.iml 7 | *.ipr 8 | .idea 9 | /out 10 | /.nb-gradle/ 11 | node_modules 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: groovy 2 | sudo: false 3 | 4 | cache: 5 | directories: 6 | - $HOME/.gradle 7 | 8 | jdk: 9 | - oraclejdk8 10 | 11 | script: 12 | - ./gradlew ci --stacktrace 13 | #- ./gradlew -p examples build --stacktrace 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | Version 1.4.0 *(unreleased)* 5 | ---------------------------- 6 | 7 | * ... 8 | 9 | Version 1.3.1 *(2019-03-18)* 10 | ---------------------------- 11 | 12 | * Using BinTray for artifact publishing 13 | 14 | Version 1.3.0 *(2019-03-17)* 15 | ---------------------------- 16 | 17 | * Relativize symlink to npmScriptFile (#319) _(fp7)_ 18 | * Added gradle build cache support for npm install (#318) _(bjornmagnusson)_ 19 | * Updated to use node version 11.12.0 as default 20 | * Bumped Gradle wrapper version to 5.2.1 21 | * Bumped Java compatibility version to 1.8 22 | 23 | Version 1.2.0 *(2017-06-14)* 24 | ---------------------------- 25 | 26 | * Support using Gradle Build Cache with Yarn tasks (#205) _(mark-vieira)_ 27 | * Bumped Gradle wrapper version to 3.5 28 | * New args can be added for npmSetup and yarnSetup (#226) 29 | * Uses --no-save for npmSetup and yarnSetup (#222) 30 | * Added execOverrides and ignoreExitValue on npmSetupTask (#196) 31 | * Added gruntFile in grunt extension to use different file (#189) 32 | * npm_* tasks using nodeModulesDir (##136) _(janario)_ 33 | 34 | Version 1.1.1 *(2017-01-16)* 35 | ---------------------------- 36 | 37 | * Plugin publishing problems (#188) 38 | 39 | Version 1.1.0 *(2017-01-13)* 40 | ---------------------------- 41 | 42 | * Override environment instead of setting it (#176) _(s0x)_ 43 | * Fix typo in resolveSingle (#166) _(s0x)_ 44 | * Add support for node options (#141, #174) _(whboyd)_ 45 | * Fix symlink problem using NPM (#164, #165, #181) _(s0x)_ 46 | * Set PATH variable for node, npm and yarn (#134, #146, #149, #164, #179) _(s0x)_ 47 | 48 | Version 1.0.1 *(2016-12-04)* 49 | ---------------------------- 50 | 51 | * Publish directly to plugins.gradle.org instead of bintray (#172) 52 | * Fixed problem with resolving Grunt and Gulp plugins (#170) 53 | 54 | Version 1.0.0 *(2016-12-02)* 55 | ---------------------------- 56 | 57 | * Move npm and yarn classes into separate package (#158) 58 | * Move grunt plugin code to this plugin (#159) 59 | * Move gulp plugin code to this plugin (#160) 60 | * Use 6.9.1 as default node version (#163) 61 | * Fix missing property exception when using plugin in conjunction with Node 6.x.x on Windows (#167) _(mark-vieira)_ 62 | * Switch over to use semantic versioning (#169) 63 | 64 | Version 0.14 *(2016-11-29)* 65 | --------------------------- 66 | 67 | * Bumped gradle wrapper version to 3.2.1 68 | * Use gradle-testkit instead of nebula (#153) 69 | * Update to use Windows zip dists when available (#142) _(datallah)_ 70 | * Added support for Yarn (#145, #151) _(kaitoy)_ 71 | 72 | Version 0.13 *(2016-06-27)* 73 | --------------------------- 74 | 75 | * Bumped gradle wrapper version to 2.14 76 | * Implement ARM compatibility _(madmas)_ 77 | * Allow node modules to be used when calling npm_run _(jmcampanini)_ 78 | * Updated plugin versions and test versions 79 | * Node.workingDir set to nodeModulesDir (fixes #107) 80 | * Creates nodeModulesDir if it does not exist (fixes #108) 81 | * Use single repo for node download (fixes #120) 82 | 83 | Version 0.12 *(2016-03-10)* 84 | --------------------------- 85 | 86 | * Updated wrapper to use Gradle 2.11 87 | * Refactored windows-specific logic for npm _(peol)_ 88 | * Use temporary repository for resolving node dependencies 89 | * Using 4.4.0 (latest LTS) as default node version 90 | * Changed default workDir location to be local to project directory 91 | 92 | Version 0.11 *(2015-09-26)* 93 | --------------------------- 94 | 95 | * Handle 4+ nodejs releases on windows _(dvaske)_ 96 | * Add npmCommand parameter to the node extension _(janrotter)_ 97 | * Set executable flag on node in SetupTask 98 | * Upgraded wrapper to use Gradle 2.7 99 | * Update node distribution base url to use https _(AvihayTsayeg)_ 100 | * Added more tests (unit, integration and functional tests) 101 | * NodeTask environment is now correctly propagated to the runner 102 | 103 | Version 0.10 *(2015-05-19)* 104 | --------------------------- 105 | 106 | * Fixed problem with spaces in workDir 107 | * Add configuration for node_modules location _(nmalaguti)_ 108 | * Solaris support _(halfninja)_ 109 | * Upgraded wrapper to use Gradle 2.4 110 | 111 | Version 0.9 *(2015-02-28)* 112 | -------------------------- 113 | 114 | * Updated some plugin references 115 | * Fixed some tests on Windows _(abelsromero)_ 116 | * Fixed issue for windows environments, not containing "PATH" but "Path" _(tspaeth)_ 117 | * Allow 64 bit windows to use the x64 node.exe _(ekaufman)_ 118 | * Renamed "ext" property on SetupTask so that it's not causing any conflicts 119 | * Added detection for FreeBSD as a Linux variant 120 | * Compiling using Java 1.6 compatiblity _(stianl)_ 121 | 122 | Version 0.8 *(2014-11-19)* 123 | -------------------------- 124 | 125 | * Publish snapshots to jcenter _(dougborg)_ 126 | * Add node to execution path for NodeExec _(dougborg)_ 127 | * Use 'com.moowork.node' id instead of 'node 128 | * Upgraded wrapper to use Gradle 2.2 129 | 130 | Version 0.7 *(2014-11-03)* 131 | -------------------------- 132 | 133 | * Allow local npm to override bundled npm _(dougborg)_ 134 | * Allow for configuring npmVersion _(dougborg)_ 135 | * Upgrade to Gradle 2.1 136 | 137 | Version 0.6 *(2014-07-10)* 138 | -------------------------- 139 | 140 | * Upgrade to Gradle 2.0 141 | * Using 'com.moowork.node' as plugin id, but 'node' still works for another version 142 | * Possible to read execResult for npm and node tasks _(johnrengelman)_ 143 | 144 | Version 0.5 *(2014-03-29)* 145 | -------------------------- 146 | 147 | * Upgraded to use Node version 0.10.22 _(tkruse)_ 148 | * Provide gradle rule to run any NPM commands _(tkruse)_ 149 | 150 | Version 0.4 *(2014-03-07)* 151 | -------------------------- 152 | 153 | * Possible to ignoreExitValue _(konrad-garus)_ 154 | * Added support for exec taks overrides (delegates down to Gradle exec task) _(konrad-garus)_ 155 | * Now adding npmInstall outside afterEvaluate to allow better dependsOn usage 156 | * Reworked SetupTask so that it is using task input/output change tracking 157 | * Updated gradle wrapper to version 1.11 158 | 159 | Version 0.3 *(2014-01-10)* 160 | -------------------------- 161 | 162 | * Initial usable version 163 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gradle Plugin for Node 2 | 3 | [![Build Status](https://travis-ci.org/srs/gradle-node-plugin.svg?branch=master)](https://travis-ci.org/srs/gradle-node-plugin) 4 | [![Windows Build status](https://ci.appveyor.com/api/projects/status/06pg08c36mnes0w3?svg=true)](https://ci.appveyor.com/project/srs/gradle-node-plugin) 5 | [![License](https://img.shields.io/github/license/srs/gradle-node-plugin.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) 6 | ![Version](https://img.shields.io/badge/Version-1.3.1-orange.svg) 7 | 8 | This plugin enabled you to use a lot of [NodeJS](https://nodejs.org)-based technologies as part of your 9 | build without having NodeJS installed locally on your system. It integrates the following NodeJS-based system 10 | with Gradle: 11 | 12 | * [NodeJS](https://nodejs.org) 13 | * [Yarn](https://yarnpkg.com/) 14 | * [Grunt](https://gruntjs.com/) 15 | * [Gulp](https://gulpjs.com/) 16 | 17 | It's actually 3 plugins in one: 18 | 19 | * [Node Plugin](https://plugins.gradle.org/plugin/com.moowork.node) (`com.moowork.node`) - [See docs](docs/node.md). 20 | * [Grunt Plugin](https://plugins.gradle.org/plugin/com.moowork.grunt) (`com.moowork.grunt`) - [See docs](docs/grunt.md) 21 | * [Gulp Plugin](https://plugins.gradle.org/plugin/com.moowork.gulp) (`com.moowork.gulp`) - [See docs](docs/gulp.md) 22 | 23 | 24 | ## Documentation 25 | 26 | Here's how you get started using this plugin. If you do not find what you are looking for, please add an 27 | issue to [GitHub Issues](https://github.com/srs/gradle-node-plugin/issues). 28 | 29 | * [Installing](docs/installing.md) 30 | * [Node Plugin](docs/node.md) 31 | * [Grunt Plugin](docs/grunt.md) 32 | * [Gulp Plugin](docs/gulp.md) 33 | * [FAQ](docs/faq.md) 34 | * [Changelog](CHANGELOG.md) 35 | 36 | 37 | ## Documentation for older releases 38 | 39 | Here's the documentation for older releases of the plugin: 40 | 41 | * [1.2.0](https://github.com/srs/gradle-node-plugin/blob/v1.2.0/README.md) 42 | * [1.1.1](https://github.com/srs/gradle-node-plugin/blob/v1.1.1/README.md) 43 | * [1.1.0](https://github.com/srs/gradle-node-plugin/blob/v1.1.0/README.md) 44 | * [1.0.1](https://github.com/srs/gradle-node-plugin/blob/v1.0.1/README.md) 45 | * [1.0.0](https://github.com/srs/gradle-node-plugin/blob/v1.0.0/README.md) 46 | * [0.14](https://github.com/srs/gradle-node-plugin/blob/v0.14/README.md) 47 | * [0.13](https://github.com/srs/gradle-node-plugin/blob/v0.13/README.md) 48 | * [0.12](https://github.com/srs/gradle-node-plugin/blob/v0.12/README.md) 49 | * [0.11](https://github.com/srs/gradle-node-plugin/blob/v0.11/README.md) 50 | * [0.10](https://github.com/srs/gradle-node-plugin/blob/v0.10/README.md) 51 | 52 | 53 | ## Building the Plugin 54 | 55 | To build the plugin, just type the following command: 56 | 57 | ```bash 58 | ./gradlew clean build 59 | ``` 60 | 61 | 62 | ## Contributing 63 | 64 | Contributions are always welcome! If you'd like to contribute (and we hope you do) please send 65 | one of the existing contributors a nudge. 66 | 67 | 68 | ## License 69 | 70 | ``` 71 | Licensed under the Apache License, Version 2.0 (the "License"); 72 | you may not use this file except in compliance with the License. 73 | You may obtain a copy of the License at 74 | 75 | http://www.apache.org/licenses/LICENSE-2.0 76 | 77 | Unless required by applicable law or agreed to in writing, software 78 | distributed under the License is distributed on an "AS IS" BASIS, 79 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 80 | See the License for the specific language governing permissions and 81 | limitations under the License. 82 | ``` 83 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{branch} {build}" 2 | 3 | build: 4 | verbosity: detailed 5 | 6 | build_script: 7 | - gradlew ci --stacktrace 8 | # - gradlew -p examples build --stacktrace 9 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | apply from: "$rootDir/gradle/buildscript.gradle", to: buildscript 3 | } 4 | 5 | group = 'com.moowork.gradle' 6 | 7 | apply plugin: 'idea' 8 | apply plugin: 'groovy' 9 | apply plugin: 'maven' 10 | apply plugin: 'maven-publish' 11 | apply plugin: 'java-gradle-plugin' 12 | apply from: "$rootDir/gradle/additional-artifacts.gradle" 13 | apply from: "$rootDir/gradle/coverage.gradle" 14 | apply from: "$rootDir/gradle/travis-ci.gradle" 15 | apply from: "$rootDir/gradle/publishing.gradle" 16 | 17 | ext.compatibilityVersion = '1.8' 18 | sourceCompatibility = compatibilityVersion 19 | targetCompatibility = compatibilityVersion 20 | 21 | repositories { 22 | jcenter() 23 | } 24 | 25 | configurations { 26 | integTestCompile.extendsFrom testCompile 27 | integTestRuntime.extendsFrom testRuntime 28 | } 29 | 30 | dependencies { 31 | compile gradleApi() 32 | testCompile 'cglib:cglib-nodep:3.2.4' 33 | testCompile 'org.apache.commons:commons-io:1.3.2' 34 | testCompile( 'org.spockframework:spock-core:1.0-groovy-2.4' ) { 35 | exclude group: 'org.codehaus.groovy' 36 | } 37 | } 38 | 39 | tasks.withType( Test ) { 40 | testLogging { 41 | events "skipped", "failed" 42 | exceptionFormat "full" 43 | } 44 | } 45 | 46 | sourceSets { 47 | integTest 48 | } 49 | 50 | task integTest( type: Test ) { 51 | shouldRunAfter test 52 | 53 | testClassesDirs = sourceSets.integTest.output.classesDirs 54 | classpath = sourceSets.integTest.runtimeClasspath 55 | } 56 | -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | This page contains a collection of frequently asked questions. 4 | 5 | 6 | # How to avoid node/npm/yarn task execution if no changes in web files? 7 | 8 | Just add to your bundle task filesets (in and out) which this task depends on: 9 | 10 | ```gradle 11 | task bundle(type: YarnTask) { 12 | inputs.files(fileTree('node_modules')) 13 | inputs.files(fileTree('src')) 14 | inputs.file('package.json') 15 | inputs.file('webpack.config.js') 16 | 17 | outputs.dir('build/resources/static') 18 | 19 | dependsOn yarn_install 20 | args = ['run', 'build'] 21 | } 22 | ``` 23 | 24 | More info in [Gradle doc](https://docs.gradle.org/current/userguide/more_about_tasks.html#sec:up_to_date_checks) 25 | 26 | 27 | # How do I set log level for NPM install task? 28 | 29 | This can be done adding some arguments to the already defined `npmInstall`-task. To set the log level to `silly` do this: 30 | 31 | ```gradle 32 | npmInstall.args = ['--loglevel', 'silly'] 33 | ``` 34 | 35 | # How do I specify a registry for the NPM setup task? 36 | 37 | This can be done by adding to the arguments for the already defined `npmSetup` task. 38 | 39 | ```gradle 40 | tasks.npmSetup { 41 | doFirst { 42 | args = args + ['--registry', 'http://myregistry.npm.com'] 43 | } 44 | } 45 | ``` 46 | 47 | You can also add any other arguments to this list that work with `npm install` i.e. more verbose modes. -------------------------------------------------------------------------------- /docs/grunt.md: -------------------------------------------------------------------------------- 1 | # Grunt Plugin 2 | 3 | This is a very simple Gradle plugin for running [Grunt](http://gruntjs.com/) tasks part of the build. It merely 4 | wraps calls to `grunt xyz` as `gradle grunt_xyz` tasks. Grunt is installed locally using npm. To 5 | start using the plugin, add this into your `build.gradle` file (see [Installing](installing.md) for details): 6 | 7 | ```gradle 8 | plugins { 9 | id "com.moowork.grunt" version "1.2.0" 10 | } 11 | ``` 12 | 13 | The plugin will also automatically apply the [Node Plugin](node-plugin.md). 14 | 15 | 16 | ## Using the plugin 17 | 18 | You can run grunt tasks using this syntax: 19 | 20 | ```bash 21 | $ gradle grunt_build # this runs grunt build 22 | $ gradle grunt_compile # this runs grunt compile 23 | ``` 24 | 25 | ... and so on. 26 | 27 | These tasks do not appear explicitly in `gradle tasks`, they only appear as task rule. 28 | Your Gruntfile.js defines what grunt_* tasks exist (see `grunt --help`, or `gradle grunt_--help`). 29 | 30 | Also (more importantly), you can depend on those tasks, e.g. 31 | 32 | ```gradle 33 | // runs "grunt build" as part of your gradle build 34 | build.dependsOn grunt_build 35 | ``` 36 | 37 | This is the main advantage of this plugin, to allow build scripts (and grunt agnostics) to run grunt 38 | tasks via gradle. 39 | 40 | It is also possible to run a grunt task only if one of its input files have changed: 41 | 42 | ```gradle 43 | def srcDir = new File(projectDir, "src/main/web") 44 | def targetDir = new File(project.buildDir, "web") 45 | grunt_dist.inputs.dir srcDir 46 | grunt_dist.outputs.dir targetDir 47 | ``` 48 | 49 | 50 | ## Extended Usage 51 | 52 | If you need to supply grunt with options, you have to create Grunt-tasks: 53 | 54 | ```gradle 55 | task gruntBuildWithOpts(type: GruntTask) { 56 | args = ["build", "arg1", "arg2"] 57 | } 58 | ``` 59 | 60 | 61 | ## NPM helpers 62 | 63 | The plugin also provides two fixed helper tasks to run once per project, which 64 | however require npm (https://npmjs.org/) to be installed: 65 | 66 | - `installGrunt`: This installs grunt and grunt-cli to the project folder, using `npm install grunt grunt-cli` 67 | - `npmInstall`: This just runs `npm install` (possibly useful for scripting) 68 | 69 | Since grunt will only be installed in your project folder, it will not interact with the rest of your 70 | system, and can easily be removed later by deleting the node_modules folders. 71 | 72 | So as an example, you can make sure a local version of grunt exists using this: 73 | 74 | ```gradle 75 | // makes sure on each build that grunt is installed 76 | grunt_build.dependsOn 'installGrunt' 77 | 78 | // processes your package.json before running grunt build 79 | grunt_build.dependsOn 'npmInstall' 80 | 81 | // runs "grunt build" as part of your gradle build 82 | build.dependsOn grunt_build 83 | ``` 84 | 85 | 86 | ## Configuring the Plugin 87 | 88 | You can configure the plugin using the `grunt` extension block, like this: 89 | 90 | ```gradle 91 | grunt { 92 | // Set the directory where Gruntfile.js should be found 93 | workDir = file("${project.projectDir}") 94 | 95 | // Whether colors should output on the terminal 96 | colors = true 97 | 98 | // Whether output from Grunt should be buffered - useful when running tasks in parallel 99 | bufferOutput = false 100 | 101 | // Use different gruntFile 102 | gruntFile = 'Gruntfile.js' 103 | } 104 | ``` 105 | -------------------------------------------------------------------------------- /docs/gulp.md: -------------------------------------------------------------------------------- 1 | # Gulp Plugin 2 | 3 | This is a very simple Gradle plugin for running [Gulp](http://gulpjs.com/) tasks part of the build. 4 | It merely wraps calls to `gulp xyz` as gradle `gulp_xyz` tasks. Gulp is installed locally using npm. 5 | To start using the plugin, add this into your `build.gradle` file (see [Installing](installing.md) for details): 6 | 7 | ```gradle 8 | plugins { 9 | id "com.moowork.gulp" version "1.2.0" 10 | } 11 | ``` 12 | 13 | The plugin will also automatically apply the [Node Plugin](node-plugin.md). 14 | 15 | 16 | ## Using the plugin 17 | 18 | You can run gulp tasks using this syntax: 19 | 20 | ```bash 21 | $ gradle gulp_build # this runs gulp build 22 | $ gradle gulp_compile # this runs gulp compile 23 | ``` 24 | 25 | ... and so on. 26 | 27 | These tasks do not appear explicitly in `gradle tasks`, they only appear as task rule. 28 | Your gulpfile.js defines what gulp_* tasks exist (see `gulp --tasks`, or `gradle gulp_--tasks`). 29 | 30 | Also (more importantly), you can depend on those tasks, e.g. 31 | 32 | ```gradle 33 | // runs "gulp build" as part of your gradle build 34 | build.dependsOn gulp_build 35 | ``` 36 | 37 | This is the main advantage of this plugin, to allow build scripts (and gulp agnostics) to run 38 | gulp tasks via gradle. 39 | 40 | It is also possible to run a gulp task only if one of its input files have changed: 41 | 42 | ```gradle 43 | def srcDir = new File(projectDir, "src/main/web") 44 | def targetDir = new File(project.buildDir, "web") 45 | gulp_dist.inputs.dir srcDir 46 | gulp_dist.outputs.dir targetDir 47 | ``` 48 | 49 | ## Example `build.gradle` file 50 | 51 | To get you started here is what it might look like to integrate this plugin to a Java project. 52 | 53 | ```gradle 54 | plugins { 55 | id "com.moowork.gulp" version "1.0" 56 | } 57 | 58 | apply plugin: "java" 59 | 60 | // run npm install 61 | gulp_default.dependsOn 'npmInstall' 62 | 63 | // run gulp install 64 | gulp_default.dependsOn 'installGulp' 65 | 66 | // processResources is a Java task. Run the gulpfile.js before this task using the 'default' task in the gulpfile.js 67 | processResources.dependsOn gulp_default 68 | 69 | // if the /node_modules directory already exists somewhere other than at the base of where your build.gradle is 70 | node { 71 | nodeModulesDir = file("WebContent") 72 | } 73 | 74 | // if the /node_modules directory already exists somewhere other than at the base of where your build.gradle is 75 | // plugin looks for gulp in the node_modules directory 76 | gulp { 77 | workDir = file("WebContent") 78 | } 79 | ``` 80 | 81 | 82 | ## Extended Usage 83 | 84 | If you need to supply gulp with options, you have to create Gulp-tasks: 85 | 86 | ```gradle 87 | task gulpBuildWithOpts(type: GulpTask) { 88 | args = ["build", "arg1", "arg2"] 89 | } 90 | ``` 91 | 92 | 93 | ## NPM helpers 94 | 95 | The plugin also provides two fixed helper tasks to run once per project, which 96 | however require npm (https://npmjs.org/) to be installed: 97 | 98 | - `installGulp`: This installs gulp to the project folder, using `npm install gulp` 99 | - `npmInstall`: This just runs `npm install` (possibly useful for scripting) 100 | 101 | Since gulp will only be installed in your project folder, it will not 102 | interact with the rest of your system, and can easily be removed later by 103 | deleting the node_modules folders. 104 | 105 | So as an example, you can make sure a local version of gulp exists using this: 106 | 107 | ```gradle 108 | // makes sure on each build that gulp is installed 109 | gulp_build.dependsOn 'installGulp' 110 | 111 | // processes your package.json before running gulp build 112 | gulp_build.dependsOn 'npmInstall' 113 | 114 | // runs "gulp build" as part of your gradle build 115 | build.dependsOn gulp_build 116 | ``` 117 | 118 | 119 | ## Configuring the Plugin 120 | 121 | You can configure the plugin using the `gulp` extension block, like this: 122 | 123 | ```gradle 124 | gulp { 125 | // Set the directory where gulpfile.js should be found 126 | workDir = file("${project.projectDir}") 127 | 128 | // Whether colors should output on the terminal 129 | colors = true 130 | 131 | // Whether output from Gulp should be buffered - useful when running tasks in parallel 132 | bufferOutput = false 133 | } 134 | ``` 135 | -------------------------------------------------------------------------------- /docs/installing.md: -------------------------------------------------------------------------------- 1 | # Installing 2 | 3 | Installing the node-related plugins can be done in multiple ways. The easiest is to use the `plugins`-closure 4 | in your `build.gradle` file: 5 | 6 | ```gradle 7 | plugins { 8 | id "com.moowork.node" version "1.3.1" 9 | } 10 | ``` 11 | 12 | If you want to install all of the node-plugins (which is pretty uncommon), then write this: 13 | 14 | ```gradle 15 | plugins { 16 | id "com.moowork.node" version "1.3.1" 17 | id "com.moowork.grunt" version "1.3.1" 18 | id "com.moowork.gulp" version "1.3.1" 19 | } 20 | ``` 21 | 22 | You can also install the plugins by using the traditional Gradle way: 23 | 24 | ```gradle 25 | buildscript { 26 | repositories { 27 | maven { 28 | url "https://plugins.gradle.org/m2/" 29 | } 30 | } 31 | 32 | dependencies { 33 | classpath "com.moowork.gradle:gradle-node-plugin:1.3.1" 34 | } 35 | } 36 | 37 | apply plugin: 'com.moowork.node' 38 | ``` 39 | 40 | 41 | ## Installing snapshots 42 | 43 | If you want to install snapshot versions of this plugin, you can add the [OJO repository](http://oss.jfrog.org) 44 | to your build: 45 | 46 | ```gradle 47 | buildscript { 48 | repositories { 49 | maven { 50 | url "http://oss.jfrog.org" 51 | } 52 | } 53 | 54 | dependencies { 55 | classpath "com.moowork.gradle:gradle-node-plugin:1.4.0-SNAPSHOT" 56 | } 57 | } 58 | 59 | apply plugin: 'com.moowork.node' 60 | ``` 61 | -------------------------------------------------------------------------------- /docs/node.md: -------------------------------------------------------------------------------- 1 | # Node Plugin 2 | 3 | This plugin enables you to run any [NodeJS](https://nodejs.org) script as part of your build. It does 4 | not depend on NodeJS (or NPM) being installed on your system. The plugin will 5 | download and manage NodeJS distributions, unpack them into your local `.gradle` 6 | directory and use them from there. It can also install [NPM](https://www.npmjs.com/) 7 | packages from NPM or [Yarn](https://yarnpkg.com/). To start using the plugin, add this into your `build.gradle` 8 | file (see [Installing](installing.md) for details): 9 | 10 | 11 | ```gradle 12 | plugins { 13 | id "com.moowork.node" version "1.3.1" 14 | } 15 | ``` 16 | 17 | ## Running a NodeJS Script 18 | 19 | To use this plugin you have to define some tasks in your `build.gradle` file. If you have a NodeJS 20 | script in `src/scripts/my.js`, then you can execute this by defining the following Gradle task: 21 | 22 | ```gradle 23 | task myScript(type: NodeTask) { 24 | script = file('src/scripts/my.js') 25 | } 26 | ``` 27 | 28 | You can also add arguments, like this: 29 | 30 | ```gradle 31 | task myScript(type: NodeTask) { 32 | script = file('src/scripts/my.js') 33 | args = ['arg1', 'arg2'] 34 | } 35 | ``` 36 | 37 | You can add node options like this: 38 | 39 | ```gradle 40 | task myScript(type: NodeTask) { 41 | script = file('src/scripts/my.js') 42 | options = ['--node-option', '--another-node-option'] 43 | } 44 | ``` 45 | 46 | `NodeTask` is a wrapper around the core `Exec` task. You can set the `ignoreExitValue` property on it: 47 | 48 | ```gradle 49 | task myScript(type: NodeTask) { 50 | script = file('src/scripts/my.js') 51 | ignoreExitValue = true 52 | } 53 | ```` 54 | 55 | You can also customize all other values on the `ExecSpec` by passing a closure to `execOverrides`. It's 56 | executed last, possibly overriding already set parameters. 57 | 58 | ```gradle 59 | task myScript(type: NodeTask) { 60 | script = file('src/scripts/my.js') 61 | execOverrides { 62 | it.ignoreExitValue = true 63 | it.workingDir = 'somewhere' 64 | it.standardOutput = new FileOutputStream('logs/my.log') 65 | } 66 | } 67 | ``` 68 | 69 | When executing this for the first time, it will run a nodeSetup task that downloads NodeJS 70 | (for your platform) and NPM (Node Package Manager) if on windows (other platforms include 71 | it into the distribution). 72 | 73 | 74 | ## Executing `npm` Tasks 75 | 76 | When adding the node plugin, you will have a `npmInstall` task already added. This task will 77 | execute `npm install` and installs all dependencies in `package.json`. It will only run when changes 78 | are made to `package.json` or `node_modules`. Execute it like this: 79 | 80 | ```bash 81 | $ gradle npmInstall 82 | ``` 83 | 84 | All npm command can also be invoked using underscore notation based on a gradle rule: 85 | 86 | ```bash 87 | $ gradle npm_install 88 | $ gradle npm_update 89 | $ gradle npm_list 90 | $ gradle npm_cache_clean 91 | ... 92 | ``` 93 | 94 | These however are not shown when running gradle tasks, as they generated dynamically. However they can 95 | be used for dependency declarations, such as: 96 | 97 | ```gradle 98 | npm_install.dependsOn(npm_cache_clean) 99 | ``` 100 | 101 | More arguments can be passed via the `build.gradle` file: 102 | 103 | ```gradle 104 | npm_update { 105 | args = ['--production', '--loglevel', 'warn'] 106 | } 107 | ``` 108 | 109 | If you want to extend the tasks more or create custom variants, you can extend the class `NpmTask`: 110 | 111 | ```gradle 112 | task installExpress(type: NpmTask) { 113 | // install the express package only 114 | args = ['install', 'express', '--save-dev'] 115 | } 116 | ``` 117 | 118 | 119 | ## Executing Yarn Tasks 120 | 121 | When adding the node plugin, you will have a yarn task already added. This task will 122 | execute `yarn` and installs all dependencies in `package.json`. It will only run when changes 123 | are made to `package.json`, `yarn.lock`, or `node_modules`. Execute it like this: 124 | 125 | ```bash 126 | $ gradle yarn 127 | ``` 128 | 129 | All yarn command can also be invoked using underscore notation based on a gradle rule: 130 | 131 | ```bash 132 | $ gradle yarn_install 133 | $ gradle yarn_upgrade 134 | $ gradle yarn_ls 135 | $ gradle yarn_cache_clean 136 | ... 137 | ``` 138 | 139 | These however are not shown when running gradle tasks, as they generated dynamically. However they can be 140 | used for dependency declarations, such as: 141 | 142 | ```gradle 143 | yarn_install.dependsOn(yarn_cache_clean) 144 | ``` 145 | 146 | More arguments can be passed via the `build.gradle` file: 147 | 148 | ```gradle 149 | yarn_cache_clean { 150 | args = ['--no-emoji', '--json'] 151 | } 152 | ``` 153 | 154 | If you want to extend the tasks more or create custom variants, you can extend the class `YarnTask`: 155 | 156 | ```gradle 157 | task addExpress(type: YarnTask) { 158 | // add the express package only 159 | args = ['add', 'express', '--dev'] 160 | } 161 | ``` 162 | 163 | 164 | ## Configuring the Plugin 165 | 166 | You can configure the plugin using the "node" extension block, like this: 167 | 168 | ```gradle 169 | node { 170 | // Version of node to use. 171 | version = '0.11.10' 172 | 173 | // Version of npm to use. 174 | npmVersion = '2.1.5' 175 | 176 | // Version of Yarn to use. 177 | yarnVersion = '0.16.1' 178 | 179 | // Base URL for fetching node distributions (change if you have a mirror). 180 | distBaseUrl = 'https://nodejs.org/dist' 181 | 182 | // If true, it will download node using above parameters. 183 | // If false, it will try to use globally installed node. 184 | download = true 185 | 186 | // Set the work directory for unpacking node 187 | workDir = file("${project.buildDir}/nodejs") 188 | 189 | // Set the work directory for NPM 190 | npmWorkDir = file("${project.buildDir}/npm") 191 | 192 | // Set the work directory for Yarn 193 | yarnWorkDir = file("${project.buildDir}/yarn") 194 | 195 | // Set the work directory where node_modules should be located 196 | nodeModulesDir = file("${project.projectDir}") 197 | } 198 | ``` 199 | 200 | **Note** that `download` flag is default to `false`. This will change in future versions. 201 | 202 | 203 | ## Using a Custom (project-local) Version of `npm` 204 | 205 | If `npmVersion` is specified, the plugin installs that version of `npm` into `npmWorkDir` 206 | by the `npmSetup` task and use it. 207 | 208 | If `npmVersion` is not specified and a locally-installed `npm` exists, the plugin will 209 | use it. 210 | 211 | Otherwise, the plugin will use the `npm` bundled with the version of node installation. 212 | 213 | 214 | ## Using a Custom (project-local) Version of `yarn` 215 | 216 | The plugin never uses a locally-installed `yarn` because it may be deleted during 217 | `yarn` execution. 218 | Instead, it installs `yarn` into `yarnWorkDir` (`.gradle/yarn/` by default) by 219 | the `yarnSetup` task and use it. 220 | 221 | If you would like the plugin to install use a custom version of yarn, you can set 222 | `yarnVersion` in the `node` extension block. 223 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | Here's a set of examples using the node plugin: 4 | 5 | * [Simple Node](simple-node) 6 | * [Simple Yarn](simple-yarn) 7 | * [Simple Grunt](simple-grunt) 8 | * [Simple Gulp](simple-gulp) 9 | 10 | To execute all examples: 11 | 12 | ```bash 13 | gradle build 14 | ``` 15 | -------------------------------------------------------------------------------- /examples/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | maven { 4 | url "https://plugins.gradle.org/m2/" 5 | } 6 | } 7 | 8 | dependencies { 9 | classpath "com.moowork.gradle:gradle-node-plugin:1.2.0-SNAPSHOT" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':simple-node' 2 | include ':simple-yarn' 3 | include ':simple-grunt' 4 | include ':simple-gulp' 5 | includeBuild '..' 6 | -------------------------------------------------------------------------------- /examples/simple-grunt/Gruntfile.js: -------------------------------------------------------------------------------- 1 | var grunt = require('grunt'); 2 | 3 | grunt.registerTask('default', 'default task description', function () { 4 | console.log('hello world'); 5 | }); 6 | -------------------------------------------------------------------------------- /examples/simple-grunt/README.md: -------------------------------------------------------------------------------- 1 | # Simple Grunt Example 2 | 3 | This example shows how to use Grunt. To execute, just run the following command: 4 | 5 | ```bash 6 | gradle build 7 | ``` 8 | -------------------------------------------------------------------------------- /examples/simple-grunt/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.moowork.grunt' 2 | 3 | node { 4 | download = true 5 | } 6 | 7 | task helloWorld( type: GruntTask, dependsOn: installGrunt ) {} 8 | 9 | task build( dependsOn: 'helloWorld' ) {} 10 | -------------------------------------------------------------------------------- /examples/simple-grunt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/simple-gulp/Gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | 3 | gulp.task('default', async function () { 4 | console.log('hello world'); 5 | }); 6 | -------------------------------------------------------------------------------- /examples/simple-gulp/README.md: -------------------------------------------------------------------------------- 1 | # Simple Gulp Example 2 | 3 | This example shows how to use Gulp. To execute, just run the following command: 4 | 5 | ```bash 6 | gradle build 7 | ``` 8 | -------------------------------------------------------------------------------- /examples/simple-gulp/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.moowork.gulp' 2 | 3 | node { 4 | download = true 5 | } 6 | 7 | task helloWorld( type: GulpTask, dependsOn: installGulp ) {} 8 | 9 | task build( dependsOn: 'helloWorld' ) {} 10 | -------------------------------------------------------------------------------- /examples/simple-gulp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/simple-node/README.md: -------------------------------------------------------------------------------- 1 | # Simple Node Example 2 | 3 | This example is just installing some packages using NPM and running a simple node program. To execute, just 4 | run the following command: 5 | 6 | ```bash 7 | gradle build 8 | ``` 9 | -------------------------------------------------------------------------------- /examples/simple-node/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.moowork.node' 2 | 3 | node { 4 | download = true 5 | } 6 | 7 | task helloWorld( type: NodeTask, dependsOn: 'npmInstall' ) { 8 | script = file( 'src/node' ) 9 | } 10 | 11 | task build( dependsOn: 'helloWorld' ) {} 12 | -------------------------------------------------------------------------------- /examples/simple-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "figlet": "^1.2.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/simple-node/src/node/index.js: -------------------------------------------------------------------------------- 1 | const figlet = require('figlet'); 2 | 3 | var out = figlet.textSync('Hello World!', { 4 | font: 'Standard' 5 | }); 6 | 7 | console.log(out); 8 | -------------------------------------------------------------------------------- /examples/simple-yarn/README.md: -------------------------------------------------------------------------------- 1 | # Simple Yarn Example 2 | 3 | This example is just installing some packages using YARN and running a simple node program. To execute, just 4 | run the following command: 5 | 6 | ```bash 7 | gradle build 8 | ``` 9 | -------------------------------------------------------------------------------- /examples/simple-yarn/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.moowork.node' 2 | 3 | node { 4 | download = true 5 | } 6 | 7 | task helloWorld( type: NodeTask, dependsOn: 'yarn' ) { 8 | script = file( 'src/node' ) 9 | } 10 | 11 | task build( dependsOn: 'helloWorld' ) {} 12 | -------------------------------------------------------------------------------- /examples/simple-yarn/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "figlet": "^1.2.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/simple-yarn/src/node/index.js: -------------------------------------------------------------------------------- 1 | const figlet = require('figlet'); 2 | 3 | var out = figlet.textSync('Hello World!', { 4 | font: 'Standard' 5 | }); 6 | 7 | console.log(out); 8 | -------------------------------------------------------------------------------- /examples/simple-yarn/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | figlet@^1.2.0: 6 | version "1.2.0" 7 | resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.2.0.tgz#6c46537378fab649146b5a6143dda019b430b410" 8 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Current version 3 | # 4 | version = 1.4.0-SNAPSHOT 5 | -------------------------------------------------------------------------------- /gradle/additional-artifacts.gradle: -------------------------------------------------------------------------------- 1 | task sourcesJar( type: Jar ) { 2 | classifier 'sources' 3 | from sourceSets.main.allSource 4 | } 5 | 6 | task groovydocJar( type: Jar, dependsOn: groovydoc ) { 7 | classifier 'groovydoc' 8 | from groovydoc.destinationDir 9 | } 10 | 11 | task javadocJar( type: Jar, dependsOn: javadoc ) { 12 | classifier 'javadoc' 13 | from javadoc.destinationDir 14 | } 15 | 16 | artifacts { 17 | archives sourcesJar 18 | archives groovydocJar 19 | archives javadocJar 20 | } 21 | -------------------------------------------------------------------------------- /gradle/buildscript.gradle: -------------------------------------------------------------------------------- 1 | repositories { 2 | jcenter() 3 | maven { 4 | url "https://plugins.gradle.org/m2/" 5 | } 6 | } 7 | 8 | dependencies { 9 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' 10 | classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:4.4.0' 11 | } 12 | -------------------------------------------------------------------------------- /gradle/coverage.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'jacoco' 2 | 3 | jacocoTestReport { 4 | reports { 5 | xml.enabled = true 6 | html.enabled = true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /gradle/publishing.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'maven' 2 | apply plugin: 'maven-publish' 3 | apply plugin: 'com.jfrog.artifactory' 4 | apply plugin: 'com.jfrog.bintray' 5 | 6 | ext { 7 | bintrayUser = resolveProperty( 'BINTRAY_USER', 'bintrayUser' ) 8 | bintrayKey = resolveProperty( 'BINTRAY_KEY', 'bintrayKey' ) 9 | } 10 | 11 | publishing { 12 | publications { 13 | mavenJava( MavenPublication ) { 14 | from components.java 15 | artifact sourcesJar 16 | artifact groovydocJar 17 | artifact javadocJar 18 | } 19 | } 20 | } 21 | 22 | artifactoryPublish { 23 | onlyIf { isMaster } 24 | mustRunAfter 'build' 25 | publications 'mavenJava' 26 | } 27 | 28 | artifactory { 29 | contextUrl = 'https://oss.jfrog.org' 30 | 31 | publish { 32 | repository { 33 | repoKey = 'oss-snapshot-local' 34 | username = bintrayUser 35 | password = bintrayKey 36 | maven = true 37 | } 38 | } 39 | } 40 | 41 | bintray { 42 | user = bintrayUser 43 | key = bintrayKey 44 | publish = true 45 | publications = ['mavenJava'] 46 | 47 | pkg { 48 | name = "${project.name}" 49 | repo = 'maven' 50 | userOrg = 'srs' 51 | licenses = ['Apache-2.0'] 52 | vcsUrl = 'https://github.com/srs/gradle-node-plugin.git' 53 | websiteUrl = "https://github.com/srs/gradle-node-plugin" 54 | issueTrackerUrl = "https://github.com/srs/gradle-node-plugin/issues" 55 | githubRepo = "srs/gradle-node-plugin" 56 | labels = ['java', 'gradle', 'node', 'yarn', 'npm', 'grunt', 'gulp'] 57 | desc = "Gradle plugin for executing node scripts." 58 | 59 | version { 60 | vcsTag = "v${project.version}" 61 | attributes = ['gradle-plugin': ["com.moowork.node:${project.group}:${project.name}", 62 | "com.moowork.grunt:${project.group}:${project.name}", 63 | "com.moowork.gulp:${project.group}:${project.name}"]] 64 | } 65 | } 66 | } 67 | 68 | String resolveProperty( String envVarKey, String projectPropKey ) 69 | { 70 | String propValue = System.getenv()[envVarKey] 71 | 72 | if ( propValue ) 73 | { 74 | return propValue 75 | } 76 | 77 | hasProperty( projectPropKey ) ? getProperty( projectPropKey ) : null 78 | } 79 | -------------------------------------------------------------------------------- /gradle/travis-ci.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | isMaster = System.env.'TRAVIS_BRANCH' == "master" 3 | isCI = System.env.'CI' 4 | } 5 | 6 | task ci { 7 | dependsOn = ['clean', 'build', 'integTest'] 8 | description = 'Continuous integration tasks' 9 | group = 'Build' 10 | } 11 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/srs/gradle-node-plugin/3052d9bd2db5c8fbe4bc3fd0306fcb8a805c0577/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Mar 17 00:46:23 CET 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS='"-Xmx64m"' 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'gradle-node-plugin' 2 | 3 | -------------------------------------------------------------------------------- /src/integTest/groovy/com/moowork/gradle/AbstractIntegTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle 2 | 3 | import org.apache.commons.io.FileUtils 4 | import org.gradle.testkit.runner.BuildResult 5 | import org.gradle.testkit.runner.BuildTask 6 | import org.gradle.testkit.runner.GradleRunner 7 | import org.junit.Rule 8 | import org.junit.rules.TemporaryFolder 9 | import spock.lang.Specification 10 | 11 | abstract class AbstractIntegTest 12 | extends Specification 13 | { 14 | @Rule 15 | final TemporaryFolder temporaryFolder = new TemporaryFolder(); 16 | 17 | def File projectDir; 18 | 19 | def File buildFile; 20 | 21 | def setup() 22 | { 23 | this.projectDir = this.temporaryFolder.root; 24 | this.buildFile = createFile( 'build.gradle' ) 25 | } 26 | 27 | protected final GradleRunner newRunner( final String... args ) 28 | { 29 | return GradleRunner.create(). 30 | withProjectDir( this.projectDir ). 31 | withArguments( args ). 32 | withPluginClasspath(); 33 | } 34 | 35 | protected final BuildResult build( final String... args ) 36 | { 37 | return newRunner( args ).build(); 38 | } 39 | 40 | protected final BuildResult buildAndFail( final String... args ) 41 | { 42 | return newRunner( args ).buildAndFail(); 43 | } 44 | 45 | protected final BuildTask buildTask( final String task ) 46 | { 47 | return build( task ).task( ':' + task ); 48 | } 49 | 50 | protected final File createFile( final String name ) 51 | { 52 | return new File( this.temporaryFolder.getRoot(), name ); 53 | } 54 | 55 | protected final void writeFile( final String name, final String text ) 56 | { 57 | File file = createFile( name ) 58 | file.parentFile.mkdirs() 59 | file << text 60 | } 61 | 62 | protected final void writePackageJson( final String text ) 63 | { 64 | writeFile( 'package.json', text ) 65 | } 66 | 67 | protected final void writeEmptyPackageLockJson() 68 | { 69 | writeEmptyPackageLockJson( 'package-lock.json' ) 70 | } 71 | 72 | protected final void writeEmptyPackageLockJson( final String name ) 73 | { 74 | writeFile( name, """ { 75 | "name": "example", 76 | "lockfileVersion": 1 77 | } 78 | """ ) 79 | } 80 | 81 | protected final void writeEmptyPackageJson() 82 | { 83 | writePackageJson( """ { 84 | "name": "example", 85 | "dependencies": {} 86 | } 87 | """ ) 88 | 89 | writeEmptyPackageLockJson() 90 | } 91 | 92 | protected final void writeBuild( final String text ) 93 | { 94 | this.buildFile << text 95 | } 96 | 97 | protected final File directory( String path, File baseDir = getProjectDir() ) 98 | { 99 | return new File( baseDir, path ).with { 100 | mkdirs() 101 | it 102 | } 103 | } 104 | 105 | protected final File file( String path, File baseDir = getProjectDir() ) 106 | { 107 | def splitted = path.split( '/' ) 108 | def directory = splitted.size() > 1 ? directory( splitted[0..-2].join( '/' ), baseDir ) : baseDir 109 | def file = new File( directory, splitted[-1] ) 110 | file.createNewFile() 111 | return file 112 | } 113 | 114 | protected void copyResources( String srcDir, String destination ) 115 | { 116 | ClassLoader classLoader = getClass().getClassLoader(); 117 | URL resource = classLoader.getResource( srcDir ); 118 | if ( resource == null ) 119 | { 120 | throw new RuntimeException( "Could not find classpath resource: $srcDir" ) 121 | } 122 | 123 | File destinationFile = file( destination ) 124 | File resourceFile = new File( resource.toURI() ) 125 | if ( resourceFile.file ) 126 | { 127 | FileUtils.copyFile( resourceFile, destinationFile ) 128 | } 129 | else 130 | { 131 | FileUtils.copyDirectory( resourceFile, destinationFile ) 132 | } 133 | } 134 | 135 | protected final boolean fileExists( String path ) 136 | { 137 | return new File( this.projectDir, path ).exists() 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/integTest/groovy/com/moowork/gradle/grunt/GruntInstall_integTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.grunt 2 | 3 | import com.moowork.gradle.AbstractIntegTest 4 | import org.gradle.testkit.runner.TaskOutcome 5 | 6 | class GruntInstall_integTest 7 | extends AbstractIntegTest 8 | { 9 | def 'grunt install'() 10 | { 11 | given: 12 | writeBuild( ''' 13 | plugins { 14 | id 'com.moowork.grunt' 15 | } 16 | 17 | node { 18 | download = true 19 | } 20 | ''' ) 21 | 22 | when: 23 | def result = buildTask( 'installGrunt' ) 24 | 25 | then: 26 | result.outcome == TaskOutcome.SUCCESS 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/integTest/groovy/com/moowork/gradle/grunt/GruntRule_integTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.grunt 2 | 3 | import com.moowork.gradle.AbstractIntegTest 4 | import org.gradle.testkit.runner.TaskOutcome 5 | 6 | class GruntRule_integTest 7 | extends AbstractIntegTest 8 | { 9 | def 'execute simple task'() 10 | { 11 | given: 12 | writeBuild( ''' 13 | plugins { 14 | id 'com.moowork.grunt' 15 | } 16 | 17 | node { 18 | download = true 19 | } 20 | ''' ) 21 | writeFile( 'Gruntfile.js', ''' 22 | module.exports = function(grunt) { 23 | grunt.registerTask('simple', []); 24 | } 25 | ''' ) 26 | 27 | when: 28 | def result = build( 'installGrunt', 'grunt_simple' ) 29 | 30 | then: 31 | result.task( ':installGrunt' ).outcome == TaskOutcome.SUCCESS 32 | result.task( ':grunt_simple' ).outcome == TaskOutcome.SUCCESS 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/integTest/groovy/com/moowork/gradle/grunt/Grunt_integTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.grunt 2 | 3 | import com.moowork.gradle.AbstractIntegTest 4 | import org.gradle.testkit.runner.TaskOutcome 5 | 6 | class Grunt_integTest 7 | extends AbstractIntegTest 8 | { 9 | def 'execute simple task'() 10 | { 11 | given: 12 | writeBuild( ''' 13 | plugins { 14 | id 'com.moowork.grunt' 15 | } 16 | 17 | node { 18 | download = true 19 | } 20 | 21 | grunt { 22 | colors = false 23 | bufferOutput = true 24 | } 25 | 26 | task simple(type: GruntTask) { 27 | args = ['simple'] 28 | } 29 | ''' ) 30 | writeFile( 'Gruntfile.js', ''' 31 | module.exports = function(grunt) { 32 | grunt.registerTask('simple', []); 33 | } 34 | ''' ) 35 | 36 | when: 37 | def result = build( 'installGrunt', 'simple' ) 38 | 39 | then: 40 | result.task( ':installGrunt' ).outcome == TaskOutcome.SUCCESS 41 | result.task( ':simple' ).outcome == TaskOutcome.SUCCESS 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/integTest/groovy/com/moowork/gradle/gulp/GulpInstall_integTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.gulp 2 | 3 | import com.moowork.gradle.AbstractIntegTest 4 | import org.gradle.testkit.runner.TaskOutcome 5 | 6 | class GulpInstall_integTest 7 | extends AbstractIntegTest 8 | { 9 | def 'gulp install'() 10 | { 11 | given: 12 | writeBuild( ''' 13 | plugins { 14 | id 'com.moowork.gulp' 15 | } 16 | 17 | node { 18 | download = true 19 | } 20 | ''' ) 21 | 22 | when: 23 | def result = buildTask( 'installGulp' ) 24 | 25 | then: 26 | result.outcome == TaskOutcome.SUCCESS 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/integTest/groovy/com/moowork/gradle/gulp/GulpRule_integTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.gulp 2 | 3 | import com.moowork.gradle.AbstractIntegTest 4 | import org.gradle.testkit.runner.TaskOutcome 5 | 6 | class GulpRule_integTest 7 | extends AbstractIntegTest 8 | { 9 | def 'execute simple task'() 10 | { 11 | given: 12 | writeBuild( ''' 13 | plugins { 14 | id 'com.moowork.gulp' 15 | } 16 | 17 | node { 18 | download = true 19 | } 20 | ''' ) 21 | writeFile( 'gulpfile.js', ''' 22 | var gulp = require('gulp'); 23 | 24 | gulp.task('simple', async function(){ 25 | }); 26 | ''' ) 27 | 28 | when: 29 | def result = build( 'installGulp', 'gulp_simple' ) 30 | 31 | then: 32 | result.task( ':installGulp' ).outcome == TaskOutcome.SUCCESS 33 | result.task( ':gulp_simple' ).outcome == TaskOutcome.SUCCESS 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/integTest/groovy/com/moowork/gradle/gulp/Gulp_integTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.gulp 2 | 3 | import com.moowork.gradle.AbstractIntegTest 4 | import org.gradle.testkit.runner.TaskOutcome 5 | 6 | class Gulp_integTest 7 | extends AbstractIntegTest 8 | { 9 | def 'execute simple task'() 10 | { 11 | given: 12 | writeBuild( ''' 13 | plugins { 14 | id 'com.moowork.gulp' 15 | } 16 | 17 | node { 18 | download = true 19 | } 20 | 21 | gulp { 22 | colors = false 23 | bufferOutput = true 24 | } 25 | 26 | task simple(type: GulpTask) { 27 | args = ['simple'] 28 | } 29 | ''' ) 30 | writeFile( 'gulpfile.js', ''' 31 | var gulp = require('gulp'); 32 | 33 | gulp.task('simple', async function(){ 34 | }); 35 | ''' ) 36 | 37 | when: 38 | def result = build( 'installGulp', 'simple' ) 39 | 40 | then: 41 | result.task( ':installGulp' ).outcome == TaskOutcome.SUCCESS 42 | result.task( ':simple' ).outcome == TaskOutcome.SUCCESS 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/integTest/groovy/com/moowork/gradle/node/npm/NpmInstall_integTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.npm 2 | 3 | import com.moowork.gradle.AbstractIntegTest 4 | import org.gradle.testkit.runner.TaskOutcome 5 | 6 | class NpmInstall_integTest 7 | extends AbstractIntegTest 8 | { 9 | def 'install packages with npm'() 10 | { 11 | given: 12 | writeBuild( ''' 13 | plugins { 14 | id 'com.moowork.node' 15 | } 16 | 17 | node { 18 | npmVersion = "6.1.0" 19 | download = true 20 | workDir = file('build/node') 21 | } 22 | ''' ) 23 | writeEmptyPackageJson() 24 | 25 | when: 26 | def result = buildTask( 'npmInstall' ) 27 | 28 | then: 29 | result.outcome == TaskOutcome.SUCCESS 30 | } 31 | 32 | def 'install packages with npm and postinstall task requiring npm and node'() 33 | { 34 | given: 35 | writeBuild( ''' 36 | plugins { 37 | id 'com.moowork.node' 38 | } 39 | node { 40 | npmVersion = "6.1.0" 41 | download = true 42 | workDir = file('build/node') 43 | } 44 | ''' ) 45 | writePackageJson( """ { 46 | "name": "example", 47 | "dependencies": {}, 48 | "versionOutput" : "node --version", 49 | "postinstall" : "npm run versionOutput" 50 | } 51 | """ ) 52 | writeEmptyPackageLockJson() 53 | 54 | when: 55 | def result = buildTask( 'npmInstall' ) 56 | 57 | then: 58 | result.outcome == TaskOutcome.SUCCESS 59 | } 60 | 61 | def 'install packages with npm in different directory'() 62 | { 63 | given: 64 | writeBuild( ''' 65 | plugins { 66 | id 'com.moowork.node' 67 | } 68 | 69 | node { 70 | npmVersion = "6.1.0" 71 | download = true 72 | workDir = file('build/node') 73 | nodeModulesDir = file('subdirectory') 74 | } 75 | ''' ) 76 | writeFile( 'subdirectory/package.json', """{ 77 | "name": "example", 78 | "dependencies": { 79 | } 80 | }""" ) 81 | writeEmptyPackageLockJson( 'subdirectory/package-lock.json' ) 82 | 83 | when: 84 | def result = build( 'npmInstall' ) 85 | 86 | then: 87 | result.task( ':npmInstall' ).outcome == TaskOutcome.SUCCESS 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/integTest/groovy/com/moowork/gradle/node/npm/NpmRule_integTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.npm 2 | 3 | import com.moowork.gradle.AbstractIntegTest 4 | import org.gradle.testkit.runner.TaskOutcome 5 | 6 | class NpmRule_integTest 7 | extends AbstractIntegTest 8 | { 9 | def 'execute npm_install rule'() 10 | { 11 | given: 12 | writeBuild( ''' 13 | plugins { 14 | id 'com.moowork.node' 15 | } 16 | 17 | node { 18 | npmVersion = "6.1.0" 19 | download = true 20 | workDir = file('build/node') 21 | } 22 | ''' ) 23 | writeEmptyPackageJson() 24 | 25 | when: 26 | def result = buildTask( 'npm_install' ) 27 | 28 | then: 29 | result.outcome == TaskOutcome.SUCCESS 30 | } 31 | 32 | def 'Use downloaded npm version'() 33 | { 34 | given: 35 | writeBuild( ''' 36 | plugins { 37 | id 'com.moowork.node' 38 | } 39 | 40 | node { 41 | npmVersion = "6.1.0" 42 | download = true 43 | } 44 | ''' ) 45 | writeEmptyPackageJson() 46 | 47 | when: 48 | def result = build( 'npm_run_--version' ) 49 | 50 | then: 51 | result.output =~ /\n6\.1\.0\n/ 52 | result.task( ':npm_run_--version' ).outcome == TaskOutcome.SUCCESS 53 | } 54 | 55 | def 'Use local npm installation'() 56 | { 57 | given: 58 | writeBuild( ''' 59 | plugins { 60 | id 'com.moowork.node' 61 | } 62 | node { 63 | npmVersion = "6.1.0" 64 | download = true 65 | } 66 | ''' ) 67 | writeEmptyPackageJson() 68 | 69 | when: 70 | build( 'npm_install_npm@4.0.2' ) 71 | def result = build( 'npm_run_--version' ) 72 | 73 | then: 74 | result.output =~ /\n4\.0\.2\n/ 75 | result.task( ':npm_run_--version' ).outcome == TaskOutcome.SUCCESS 76 | } 77 | 78 | def 'can execute an npm module using npm_run_'() 79 | { 80 | given: 81 | writeBuild( ''' 82 | plugins { 83 | id 'com.moowork.node' 84 | } 85 | 86 | node { 87 | npmVersion = "6.1.0" 88 | download = true 89 | } 90 | ''' ) 91 | 92 | copyResources( 'fixtures/npm-missing/package.json', 'package.json' ) 93 | writeEmptyPackageLockJson() 94 | 95 | when: 96 | def result = buildTask( 'npm_run_echoTest' ) 97 | 98 | then: 99 | result.outcome == TaskOutcome.SUCCESS 100 | fileExists( 'test.txt' ) 101 | } 102 | 103 | def 'succeeds to run npm module using npm_run_ when shrinkwrap contains local npm'() 104 | { 105 | given: 106 | writeBuild( ''' 107 | plugins { 108 | id 'com.moowork.node' 109 | } 110 | 111 | node { 112 | npmVersion = "6.1.0" 113 | download = true 114 | } 115 | ''' ) 116 | 117 | copyResources( 'fixtures/npm-present/package.json', 'package.json' ) 118 | copyResources( 'fixtures/npm-present/npm-shrinkwrap.json', 'npm-shrinkwrap.json' ) 119 | writeEmptyPackageLockJson() 120 | 121 | when: 122 | def result = buildTask( 'npm_run_parent' ) 123 | 124 | then: 125 | result.outcome == TaskOutcome.SUCCESS 126 | fileExists( 'child1.txt' ) 127 | fileExists( 'child2.txt' ) 128 | fileExists( 'parent1.txt' ) 129 | fileExists( 'parent2.txt' ) 130 | } 131 | 132 | def 'can execute subtasks using npm'() 133 | { 134 | given: 135 | writeBuild( ''' 136 | plugins { 137 | id 'com.moowork.node' 138 | } 139 | node { 140 | download = true 141 | } 142 | ''' ) 143 | writePackageJson( """ { 144 | "name": "example", 145 | "dependencies": {}, 146 | "scripts": { 147 | "parent" : "echo 'parent1' > parent1.txt && npm run child1 && npm run child2 && echo 'parent2' > parent2.txt", 148 | "child1": "echo 'child1' > child1.txt", 149 | "child2": "echo 'child2' > child2.txt" 150 | } 151 | } 152 | """ ) 153 | writeEmptyPackageLockJson() 154 | 155 | when: 156 | def result = buildTask( 'npm_run_parent' ) 157 | 158 | then: 159 | result.outcome == TaskOutcome.SUCCESS 160 | fileExists( 'parent1.txt' ) 161 | fileExists( 'child1.txt' ) 162 | fileExists( 'child2.txt' ) 163 | fileExists( 'parent2.txt' ) 164 | } 165 | 166 | def 'Custom workingDir'() 167 | { 168 | given: 169 | writeBuild( ''' 170 | plugins { 171 | id 'com.moowork.node' 172 | } 173 | node { 174 | npmVersion = "6.1.0" 175 | download = true 176 | nodeModulesDir = file("frontend") 177 | } 178 | ''' ) 179 | writeFile( 'frontend/package.json', """{ 180 | "name": "example", 181 | "dependencies": {}, 182 | "scripts": { 183 | "whatVersion": "npm run --version" 184 | } 185 | }""" ) 186 | writeEmptyPackageLockJson( 'frontend/package-lock.json' ) 187 | 188 | when: 189 | def result = build( 'npm_run_whatVersion' ) 190 | 191 | then: 192 | result.output =~ /\n6\.1\.0\n/ 193 | result.task( ':npm_run_whatVersion' ).outcome == TaskOutcome.SUCCESS 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/integTest/groovy/com/moowork/gradle/node/task/Node_integTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.task 2 | 3 | import com.moowork.gradle.AbstractIntegTest 4 | import org.gradle.testkit.runner.TaskOutcome 5 | 6 | class Node_integTest 7 | extends AbstractIntegTest 8 | { 9 | def 'exec simple node program'() 10 | { 11 | given: 12 | writeBuild( ''' 13 | plugins { 14 | id 'com.moowork.node' 15 | } 16 | 17 | node { 18 | download = true 19 | workDir = file('build/node') 20 | } 21 | 22 | task simple(type: NodeTask) { 23 | script = file('simple.js') 24 | args = [] 25 | } 26 | ''' ) 27 | writeEmptyPackageJson() 28 | writeFile( 'simple.js', """ 29 | console.log("Hello World"); 30 | """ ) 31 | 32 | when: 33 | def result = buildTask( 'simple' ) 34 | 35 | then: 36 | result.outcome == TaskOutcome.SUCCESS 37 | } 38 | 39 | def 'check environment settings'() 40 | { 41 | given: 42 | writeBuild( ''' 43 | plugins { 44 | id 'com.moowork.node' 45 | } 46 | 47 | node { 48 | download = true 49 | workDir = file('build/node') 50 | } 51 | 52 | task simple(type: NodeTask) { 53 | script = file('simple.js') 54 | args = [] 55 | environment = ['MYENV': 'value'] 56 | } 57 | ''' ) 58 | writeEmptyPackageJson() 59 | writeFile( 'simple.js', """ 60 | if (process.env.MYENV == 'value') { 61 | console.log("Hello MYENV=" + process.env.MYENV); 62 | } else { 63 | throw "Environment MYENV should be visible"; 64 | } 65 | """ ) 66 | 67 | when: 68 | def result = buildTask( 'simple' ) 69 | 70 | then: 71 | result.outcome == TaskOutcome.SUCCESS 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/integTest/groovy/com/moowork/gradle/node/task/Setup_integTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.task 2 | 3 | import com.moowork.gradle.AbstractIntegTest 4 | import org.gradle.testkit.runner.TaskOutcome 5 | 6 | class Setup_integTest 7 | extends AbstractIntegTest 8 | { 9 | private final static OS_NAME = System.getProperty( 'os.name' ) 10 | 11 | def cleanup() 12 | { 13 | System.setProperty( 'os.name', OS_NAME ) 14 | } 15 | 16 | def 'setup node'() 17 | { 18 | given: 19 | writeBuild( ''' 20 | plugins { 21 | id 'com.moowork.node' 22 | } 23 | 24 | node { 25 | version = "0.10.33" 26 | download = false 27 | } 28 | ''' ) 29 | 30 | when: 31 | def result = buildTask( 'nodeSetup' ) 32 | 33 | then: 34 | result.outcome == TaskOutcome.SKIPPED 35 | } 36 | 37 | def 'setup node (download)'() 38 | { 39 | given: 40 | writeBuild( ''' 41 | plugins { 42 | id 'com.moowork.node' 43 | } 44 | 45 | node { 46 | version = "0.10.33" 47 | download = true 48 | } 49 | ''' ) 50 | 51 | when: 52 | def result = buildTask( 'nodeSetup' ) 53 | 54 | then: 55 | result.outcome == TaskOutcome.SUCCESS 56 | } 57 | 58 | def 'setup node (windows)'() 59 | { 60 | System.setProperty( 'os.name', 'Windows' ) 61 | 62 | given: 63 | writeBuild( ''' 64 | plugins { 65 | id 'com.moowork.node' 66 | } 67 | 68 | node { 69 | version = "0.10.33" 70 | download = false 71 | } 72 | ''' ) 73 | 74 | when: 75 | def result = buildTask( 'nodeSetup' ) 76 | 77 | then: 78 | result.outcome == TaskOutcome.SKIPPED 79 | } 80 | 81 | def 'setup node (windows download)'() 82 | { 83 | System.setProperty( 'os.name', 'Windows' ) 84 | 85 | given: 86 | writeBuild( ''' 87 | plugins { 88 | id 'com.moowork.node' 89 | } 90 | 91 | node { 92 | version = "4.5.0" 93 | download = true 94 | } 95 | ''' ) 96 | 97 | when: 98 | def result = buildTask( 'nodeSetup' ) 99 | 100 | then: 101 | result.outcome == TaskOutcome.SUCCESS 102 | } 103 | 104 | def 'setup node (windows download separate exe)'() 105 | { 106 | System.setProperty( 'os.name', 'Windows' ) 107 | 108 | given: 109 | writeBuild( ''' 110 | plugins { 111 | id 'com.moowork.node' 112 | } 113 | 114 | node { 115 | version = "0.10.33" 116 | download = true 117 | } 118 | ''' ) 119 | 120 | when: 121 | def result = buildTask( 'nodeSetup' ) 122 | 123 | then: 124 | result.outcome == TaskOutcome.SUCCESS 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/integTest/groovy/com/moowork/gradle/node/yarn/YarnInstall_integTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.yarn 2 | 3 | import com.moowork.gradle.AbstractIntegTest 4 | import org.gradle.testkit.runner.TaskOutcome 5 | 6 | class YarnInstall_integTest 7 | extends AbstractIntegTest 8 | { 9 | def 'install packages with yarn'() 10 | { 11 | given: 12 | writeBuild( ''' 13 | plugins { 14 | id 'com.moowork.node' 15 | } 16 | 17 | node { 18 | yarnVersion = "1.15.2" 19 | download = true 20 | workDir = file('build/node') 21 | yarnWorkDir = file('build/yarn') 22 | } 23 | ''' ) 24 | writeEmptyPackageJson() 25 | writeFile( "yarn.lock", "" ) 26 | 27 | when: 28 | def result = buildTask( 'yarn' ) 29 | 30 | then: 31 | result.outcome == TaskOutcome.SUCCESS 32 | } 33 | 34 | def 'install packages with yarn and and postinstall task requiring node and yarn'() 35 | { 36 | given: 37 | writeBuild( ''' 38 | plugins { 39 | id 'com.moowork.node' 40 | } 41 | node { 42 | yarnVersion = "1.15.2" 43 | download = true 44 | workDir = file('build/node') 45 | yarnWorkDir = file('build/yarn') 46 | } 47 | ''' ) 48 | writePackageJson(""" { 49 | "name": "example", 50 | "dependencies": {}, 51 | "versionOutput" : "node --version", 52 | "postinstall" : "yarn run versionOutput" 53 | } 54 | """) 55 | writeFile( "yarn.lock", "" ) 56 | 57 | when: 58 | def result = buildTask( 'yarn' ) 59 | 60 | then: 61 | result.outcome == TaskOutcome.SUCCESS 62 | } 63 | 64 | def 'install packages with yarn in different directory'() 65 | { 66 | given: 67 | writeBuild( ''' 68 | plugins { 69 | id 'com.moowork.node' 70 | } 71 | 72 | node { 73 | yarnVersion = "1.15.2" 74 | download = true 75 | workDir = file('build/node') 76 | yarnWorkDir = file('build/yarn') 77 | nodeModulesDir = file('subdirectory') 78 | } 79 | ''' ) 80 | writeFile( 'subdirectory/package.json', """{ 81 | "name": "example", 82 | "dependencies": { 83 | } 84 | }""" ) 85 | writeFile( "subdirectory/yarn.lock", "" ) 86 | 87 | when: 88 | def result = buildTask( 'yarn' ) 89 | 90 | then: 91 | result.outcome == TaskOutcome.SUCCESS 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/integTest/groovy/com/moowork/gradle/node/yarn/YarnRule_integTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.yarn 2 | 3 | import com.moowork.gradle.AbstractIntegTest 4 | import org.gradle.testkit.runner.TaskOutcome 5 | 6 | class YarnRule_integTest 7 | extends AbstractIntegTest 8 | { 9 | def 'execute yarn_install rule'() 10 | { 11 | given: 12 | writeBuild( ''' 13 | plugins { 14 | id 'com.moowork.node' 15 | } 16 | 17 | node { 18 | yarnVersion = "1.15.2" 19 | download = true 20 | workDir = file('build/node') 21 | yarnWorkDir = file('build/yarn') 22 | } 23 | ''' ) 24 | writeEmptyPackageJson() 25 | 26 | when: 27 | def result = buildTask( 'yarn_install' ) 28 | 29 | then: 30 | result.outcome == TaskOutcome.SUCCESS 31 | } 32 | 33 | def 'can execute an yarn module using yarn_run_'() 34 | { 35 | given: 36 | writeBuild( ''' 37 | plugins { 38 | id 'com.moowork.node' 39 | } 40 | 41 | node { 42 | yarnVersion = "1.15.2" 43 | download = true 44 | } 45 | ''' ) 46 | 47 | copyResources( 'fixtures/yarn/package.json', 'package.json' ) 48 | writeFile( "yarn.lock", "" ) 49 | 50 | when: 51 | def result = buildTask( 'yarn_run_parent' ) 52 | 53 | then: 54 | result.outcome == TaskOutcome.SUCCESS 55 | fileExists( 'child1.txt' ) 56 | fileExists( 'child2.txt' ) 57 | fileExists( 'parent1.txt' ) 58 | fileExists( 'parent2.txt' ) 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/integTest/resources/fixtures/npm-missing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "dependencies": { 4 | "less": "2.6.1" 5 | }, 6 | "scripts": { 7 | "echoTest": "echo \"test\" > test.txt" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/integTest/resources/fixtures/npm-present/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "devDependencies": { 4 | "npm": "3.8.3", 5 | "npm-run-all": "1.6.0" 6 | }, 7 | "dependencies": { 8 | "less": "2.6.1" 9 | }, 10 | "scripts": { 11 | "child1": "echo \"child1\" > child1.txt", 12 | "child2": "echo \"child2\" > child2.txt", 13 | "parent": "echo \"parent1\" > parent1.txt && npm-run-all child1 child2 && echo \"parent2\" > parent2.txt" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/integTest/resources/fixtures/yarn/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "devDependencies": { 4 | "yarn": "0.16.1", 5 | "concurrently": "3.1.0" 6 | }, 7 | "dependencies": { 8 | "less": "2.6.1" 9 | }, 10 | "scripts": { 11 | "child1": "echo \"child1\" > child1.txt", 12 | "child2": "echo \"child2\" > child2.txt", 13 | "parent": "echo \"parent1\" > parent1.txt && concurrently \"yarn run child1\" \"yarn run child2\" && echo \"parent2\" > parent2.txt" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/grunt/GruntExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.grunt 2 | 3 | import org.gradle.api.Project 4 | 5 | class GruntExtension 6 | { 7 | final static String NAME = 'grunt' 8 | 9 | File workDir 10 | 11 | Boolean colors = true 12 | 13 | Boolean bufferOutput = false 14 | 15 | String gruntFile = 'Gruntfile.js' 16 | 17 | GruntExtension( final Project project ) 18 | { 19 | this.workDir = project.projectDir 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/grunt/GruntInstallTask.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.grunt 2 | 3 | import com.moowork.gradle.node.npm.NpmTask 4 | 5 | class GruntInstallTask 6 | extends NpmTask 7 | { 8 | public GruntInstallTask() 9 | { 10 | super() 11 | 12 | this.group = 'Grunt' 13 | this.description = "Runs 'npm install grunt-cli grunt' to install grunt-cli" 14 | 15 | setArgs( ['install', 'grunt-cli', 'grunt'] ) 16 | 17 | this.project.afterEvaluate { 18 | setWorkingDir( this.project.node.nodeModulesDir ) 19 | getOutputs().dir( new File( this.project.node.nodeModulesDir, 'node_modules/grunt' ) ) 20 | getOutputs().dir( new File( this.project.node.nodeModulesDir, 'node_modules/grunt-cli' ) ) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/grunt/GruntPlugin.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.grunt 2 | 3 | import com.moowork.gradle.node.NodePlugin 4 | import org.gradle.api.Plugin 5 | import org.gradle.api.Project 6 | import org.gradle.api.Task 7 | 8 | class GruntPlugin 9 | implements Plugin 10 | { 11 | static final String GRUNT_INSTALL_NAME = "installGrunt"; 12 | 13 | @Override 14 | void apply( final Project project ) 15 | { 16 | project.plugins.apply( NodePlugin.class ) 17 | 18 | project.extensions.create( GruntExtension.NAME, GruntExtension, project ) 19 | 20 | project.extensions.extraProperties.set( 'GruntTask', GruntTask.class ) 21 | project.tasks.create( GRUNT_INSTALL_NAME, GruntInstallTask.class ) 22 | 23 | // note this rule also makes it possible to specify e.g. "dependsOn grunt_install" 24 | project.getTasks().addRule( 'Pattern: "grunt_": Executes a named grunt task.' ) { String taskName -> 25 | if ( taskName.startsWith( 'grunt_' ) ) 26 | { 27 | Task gruntTask = project.getTasks().create( taskName, GruntTask.class ) 28 | gruntTask.args = [( taskName - 'grunt_' )] 29 | return gruntTask 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/grunt/GruntTask.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.grunt 2 | 3 | import com.moowork.gradle.node.task.NodeTask 4 | import org.gradle.api.GradleException 5 | 6 | class GruntTask 7 | extends NodeTask 8 | { 9 | private final static String GRUNT_SCRIPT = 'node_modules/grunt-cli/bin/grunt' 10 | 11 | GruntTask() 12 | { 13 | this.group = 'Grunt' 14 | } 15 | 16 | @Override 17 | void exec() 18 | { 19 | def gruntFile = "${this.project.grunt.workDir}/${this.project.grunt.gruntFile}" 20 | def localGrunt = this.project.file( new File( (File) this.project.node.nodeModulesDir, GRUNT_SCRIPT ) ) 21 | if ( !localGrunt.isFile() ) 22 | { 23 | throw new GradleException( 24 | "Grunt-CLI not installed in node_modules, please first run 'gradle ${GruntPlugin.GRUNT_INSTALL_NAME}'" ) 25 | } 26 | 27 | // On multi project scenario, need to specify Gruntfile.js location 28 | addArgs( '--gruntfile', gruntFile ) 29 | 30 | // If colors are disabled, add --no-color to args 31 | if ( !this.project.grunt.colors ) 32 | { 33 | addArgs( '--no-color' ) 34 | } 35 | 36 | // If output should be buffered (useful when running in parallel) 37 | // set standardOutput of ExecRunner to a buffer 38 | ByteArrayOutputStream bufferedOutput 39 | if ( this.project.grunt.bufferOutput ) 40 | { 41 | bufferedOutput = new ByteArrayOutputStream() 42 | setExecOverrides( { 43 | it.standardOutput = bufferedOutput 44 | } ) 45 | } 46 | 47 | setWorkingDir( this.project.grunt.workDir ) 48 | setScript( localGrunt ) 49 | 50 | // If the exec fails, make sure to still print output 51 | try 52 | { 53 | super.exec() 54 | } 55 | finally 56 | { 57 | // If we were buffering output, print it 58 | if ( this.project.grunt.bufferOutput && ( bufferedOutput != null ) ) 59 | { 60 | println "Output from ${gruntFile}" 61 | println bufferedOutput.toString() 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/gulp/GulpExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.gulp 2 | 3 | import org.gradle.api.Project 4 | 5 | class GulpExtension 6 | { 7 | final static String NAME = 'gulp' 8 | 9 | File workDir 10 | 11 | Boolean colors = true 12 | 13 | Boolean bufferOutput = false 14 | 15 | GulpExtension( final Project project ) 16 | { 17 | this.workDir = project.projectDir 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/gulp/GulpInstallTask.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.gulp 2 | 3 | import com.moowork.gradle.node.npm.NpmTask 4 | 5 | class GulpInstallTask 6 | extends NpmTask 7 | { 8 | public GulpInstallTask() 9 | { 10 | super() 11 | 12 | this.group = 'Gulp' 13 | this.description = "Runs 'npm install gulp' to install gulp" 14 | 15 | setArgs( ['install', 'gulp'] ) 16 | 17 | this.project.afterEvaluate { 18 | setWorkingDir( this.project.node.nodeModulesDir ) 19 | getOutputs().dir( new File( this.project.node.nodeModulesDir, 'node_modules/gulp' ) ) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/gulp/GulpPlugin.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.gulp 2 | 3 | import com.moowork.gradle.node.NodePlugin 4 | import org.gradle.api.Plugin 5 | import org.gradle.api.Project 6 | import org.gradle.api.Task 7 | 8 | class GulpPlugin 9 | implements Plugin 10 | { 11 | static final String GULP_INSTALL_NAME = "installGulp"; 12 | 13 | @Override 14 | void apply( final Project project ) 15 | { 16 | project.plugins.apply( NodePlugin.class ) 17 | 18 | project.extensions.create( GulpExtension.NAME, GulpExtension, project ) 19 | 20 | project.extensions.extraProperties.set( 'GulpTask', GulpTask.class ) 21 | project.tasks.create( GULP_INSTALL_NAME, GulpInstallTask.class ) 22 | 23 | // note this rule also makes it possible to specify e.g. "dependsOn gulp_install" 24 | project.getTasks().addRule( 'Pattern: "gulp_": Executes a named gulp task.' ) { String taskName -> 25 | if ( taskName.startsWith( 'gulp_' ) ) 26 | { 27 | Task gulpTask = project.getTasks().create( taskName, GulpTask.class ) 28 | gulpTask.args = [( taskName - 'gulp_' )] 29 | return gulpTask 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/gulp/GulpTask.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.gulp 2 | 3 | import com.moowork.gradle.node.task.NodeTask 4 | import org.gradle.api.GradleException 5 | 6 | class GulpTask 7 | extends NodeTask 8 | { 9 | private final static String GULP_SCRIPT = 'node_modules/gulp/bin/gulp.js' 10 | 11 | GulpTask() 12 | { 13 | this.group = 'Gulp' 14 | } 15 | 16 | @Override 17 | void exec() 18 | { 19 | def localGulp = this.project.file( new File( (File) this.project.node.nodeModulesDir, GULP_SCRIPT ) ) 20 | if ( !localGulp.isFile() ) 21 | { 22 | throw new GradleException( "gulp not installed in node_modules, please first run 'gradle ${GulpPlugin.GULP_INSTALL_NAME}'" ) 23 | } 24 | 25 | // If colors are disabled, add --no-color to args 26 | if ( !this.project.gulp.colors ) 27 | { 28 | addArgs( '--no-color' ) 29 | } 30 | 31 | // If output should be buffered (useful when running in parallel) 32 | // set standardOutput of ExecRunner to a buffer 33 | ByteArrayOutputStream bufferedOutput 34 | if ( this.project.gulp.bufferOutput ) 35 | { 36 | bufferedOutput = new ByteArrayOutputStream() 37 | setExecOverrides( { 38 | it.standardOutput = bufferedOutput 39 | } ) 40 | } 41 | 42 | setWorkingDir( this.project.gulp.workDir ) 43 | setScript( localGulp ) 44 | 45 | // If the exec fails, make sure to still print output 46 | try 47 | { 48 | super.exec() 49 | } 50 | finally 51 | { 52 | // If we were buffering output, print it 53 | if ( this.project.gulp.bufferOutput && ( bufferedOutput != null ) ) 54 | { 55 | println "Output from ${this.project.gulp.workDir}/gulpfile.js" 56 | println bufferedOutput.toString() 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/NodeExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node 2 | 3 | import com.moowork.gradle.node.variant.Variant 4 | import org.gradle.api.Project 5 | 6 | class NodeExtension 7 | { 8 | final static String NAME = 'node' 9 | 10 | File workDir 11 | 12 | File npmWorkDir 13 | 14 | File yarnWorkDir 15 | 16 | File nodeModulesDir 17 | 18 | String version = '10.15.3' 19 | 20 | String npmVersion = '' 21 | 22 | String yarnVersion = '' 23 | 24 | String distBaseUrl = 'https://nodejs.org/dist' 25 | 26 | String npmCommand = 'npm' 27 | 28 | String yarnCommand = 'yarn' 29 | 30 | boolean download = false 31 | 32 | Variant variant 33 | 34 | NodeExtension( final Project project ) 35 | { 36 | def cacheDir = new File( project.projectDir, '.gradle' ) 37 | this.workDir = new File( cacheDir, 'nodejs' ) 38 | this.npmWorkDir = new File( cacheDir, 'npm' ) 39 | this.yarnWorkDir = new File( cacheDir, 'yarn' ) 40 | this.nodeModulesDir = project.projectDir 41 | } 42 | 43 | static NodeExtension get( final Project project ) 44 | { 45 | return project.extensions.getByType( NodeExtension ) 46 | } 47 | 48 | static NodeExtension create( final Project project ) 49 | { 50 | return project.extensions.create( NAME, NodeExtension, project ) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/NodePlugin.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node 2 | 3 | import com.moowork.gradle.node.npm.NpmInstallTask 4 | import com.moowork.gradle.node.npm.NpmSetupTask 5 | import com.moowork.gradle.node.npm.NpmTask 6 | import com.moowork.gradle.node.task.NodeTask 7 | import com.moowork.gradle.node.task.SetupTask 8 | import com.moowork.gradle.node.variant.VariantBuilder 9 | import com.moowork.gradle.node.yarn.YarnInstallTask 10 | import com.moowork.gradle.node.yarn.YarnSetupTask 11 | import com.moowork.gradle.node.yarn.YarnTask 12 | import org.gradle.api.Plugin 13 | import org.gradle.api.Project 14 | 15 | class NodePlugin 16 | implements Plugin 17 | { 18 | private Project project 19 | 20 | private NodeExtension config 21 | 22 | private SetupTask setupTask 23 | 24 | private NpmSetupTask npmSetupTask 25 | 26 | private YarnSetupTask yarnSetupTask 27 | 28 | @Override 29 | void apply( final Project project ) 30 | { 31 | this.project = project 32 | this.config = NodeExtension.create( this.project ) 33 | 34 | addGlobalTypes() 35 | addTasks() 36 | addNpmRule() 37 | addYarnRule() 38 | 39 | this.project.afterEvaluate { 40 | this.config.variant = new VariantBuilder( this.config ).build() 41 | configureSetupTask() 42 | configureNpmSetupTask() 43 | configureYarnSetupTask() 44 | } 45 | } 46 | 47 | private void addGlobalTypes() 48 | { 49 | addGlobalTaskType( NodeTask ) 50 | addGlobalTaskType( NpmTask ) 51 | addGlobalTaskType( YarnTask ) 52 | } 53 | 54 | private void addTasks() 55 | { 56 | this.project.tasks.create( NpmInstallTask.NAME, NpmInstallTask ) 57 | this.project.tasks.create( YarnInstallTask.NAME, YarnInstallTask ) 58 | this.setupTask = this.project.tasks.create( SetupTask.NAME, SetupTask ) 59 | this.npmSetupTask = this.project.tasks.create( NpmSetupTask.NAME, NpmSetupTask ) 60 | this.yarnSetupTask = this.project.tasks.create( YarnSetupTask.NAME, YarnSetupTask ) 61 | } 62 | 63 | private void addGlobalTaskType( Class type ) 64 | { 65 | this.project.extensions.extraProperties.set( type.getSimpleName(), type ) 66 | } 67 | 68 | private void addNpmRule() 69 | { 70 | // note this rule also makes it possible to specify e.g. "dependsOn npm_install" 71 | def workingDir 72 | this.project.afterEvaluate { 73 | workingDir = this.project.node.nodeModulesDir 74 | } 75 | project.getTasks().addRule( 'Pattern: "npm_": Executes an NPM command.' ) { String taskName -> 76 | if ( taskName.startsWith( "npm_" ) ) 77 | { 78 | NpmTask npmTask = project.getTasks().create( taskName, NpmTask.class ) 79 | if ( workingDir ) 80 | { 81 | npmTask.afterEvaluate( workingDir ) 82 | } 83 | 84 | String[] tokens = taskName.split( '_' ).tail() // all except first 85 | npmTask.npmCommand = tokens 86 | 87 | if ( tokens.head().equalsIgnoreCase( "run" ) ) 88 | { 89 | npmTask.dependsOn( NpmInstallTask.NAME ) 90 | } 91 | 92 | return npmTask 93 | } 94 | } 95 | } 96 | 97 | private void addYarnRule() 98 | { 99 | // note this rule also makes it possible to specify e.g. "dependsOn yarn_install" 100 | project.getTasks().addRule( 'Pattern: "yarn_": Executes an Yarn command.' ) { String taskName -> 101 | if ( taskName.startsWith( "yarn_" ) ) 102 | { 103 | YarnTask yarnTask = project.getTasks().create( taskName, YarnTask.class ) 104 | String[] tokens = taskName.split( '_' ).tail() // all except first 105 | yarnTask.yarnCommand = tokens 106 | 107 | if ( tokens.head().equalsIgnoreCase( "run" ) ) 108 | { 109 | yarnTask.dependsOn( YarnInstallTask.NAME ) 110 | } 111 | 112 | return yarnTask 113 | } 114 | } 115 | } 116 | 117 | private void configureSetupTask() 118 | { 119 | this.setupTask.setEnabled( this.config.download ) 120 | } 121 | 122 | private void configureNpmSetupTask() 123 | { 124 | this.npmSetupTask.configureVersion( this.config.npmVersion ) 125 | } 126 | 127 | private void configureYarnSetupTask() 128 | { 129 | this.yarnSetupTask.configureVersion( this.config.yarnVersion ) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/exec/ExecRunner.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.exec 2 | 3 | import com.moowork.gradle.node.NodeExtension 4 | import com.moowork.gradle.node.variant.Variant 5 | import org.gradle.api.Project 6 | import org.gradle.process.ExecResult 7 | 8 | abstract class ExecRunner 9 | { 10 | protected Project project 11 | 12 | protected NodeExtension ext 13 | 14 | protected Variant variant 15 | 16 | def Map environment = [:] 17 | 18 | def Object workingDir 19 | 20 | def List arguments = [] 21 | 22 | def boolean ignoreExitValue 23 | 24 | def Closure execOverrides 25 | 26 | public ExecRunner( final Project project ) 27 | { 28 | this.project = project 29 | this.environment << System.getenv() 30 | } 31 | 32 | protected final ExecResult run( final String exec, final List args ) 33 | { 34 | def realExec = exec 35 | def realArgs = args 36 | 37 | return this.project.exec( { 38 | it.executable = realExec 39 | it.args = realArgs 40 | it.environment = this.environment 41 | 42 | if ( this.workingDir != null ) 43 | { 44 | it.workingDir = this.workingDir 45 | } 46 | 47 | if ( this.ignoreExitValue != null ) 48 | { 49 | it.ignoreExitValue = this.ignoreExitValue 50 | } 51 | 52 | if ( this.execOverrides != null ) 53 | { 54 | this.execOverrides( it ) 55 | } 56 | } ) 57 | } 58 | 59 | public final ExecResult execute() 60 | { 61 | this.ext = NodeExtension.get( this.project ) 62 | this.variant = this.ext.variant 63 | return doExecute() 64 | } 65 | 66 | protected abstract ExecResult doExecute() 67 | } 68 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/exec/NodeExecRunner.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.exec 2 | 3 | import org.gradle.api.Project 4 | import org.gradle.process.ExecResult 5 | 6 | class NodeExecRunner 7 | extends ExecRunner 8 | { 9 | public NodeExecRunner( final Project project ) 10 | { 11 | super( project ) 12 | } 13 | 14 | @Override 15 | protected ExecResult doExecute() 16 | { 17 | def exec = 'node' 18 | if ( this.ext.download ) 19 | { 20 | def nodeBinDirPath = this.variant.nodeBinDir.getAbsolutePath() 21 | 22 | // Take care of Windows environments that may contain "Path" OR "PATH" - both existing 23 | // possibly (but not in parallel as of now) 24 | if ( environment['Path'] != null ) 25 | { 26 | environment['Path'] = nodeBinDirPath + File.pathSeparator + environment['Path'] 27 | } 28 | else 29 | { 30 | environment['PATH'] = nodeBinDirPath + File.pathSeparator + environment['PATH'] 31 | } 32 | 33 | exec = this.variant.nodeExec 34 | } 35 | 36 | return run( exec, this.arguments ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/npm/NpmExecRunner.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.npm 2 | 3 | import com.moowork.gradle.node.exec.ExecRunner 4 | import com.moowork.gradle.node.exec.NodeExecRunner 5 | import org.gradle.api.InvalidUserDataException 6 | import org.gradle.api.Project 7 | import org.gradle.process.ExecResult 8 | 9 | class NpmExecRunner 10 | extends ExecRunner 11 | { 12 | public NpmExecRunner( final Project project ) 13 | { 14 | super( project ) 15 | } 16 | 17 | @Override 18 | protected ExecResult doExecute() 19 | { 20 | 21 | def exec = this.variant.npmExec 22 | def arguments = this.arguments 23 | 24 | if ( this.ext.download ) 25 | { 26 | def npmBinDir = this.variant.npmBinDir.getAbsolutePath(); 27 | 28 | def nodeBinDir = this.variant.nodeBinDir.getAbsolutePath(); 29 | 30 | def path = npmBinDir + File.pathSeparator + nodeBinDir; 31 | 32 | // Take care of Windows environments that may contain "Path" OR "PATH" - both existing 33 | // possibly (but not in parallel as of now) 34 | if ( environment['Path'] != null ) 35 | { 36 | environment['Path'] = path + File.pathSeparator + environment['Path'] 37 | } 38 | else 39 | { 40 | environment['PATH'] = path + File.pathSeparator + environment['PATH'] 41 | } 42 | 43 | def File localNpm = project.file( new File( this.ext.nodeModulesDir, 'node_modules/npm/bin/npm-cli.js' ) ) 44 | if ( localNpm.exists() ) 45 | { 46 | exec = this.variant.nodeExec 47 | arguments = [localNpm.absolutePath] + arguments 48 | } 49 | else if ( !new File(exec).exists() ) 50 | { 51 | exec = this.variant.nodeExec 52 | arguments = [this.variant.npmScriptFile] + arguments 53 | } 54 | } 55 | return run( exec, arguments ) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/npm/NpmInstallTask.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.npm 2 | import com.moowork.gradle.node.NodeExtension 3 | import org.gradle.api.tasks.InputFile 4 | import org.gradle.api.tasks.Optional 5 | import org.gradle.api.tasks.OutputDirectory 6 | import org.gradle.api.tasks.PathSensitive 7 | import org.gradle.api.tasks.PathSensitivity 8 | 9 | /** 10 | * npm install that only gets executed if gradle decides so.*/ 11 | class NpmInstallTask 12 | extends NpmTask 13 | { 14 | public final static String NAME = 'npmInstall' 15 | 16 | NpmInstallTask() 17 | { 18 | this.group = 'Node' 19 | this.description = 'Install node packages from package.json.' 20 | setNpmCommand( 'install' ) 21 | dependsOn( [NpmSetupTask.NAME] ) 22 | 23 | this.project.afterEvaluate { 24 | getInputs().file( new File( (File) this.project.node.nodeModulesDir, 'package.json' ) ) 25 | getInputs().file( new File( (File) this.project.node.nodeModulesDir, 'package-lock.json')) 26 | getOutputs().dir( new File( (File) this.project.node.nodeModulesDir, 'node_modules' ) ) 27 | } 28 | } 29 | 30 | @InputFile 31 | @Optional 32 | @PathSensitive(PathSensitivity.RELATIVE) 33 | protected getPackageJsonFile() 34 | { 35 | def packageJsonFile = new File(this.project.extensions.getByType(NodeExtension).nodeModulesDir, 'package.json') 36 | return packageJsonFile.exists() ? packageJsonFile : null 37 | } 38 | 39 | @InputFile 40 | @Optional 41 | @PathSensitive(PathSensitivity.RELATIVE) 42 | protected getPackageLockFile() 43 | { 44 | def lockFile = new File(this.project.extensions.getByType(NodeExtension).nodeModulesDir, 'package-lock.json') 45 | return lockFile.exists() ? lockFile : null 46 | } 47 | 48 | @OutputDirectory 49 | protected getNodeModulesDir() 50 | { 51 | return new File(this.project.extensions.getByType(NodeExtension).nodeModulesDir, 'node_modules') 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/npm/NpmSetupTask.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.npm 2 | 3 | import com.moowork.gradle.node.NodeExtension 4 | import com.moowork.gradle.node.task.SetupTask 5 | import org.gradle.api.DefaultTask 6 | import org.gradle.api.tasks.Input 7 | import org.gradle.api.tasks.Internal 8 | import org.gradle.api.tasks.OutputDirectory 9 | import org.gradle.api.tasks.TaskAction 10 | import org.gradle.process.ExecResult 11 | 12 | /** 13 | * npm install that only gets executed if gradle decides so. 14 | **/ 15 | class NpmSetupTask 16 | extends DefaultTask 17 | { 18 | public final static String NAME = 'npmSetup' 19 | 20 | private NpmExecRunner runner 21 | 22 | private NodeExtension config 23 | 24 | protected List args = [] 25 | 26 | private ExecResult result 27 | 28 | NpmSetupTask() 29 | { 30 | dependsOn( SetupTask.NAME ) 31 | 32 | this.group = 'Node' 33 | this.description = 'Setup a specific version of npm to be used by the build.' 34 | this.enabled = false 35 | 36 | this.runner = new NpmExecRunner( this.project ) 37 | } 38 | 39 | @Input 40 | Set getInput() 41 | { 42 | def set = new HashSet<>() 43 | set.add( getConfig().download ) 44 | set.add( getConfig().npmVersion ) 45 | set.add( getConfig().npmWorkDir ) 46 | return set 47 | } 48 | 49 | @OutputDirectory 50 | File getNpmDir() 51 | { 52 | return getVariant().npmDir 53 | } 54 | 55 | @Internal 56 | ExecResult getResult() 57 | { 58 | return this.result 59 | } 60 | 61 | @Internal 62 | protected getConfig() 63 | { 64 | if ( this.config != null ) 65 | { 66 | return this.config 67 | } 68 | 69 | this.config = NodeExtension.get( this.project ) 70 | return this.config 71 | } 72 | 73 | @Internal 74 | protected getVariant() 75 | { 76 | return getConfig().variant 77 | } 78 | 79 | List getArgs() 80 | { 81 | return this.args 82 | } 83 | 84 | @Internal 85 | void setArgs( final Iterable value ) 86 | { 87 | this.args = value.toList() 88 | } 89 | 90 | void setIgnoreExitValue( final boolean value ) 91 | { 92 | this.runner.ignoreExitValue = value 93 | } 94 | 95 | void setExecOverrides( final Closure closure ) 96 | { 97 | this.runner.execOverrides = closure 98 | } 99 | 100 | @TaskAction 101 | void exec() 102 | { 103 | this.runner.arguments.addAll( this.args ) 104 | this.result = this.runner.execute() 105 | } 106 | 107 | void configureVersion( String npmVersion ) 108 | { 109 | if ( !npmVersion.isEmpty() ) 110 | { 111 | logger.debug( "Setting npmVersion to ${npmVersion}" ) 112 | setArgs( ['install', '--global', '--no-save', '--prefix', getVariant().npmDir, "npm@${npmVersion}"] ) 113 | enabled = true 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/npm/NpmTask.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.npm 2 | 3 | import org.gradle.api.DefaultTask 4 | import org.gradle.api.tasks.Internal 5 | import org.gradle.api.tasks.TaskAction 6 | import org.gradle.process.ExecResult 7 | 8 | class NpmTask 9 | extends DefaultTask 10 | { 11 | protected NpmExecRunner runner 12 | 13 | private List args = [] 14 | 15 | private ExecResult result 16 | 17 | private String[] npmCommand 18 | 19 | NpmTask() 20 | { 21 | this.runner = new NpmExecRunner( this.project ) 22 | dependsOn( NpmSetupTask.NAME ) 23 | 24 | this.project.afterEvaluate { 25 | afterEvaluate( this.project.node.nodeModulesDir ) 26 | } 27 | } 28 | 29 | void afterEvaluate(nodeModulesDir) { 30 | if ( !this.runner.workingDir ) 31 | { 32 | setWorkingDir( nodeModulesDir ) 33 | } 34 | 35 | if ( !this.runner.workingDir.exists() ) 36 | { 37 | this.runner.workingDir.mkdirs(); 38 | } 39 | } 40 | 41 | void setArgs( final Iterable value ) 42 | { 43 | this.args = value.asList() 44 | } 45 | 46 | void setNpmCommand( String[] cmd ) 47 | { 48 | this.npmCommand = cmd 49 | } 50 | 51 | @Internal 52 | List getArgs() 53 | { 54 | return this.args 55 | } 56 | 57 | void setEnvironment( final Map value ) 58 | { 59 | this.runner.environment << value 60 | } 61 | 62 | void setWorkingDir( final Object value ) 63 | { 64 | this.runner.workingDir = value 65 | } 66 | 67 | void setIgnoreExitValue( final boolean value ) 68 | { 69 | this.runner.ignoreExitValue = value 70 | } 71 | 72 | void setExecOverrides( final Closure closure ) 73 | { 74 | this.runner.execOverrides = closure 75 | } 76 | 77 | @Internal 78 | ExecResult getResult() 79 | { 80 | return this.result 81 | } 82 | 83 | @TaskAction 84 | void exec() 85 | { 86 | if ( this.npmCommand != null ) 87 | { 88 | this.runner.arguments.addAll( this.npmCommand ) 89 | } 90 | 91 | this.runner.arguments.addAll( this.args ) 92 | this.result = this.runner.execute() 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/task/NodeTask.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.task 2 | 3 | import com.moowork.gradle.node.exec.NodeExecRunner 4 | import org.gradle.api.DefaultTask 5 | import org.gradle.api.tasks.Internal 6 | import org.gradle.api.tasks.TaskAction 7 | import org.gradle.process.ExecResult 8 | 9 | class NodeTask 10 | extends DefaultTask 11 | { 12 | protected NodeExecRunner runner 13 | 14 | private File script 15 | 16 | private List args = [] 17 | 18 | private Iterable options = [] 19 | 20 | private ExecResult result 21 | 22 | NodeTask() 23 | { 24 | this.runner = new NodeExecRunner( this.project ) 25 | dependsOn( SetupTask.NAME ) 26 | } 27 | 28 | void setScript( final File value ) 29 | { 30 | this.script = value 31 | } 32 | 33 | void setArgs( final Iterable value ) 34 | { 35 | this.args = value.asList() 36 | } 37 | 38 | void addArgs( final Object... args ) 39 | { 40 | this.args.addAll( args ) 41 | } 42 | 43 | void setOptions( final Iterable value ) 44 | { 45 | this.options = value 46 | } 47 | 48 | void setEnvironment( final Map value ) 49 | { 50 | this.runner.environment << value 51 | } 52 | 53 | void setWorkingDir( final Object value ) 54 | { 55 | this.runner.workingDir = value 56 | } 57 | 58 | void setIgnoreExitValue( final boolean value ) 59 | { 60 | this.runner.ignoreExitValue = value 61 | } 62 | 63 | void setExecOverrides( final Closure closure ) 64 | { 65 | this.runner.execOverrides = closure 66 | } 67 | 68 | @Internal 69 | ExecResult getResult() 70 | { 71 | return this.result 72 | } 73 | 74 | @Internal 75 | List getArgs() 76 | { 77 | return this.args 78 | } 79 | 80 | @Internal 81 | Iterable getOptions() 82 | { 83 | return this.options 84 | } 85 | 86 | @TaskAction 87 | void exec() 88 | { 89 | if ( this.script == null ) 90 | { 91 | throw new IllegalStateException( 'Required script property is not set.' ) 92 | } 93 | 94 | def execArgs = [] 95 | execArgs.addAll( this.options as List ) 96 | execArgs.add( this.script.absolutePath ) 97 | execArgs.addAll( this.args as List ) 98 | 99 | this.runner.arguments = execArgs 100 | this.result = this.runner.execute() 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/task/SetupTask.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.task 2 | 3 | import com.moowork.gradle.node.NodeExtension 4 | import com.moowork.gradle.node.variant.Variant 5 | import org.gradle.api.DefaultTask 6 | import org.gradle.api.artifacts.repositories.ArtifactRepository 7 | import org.gradle.api.artifacts.repositories.IvyArtifactRepository 8 | import org.gradle.api.tasks.Input 9 | import org.gradle.api.tasks.Internal 10 | import org.gradle.api.tasks.OutputDirectory 11 | import org.gradle.api.tasks.TaskAction 12 | 13 | import java.nio.file.Files 14 | import java.nio.file.Path 15 | import java.nio.file.Paths 16 | 17 | class SetupTask 18 | extends DefaultTask 19 | { 20 | public final static String NAME = 'nodeSetup' 21 | 22 | private NodeExtension config 23 | 24 | protected Variant variant 25 | 26 | private IvyArtifactRepository repo 27 | 28 | private List allRepos; 29 | 30 | SetupTask() 31 | { 32 | this.group = 'Node' 33 | this.description = 'Download and install a local node/npm version.' 34 | this.enabled = false 35 | } 36 | 37 | @Input 38 | public Set getInput() 39 | { 40 | configureIfNeeded() 41 | 42 | def set = new HashSet<>() 43 | set.add( this.config.download ) 44 | set.add( this.variant.archiveDependency ) 45 | set.add( this.variant.exeDependency ) 46 | return set 47 | } 48 | 49 | @OutputDirectory 50 | public File getNodeDir() 51 | { 52 | configureIfNeeded() 53 | return this.variant.nodeDir 54 | } 55 | 56 | private void configureIfNeeded() 57 | { 58 | if ( this.config != null ) 59 | { 60 | return 61 | } 62 | 63 | this.config = NodeExtension.get( this.project ) 64 | this.variant = this.config.variant 65 | } 66 | 67 | @TaskAction 68 | void exec() 69 | { 70 | configureIfNeeded() 71 | addRepository() 72 | 73 | if ( this.variant.exeDependency ) 74 | { 75 | copyNodeExe() 76 | } 77 | 78 | deleteExistingNode() 79 | unpackNodeArchive() 80 | setExecutableFlag() 81 | restoreRepositories() 82 | } 83 | 84 | private void copyNodeExe() 85 | { 86 | this.project.copy { 87 | from getNodeExeFile() 88 | into this.variant.nodeBinDir 89 | rename 'node.+\\.exe', 'node.exe' 90 | } 91 | } 92 | 93 | private void deleteExistingNode() 94 | { 95 | this.project.delete( getNodeDir().parent ) 96 | } 97 | 98 | private void unpackNodeArchive() 99 | { 100 | if ( getNodeArchiveFile().getName().endsWith( 'zip' ) ) 101 | { 102 | this.project.copy { 103 | from this.project.zipTree( getNodeArchiveFile() ) 104 | into getNodeDir().parent 105 | } 106 | } 107 | else if ( this.variant.exeDependency ) 108 | { 109 | //Remap lib/node_modules to node_modules (the same directory as node.exe) because that's how the zip dist does it 110 | this.project.copy { 111 | from this.project.tarTree( getNodeArchiveFile() ) 112 | into this.variant.nodeBinDir 113 | eachFile { 114 | def m = it.path =~ /^.*?[\\/]lib[\\/](node_modules.*$)/ 115 | if ( m.matches() ) 116 | { 117 | // remap the file to the root 118 | it.path = m.group( 1 ) 119 | } 120 | else 121 | { 122 | it.exclude() 123 | } 124 | } 125 | includeEmptyDirs = false 126 | } 127 | } 128 | else 129 | { 130 | this.project.copy { 131 | from this.project.tarTree( getNodeArchiveFile() ) 132 | into getNodeDir().parent 133 | } 134 | // Fix broken symlink 135 | Path npm = Paths.get( variant.nodeBinDir.path, 'npm' ) 136 | if ( Files.deleteIfExists( npm ) ) 137 | { 138 | Files.createSymbolicLink( 139 | npm, 140 | variant.nodeBinDir.toPath().relativize(Paths.get(variant.npmScriptFile))) 141 | } 142 | } 143 | } 144 | 145 | private void setExecutableFlag() 146 | { 147 | if ( !this.variant.windows ) 148 | { 149 | new File( this.variant.nodeExec ).setExecutable( true ) 150 | } 151 | } 152 | 153 | @Internal 154 | protected File getNodeExeFile() 155 | { 156 | return resolveSingle( this.variant.exeDependency ) 157 | } 158 | 159 | @Internal 160 | protected File getNodeArchiveFile() 161 | { 162 | return resolveSingle( this.variant.archiveDependency ) 163 | } 164 | 165 | private File resolveSingle( String name ) 166 | { 167 | def dep = this.project.dependencies.create( name ) 168 | def conf = this.project.configurations.detachedConfiguration( dep ) 169 | conf.transitive = false 170 | return conf.resolve().iterator().next(); 171 | } 172 | 173 | private void addRepository() 174 | { 175 | this.allRepos = new ArrayList<>() 176 | this.allRepos.addAll( this.project.repositories ) 177 | this.project.repositories.clear() 178 | 179 | def distUrl = this.config.distBaseUrl 180 | this.repo = this.project.repositories.ivy { 181 | url distUrl 182 | layout 'pattern', { 183 | artifact 'v[revision]/[artifact](-v[revision]-[classifier]).[ext]' 184 | ivy 'v[revision]/ivy.xml' 185 | } 186 | } 187 | } 188 | 189 | private void restoreRepositories() 190 | { 191 | this.project.repositories.clear(); 192 | this.project.repositories.addAll( this.allRepos ); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/util/PlatformHelper.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.util 2 | 3 | class PlatformHelper 4 | { 5 | static PlatformHelper INSTANCE = new PlatformHelper() 6 | 7 | private final Properties props; 8 | 9 | public PlatformHelper() 10 | { 11 | this( System.getProperties() ) 12 | } 13 | 14 | public PlatformHelper( final Properties props ) 15 | { 16 | this.props = props; 17 | } 18 | 19 | private String property( final String name ) 20 | { 21 | def value = this.props.getProperty( name ) 22 | return value != null ? value : System.getProperty( name ) 23 | } 24 | 25 | public String getOsName() 26 | { 27 | final String name = property( "os.name" ).toLowerCase() 28 | if ( name.contains( "windows" ) ) 29 | { 30 | return "win" 31 | } 32 | 33 | if ( name.contains( "mac" ) ) 34 | { 35 | return "darwin" 36 | } 37 | 38 | if ( name.contains( "linux" ) ) 39 | { 40 | return "linux" 41 | } 42 | 43 | if ( name.contains( "freebsd" ) ) 44 | { 45 | return "linux" 46 | } 47 | 48 | if ( name.contains( "sunos" ) ) 49 | { 50 | return "sunos" 51 | } 52 | 53 | throw new IllegalArgumentException( "Unsupported OS: " + name ) 54 | } 55 | 56 | public String getOsArch() 57 | { 58 | final String arch = property( "os.arch" ).toLowerCase() 59 | if ( arch.contains( "64" ) ) 60 | { 61 | return "x64" 62 | } 63 | //as Java just returns "arm" on all ARM variants, we need a system call to determine the exact arch 64 | if( arch.equals( "arm" )) 65 | { 66 | def systemArch = 'uname -m'.execute().text.trim() 67 | //the node binaries for 'armv8l' are called 'arm64', so we need to distinguish here 68 | if(systemArch.equals("armv8l")) 69 | { 70 | return "arm64" 71 | } 72 | else 73 | { 74 | return systemArch 75 | } 76 | } 77 | 78 | return "x86" 79 | } 80 | 81 | public boolean isWindows() 82 | { 83 | return getOsName().equals( "win" ) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/variant/Variant.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.variant 2 | 3 | class Variant 4 | { 5 | def boolean windows 6 | 7 | /* Node */ 8 | 9 | def String nodeExec 10 | 11 | def String npmScriptFile 12 | 13 | def File nodeDir 14 | 15 | def File nodeBinDir 16 | 17 | /* NPM */ 18 | 19 | def String npmExec 20 | 21 | def File npmDir 22 | 23 | def File npmBinDir 24 | 25 | /* Yarn */ 26 | 27 | def String yarnExec 28 | 29 | def File yarnDir 30 | 31 | def File yarnBinDir 32 | 33 | /* Dependencies */ 34 | 35 | def String archiveDependency 36 | 37 | def String exeDependency 38 | } 39 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/variant/VariantBuilder.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.variant 2 | 3 | import com.moowork.gradle.node.NodeExtension 4 | import com.moowork.gradle.node.util.PlatformHelper 5 | 6 | class VariantBuilder 7 | { 8 | private final NodeExtension ext 9 | private PlatformHelper platformHelper 10 | 11 | public VariantBuilder( final NodeExtension ext ) 12 | { 13 | this.ext = ext 14 | this.platformHelper = PlatformHelper.INSTANCE 15 | } 16 | 17 | public VariantBuilder( final NodeExtension ext , PlatformHelper platformHelper) 18 | { 19 | this(ext) 20 | this.platformHelper = platformHelper 21 | } 22 | 23 | public Variant build() 24 | { 25 | def osName = platformHelper.getOsName() 26 | def osArch = platformHelper.getOsArch() 27 | 28 | def variant = new Variant() 29 | variant.windows = platformHelper.isWindows() 30 | 31 | variant.nodeDir = getNodeDir( osName, osArch ) 32 | variant.npmDir = ext.npmVersion ? getNpmDir() : variant.nodeDir 33 | variant.yarnDir = getYarnDir() 34 | 35 | variant.nodeBinDir = variant.nodeDir 36 | variant.npmBinDir = variant.npmDir 37 | variant.yarnBinDir = variant.yarnDir 38 | 39 | variant.nodeExec = "node" 40 | variant.npmExec = this.ext.npmCommand 41 | variant.yarnExec = this.ext.yarnCommand 42 | 43 | if ( variant.windows ) 44 | { 45 | if (variant.npmExec == 'npm') { 46 | variant.npmExec = 'npm.cmd' 47 | } 48 | if (variant.yarnExec == 'yarn') { 49 | variant.yarnExec = 'yarn.cmd' 50 | } 51 | 52 | if (hasWindowsZip()) 53 | { 54 | variant.archiveDependency = getArchiveDependency( osName, osArch, 'zip' ) 55 | } 56 | else 57 | { 58 | variant.archiveDependency = getArchiveDependency( 'linux', 'x86', 'tar.gz' ) 59 | variant.exeDependency = getExeDependency() 60 | } 61 | variant.npmScriptFile = new File( variant.nodeDir , 'node_modules/npm/bin/npm-cli.js') 62 | } 63 | else 64 | { 65 | variant.nodeBinDir = new File( variant.nodeBinDir, 'bin' ) 66 | variant.npmBinDir = new File( variant.npmBinDir, 'bin' ) 67 | variant.yarnBinDir = new File( variant.yarnBinDir, 'bin' ) 68 | variant.archiveDependency = getArchiveDependency( osName, osArch, 'tar.gz' ) 69 | variant.npmScriptFile = new File( variant.nodeDir , 'lib/node_modules/npm/bin/npm-cli.js') 70 | } 71 | 72 | if (this.ext.download) 73 | { 74 | if (variant.nodeExec == "node" && variant.windows) { 75 | variant.nodeExec = "node.exe" 76 | } 77 | 78 | variant.nodeExec = new File( variant.nodeBinDir, variant.nodeExec ).absolutePath 79 | variant.npmExec = new File( variant.npmBinDir, variant.npmExec ).absolutePath 80 | variant.yarnExec = new File( variant.yarnBinDir, variant.yarnExec ).absolutePath 81 | } 82 | 83 | return variant 84 | } 85 | 86 | private String getArchiveDependency( final String osName, final String osArch, final String type ) 87 | { 88 | def version = this.ext.version 89 | return "org.nodejs:node:${version}:${osName}-${osArch}@${type}" 90 | } 91 | 92 | private String getExeDependency() 93 | { 94 | def version = this.ext.version 95 | def osArch = platformHelper.getOsArch() 96 | def majorVersion = version.tokenize( '.' )[0].toInteger() 97 | if ( majorVersion > 3 ) 98 | { 99 | if ( osArch.equals( "x86" ) ) 100 | { 101 | return "org.nodejs:win-x86/node:${version}@exe" 102 | } 103 | else 104 | { 105 | return "org.nodejs:win-x64/node:${version}@exe" 106 | } 107 | } 108 | else 109 | { 110 | if ( osArch.equals( "x86" ) ) 111 | { 112 | return "org.nodejs:node:${version}@exe" 113 | } 114 | else 115 | { 116 | return "org.nodejs:x64/node:${version}@exe" 117 | } 118 | } 119 | } 120 | 121 | //https://github.com/nodejs/node/pull/5995 122 | private boolean hasWindowsZip() 123 | { 124 | def version = this.ext.version 125 | def osArch = platformHelper.getOsArch() 126 | def tokens = version.tokenize( '.' ); 127 | def majorVersion = tokens[0].toInteger() 128 | def minorVersion = tokens[1].toInteger() 129 | def microVersion = tokens[2].toInteger() 130 | if ( 131 | ( majorVersion == 4 && minorVersion >= 5 ) // >= 4.5.0 132 | || ( majorVersion == 6 && (minorVersion > 2 || (minorVersion == 2 && microVersion >= 1)) ) // >= 6.2.1 133 | || majorVersion > 6 134 | ) 135 | { 136 | return true 137 | } 138 | return false 139 | } 140 | 141 | private File getNodeDir( final String osName, final String osArch ) 142 | { 143 | def version = this.ext.version 144 | def dirName = "node-v${version}-${osName}-${osArch}" 145 | return new File( this.ext.workDir, dirName ) 146 | } 147 | 148 | private File getNpmDir() 149 | { 150 | def version = this.ext.npmVersion 151 | return new File(this.ext.npmWorkDir, "npm-v${version}") 152 | } 153 | 154 | private File getYarnDir() 155 | { 156 | def dirname = "yarn" 157 | if (this.ext.yarnVersion) 158 | { 159 | dirname += "-v${this.ext.yarnVersion}" 160 | } 161 | else 162 | { 163 | dirname += "-latest" 164 | } 165 | return new File( this.ext.yarnWorkDir, dirname ) 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/yarn/YarnExecRunner.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.yarn 2 | 3 | import com.moowork.gradle.node.exec.ExecRunner 4 | import com.moowork.gradle.node.exec.NodeExecRunner 5 | import org.gradle.api.InvalidUserDataException 6 | import org.gradle.api.Project 7 | import org.gradle.process.ExecResult 8 | 9 | class YarnExecRunner 10 | extends ExecRunner 11 | { 12 | public YarnExecRunner( final Project project ) 13 | { 14 | super( project ) 15 | } 16 | 17 | @Override 18 | protected ExecResult doExecute() 19 | { 20 | if ( this.ext.download ) 21 | { 22 | def yarnBinDir = this.variant.yarnBinDir.getAbsolutePath(); 23 | 24 | def npmBinDir = this.variant.npmBinDir.getAbsolutePath(); 25 | 26 | def nodeBinDir = this.variant.nodeBinDir.getAbsolutePath(); 27 | 28 | def path = yarnBinDir + File.pathSeparator + npmBinDir + File.pathSeparator + nodeBinDir; 29 | 30 | // Take care of Windows environments that may contain "Path" OR "PATH" - both existing 31 | // possibly (but not in parallel as of now) 32 | if ( this.environment['Path'] != null ) 33 | { 34 | this.environment['Path'] = path + File.pathSeparator + this.environment['Path'] 35 | } 36 | else 37 | { 38 | this.environment['PATH'] = path + File.pathSeparator + this.environment['PATH'] 39 | } 40 | } 41 | 42 | return run( this.variant.yarnExec, this.arguments ) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/yarn/YarnInstallTask.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.yarn 2 | 3 | import com.moowork.gradle.node.NodeExtension 4 | import org.gradle.api.tasks.InputFile 5 | import org.gradle.api.tasks.Optional 6 | import org.gradle.api.tasks.OutputDirectory 7 | import org.gradle.api.tasks.PathSensitive 8 | import org.gradle.api.tasks.PathSensitivity 9 | 10 | /** 11 | * yarn install that only gets executed if gradle decides so.*/ 12 | class YarnInstallTask 13 | extends YarnTask 14 | { 15 | public final static String NAME = 'yarn' 16 | 17 | public YarnInstallTask() 18 | { 19 | this.group = 'Node' 20 | this.description = 'Install node packages using Yarn.' 21 | setYarnCommand( '' ) 22 | dependsOn( [YarnSetupTask.NAME] ) 23 | 24 | this.project.afterEvaluate { 25 | getInputs().file( new File( (File) this.project.node.nodeModulesDir, 'package.json' ) ) 26 | getInputs().file( new File( (File) this.project.node.nodeModulesDir, 'yarn.lock' ) ) 27 | getOutputs().dir( new File( (File) this.project.node.nodeModulesDir, 'node_modules' ) ) 28 | } 29 | } 30 | 31 | @InputFile 32 | @Optional 33 | @PathSensitive(PathSensitivity.RELATIVE) 34 | protected getPackageJsonFile() 35 | { 36 | def packageJsonFile = new File(this.project.extensions.getByType(NodeExtension).nodeModulesDir, 'package.json') 37 | return packageJsonFile.exists() ? packageJsonFile : null 38 | } 39 | 40 | @InputFile 41 | @Optional 42 | @PathSensitive(PathSensitivity.RELATIVE) 43 | protected getYarnLockFile() 44 | { 45 | def lockFile = new File(this.project.extensions.getByType(NodeExtension).nodeModulesDir, 'yarn.lock') 46 | return lockFile.exists() ? lockFile : null 47 | } 48 | 49 | @OutputDirectory 50 | protected getNodeModulesDir() 51 | { 52 | return new File(this.project.extensions.getByType(NodeExtension).nodeModulesDir, 'node_modules') 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/yarn/YarnSetupTask.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.yarn 2 | 3 | import com.moowork.gradle.node.npm.NpmSetupTask 4 | import org.gradle.api.tasks.Input 5 | import org.gradle.api.tasks.OutputDirectory 6 | 7 | /** 8 | * Setup a specific version of Yarn to be used by the build. 9 | **/ 10 | class YarnSetupTask 11 | extends NpmSetupTask 12 | { 13 | public final static String NAME = 'yarnSetup' 14 | 15 | YarnSetupTask() 16 | { 17 | this.group = 'Node' 18 | this.description = 'Setup a specific version of Yarn to be used by the build.' 19 | 20 | this.enabled = false; 21 | } 22 | 23 | @Input 24 | Set getInput() 25 | { 26 | def set = new HashSet<>() 27 | set.add( this.getConfig().download ) 28 | set.add( this.getConfig().yarnVersion ) 29 | return set 30 | } 31 | 32 | @OutputDirectory 33 | File getYarnDir() 34 | { 35 | return this.getVariant().yarnDir 36 | } 37 | 38 | void configureVersion( String yarnVersion ) 39 | { 40 | def pkg = "yarn" 41 | 42 | if ( !yarnVersion.isEmpty() ) 43 | { 44 | logger.debug( "Setting yarnVersion to ${yarnVersion}" ) 45 | pkg += "@${yarnVersion}" 46 | } 47 | 48 | this.setArgs( ['install', '--global', '--no-save', '--prefix', this.getVariant().yarnDir, pkg] ) 49 | enabled = true 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/groovy/com/moowork/gradle/node/yarn/YarnTask.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.yarn 2 | 3 | import org.gradle.api.DefaultTask 4 | import org.gradle.api.tasks.Internal 5 | import org.gradle.api.tasks.TaskAction 6 | import org.gradle.process.ExecResult 7 | 8 | class YarnTask 9 | extends DefaultTask 10 | { 11 | protected YarnExecRunner runner 12 | 13 | private Iterable args = [] 14 | 15 | private ExecResult result 16 | 17 | private String[] yarnCommand 18 | 19 | public YarnTask() 20 | { 21 | this.runner = new YarnExecRunner( this.project ) 22 | dependsOn( YarnSetupTask.NAME ) 23 | 24 | this.project.afterEvaluate { 25 | if ( !this.runner.workingDir ) 26 | { 27 | def workingDir = this.project.node.nodeModulesDir 28 | setWorkingDir( workingDir ) 29 | } 30 | 31 | if ( !this.runner.workingDir.exists() ) 32 | { 33 | this.runner.workingDir.mkdirs(); 34 | } 35 | } 36 | } 37 | 38 | void setArgs( final Iterable value ) 39 | { 40 | this.args = value 41 | } 42 | 43 | void setYarnCommand( String[] cmd ) 44 | { 45 | this.yarnCommand = cmd 46 | } 47 | 48 | @Internal 49 | Iterable getArgs() 50 | { 51 | return this.args 52 | } 53 | 54 | void setEnvironment( final Map value ) 55 | { 56 | this.runner.environment << value 57 | } 58 | 59 | void setWorkingDir( final Object value ) 60 | { 61 | this.runner.workingDir = value 62 | } 63 | 64 | void setIgnoreExitValue( final boolean value ) 65 | { 66 | this.runner.ignoreExitValue = value 67 | } 68 | 69 | void setExecOverrides( final Closure closure ) 70 | { 71 | this.runner.execOverrides = closure 72 | } 73 | 74 | @Internal 75 | ExecResult getResult() 76 | { 77 | return this.result 78 | } 79 | 80 | @TaskAction 81 | void exec() 82 | { 83 | if ( this.yarnCommand != null ) 84 | { 85 | this.runner.arguments.addAll( this.yarnCommand ) 86 | } 87 | 88 | this.runner.arguments.addAll( this.args ) 89 | this.result = this.runner.execute() 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/gradle-plugins/com.moowork.grunt.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.moowork.gradle.grunt.GruntPlugin 2 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/gradle-plugins/com.moowork.gulp.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.moowork.gradle.gulp.GulpPlugin 2 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/gradle-plugins/com.moowork.node.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.moowork.gradle.node.NodePlugin 2 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/AbstractProjectTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle 2 | 3 | import org.gradle.api.internal.project.ProjectInternal 4 | import org.gradle.testfixtures.ProjectBuilder 5 | import org.junit.Rule 6 | import org.junit.rules.TemporaryFolder 7 | import spock.lang.Specification 8 | 9 | class AbstractProjectTest 10 | extends Specification 11 | { 12 | @Rule 13 | final TemporaryFolder temporaryFolder = new TemporaryFolder(); 14 | 15 | def ProjectInternal project 16 | 17 | def File projectDir; 18 | 19 | def setup() 20 | { 21 | this.projectDir = this.temporaryFolder.root; 22 | this.project = (ProjectInternal) ProjectBuilder.builder(). 23 | withProjectDir( this.projectDir ). 24 | build(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/grunt/AbstractTaskTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.grunt 2 | 3 | import com.moowork.gradle.AbstractProjectTest 4 | import com.moowork.gradle.node.NodeExtension 5 | import com.moowork.gradle.node.util.PlatformHelper 6 | import org.gradle.process.ExecResult 7 | import org.gradle.process.ExecSpec 8 | 9 | abstract class AbstractTaskTest 10 | extends AbstractProjectTest 11 | { 12 | def ExecResult execResult 13 | 14 | def ExecSpec execSpec 15 | 16 | def Properties props 17 | 18 | def NodeExtension ext 19 | 20 | def setup() 21 | { 22 | this.props = new Properties() 23 | PlatformHelper.INSTANCE = new PlatformHelper( this.props ) 24 | 25 | this.execResult = Mock( ExecResult ) 26 | 27 | this.project.apply plugin: 'com.moowork.grunt' 28 | this.ext = NodeExtension.get( this.project ) 29 | 30 | mockExec() 31 | } 32 | 33 | private void mockExec() 34 | { 35 | this.project.metaClass.invokeMethod = { String name, Object[] args -> 36 | if ( name == 'exec' ) 37 | { 38 | Closure closure = (Closure) args.first() 39 | closure.call( this.execSpec ) 40 | return this.execResult 41 | } 42 | else 43 | { 44 | MetaMethod metaMethod = delegate.class.metaClass.getMetaMethod( name, args ) 45 | return metaMethod?.invoke( delegate, args ) 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/grunt/GruntPluginTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.grunt 2 | 3 | import com.moowork.gradle.AbstractProjectTest 4 | 5 | class GruntPluginTest 6 | extends AbstractProjectTest 7 | { 8 | def 'check default tasks'() 9 | { 10 | when: 11 | this.project.apply plugin: 'com.moowork.grunt' 12 | this.project.evaluate() 13 | 14 | then: 15 | this.project.extensions.getByName( 'grunt' ) 16 | this.project.tasks.getByName( 'installGrunt' ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/grunt/GruntTaskTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.grunt 2 | 3 | import org.gradle.api.GradleException 4 | 5 | class GruntTaskTest 6 | extends AbstractTaskTest 7 | { 8 | def "grunt not installed"() 9 | { 10 | given: 11 | def task = this.project.tasks.create( 'simple', GruntTask ) 12 | 13 | when: 14 | this.project.evaluate() 15 | task.exec() 16 | 17 | then: 18 | thrown( GradleException ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/gulp/AbstractTaskTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.gulp 2 | 3 | import com.moowork.gradle.AbstractProjectTest 4 | import com.moowork.gradle.node.NodeExtension 5 | import com.moowork.gradle.node.util.PlatformHelper 6 | import org.gradle.process.ExecResult 7 | import org.gradle.process.ExecSpec 8 | 9 | abstract class AbstractTaskTest 10 | extends AbstractProjectTest 11 | { 12 | def ExecResult execResult 13 | 14 | def ExecSpec execSpec 15 | 16 | def Properties props 17 | 18 | def NodeExtension ext 19 | 20 | def setup() 21 | { 22 | this.props = new Properties() 23 | PlatformHelper.INSTANCE = new PlatformHelper( this.props ) 24 | 25 | this.execResult = Mock( ExecResult ) 26 | 27 | this.project.apply plugin: 'com.moowork.gulp' 28 | this.ext = NodeExtension.get( this.project ) 29 | 30 | mockExec() 31 | } 32 | 33 | private void mockExec() 34 | { 35 | this.project.metaClass.invokeMethod = { String name, Object[] args -> 36 | if ( name == 'exec' ) 37 | { 38 | Closure closure = (Closure) args.first() 39 | closure.call( this.execSpec ) 40 | return this.execResult 41 | } 42 | else 43 | { 44 | MetaMethod metaMethod = delegate.class.metaClass.getMetaMethod( name, args ) 45 | return metaMethod?.invoke( delegate, args ) 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/gulp/GulpPluginTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.gulp 2 | 3 | import com.moowork.gradle.AbstractProjectTest 4 | 5 | class GulpPluginTest 6 | extends AbstractProjectTest 7 | { 8 | def 'check default tasks'() 9 | { 10 | when: 11 | this.project.apply plugin: 'com.moowork.gulp' 12 | this.project.evaluate() 13 | 14 | then: 15 | this.project.extensions.getByName( 'gulp' ) 16 | this.project.tasks.getByName( 'installGulp' ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/gulp/GulpTaskTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.gulp 2 | 3 | import org.gradle.api.GradleException 4 | 5 | class GulpTaskTest 6 | extends AbstractTaskTest 7 | { 8 | def "gulp not installed"() 9 | { 10 | given: 11 | def task = this.project.tasks.create( 'simple', GulpTask ) 12 | 13 | when: 14 | this.project.evaluate() 15 | task.exec() 16 | 17 | then: 18 | thrown( GradleException ) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/node/NodeExtensionTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node 2 | 3 | import com.moowork.gradle.AbstractProjectTest 4 | 5 | class NodeExtensionTest 6 | extends AbstractProjectTest 7 | { 8 | def "check default values for extension"() 9 | { 10 | when: 11 | this.project.apply plugin: 'com.moowork.node' 12 | def ext = NodeExtension.get( this.project ) 13 | 14 | then: 15 | ext != null 16 | ext.npmCommand == 'npm' 17 | ext.distBaseUrl == 'https://nodejs.org/dist' 18 | ext.workDir != null 19 | ext.nodeModulesDir != null 20 | ext.version == '10.15.3' 21 | !ext.download 22 | ext.npmVersion == '' 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/node/NodePluginTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node 2 | 3 | import com.moowork.gradle.AbstractProjectTest 4 | import com.moowork.gradle.node.util.PlatformHelper 5 | 6 | class NodePluginTest 7 | extends AbstractProjectTest 8 | { 9 | private Properties props 10 | 11 | def setup() 12 | { 13 | this.props = new Properties() 14 | PlatformHelper.INSTANCE = new PlatformHelper( this.props ) 15 | } 16 | 17 | def 'check default tasks'() 18 | { 19 | when: 20 | this.project.apply plugin: 'com.moowork.node' 21 | this.project.evaluate() 22 | 23 | then: 24 | this.project.extensions.getByName( 'node' ) 25 | this.project.tasks.getByName( 'nodeSetup' ) 26 | this.project.tasks.getByName( 'npmInstall' ) 27 | this.project.tasks.getByName( 'npmSetup' ) 28 | } 29 | 30 | def 'check repository and dependencies (no download)'() 31 | { 32 | when: 33 | this.project.apply plugin: 'com.moowork.node' 34 | this.project.evaluate() 35 | 36 | then: 37 | this.project.repositories.size() == 0 38 | !this.project.configurations.contains( 'nodeDist' ) 39 | } 40 | 41 | def 'check npm rule task'() 42 | { 43 | when: 44 | this.project.apply plugin: 'com.moowork.node' 45 | this.project.evaluate() 46 | 47 | then: 48 | this.project.tasks.getRules().size() == 2 49 | def npmRule = this.project.tasks.getRules().get( 0 ) 50 | def yarnRule = this.project.tasks.getRules().get( 1 ) 51 | 52 | !this.project.tasks.getAsMap().containsKey( 'npm_install' ) 53 | npmRule.apply( 'npm_install' ) 54 | this.project.tasks.getAsMap().containsKey( 'npm_install' ) 55 | 56 | !this.project.tasks.getAsMap().containsKey( 'yarn_upgrade' ) 57 | yarnRule.apply( 'yarn_upgrade' ) 58 | this.project.tasks.getAsMap().containsKey( 'yarn_upgrade' ) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/node/npm/NpmSetupTaskTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.npm 2 | 3 | import com.moowork.gradle.node.npm.NpmSetupTask 4 | import com.moowork.gradle.node.task.AbstractTaskTest 5 | import org.gradle.process.ExecSpec 6 | 7 | class NpmSetupTaskTest 8 | extends AbstractTaskTest 9 | { 10 | def "exec npmSetup task"() 11 | { 12 | given: 13 | this.props.setProperty( 'os.name', 'Linux' ) 14 | this.execSpec = Mock( ExecSpec ) 15 | 16 | def task = this.project.tasks.create( 'simple', NpmSetupTask ) 17 | 18 | when: 19 | this.project.evaluate() 20 | task.exec() 21 | 22 | then: 23 | task.result.exitValue == 0 24 | 1 * this.execSpec.setArgs( [] ) 25 | } 26 | 27 | def "exec npmSetup task (version specified)"() 28 | { 29 | given: 30 | this.props.setProperty( 'os.name', 'Linux' ) 31 | this.ext.npmVersion = '2.1.6' 32 | this.execSpec = Mock( ExecSpec ) 33 | 34 | this.execSpec = Mock( ExecSpec ) 35 | def task = this.project.tasks.create( 'simple', NpmSetupTask ) 36 | 37 | when: 38 | this.project.evaluate() 39 | task.exec() 40 | 41 | then: 42 | task.result.exitValue == 0 43 | 1 * this.execSpec.setArgs( [] ) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/node/npm/NpmTaskTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.npm 2 | 3 | import com.moowork.gradle.node.npm.NpmTask 4 | import com.moowork.gradle.node.task.AbstractTaskTest 5 | import org.gradle.process.ExecSpec 6 | 7 | class NpmTaskTest 8 | extends AbstractTaskTest 9 | { 10 | def "exec npm task"() 11 | { 12 | given: 13 | this.props.setProperty( 'os.name', 'Linux' ) 14 | this.execSpec = Mock( ExecSpec ) 15 | 16 | def task = this.project.tasks.create( 'simple', NpmTask ) 17 | task.args = ['a', 'b'] 18 | task.environment = ['a': '1'] 19 | task.ignoreExitValue = true 20 | task.workingDir = this.projectDir 21 | task.execOverrides = {} 22 | 23 | when: 24 | this.project.evaluate() 25 | task.exec() 26 | 27 | then: 28 | task.args == ['a', 'b'] 29 | task.result.exitValue == 0 30 | 1 * this.execSpec.setIgnoreExitValue( true ) 31 | 1 * this.execSpec.setEnvironment( { it['a'] == '1' && containsPath(it) } ) 32 | 1 * this.execSpec.setExecutable( 'npm' ) 33 | 1 * this.execSpec.setArgs( ['a', 'b'] ) 34 | } 35 | 36 | def "exec npm task (windows)"() 37 | { 38 | given: 39 | this.props.setProperty( 'os.name', 'Windows' ) 40 | this.execSpec = Mock( ExecSpec ) 41 | 42 | def task = this.project.tasks.create( 'simple', NpmTask ) 43 | task.args = ['a', 'b'] 44 | task.environment = ['a': '1'] 45 | task.ignoreExitValue = true 46 | task.workingDir = this.projectDir 47 | task.execOverrides = {} 48 | 49 | when: 50 | this.project.evaluate() 51 | task.exec() 52 | 53 | then: 54 | task.args == ['a', 'b'] 55 | task.result.exitValue == 0 56 | 1 * this.execSpec.setIgnoreExitValue( true ) 57 | 1 * this.execSpec.setEnvironment( { it['a'] == '1' && containsPath( it ) } ) 58 | 1 * this.execSpec.setExecutable( 'npm.cmd' ) 59 | 1 * this.execSpec.setArgs( ['a', 'b'] ) 60 | } 61 | 62 | def "exec npm task (download)"() 63 | { 64 | given: 65 | this.props.setProperty( 'os.name', 'Linux' ) 66 | this.ext.download = true 67 | this.execSpec = Mock( ExecSpec ) 68 | 69 | def task = this.project.tasks.create( 'simple', NpmTask ) 70 | 71 | when: 72 | this.project.evaluate() 73 | task.exec() 74 | 75 | then: 76 | task.result.exitValue == 0 77 | 1 * this.execSpec.setIgnoreExitValue( false ) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/node/task/AbstractTaskTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.task 2 | 3 | import com.moowork.gradle.AbstractProjectTest 4 | import com.moowork.gradle.node.NodeExtension 5 | import com.moowork.gradle.node.util.PlatformHelper 6 | import org.gradle.process.ExecResult 7 | import org.gradle.process.ExecSpec 8 | 9 | abstract class AbstractTaskTest 10 | extends AbstractProjectTest 11 | { 12 | def ExecResult execResult 13 | 14 | def ExecSpec execSpec 15 | 16 | def Properties props 17 | 18 | def NodeExtension ext 19 | 20 | def setup() 21 | { 22 | this.props = new Properties() 23 | PlatformHelper.INSTANCE = new PlatformHelper( this.props ) 24 | 25 | this.execResult = Mock( ExecResult ) 26 | 27 | this.project.apply plugin: 'com.moowork.node' 28 | this.ext = NodeExtension.get( this.project ) 29 | 30 | mockExec() 31 | } 32 | 33 | private void mockExec() 34 | { 35 | this.project.metaClass.invokeMethod = { String name, Object[] args -> 36 | if ( name == 'exec' ) 37 | { 38 | Closure closure = (Closure) args.first() 39 | closure.call( this.execSpec ) 40 | return this.execResult 41 | } 42 | else 43 | { 44 | MetaMethod metaMethod = delegate.class.metaClass.getMetaMethod( name, args ) 45 | return metaMethod?.invoke( delegate, args ) 46 | } 47 | } 48 | } 49 | 50 | protected containsPath( final Map env ) { 51 | return env['PATH'] != null || env['Path'] != null; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/node/task/NodeTaskTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.task 2 | 3 | import org.gradle.process.ExecSpec 4 | 5 | class NodeTaskTest 6 | extends AbstractTaskTest 7 | { 8 | def "script not set"() 9 | { 10 | given: 11 | def task = this.project.tasks.create( 'simple', NodeTask ) 12 | 13 | when: 14 | this.project.evaluate() 15 | task.exec() 16 | 17 | then: 18 | thrown( IllegalStateException ) 19 | } 20 | 21 | def "exec node task"() 22 | { 23 | given: 24 | this.props.setProperty( 'os.name', 'Linux' ) 25 | this.execSpec = Mock( ExecSpec ) 26 | this.ext.download = false 27 | 28 | def task = this.project.tasks.create( 'simple', NodeTask ) 29 | task.args = ['a', 'b'] 30 | task.options = ['c', 'd'] 31 | task.environment = ['a': '1'] 32 | task.ignoreExitValue = true 33 | 34 | def script = new File( this.projectDir, 'script.js' ) 35 | task.script = script 36 | task.workingDir = this.projectDir 37 | task.execOverrides = {} 38 | 39 | when: 40 | this.project.evaluate() 41 | task.exec() 42 | 43 | then: 44 | task.args == ['a', 'b'] 45 | task.options == ['c', 'd'] 46 | task.result.exitValue == 0 47 | 1 * this.execSpec.setIgnoreExitValue( true ) 48 | 1 * this.execSpec.setEnvironment( { it['a'] == '1' && containsPath( it ) } ) 49 | 1 * this.execSpec.setExecutable( 'node' ) 50 | 1 * this.execSpec.setArgs( ['c', 'd', script.absolutePath, 'a', 'b'] ) 51 | } 52 | 53 | def "exec node task (download)"() 54 | { 55 | given: 56 | this.props.setProperty( 'os.name', 'Linux' ) 57 | this.ext.download = true 58 | this.execSpec = Mock( ExecSpec ) 59 | 60 | def task = this.project.tasks.create( 'simple', NodeTask ) 61 | def script = new File( this.projectDir, 'script.js' ) 62 | task.script = script 63 | 64 | when: 65 | this.project.evaluate() 66 | task.exec() 67 | 68 | then: 69 | task.result.exitValue == 0 70 | 1 * this.execSpec.setIgnoreExitValue( false ) 71 | 1 * this.execSpec.setEnvironment( { containsPath( it ) } ) 72 | 1 * this.execSpec.setArgs( [script.absolutePath] ) 73 | } 74 | 75 | def "exec node task (windows)"() 76 | { 77 | given: 78 | this.props.setProperty( 'os.name', 'Windows' ) 79 | this.ext.download = false 80 | this.execSpec = Mock( ExecSpec ) 81 | 82 | def task = this.project.tasks.create( 'simple', NodeTask ) 83 | def script = new File( this.projectDir, 'script.js' ) 84 | 85 | task.args = ['a', 'b'] 86 | task.options = ['c', 'd'] 87 | task.script = script 88 | 89 | when: 90 | this.project.evaluate() 91 | task.exec() 92 | 93 | then: 94 | task.result.exitValue == 0 95 | 1 * this.execSpec.setIgnoreExitValue( false ) 96 | 1 * this.execSpec.setEnvironment( { containsPath( it ) } ) 97 | 1 * this.execSpec.setExecutable( 'node' ) 98 | 1 * this.execSpec.setArgs( ['c', 'd', script.absolutePath, 'a', 'b'] ) 99 | } 100 | 101 | def "exec node task (windows download)"() 102 | { 103 | given: 104 | this.props.setProperty( 'os.name', 'Windows' ) 105 | this.ext.download = true 106 | this.execSpec = Mock( ExecSpec ) 107 | 108 | def task = this.project.tasks.create( 'simple', NodeTask ) 109 | def script = new File( this.projectDir, 'script.js' ) 110 | task.script = script 111 | 112 | when: 113 | this.project.evaluate() 114 | task.exec() 115 | 116 | then: 117 | task.result.exitValue == 0 118 | 1 * this.execSpec.setEnvironment( { containsPath( it ) } ) 119 | 1 * this.execSpec.setIgnoreExitValue( false ) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/node/util/PlatformHelperTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.util 2 | 3 | import spock.lang.Specification 4 | import spock.lang.Unroll 5 | 6 | class PlatformHelperTest 7 | extends Specification 8 | { 9 | private Properties props 10 | 11 | private PlatformHelper helper 12 | 13 | def setup() 14 | { 15 | this.props = new Properties() 16 | PlatformHelper.INSTANCE = this.helper = new PlatformHelper( this.props ) 17 | } 18 | 19 | @Unroll 20 | def "check os and architecture for #osProp (#archProp)"() 21 | { 22 | given: 23 | this.props.setProperty( "os.name", osProp ) 24 | this.props.setProperty( "os.arch", archProp ) 25 | 26 | expect: 27 | this.helper.getOsName() == osName 28 | this.helper.getOsArch() == osArch 29 | this.helper.isWindows() == isWindows 30 | 31 | where: 32 | osProp | archProp | osName | osArch | isWindows 33 | 'Windows 8' | 'x86' | 'win' | 'x86' | true 34 | 'Windows 8' | 'x86_64' | 'win' | 'x64' | true 35 | 'Mac OS X' | 'x86' | 'darwin' | 'x86' | false 36 | 'Mac OS X' | 'x86_64' | 'darwin' | 'x64' | false 37 | 'Linux' | 'x86' | 'linux' | 'x86' | false 38 | 'Linux' | 'x86_64' | 'linux' | 'x64' | false 39 | 'SunOS' | 'x86' | 'sunos' | 'x86' | false 40 | 'SunOS' | 'x86_64' | 'sunos' | 'x64' | false 41 | } 42 | 43 | def "throw exception if unsupported os"() 44 | { 45 | given: 46 | this.props.setProperty( "os.name", 'Nonsense' ) 47 | 48 | when: 49 | this.helper.getOsName() 50 | 51 | then: 52 | thrown( IllegalArgumentException ) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/node/variant/VariantBuilderTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.variant 2 | 3 | import com.moowork.gradle.node.NodeExtension 4 | import com.moowork.gradle.node.util.PlatformHelper 5 | import org.gradle.testfixtures.ProjectBuilder 6 | import spock.lang.Specification 7 | import spock.lang.Unroll 8 | 9 | class VariantBuilderTest 10 | extends Specification 11 | { 12 | /* OS dependant line separator */ 13 | 14 | static final String PS = File.separator 15 | 16 | /* Relative base path for nodejs installation */ 17 | 18 | static final String NODE_BASE_PATH = "${PS}.gradle${PS}node${PS}" 19 | 20 | private Properties props 21 | 22 | def setup() 23 | { 24 | this.props = new Properties() 25 | PlatformHelper.INSTANCE = new PlatformHelper(this.props) 26 | } 27 | 28 | @Unroll 29 | def "test variant on windows version <4 (#osArch)"() 30 | { 31 | given: 32 | def project = ProjectBuilder.builder().build() 33 | 34 | this.props.setProperty("os.name", "Windows 8") 35 | this.props.setProperty("os.arch", osArch) 36 | 37 | def ext = new NodeExtension(project) 38 | ext.download = true 39 | ext.version = '0.11.1' 40 | ext.workDir = new File('.gradle/node').absoluteFile 41 | 42 | def builder = new VariantBuilder(ext) 43 | def variant = builder.build() 44 | 45 | expect: 46 | variant != null 47 | variant.windows 48 | variant.exeDependency == exeDependency 49 | variant.archiveDependency == 'org.nodejs:node:0.11.1:linux-x86@tar.gz' 50 | 51 | variant.nodeDir.toString().endsWith(NODE_BASE_PATH + nodeDir) 52 | variant.nodeBinDir.toString().endsWith(NODE_BASE_PATH + nodeDir) 53 | variant.nodeExec.toString().endsWith(NODE_BASE_PATH + nodeDir + PS + "node.exe") 54 | variant.npmScriptFile.toString().endsWith(NODE_BASE_PATH + nodeDir + PS + "node_modules${PS}npm${PS}bin${PS}npm-cli.js") 55 | 56 | where: 57 | osArch | nodeDir | exeDependency 58 | 'x86' | 'node-v0.11.1-win-x86' | 'org.nodejs:node:0.11.1@exe' 59 | 'x86_64' | 'node-v0.11.1-win-x64' | 'org.nodejs:x64/node:0.11.1@exe' 60 | } 61 | 62 | @Unroll 63 | def "test variant on windows version 4.+ with exe (#osArch)"() 64 | { 65 | given: 66 | def project = ProjectBuilder.builder().build() 67 | 68 | this.props.setProperty("os.name", "Windows 8") 69 | this.props.setProperty("os.arch", osArch) 70 | 71 | def ext = new NodeExtension(project) 72 | ext.download = true 73 | ext.version = '4.0.0' 74 | ext.workDir = new File('.gradle/node').absoluteFile 75 | 76 | def builder = new VariantBuilder(ext) 77 | def variant = builder.build() 78 | 79 | expect: 80 | variant != null 81 | variant.windows 82 | variant.exeDependency == exeDependency 83 | variant.archiveDependency == 'org.nodejs:node:4.0.0:linux-x86@tar.gz' 84 | 85 | variant.nodeDir.toString().endsWith(NODE_BASE_PATH + nodeDir) 86 | variant.nodeBinDir.toString().endsWith(NODE_BASE_PATH + nodeDir) 87 | variant.nodeExec.toString().endsWith(NODE_BASE_PATH + nodeDir + PS + "node.exe") 88 | variant.npmScriptFile.toString().endsWith(NODE_BASE_PATH + nodeDir + PS + "node_modules${PS}npm${PS}bin${PS}npm-cli.js") 89 | 90 | where: 91 | osArch | nodeDir | exeDependency 92 | 'x86' | 'node-v4.0.0-win-x86' | 'org.nodejs:win-x86/node:4.0.0@exe' 93 | 'x86_64' | 'node-v4.0.0-win-x64' | 'org.nodejs:win-x64/node:4.0.0@exe' 94 | } 95 | 96 | @Unroll 97 | def "test variant on windows without exe (#osArch)"() 98 | { 99 | given: 100 | def project = ProjectBuilder.builder().build() 101 | 102 | this.props.setProperty("os.name", "Windows 8") 103 | this.props.setProperty("os.arch", osArch) 104 | 105 | def ext = new NodeExtension(project) 106 | ext.download = true 107 | ext.version = version 108 | ext.workDir = new File('.gradle/node').absoluteFile 109 | 110 | def builder = new VariantBuilder(ext) 111 | def variant = builder.build() 112 | def nodeDir = "node-v${version}-${osArch}".toString() 113 | def depName = "org.nodejs:node:${version}:${osArch}@zip".toString() 114 | 115 | expect: 116 | variant != null 117 | variant.windows 118 | variant.exeDependency == null 119 | variant.archiveDependency == depName 120 | 121 | variant.nodeDir.toString().endsWith(NODE_BASE_PATH + nodeDir) 122 | variant.nodeBinDir.toString().endsWith(NODE_BASE_PATH + nodeDir) 123 | variant.nodeExec.toString().endsWith(NODE_BASE_PATH + nodeDir + PS + "node.exe") 124 | variant.npmScriptFile.toString().endsWith(NODE_BASE_PATH + nodeDir + PS + "node_modules${PS}npm${PS}bin${PS}npm-cli.js") 125 | where: 126 | version | osArch 127 | "4.5.0" | "win-x86" 128 | "6.2.1" | "win-x86" 129 | "7.0.0" | "win-x86" 130 | "4.5.0" | "win-x64" 131 | "6.2.1" | "win-x64" 132 | "7.0.0" | "win-x64" 133 | } 134 | 135 | @Unroll 136 | def "test variant on non-windows (#osName, #osArch)"() 137 | { 138 | given: 139 | this.props.setProperty("os.name", osName) 140 | this.props.setProperty("os.arch", osArch) 141 | 142 | def project = ProjectBuilder.builder().build() 143 | def ext = new NodeExtension(project) 144 | ext.download = true 145 | ext.version = '0.11.1' 146 | ext.workDir = new File('.gradle/node').absoluteFile 147 | 148 | def builder = new VariantBuilder(ext) 149 | def variant = builder.build() 150 | 151 | expect: 152 | variant != null 153 | !variant.windows 154 | variant.exeDependency == null 155 | variant.archiveDependency == depName 156 | 157 | variant.nodeDir.toString().endsWith(NODE_BASE_PATH + nodeDir) 158 | variant.nodeBinDir.toString().endsWith(NODE_BASE_PATH + nodeDir + PS + 'bin') 159 | variant.nodeExec.toString().endsWith(NODE_BASE_PATH + nodeDir + PS + "bin${PS}node") 160 | variant.npmScriptFile.toString().endsWith(NODE_BASE_PATH + nodeDir + PS + "lib${PS}node_modules${PS}npm${PS}bin${PS}npm-cli.js") 161 | 162 | where: 163 | osName | osArch | nodeDir | depName 164 | 'Linux' | 'x86' | 'node-v0.11.1-linux-x86' | 'org.nodejs:node:0.11.1:linux-x86@tar.gz' 165 | 'Linux' | 'x86_64' | 'node-v0.11.1-linux-x64' | 'org.nodejs:node:0.11.1:linux-x64@tar.gz' 166 | 'Mac OS X' | 'x86' | 'node-v0.11.1-darwin-x86' | 'org.nodejs:node:0.11.1:darwin-x86@tar.gz' 167 | 'Mac OS X' | 'x86_64' | 'node-v0.11.1-darwin-x64' | 'org.nodejs:node:0.11.1:darwin-x64@tar.gz' 168 | 'FreeBSD' | 'x86' | 'node-v0.11.1-linux-x86' | 'org.nodejs:node:0.11.1:linux-x86@tar.gz' 169 | 'FreeBSD' | 'x86_64' | 'node-v0.11.1-linux-x64' | 'org.nodejs:node:0.11.1:linux-x64@tar.gz' 170 | 'SunOS' | 'x86' | 'node-v0.11.1-sunos-x86' | 'org.nodejs:node:0.11.1:sunos-x86@tar.gz' 171 | 'SunOS' | 'x86_64' | 'node-v0.11.1-sunos-x64' | 'org.nodejs:node:0.11.1:sunos-x64@tar.gz' 172 | } 173 | 174 | @Unroll 175 | def "test variant on ARM (#osName, #osArch, #sysOsArch)"() 176 | { 177 | given: 178 | this.props.setProperty("os.name", osName) 179 | this.props.setProperty("os.arch", osArch) 180 | 181 | def project = ProjectBuilder.builder().build() 182 | def ext = new NodeExtension(project) 183 | ext.download = true 184 | ext.version = '5.6.0' 185 | ext.workDir = new File('.gradle/node').absoluteFile 186 | 187 | def platformHelperSpy = Spy(PlatformHelper, constructorArgs: [this.props]) 188 | platformHelperSpy.osArch >> { sysOsArch } 189 | def builder = new VariantBuilder(ext, platformHelperSpy) 190 | def variant = builder.build() 191 | 192 | expect: 193 | variant != null 194 | !variant.windows 195 | variant.exeDependency == null 196 | variant.archiveDependency == depName 197 | 198 | variant.nodeDir.toString().endsWith(NODE_BASE_PATH + nodeDir) 199 | variant.nodeBinDir.toString().endsWith(NODE_BASE_PATH + nodeDir + PS + 'bin') 200 | variant.nodeExec.toString().endsWith(NODE_BASE_PATH + nodeDir + PS + "bin${PS}node") 201 | variant.npmScriptFile.toString().endsWith(NODE_BASE_PATH + nodeDir + PS + "lib${PS}node_modules${PS}npm${PS}bin${PS}npm-cli.js") 202 | 203 | where: 204 | osName | osArch | sysOsArch | nodeDir | depName 205 | 'Linux' | 'arm' | 'armv6l' | 'node-v5.6.0-linux-armv6l' | 'org.nodejs:node:5.6.0:linux-armv6l@tar.gz' 206 | 'Linux' | 'arm' | 'armv7l' | 'node-v5.6.0-linux-armv7l' | 'org.nodejs:node:5.6.0:linux-armv7l@tar.gz' 207 | 'Linux' | 'arm' | 'arm64' | 'node-v5.6.0-linux-arm64' | 'org.nodejs:node:5.6.0:linux-arm64@tar.gz' 208 | } 209 | 210 | @Unroll 211 | def "test npm paths on windows"() 212 | { 213 | given: 214 | this.props.setProperty("os.name", "Windows 8") 215 | this.props.setProperty("os.arch", "x86") 216 | def project = ProjectBuilder.builder().build() 217 | 218 | def ext = new NodeExtension(project) 219 | ext.download = download 220 | ext.npmVersion = npmVersion 221 | 222 | def builder = new VariantBuilder(ext) 223 | def variant = builder.build() 224 | 225 | def npmDir = variant.nodeDir 226 | def npm = ext.npmCommand + ".cmd" 227 | 228 | if (npmVersion != "") { 229 | npmDir = new File(ext.npmWorkDir, "npm-v${npmVersion}".toString()) 230 | } 231 | 232 | if (download) { 233 | npm = new File(npmDir, npm).toString() 234 | } 235 | expect: 236 | variant.npmDir == npmDir 237 | variant.npmBinDir == npmDir 238 | variant.npmExec == npm 239 | 240 | // if no version use node paths 241 | npmVersion != "" || variant.npmDir == variant.nodeDir 242 | npmVersion != "" || variant.npmBinDir == variant.nodeBinDir 243 | 244 | where: 245 | download | npmVersion 246 | true | "4.0.2" 247 | true | "" 248 | false | "4.0.2" 249 | false | "" 250 | } 251 | 252 | @Unroll 253 | def "test npm paths on non-windows"() 254 | { 255 | given: 256 | this.props.setProperty("os.name", "Linux") 257 | this.props.setProperty("os.arch", "x86") 258 | def project = ProjectBuilder.builder().build() 259 | 260 | def ext = new NodeExtension(project) 261 | ext.download = download 262 | ext.npmVersion = npmVersion 263 | 264 | def builder = new VariantBuilder(ext) 265 | def variant = builder.build() 266 | 267 | def npmDir = variant.nodeDir 268 | def npm = ext.npmCommand 269 | 270 | if (npmVersion != "") { 271 | npmDir = new File(ext.npmWorkDir, "npm-v${npmVersion}".toString()) 272 | } 273 | 274 | def npmBinDir = new File(npmDir, "bin") 275 | 276 | if (download) { 277 | npm = new File(npmBinDir, npm).toString() 278 | } 279 | expect: 280 | variant.npmDir == npmDir 281 | variant.npmBinDir == npmBinDir 282 | variant.npmExec == npm 283 | 284 | // if no version use node paths 285 | npmVersion != "" || variant.npmDir == variant.nodeDir 286 | npmVersion != "" || variant.npmBinDir == variant.nodeBinDir 287 | 288 | where: 289 | download | npmVersion 290 | true | "4.0.2" 291 | true | "" 292 | false | "4.0.2" 293 | false | "" 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/node/yarn/YarnSetupTaskTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.yarn 2 | 3 | import com.moowork.gradle.node.task.AbstractTaskTest 4 | import com.moowork.gradle.node.yarn.YarnSetupTask 5 | import org.gradle.process.ExecSpec 6 | 7 | class YarnSetupTaskTest 8 | extends AbstractTaskTest 9 | { 10 | def "exec yarnSetup task"() 11 | { 12 | given: 13 | this.execSpec = Mock( ExecSpec ) 14 | 15 | def task = this.project.tasks.create( 'simple', YarnSetupTask ) 16 | 17 | when: 18 | this.project.evaluate() 19 | task.exec() 20 | 21 | then: 22 | task.result.exitValue == 0 23 | 1 * this.execSpec.setArgs( [] ) 24 | } 25 | 26 | def "exec yarnSetup task (version specified)"() 27 | { 28 | given: 29 | this.ext.npmVersion = '0.15.1' 30 | this.execSpec = Mock( ExecSpec ) 31 | 32 | def task = this.project.tasks.create( 'simple', YarnSetupTask ) 33 | 34 | when: 35 | this.project.evaluate() 36 | task.exec() 37 | 38 | then: 39 | task.result.exitValue == 0 40 | 1 * this.execSpec.setArgs( [] ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/groovy/com/moowork/gradle/node/yarn/YarnTaskTest.groovy: -------------------------------------------------------------------------------- 1 | package com.moowork.gradle.node.yarn 2 | 3 | import com.moowork.gradle.node.task.AbstractTaskTest 4 | import com.moowork.gradle.node.yarn.YarnTask 5 | import org.gradle.process.ExecSpec 6 | 7 | class YarnTaskTest 8 | extends AbstractTaskTest 9 | { 10 | def "exec yarn task"() 11 | { 12 | given: 13 | this.execSpec = Mock( ExecSpec ) 14 | 15 | def task = this.project.tasks.create( 'simple', YarnTask ) 16 | task.args = ['a', 'b'] 17 | task.environment = ['a': '1'] 18 | task.ignoreExitValue = true 19 | task.workingDir = this.projectDir 20 | task.execOverrides = {} 21 | 22 | when: 23 | this.project.evaluate() 24 | task.exec() 25 | 26 | then: 27 | task.args == ['a', 'b'] 28 | task.result.exitValue == 0 29 | 1 * this.execSpec.setIgnoreExitValue( true ) 30 | 1 * this.execSpec.setEnvironment( { it['a'] == '1' && containsPath(it) } ) 31 | 1 * this.execSpec.setArgs( ['a', 'b'] ) 32 | } 33 | 34 | def "exec yarn task (download)"() 35 | { 36 | given: 37 | this.ext.download = true 38 | this.execSpec = Mock( ExecSpec ) 39 | 40 | def task = this.project.tasks.create( 'simple', YarnTask ) 41 | 42 | when: 43 | this.project.evaluate() 44 | task.exec() 45 | 46 | then: 47 | task.result.exitValue == 0 48 | 1 * this.execSpec.setIgnoreExitValue( false ) 49 | } 50 | } 51 | --------------------------------------------------------------------------------