Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

npm install if package.json was modified

TL;DR: Is there a way to have npm install run automatically before running any npm script if your package.json has been modified?

Problem Scenario

You pull or checkout a branch that updated package.json. You run npm run my-script. my-script depends on a package that has newly been added to package.json. my-script fails. You wonder why. Before flipping over your desk you run npm install just to be sure. my-script runs successfully. You don't need a new desk.

I know that build / task runner tools like gradle make sure that your dependencies are up-to-date before running a task. I has always been a (minor) pain point that npm doesn't do it. I stumbled over two solutions that I don't particluarly like.

Non-Ideal Solution: make

Instead of relying on npm scripts in your package.json to run commands you use make and make use of its integrated dependency tracking with the following trick:

# Smart install: Only executes if package.json's # modification date is later than node_module's  node_modules: package.json     npm install     @rm -f node_modules/.modified     @touch -m node_modules/.modified  install: node_modules  

Source: https://mattandre.ws/2016/05/make-for-hipsters/

The problem is that you know have to rely on make to run scripts and lose certain advantages of npm scripts such as conveniently referring to other scripts and running scripts in parallel (npm-run-all). It's also harder to work with others if they don't know make or have problems running it (Windows). It's an archaic tool outside of the node/npm ecosystem and too costly just for this smart install advantage.

Non-Ideal Solution: Git hook

Another way is to add a post-merge git hook.

The problem is that this solution is local to the repository and can't be easily shared. npm install will only be run automatically on git merges. When you change package.json in any other way you still have to remember running npm install. Admittedly, that's a minor point in practice. Nonetheless, it would be nice to never have to think about running npm install at all when you want to run a script.

Source: https://davidwalsh.name/git-hook-npm-install-package-json-modified

Ideal Solution

I'd like to define my package.json in a way similar to:

{   "scripts": {     "pre-run": "npm-smart-install",     "my-script": "…"   },   "dependencies": {     "npm-smart-install": "1.0.0"   } } 

npm-smart-install is a hypothetical npm package that I wish existed. pre-run is a hypothetical npm-scripts lifecycle hook. When I run npm run my-script and package.json has been modified since the last run of any script, run npm install before running my-script.

To repeat: Is there a way to have npm install run automatically before running any npm script if your package.json has been modified without relying on tools outside the npm ecosystem?

like image 297
Eugen Avatar asked Sep 23 '18 13:09

Eugen


People also ask

Does npm install modify package json?

npm ci will install packages based on package-lock. json file and if the file does not exist or does not match the packages specified in the package. json it will throw an error and fail.

Does npm install automatically add to json?

I found that npm init had automatically added dependencies based on installed packages and that there was no need to run the second command.

Does npm install install everything in Package json?

By default, npm install will install all modules listed as dependencies in package. json .

Can you manually edit package json?

You can add dependencies to a package. json file from the command line or by manually editing the package.


1 Answers

Okay so I'm done with the package. Here it is. You can use it exactly the same way you specified in your ideal scenario. Just npm install install-changed and add it to a custom script, like pre-run in your example. It should figure out whether or not it needs to npm install and does so if it needs to.

 {   "scripts": {     "pre-run": "install-changed",     "my-script": "…"   }, 

You can also do this programatically but I don't think you're going to need this.

let installChanged = require('install-changed')  let isModified = installChanged.watchPackage()  

The function above does exactly the same thing, additonally it also returns a boolean value which you might find useful.

like image 71
ninesalt Avatar answered Sep 28 '22 01:09

ninesalt