Skip to main content

npm

package.json docs: https://docs.npmjs.com/cli/v11/configuring-npm/package-json

https://npmtrends.com - Compare package download counts over time

tip

Use https://github.com/nodejs/corepack to use Yarn, npm, and pnpm without having to install them. It comes with Node.js.

For example, corepack enable yarn.

CLI

CLI docs: https://docs.npmjs.com/cli-documentation

Commands: https://docs.npmjs.com/cli/v11/commands

  • List all commands: npm.
  • List all commands with usage info: npm -l. Very long output, better use next option (-h):
  • See options for a command: npm <command> -h or npm <command> --help (eg npm install -h).
  • Get help for a command (a man page, very long): npm help <command> (eg npm help install).

npm ←→ yarn:

Tip: npm run lists all the executable commands/scripts.

Upgrade npm itself: npm install npm@latest -g

(If this command fails and we then get zsh: command not found: npm we can fix it with brew reinstall node.)

List packages: npm list or npm list --depth=0

Find version of an installed package: npm list <package> --depth=0 source

List all outdated packages: npm outdated. Also see https://github.com/raineorshine/npm-check-updates - npx npm-check-updates

Uninstall a package: npm uninstall <package>

Show package info: npm info <package>

Open docs (eg README) in the browser: npm docs <package>

To pass arguments to a script you need to add -- (see npm run docs). Eg if we have the script "test": "jest" and we want to run Jest in watch mode, we need to do npm test -- --watch. Note: in this case doing npx jest --watch also works.

Report vulnerabilities: npm audit. Fix them with npm audit fix. Use --audit-level=<level> to set the minimum vulnerability level that will cause the command to fail (eg npm audit fix --audit-level=high will only fix vulnerabilities with severity "high" or above).

npm init

https://docs.npmjs.com/cli/v11/commands/npm-init

Creates package.json.

Init without questions: npm init -y

npm install

Install only "dependencies" but not "devDependencies" packages: npm install --omit=dev. See omit docs. Note that "If the resulting omit list includes 'dev', then the NODE_ENV environment variable will be set to 'production' for all lifecycle scripts"

npm install --force vs npm install --legacy-peer-deps

Important: use --force, not --legacy-peer-deps.

--force was introduced in npm 7, see https://github.blog/news-insights/product-news/npm-7-is-now-generally-available/#peer-dependencies

Automatically installing peer dependencies is an exciting new feature introduced in npm 7. In previous versions of npm (4-6), peer dependencies conflicts presented a warning that versions were not compatible, but would still install dependencies without an error. npm 7 will block installations if an upstream dependency conflict is present that cannot be automatically resolved.

You have the option to retry with --force to bypass the conflict or --legacy-peer-deps command to ignore peer dependencies entirely (this behavior is similar to versions 4-6).

Since many packages in the ecosystem have come to rely on loose peer dependencies resolutions, npm 7 will print a warning and work around most peer conflicts that exist deep within the package tree, since you can’t fix those anyway. To enforce strictly correct peer dependency resolutions at all levels, use the --strict-peer-deps flag.

https://stackoverflow.com/questions/66020820/npm-when-to-use-force-and-legacy-peer-deps

--force still pins many dependency versions which is stricter.

--legacy-peer-deps ignores peer dependencies entirely, which can screw up your dependency resolution. --force on the other hand simply sets a different peer dependency version for conflicting dependencies.

There is also --strict-peer-deps which is the opposite of --legacy-peer-deps and will cause installation to fail if there are any peer dependency conflicts.

npm install vs npm ci (clean install)

Better use npm ci --no-audit --no-fund. Also, consider adding --omit=dev if NODE_ENV is not 'production' to avoid installing dev dependencies.

https://www.stefanjudis.com/today-i-learned/how-to-override-your-dependencys-dependencies/

Install a package

Install to devDependencies: npm i -D webpack or npm install --save-dev webpack

Exact version: npm i -E express or npm install --save-exact express

Suppress output like '204 packages are looking for funding' and '8 high severity vulnerabilities': npm i --no-audit --no-fund

Update a package

Just use npm i [-D] [-E] somepackage@latest (eg npm i -D -E typescript@latest) because using npm update doesn't update package.json:

Note that by default npm update will not update the semver values of direct dependencies in your project package.json, if you want to also update values in package.json you can run: npm update --save (or add the save=true option to a configuration file to make that the default behavior).

Global

List global packages: npm list -g --depth=0

List outdated global packages: npm outdated -g

Add global package: npm install -g <package>

Update 1 global package: npm update -g <package>

Update all global packages: npm update -g

Remove global package: npm uninstall -g <package>

dependencies vs devDependencies

Putting packages in dependencies or devDependencies matters on Node.js and libraries, but not when creating a bundle (eg with Create React App).

How do I decide whether @types/* goes into dependencies or devDependencies? - https://stackoverflow.com/questions/45176661/how-do-i-decide-whether-types-goes-into-dependencies-or-devdependencies

create-react-app install devDependencies in dependencies section - https://stackoverflow.com/questions/44868453/create-react-app-install-devdepencies-in-dependencies-section

Consider moving react-scripts to devDependencies in the generated package - https://github.com/facebook/create-react-app/issues/4342

I don't think npm's advice is very relevant here. It is primarily concerning Node apps. CRA doesn't give you a Node app. From that perspective, all dependencies (including React) are "dev" dependencies because they're only necessary for the build: once you build the app, it has no deps at all.

Everything goes into dependencies? - https://github.com/facebook/create-react-app/issues/6180

The distinction is meaningful for Node apps because they actually are deployed as runtime. So you might not want to deploy development dependencies.

In case of CRA, the end result is a static bundle. So in a sense all dependencies are "development dependencies", even React or libraries you use. They're used only at the build time.

Dependency version

https://docs.npmjs.com/cli/v11/configuring-npm/package-json#dependencies

semver calculator: https://semver.npmjs.com

MAJOR.MINOR.PATCH

"dependencies": {
"exact": "15.7.2",
"same-major": "^15.7.2", // upgrade to 15.X.Y (eg 15.7.3 and 15.8.0, but not 16.0.0)
"same-major-and-minor": "~15.7.2" // upgrade to 15.7.X
}

There are more operators you can use in addition to ^ and ~, like >=1.2.3, <1.2.3 || >=4.5.6, 1.0.0 - 1.2.0. See https://semver.npmjs.com and https://medium.com/helpshift-engineering/package-lock-json-the-complete-guide-2ae40175ebdd

To save exact do: npm install --save-exact express or npm i -E express

Consistent dependency versions in large JavaScript Monorepos - https://jamiemason.github.io/syncpack/ - https://github.com/JamieMason/syncpack

Exact or range versions?

https://www.reddit.com/r/javascript/comments/ira5gz/askjs_do_you_use_exact_or_range_versions_for_your/

tip

Force exact package versions with a .npmrc file:

.npmrc
save-exact=true

A recommended approach (best practice) is:

  • For applications:
    • Production dependencies: Use exact versions
    • Dev dependencies: Use ranges (^)
    • Benefits:
      • Reproducible builds across all environments
      • No surprises in production deployments
      • Easier debugging (same versions everywhere)
      • Security control (you decide when to update)
  • For libraries/packages:
    • Dependencies: Use ranges (^)
    • Peer dependencies: Use ranges (^)+
    • Benefits:
      • Automatic bug fixes and patches
      • Latest TypeScript improvements
      • Updated linting rules
      • Less maintenance overhead

Find version of an installed package

npm list <package> --depth=0

source

Find parent of transitive dependency

npm ls @typescript-eslint/typescript-estree

This will print who is using @typescript-eslint/typescript-estree:

MyProject
└─┬ @react-native-community/eslint-config@3.0.1
├─┬ @typescript-eslint/eslint-plugin@4.28.1
│ └─┬ @typescript-eslint/experimental-utils@4.28.1
│ └── @typescript-eslint/typescript-estree@4.28.1 deduped
└─┬ @typescript-eslint/parser@4.28.1
└── @typescript-eslint/typescript-estree@4.28.1

Here the transitive dependency @typescript-eslint/typescript-estree is being imported by the direct dependency @react-native-community/eslint-config (which appears in package.json).

Yarn has yarn list.

source

package-lock.json

Never delete it: https://tkdodo.eu/blog/solving-conflicts-in-package-lock-json If you delete package-lock.json now from any one of your projects and run npm install again, it will most certainly look completely different.

.npmrc

Set config settings.

https://docs.npmjs.com/cli/v11/configuring-npm/npmrc

Configuration options: https://docs.npmjs.com/cli/v11/using-npm/config

There are 4 levels of config files:

  • Project: /path/to/my/project/.npmrc
  • User: $HOME/.npmrc, ~/.npmrc
  • Global: $PREFIX/etc/npmrc
  • Builtin: internal defaults
touch .npmrc && echo 'save-exact=true' > .npmrc

You can use npm config to set config values:

npm config set save-exact true # Modify user config file
npm config set save-exact true --location=global # Modify global config file
npm config set save-exact true --location=project # Modify project config file

Find unused packages

https://github.com/dylang/npm-check

https://github.com/depcheck/depcheck - Usage: npx depcheck

https://github.com/webpro/knip

Get rid of 'x packages are looking for funding' forever

npm config set fund false
# check the value, it should be false now
npm config get fund

https://docs.npmjs.com/cli/v11/commands/npm-install#fund

npx

npx runs a binary or package. It can be a local package (eg a binary in ./node_modules/.bin/) or fetched remotely. See https://docs.npmjs.com/cli/v11/commands/npx

npm scripts

info

Do not use npx <package> in npm scripts. Just use <package>.

{
"scripts": {
"ok": "prettier --write .",
"unnecessary-npx": "npx prettier --write ."
}
}

When you run a command via npm run, npm automatically adds node_modules/.bin to the PATH for that specific execution. It temporarily updates your shell's environment variables so that the local project binaries are prioritized. This means any package installed in your project can be called by its name directly, without needing npx.

In addition, if prettier is not installed, npx will fall back to downloading it; without npx, the script will just fail. Also, npx adds a bit of startup overhead (checking/installing). It also guarantees that you're using the version in your devDependencies (npx might download the latest version). In a monorepo, npx may resolve binaries from the root workspace, current package or even fetch remotely.

Running cross-platform tasks via npm package scripts: https://2ality.com/2022/08/npm-package-scripts.html → Outdated! New link is https://exploringjs.com/nodejs-shell-scripting/ch_package-scripts.html, so it's a chapter of the book Shell scripting with Node.js

You can run npm test, npm start, npm stop and npm restart without the run. For other scripts you need to do npm run <script>.

You can have pre and post scripts:

{
"scripts": {
"format": "prettier --write .",
"preformat": "echo 'Running pre-format script...'",
"postformat": "echo 'Running post-format script...'"
}
"devDependencies": {
"prettier": "3.6.2"
}
}

When you run npm run format, it will first run preformat, then format, and finally postformat.