yarn icon indicating copy to clipboard operation
yarn copied to clipboard

[Question] How to prevent people from using `npm install <package name>`

Open hekod777 opened this issue 6 years ago • 19 comments

By digging the issues I found

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

to prevent people from running npm install

But this line fails to prevent people from running npm install <package name>

Is there any way to prevent people from running npm install <package name>?

hekod777 avatar Nov 09 '17 23:11 hekod777

Thanks! You are right, npm install <package> --save still works, which is a bummer. And I couldn't find any trick (life-cycle callback, npmrc, ...) to convice npm to disallow installation of new packages; but a git pre-commit hooks might be a solution.

I've made this small change to have a "cleaner" package.json file:

package.json:

  "scripts": {
    "preinstall": "node tools/preinstall-script.js"
  }

tools/preinstall-script.js:

/**
 * Do NOT allow using `npm` as package manager.
 */
if (process.env.npm_execpath.indexOf('yarn') === -1) {
  console.error('You must use Yarn to install dependencies:');
  console.error('  $ yarn install');
  process.exit(1);
}

EDIT: Just found this in AMP: https://github.com/ampproject/amphtml/blob/master/build-system/check-package-manager.js

SchnWalter avatar Nov 10 '17 10:11 SchnWalter

@SchnWalter I believe the preinstall trick above would also make it impossible for a user to install your package with npm, no?

jasonkarns avatar Nov 19 '17 17:11 jasonkarns

As a dependency? I'm not sure. I'm not using this for public projects/libraries that someone would require as a dependency.

SchnWalter avatar Nov 25 '17 22:11 SchnWalter

Hi. There's a way to do this already. You add a fake engine version like so in package.json:

  "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@

adamscybot avatar Oct 23 '19 21:10 adamscybot

Hi. There's a way to do this already. You add a fake engine version like so in package.json:

  "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@

It doesn't worked here.

$ npm --version
6.12.0
$ node --version
v12.13.0

Accordingly with npm website engineStrict was removed.

engineStrict This feature was removed in npm 3.0.0 Prior to npm 3.0.0, this feature was used to treat this package as if the user had set engine-strict. It is no longer used.

rafaelfess avatar Nov 15 '19 02:11 rafaelfess

@rafaelfesi, that's for package.json, not for the npmrc files, which are used to configure NPM.

  • https://docs.npmjs.com/files/npmrc
  • https://docs.npmjs.com/misc/config#engine-strict

And the configuration works for me:

$ cat .npmrc 
engine-strict=true

$ npm install
npm ERR! code ENOTSUP
npm ERR! notsup Unsupported engine for [email protected]: wanted: {"node":"^10.16","npm":"please-use-yarn"} (current: {"node":"10.16.3","npm":"6.13.0"})
...

SchnWalter avatar Nov 16 '19 20:11 SchnWalter

I think pnpm has released the suitable package for this: only-allow

hckhanh avatar May 15 '20 01:05 hckhanh

@hckhanh Neither only-allow nor any other solutions posted here prevent npm install <package-name> unfortunately...

papb avatar Aug 02 '20 18:08 papb

@hckhanh Neither only-allow nor any other solutions posted here prevent npm install <package-name> unfortunately...

You're right actually. My solution doesn't prevent that (though hopefully, the devs suspicions that you should use yarn would already be aroused if they previously tried to do a plain old npm install). I would suggest this is actually a bug in NPM. That operation modifies the lock and package json, so it should check the engine. We should potentially raise that separately.

adam-thomas-privitar avatar Oct 26 '20 19:10 adam-thomas-privitar

I would suggest this is actually a bug in NPM. That operation modifies the lock and package json, so it should check the engine. We should potentially raise that separately.

An RFC for having npm install X run the preinstall script is here: https://github.com/npm/rfcs/issues/325

I don't know if there's one yet for the engine check you mention.

Venryx avatar Jun 07 '21 10:06 Venryx

Do you guys have an update on this topic?

Tynael avatar Sep 23 '21 17:09 Tynael

This might be helpful - https://www.freecodecamp.org/news/how-to-force-use-yarn-or-npm/.

aryanshridhar avatar Dec 26 '21 13:12 aryanshridhar

This might be helpful - https://www.freecodecamp.org/news/how-to-force-use-yarn-or-npm/.

I'm actually the author of that article and here I am looking for a fix/workaround lmao

Tynael avatar Dec 27 '21 10:12 Tynael

The right way going forward would be the packageManager field, but npm doesn't support it yet.

arcanis avatar Dec 27 '21 12:12 arcanis

Hi team, is there a way to adding constraints to package.json for preventing installing packages for certain users kind of access previlige

kaariwedoms avatar Mar 02 '22 13:03 kaariwedoms

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

@adamscybot thanks for the approach, unfortunately Heroku does not like it 😞 it throws an error when trying to push to a remote to deploy.

remote: -----> Installing binaries
remote:        engines.node (package.json):  ^12.18
remote:        engines.npm (package.json):   please-use-yarn
remote:        engines.yarn (package.json):  ^1.17
remote:        
remote:        Resolving node version ^12.18...
remote:        Downloading and installing node 12.22.10...
remote:        Bootstrapping npm please-use-yarn (replacing 6.14.16)...
remote:        Unable to install npm please-use-yarn; does it exist?

Too bad because it was working perfectly! I'll stick to the preinstall script.

ochicf avatar Mar 02 '22 16:03 ochicf

For those looking for an alternative solution, I've just published a tiny package that does the job: enforcepm

You don't need to install it, but you need to create a Git pre-commit hook and put this command inside of it:

npx enforcepm yarn

By the way you can take the "creating a pre-commit hook" thing as an excuse for using tools such as husky or lint-staged.
If you are already using such tool, then enforcing yarn is just a matter of copy/pasting the command above.

Hope it helps.

DaviDevMod avatar Apr 05 '22 02:04 DaviDevMod

Some hosting providers will attempt to parse package.json and eventually fail when they encounter a key like "yarn" : "Please use npm"

In order to bypass this issue you can maximize the version of the package manager you would like to exclude from the list, so for example:

 "engines": {
   "npm": ">= 8.0.0",
   "yarn": "9999.9.9",
 }

The above solution worked for Railway which parses package.json. I haven't tried with Heroku but I can imagine it will work as well.

This makes the assumption [email protected] won't happen in at least a million years but if you are skeptical just raise that number even more

denik1981 avatar Apr 16 '22 07:04 denik1981

I'm trying to do the reverse just using npm and not yarn but yarn 3 doesn't care about anything is like a Honey Badger

nahumzs avatar Jul 31 '22 00:07 nahumzs

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

Works like a charm for my project, deploying only on netlify and vercel 😎

seedy avatar Mar 23 '23 11:03 seedy

I added this to package.json

"engines": {
    "npm": "please-use-yarn",
    "node": "12.x - 16.x"
  },

and

engine-strict = true

to .npmrc

It worked just fine. Thanks folks :)

Screenshot 2023-04-14 at 9 42 08 PM

parikshit223933 avatar Apr 14 '23 16:04 parikshit223933

I would note that you should only do this on your projects. If you do this on a package that is intended to be a dependency of another project (outside your control), I found that it forced the consumer to use my manager of choice instead of theirs.

cah-brian-gantzler avatar Sep 03 '23 21:09 cah-brian-gantzler