Using Self Contained Node.js and npm instances with Gradle
Recently I was given the task to get a new project started from ground zero. This is the day I always dreaded since jumping into an existing project is easy but starting from scratch involves a lot of time just setting up the project scaffolding and creating a lot of configuration items. Oh and one more thing was tossed in: the build server doesn’t have node.js or npm (Node Package Manager) installed but we really wanted to use it. At this point I was thinking this is a not-so-fun situation but after researching how to do this, I found out not only is it possible but it actually makes sense to have your node.js and npm version defined in the build script so that the developers and build server are all using the same build tool versions. Not just that, but some places still have environments so locked down that developers still aren’t able to install applications. There are workarounds of course to this but a universal solution is talked about here.
For this example, I’m going to show you how to get node.js and npm installed via gradle. In a post coming up later, I’ll explain how I got react working with webpack to transcompile all my scripts for me, all with using gradle only and not needing to have node.js or npm globally installed. Let’s start with a simple Spring Boot application with a few modifications:
A few things of note here:
- We’re using the gradle-node-plugin
- Jcenter is added to the build repositories (not hosted on maven central)
- There’s a node section defining the version numbers we want
To get node and npm downloaded, just run:
gradlew npmInstall
after the gradle items are setup and that’s it. That’s great and all but I haven’t given you enough to actually use it. Common tasks for npm are normally to install and delete dependencies. Let’s start with an empty package.json file in the root of our project:
If npm were installed, we could something like “npm install –save angular
” or something similar. We can do this as well but we’d have to change to the node_modules/.bin directory and run the command. Now there are plenty of ways to solve this problem and this shouldn’t be taken gospel if you have a better idea. I tried really hard to get this to work via gradle but I ran into a lot of issues. One of the issues is that if you have bootRun running, you can’t run any gradle commands since it locks a gradle file. Not a huge deal but once I got it working, I found out it worked great by itself but then always ran before bootRun would start and then would fail since I added error checking which threw an error if the task wasn’t passed in the dependency parameter. Bummer. The gradle idea wasn’t too good though since gradle doesn’t handle command line parameters that well. I was running commands like “gradle npmInstallSave -Pdep=angular” and I would need to create a task for every common task or create longer command line arguments for a generic task. The -P arguments don’t exactly role off the fingers at all. The other solutions for this are to change to the directory or write a script. I went the script route and committed these to the root of the project.
Here are the super complex scripts to get this working (be kind and commit both):
Windows – npm.cmd
*nix – npm
Now I can run npm from the root of the project and use it as if the command was installed system wide. These simple scripts pass all the parameters you give it to the actual npm program. You can test it by running “npm install -save angular
" and you should see the custom banner at top and also the package.json file should be updated. Full working example is available here. Check back later for another example showing how to use Webpack, React, and gradle together to compile and package javascript files with gradle.
Does it really make sense to have node_modules on the project home dir instead of putting it inside src/main/resources ?
everything generated by npm will be used/accessed as resources within a running application!
Im trying to put packages.json inside the resources folder and build everything there.. but im failing to get it right
Alla, nothing from node_modules will be accessed via a running application. Webpack will figure out what your application needs by you defining entry points and it then digging through your source and dependencies to see what it needs to include inside the bundle. This bundle has everything it needs in one giant file or multiple files if you set it up that way. You can then debug the code by using source maps (turned on in this example) for browsers to reconstruct the original source and allow you to put break points in as if you were using the original source files.
do you know how to integrate a custom .npmrc file into the build for gradle’s npm instance to use? -TIA