Force yarn install instead of npm install for Node module?

流过昼夜 提交于 2021-02-06 15:13:28

问题


I want to force using yarn install instead of npm install. I want to raise error in npm install. What should I do in package.json?


回答1:


UPDATE: Alexander's answer is the better solution and uses the same technique I describe here. I am leaving my answer in tact for posterity. The original point of my answer was to show that you can execute a small node script which should work on all platforms.

In your preinstall script you can run a mini node script which should work on all platforms, whereas things like pgrep (and other common *nix commands and operators) won't work on Windows until Windows 10 has received widespread adoption.

I tested the below script on Node v4.7.0 (npm v2.15.11) and Node v7.2.1 (npm v3.10.10). I assume it works on everything in between. It works by checking the environment variables on the currently running process - the npm_execpath is the path to the currently running "npm" script. In the case of yarn, it should point to /path/to/yarn/on/your/machine/yarn.js.

"scripts": {
    "preinstall": "node -e \"if(process.env.npm_execpath.indexOf('yarn') === -1) throw new Error('You must use Yarn to install, not NPM')\""
}

You can read more about npm scripts here: https://docs.npmjs.com/misc/scripts

As far as the npm_execpath environment variable, while not documented I doubt that it will ever change. It's been around for multiple major releases of npm and it doesn't really pass the "there's a better name for this" test.




回答2:


Like the other answers, I'd recommend using a preinstall script and checking your environment. For a portable solution that won't have false-positives if another npm process happens to be running, using node -e 'JS_CODE' is probably the best option.

In that JS code, you can check the package manager's path using the following:

process.env.npm_execpath

Yarn's binary is yarn.js, compared to npm-cli.js used by NPM. We can use a regex like the following to check that this string ends with yarn.js.

/yarn\.js$/

By using this regex, we can be sure it won't accidentally match somewhere earlier in the file system. Most-likely yarn won't appear in the file path, but you can never be too sure.

Here's a minimal example:

{
  "name": "test",
  "version": "1.0.0",
  "scripts": {
    "preinstall": "node -e 'if(!/yarn\\.js$/.test(process.env.npm_execpath))throw new Error(\"Use yarn\")'"
  }
}

Of course, the user will still be able to get around this check be editing the JSON or using the --ignore-scripts options:

npm install --ignore-scripts



回答3:


After trying these options and not being very satisfied, I recommend only-allow.

Just add:

{
  "scripts": {
    "preinstall": "npx only-allow yarn"
  }
}

I like that it provides a clear warning message, and instructions how to install yarn:

Credit to Adam Thomas' answer for providing the thread recommending this.




回答4:


Most of the answers here involve hacky scripts but there's a built in way to achieve this which I posted over on the Yarn github issue. Unlike soe of the other ways, this works for any and all NPM commands.

You add a fake engine version like so in package.json (you may want to tweak the yarn and node entries):

  "engines": {
    "npm": "please-use-yarn",
    "yarn": ">= 1.17.3",
    "node": ">= 12.5.0"
  }

Then you add an .npmrc file to the project root with this:

engine-strict = true

Running NPM then raises an error:

npm ERR! code ENOTSUP
npm ERR! notsup Unsupported engine for root@: wanted: {"npm":"please-use-yarn","yarn":">= 1.17.3","node":">= 12.5.0"} (current: {"node":"12.9.1","npm":"6.10.2"})
npm ERR! notsup Not compatible with your version of node/npm: root@



回答5:


You can use the preinstall hook along with some shell script to achieve this.

sample package.json:

  "scripts": {
    "preinstall": "pgrep npm && exit 1"
  }



回答6:


I've just released a module that includes a CLI for this (useful for npm preinstall scripts): https://github.com/adjohnson916/use-yarn

Also, I've just released a helper for Danger to check for missing yarn.lock changes on CI: https://github.com/adjohnson916/danger-yarn-lock

See also discussion here:

  • https://github.com/yarnpkg/yarn/issues/1732
  • https://github.com/alexanderwallin/use-yarn-instead/issues/1



回答7:


If you want to simply test whether packages are being installed under yarn or npm, I tweaked Alexander O'Mara's answer slightly since it worked for me on OS X:

"scripts": {
    "preinstall": "if node -e \"process.exitCode=!/yarn\\.js$/.test(process.env.npm_execpath)\" ; then echo yarn ; else echo npm ; fi",
    "postinstall": ""
}

There are quite a few concepts happening in this short snippet:

  • The \\. portion is escaped so that \\ becomes \ and results in a properly escaped \. to detect a period in the regex.

  • process.exitCode= can be used to set the process's exit code and is safer than calling process.exit(N) due to the asynchronous nature of Node.js.

  • In Alexander's example, throw new Error(\"Use yarn\") caused node to exit with code 1 and print the stack trace to stderr. You can try running these on the console to see how that works: node -e 'throw new Error("Oops")' and node -e 'throw new Error("Oops")' 2> /dev/null (which directs the stderr stream to /dev/null). Then you can verify that the exit code was 1 with echo $? (which prints the last exit code).

  • The shell's if XXXX ; then YYYY ; else ZZZZ ; fi conditional logic checks the exit code of XXXX and goes to the then case for 0 (any other value goes to the else case). So if the regex detects yarn.js at the end of process.env.npm_execpath then it returns true. This must be negated so that the node process exits with code 0 and satisfies the if.

  • You could also console.log() the regex result and compare the output in the shell (this is just a little more verbose). Here are some examples of how to do that: https://unix.stackexchange.com/a/52801 and https://superuser.com/a/688902

  • You can append true ; or false ; to any shell statement to set the exit code manually. For example you can try true ; echo $? or false ; echo $?.

  • You can also leave off the else echo npm ; portion entirely if you don't need it.

With all of that out of the way, you can substitute the echo yarn and echo npm portions with other commands. For example, you could put multiple commands in a subshell like (echo yarn) or echo $(echo yarn).

In my case, I needed to work around an issue where one of the packages installed but had bugs under yarn so I had to run an npm install --ignore-scripts in the success case. Note that this should probably never be done in production, but can be a lifesaver if you just need to get something done or don't have control over which package manager will be used down the road.

I haven't tried this on Windows, so if someone can test the syntax there I will update my answer with what works. It would be best if the preinstall script is identical under both Windows and the Mac/Linux shell.




回答8:


Found an alternate solution on Reddit. I added this to the end of my .zshenv file:

NPM_PATH=$(which npm)
npm () {
  if [ -e yarn.lock ]
  then
    echo "Please use yarn with this project"
  else
    $NPM_PATH "$@"
  fi
}

It now stops me from absentmindedly running commands like npm i on any yarn project on my Mac.



来源:https://stackoverflow.com/questions/41076172/force-yarn-install-instead-of-npm-install-for-node-module

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