npm install if package.json was modified

做~自己de王妃 提交于 2020-12-24 04:07:50

问题


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?


回答1:


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.




回答2:


You can create a custom script that will run your smart install.

smart-install.sh file

#!/usr/bin/env bash

changedFiles="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)"

checkForChangedFiles() {
    echo "$changedFiles" | grep --quiet "$1" && eval "$2"
}

packageJsonHasChanged() {
  echo "Changes to package.json detected, installing updates"
  npm i
}

checkForChangedFiles package.json packageJsonHasChanged

Then if you have husky you can add that to the post-checkout hook or any hook you like. If you don't have husky, you can also add it directly to the scripts which essentially do the same thing.

.huskyrc file

{
  "hooks": {
    "post-checkout": "npm run smart-install"
  }
}

package.json file

"scripts": {
  ...
  "smart-install": "bash ./bin/smart-install.sh",
}

Either way it's a good idea to create a npm script to run smart-install




回答3:


Like other answers, but I think simpler because it's one line of shell script in package.json:

{
    "scripts": {
        "install-if-needed": "[ package.json -nt node_modules ] && npm install && touch node_modules",
        "my-script": "npm run install-if-needed && ..."
    }
}

or, basically equivalent:

{
    "scripts": {
        "install-if-needed": "[ package.json -nt node_modules ] && npm install && touch node_modules",
        "premy-script": "npm run install-if-needed",
        "my-script": "..."
    }
}

You'll have to either inline npm run install-if-needed or have a pre... script for each script that needs it -- I don't know any other way to have it run before multiple other scripts.

Explanation: install-if-needed checks the modification times on package.json and node_modules. If node_modules is newer, it does nothing; otherwise it runs npm install. The final touch node_modules is necessary because npm install may itself change the package.json modification time (if it is correcting whitespace in package.json for example).



来源:https://stackoverflow.com/questions/52466740/npm-install-if-package-json-was-modified

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!