{ mfstapert; }

[node] Why I no longer use npx in CI

Published: 2024-04-01

I always prefixed “scripts” in the scripts property of my package.json with npx. But recently I came to the conclusion this might be a bad idea, why? Let’s take a look at what npx does.

Executes <command> either from a local node_modules/.bin, or from a central cache, installing any packages needed in order for <command> to run.

So, when you run a script, npx prettier it will look in your node_modules/.bin first for an executable to run. If nothing is found in your node_modules, npx will start looking for any version in your local cache (~/.npm folder on unix, %LocalAppData%\npm-cache on windows). If nothing is found, it will install the latest version of the package it can find.

While this is great if you just want to run a standalone script: like scaffolding a new project or formatting some code, this can lead to issues in CI. In a CI environment, you want to run commands with the versions specified in your package.json, nothing else.

If something in your pipeline breaks around the installation of node_modules, npx usage can lead to further issues. Say you are using an older version of jest, but you run a script with npx, your test can be run with a different jest version than the one you’ve configured. This can lead to unintended behavior or worse, false positives.

Running your scripts without npx ensures this is not possible, your CI environment will only use the dependencies specified or break. This seems preferable to me versus a mechanism that can potentially introduce flakiness.