HAP juggling floating HTML code tags

Explanation

What npm is

A registry, a command, and a folder full of other people's code

Node and npm arrive together and get talked about together, but they do completely different jobs. Node runs JavaScript, while npm goes and fetches JavaScript that other people wrote. When you keep these two separate in your head, error messages become much easier to read.

One Name, Three Things

Documentation uses the word "npm" for three different things and rarely says which one it means.

The command

This is a program on your computer, installed alongside Node. When you type npm install, this is what runs.

The registry

This is a website at npmjs.com holding several million published packages. The command talks to it over the internet. It is a public library, and anyone can add to it.

The package format

This refers to an agreement about what a downloadable folder of JavaScript looks like: it has a package.json at its root, and that file names it and lists what it needs.

Why the confusion sticks

npm ships inside the Node installer, so nobody installs it on purpose and nobody gets a moment where they learn what it is. It is already there the first time anyone needs it.

One command, touching all three:
npm install http-server
 │   │       └── the package: a folder of code, named in the registry
 │   └────────── the command you typed
 └────────────── the CLI program, installed alongside Node

The three, and one command using all of them

Here is HAP on the same three meanings, with a real package page and a real node_modules folder on screen rather than descriptions of them. It ends where this page picks up next: what that folder holds, and why it never goes into a repository. This video runs for nearly four minutes.

What A Package Is

A package is a folder of files with a package.json at the top. That is the entire definition. The package.json gives it a name, a version number, and a list of the other packages it needs.

The last part is where the size comes from. A package you install depends on other packages, which depend on other packages. npm follows that chain to the bottom and downloads every link in it. A single tool install routinely downloads two hundred folders, which is normal behavior.

HAP with a backpack looking confused while reading a treasure map

🟠 What node_modules actually is

This is a plain folder full of plain folders, each one downloaded from the registry and each one holding readable JavaScript. None of it is compiled or encrypted, so opening it and reading a package's source is a completely reasonable thing to do.

It is also enormous, entirely reconstructable from package.json, and therefore never committed to git. You can delete this folder and run npm install again to solve most package issues.

The Two Files That Matter

package.json configures your project

A person writes this file. It is short, readable, and edited by hand. The file records your intent, such as wanting version 14-point-something of a tool. It is always committed to git.

package-lock.json is generated for security

npm writes this file. It is long, unreadable, and never edited by hand. The file records the exact version of every single package installed, including the ones you never asked for. It is also committed to git.

The lock file is why a project installs the same way on someone else's laptop. package.json says "version 14-point-anything is acceptable" and that could mean 14.1.1 today and 14.3.0 next month. The lock file pins the answer, so a teammate cloning the project gets your versions.

One entry out of a package-lock.json:
"node_modules/http-server": {
  "version": "14.1.1",
  "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz",
  "integrity": "sha512-...",
  "dev": true
}

The integrity field is a checksum of the downloaded file. If the contents ever stop matching that hash, npm refuses the install rather than running code that changed underneath you. That is the security feature ensuring you only run the exact code you intended to download rather than a malicious version injected by a hacker.

dependencies And devDependencies

Packages land in one of two lists, and which list is a statement about when the code is needed.

Both lists in a package.json:
{
  "dependencies": {
    "some-library": "^2.1.0"
  },
  "devDependencies": {
    "http-server": "^14.1.1"
  }
}

dependencies

This is code that must be present for the finished site to work. It ships, and a visitor's browser or a running server ends up needing it.

devDependencies

These are tools used while building the project. They include servers, linters, formatters, and generators, none of which need to be in a browser.

How they get there

The command npm install package-name writes to dependencies, while npm install --save-dev thing writes to devDependencies.

For a static site

Almost everything is a devDependency for a static site because the output is finished HTML and CSS. The tools do their work before deployment and are not needed afterward.

The short version

You can determine where a package belongs by asking if a visitor's browser needs the code. If not, it belongs in devDependencies.

Installing Is Running Somebody Else's Code

Grace Hopper robot pointing up with a glowing vacuum tube, beside a terminal

A note from Grace Hopper

The registry is public. Anyone may publish to it, and a published package may run a script of its own at the moment it is installed. A single install command can therefore execute code written by a person you have never evaluated.

That is a reason to be deliberate about packages rather than a reason to avoid them. You must confirm the name character by character before installing because typo-squatting is a known attack. Where the platform already does the job, you can use the platform instead. Installing fewer things is also a good practice.

What Happens When You npm Install

An install command in a project folder produces three changes. Knowing which is which makes the next page's steps predictable.

  1. node_modules/ appears or grows. This folder contains downloaded code and is ignored by git.
  2. package.json gains a line naming what you asked for. This is your file, and you commit it.
  3. package-lock.json is created or updated with exact versions. This file is generated, but you commit it anyway.

The next page runs those commands and reads the results line by line.