My DEV posts

How to force package installs to use yarn not npm

January 18, 2019

Leaving aside arguments over which node package manager is better, one thing that should be clear is that it’s a bad idea to switch between them in the same project, as conflicting lockfiles can leave the project in an unpredictable state. If you run yarn in a project that has a package-lock.json from npm then it does warn you about this, but npm doesn’t do the reverse. If you run npm install in a project with a yarn.lock it will happily install the packages and create a package-lock.json for yarn to complain about next time. This is my solution.

If you add a "preinstall" script to your package.json it is run before any install by npm or yarn. If it exits with an error code then the install is aborted. This means it’s the perfect place to check. During script execution, the environment variable $npm_execpath is set to the path of the npm or yarn executable, so we can use this to check what the install is using. You could use node to check this, but that seems like overkill, so I’m going to use shell commands directly inside the script. I echo the contents of the variable, pipe it to grep, and if it doesn’t match yarn then it exits with an error. (I ❤️ emojis in shell scripts):

echo "$npm_execpath" | grep -q "yarn\.js$" || (echo '⚠️ Use yarn not npm! ⚠️' && exit 1)

This works great, but it can look confusing as it displays the full command when it’s run, so looks like there’s an error even when all is well. The way around this is to make it a separate script, which is run with the --quiet flag. We can use $npm_execpath again to be sure we’re running it with the same script:

"scripts": {
        "preinstall": "$npm_execpath --silent run checkyarn",
        "checkyarn": "echo \"$npm_execpath\" | grep -q \"yarn\\.js$\" || (echo '⚠️  Use yarn not npm! ⚠️ ' && echo && exit 1)"
}

If you run npm install you get the error:

example git:(master) ✗ npm i

> example@0.0.1 preinstall /Users/matt/Documents/repos/example
> $npm_execpath --silent run checkyarn

⚠️  Use yarn not npm! ⚠️

npm ERR! code ELIFECYCLE
errno 1

…but if you run yarn:

➜  example git:(master) ✗ yarn
yarn install v1.12.3
$ $npm_execpath --silent run checkyarn
[1/4] 🔍  Resolving packages...
success Already up-to-date.
✨  Done in 1.35s.

Happy installing!