Packages and npm

By Syncfusion®, Inc. · Last updated

What npm and the npm registry are, package.json fields, dependency types, installing and updating packages, semantic versioning and version ranges, package-lock.json and npm ci, npm scripts and node --run, npx and local binaries, how Vite and the Angular CLI use Node.js, npm audit, package entry points with exports and imports, and optional workspaces, publishing and other package managers.

Packages and npm

Package = a folder of code with a package.json file, ready to share.

npm = the tool that downloads, updates and shares packages.

  • Other people have already written code for common jobs.
  • You can use their packages instead of writing everything yourself.
  • npm comes with Node.js. You installed it in Introduction to Node.js.

Simple lesson: a package is shared code, and npm is the tool that moves it around.

A Real-World Example

Think of a school library.

  • The library has thousands of books on its shelves.
  • A student fills in a borrow slip with the book name and edition.
  • The librarian fetches the book from the shelf.
  • The register keeps the exact copy number, so the same copy can be found again.

Now the mapping:

  • The shelves are the npm registry.
  • The borrow slip is package.json.
  • The librarian is the npm command.
  • The register is package-lock.json.

What You Will Learn

In this lesson, you will learn:

  1. What npm and the npm registry are
  2. The main fields of package.json
  3. Dependency types and installing, updating and removing packages
  4. Semantic versioning and version ranges
  5. package-lock.json, npm install and npm ci
  6. npm scripts, node --run, npx and local tools
  7. How Vite and the Angular CLI use Node.js
  8. Package safety with npm audit
  9. Package entry points, and optional extras: workspaces, publishing, pnpm and Yarn

npm Fundamentals

  • npm registry = a huge online store of free packages at npmjs.com.
  • npm (the command) = the program you type in the terminal, such as npm install.
  • Node.js runs your code. npm manages the packages your code uses.
  • Packages go into a folder called node_modules inside your project.

Never edit files inside node_modules. The next npm install replaces them.

Animation: npm reads package.json, downloads a package from the registry into node_modules, and writes package-lock.json

package.json "dayjs": "^1.11.0"

npm install

npm registry

node_modules

npm also writes package-lock.json with the exact versions

package.json says what you want, npm fetches it, and node_modules holds it.

Simple lesson: the registry stores packages, and npm copies them into your project.

package.json in Depth

npm init -y makes a starter package.json.

$ npm init -y
Wrote to /home/asha/school/package.json:

{
  "name": "school",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

The main fields:

  • name and version = who the package is and which edition.
  • description, author, license = facts for other people.
  • main = the entry file when someone loads your package.
  • type = "module" makes .js files ES modules.
  • scripts = short names for commands, such as "start": "node app.js".
  • engines = which Node.js versions the project needs, such as { "node": ">=22" }.
  • private: true = blocks npm publish, so a school project is never published by mistake.
  • files = which files go into the package when you publish it.

Simple lesson: package.json is the ID card and the command list of your project.

Dependencies

Dependency = a package your project needs.

  • dependencies = needed when the app runs, such as express.
  • devDependencies = needed only while you build and test, such as typescript.
  • peerDependencies = a package your package expects the user to install, such as a plugin that needs react.
  • optionalDependencies = nice to have. Install does not fail without them.
npm install dayjs              # add to dependencies
npm install -D typescript      # add to devDependencies
npm install dayjs@1.11.10      # an exact version
npm install -g nodemon         # global: a tool for every project
npm uninstall dayjs            # remove it
  • Local install = goes into this project's node_modules. Use this almost always.
  • Global install (-g) = goes into one shared place on your computer. Use it only for command-line tools.

Checking Your Packages

  • npm ls = show the installed packages as a tree.
  • npm outdated = show packages that have newer versions.
  • npm update = update packages inside the allowed version range.

package.json

{
  "name": "school-report",
  "version": "1.0.0",
  "private": true
}
$ npm ls
school-report@1.0.0 /home/asha/school
`-- (empty)

Simple lesson: app needs go in dependencies, tool needs go in devDependencies.

Versioning

Semantic versioning (SemVer) = a version number in three parts: MAJOR.MINOR.PATCH.

  • PATCH (1.4.2 → 1.4.3) = a bug fix. Nothing breaks.
  • MINOR (1.4.2 → 1.5.0) = a new feature. Old code still works.
  • MAJOR (1.4.2 → 2.0.0) = a breaking change. Old code may stop working.
  • Prerelease (2.0.0-beta.1) = a test version before the real release.

Version Ranges

RangeMeansAllows
1.4.2Exactly thisonly 1.4.2
^1.4.2Same major1.4.2 up to below 2.0.0
~1.4.2Same minor1.4.2 up to below 1.5.0
>=1.4.2This or neweranything from 1.4.2
*Any versioneverything (risky)

npm install writes ^ by default. You get fixes and features, but no breaking changes.

Commit package-lock.json to Git. It makes every install on every computer the same.

This is a small copy of what npm does. The real rules also handle 0.x versions and prereleases.

Simple lesson: read a version as major.minor.patch, and ^ never jumps to a new major.

Which versions ^1.4.2 and ~1.4.2 accept Version 1.4.2 = MAJOR . MINOR . PATCH 1.4.2 1.4.9 1.7.0 2.0.0 ^1.4.2accepts 1.4.2, 1.4.9 and 1.7.0. Never 2.0.0. ~1.4.2accepts 1.4.2 and 1.4.9 only. A new MAJOR number means breaking changes, so npm never jumps to it on its own.

^ allows new minor and patch versions. ~ allows only new patch versions.

Lockfiles

package-lock.json = the exact version of every installed package, including packages of packages.

  • package.json says ^1.11.0. That is a range.
  • package-lock.json says 1.11.13. That is the exact copy.
  • Commit the lockfile to Git. Then everyone installs the same versions.

npm install vs npm ci:

  • npm install = install and update the lockfile when needed. Use it while you work.
  • npm ci = delete node_modules and install exactly what the lockfile says. It fails if the lockfile does not match package.json. Use it on build servers.

Simple lesson: package.json allows a range, the lockfile pins one exact version.

npm Scripts

npm script = a named command in the "scripts" part of package.json.

  • npm run report runs the "report" script.
  • npm start and npm test are short forms for two common names.
  • Words after -- are passed to the script: npm run greet -- Asha.
  • npm adds node_modules/.bin to the path, so installed tools run by name.
  • npm sets variables such as npm_package_name for the script.
  • node --run report runs a script faster, without npm's extra work.

Click ▶ Run. The lab runs npm run greet -- Meena.

  • The banner: npm prints the package name, version and script name first.
  • The argument: Meena arrived in process.argv[2], because it came after --.
  • The variable: npm_package_name was filled in by npm.

Lifecycle Scripts: pre and post

  • A script named prebuild runs before build. A script named postbuild runs after it.
  • npm install runs a package's install and postinstall scripts. This is why you should trust a package before installing it.

package.json

{
  "name": "school-report",
  "version": "1.0.0",
  "scripts": {
    "prebuild": "echo 1. cleaning old files",
    "build": "echo 2. building the report",
    "postbuild": "echo 3. done"
  }
}
$ npm run build

> school-report@1.0.0 prebuild
> echo 1. cleaning old files

1. cleaning old files

> school-report@1.0.0 build
> echo 2. building the report

2. building the report

> school-report@1.0.0 postbuild
> echo 3. done

3. done

Environment Variables in Scripts

  • "start": "NODE_ENV=production node app.js" sets a variable for one command.
  • That form works on Mac and Linux. On Windows Command Prompt it does not.
  • For all systems, use node --env-file=.env app.js, or the cross-env package.

Simple lesson: scripts give long commands short names, and -- passes your own words to them.

Running Packages: npx and Local Tools

  • Installed tools put their commands in node_modules/.bin.
  • npx <tool> = run a tool from node_modules/.bin. If it is missing, npx offers to download it and run it once.
  • Example: npx tsc --version runs the TypeScript compiler of this project.
  • Corepack = a helper that sets up pnpm or Yarn. It came with older Node.js versions. Newer versions need npm install -g corepack.

How Vite and the Angular CLI Use Node.js

The React, Vue and Angular courses start with commands like npm run dev. Here is what happens.

  • npm create vite@latest downloads the Vite starter package and runs it with Node.js.
  • npm run dev runs the "dev" script, which starts Vite, a Node.js program.
  • Vite reads your files with fs, builds them and serves them on localhost with http.
  • ng serve in Angular works the same way: the Angular CLI is a Node.js program.
  • In browser code, Vite gives you import.meta.env. In Node.js code, you use process.env.

Simple lesson: frontend tools are Node.js programs, and npm scripts start them.

Package Safety

A package runs on your computer with your rights. Check it first.

  • npm audit = checks your installed packages against a list of known security problems.
  • npm audit fix = updates packages to fixed versions, inside your version ranges.
  • Before installing, check the weekly downloads, the last update date and the GitHub page.
  • Watch for look-alike names, such as expres instead of express. This trick is called typosquatting.
  • Read postinstall scripts of small, unknown packages. They run during install.
$ npm audit
# npm audit report

minimist  <1.2.6
Severity: critical
Prototype Pollution in minimist
fix available via `npm audit fix`

1 critical severity vulnerability

This output is an example. Your report depends on your packages.

npm audit fix --force may install breaking major versions. Read what it changes, and run your tests after.

Simple lesson: treat a new package like a stranger's USB stick. Check it before you plug it in.

Package Entry Points (Advanced)

exports field = the list of files other people may import from your package.

  • Without exports, anyone can import any file inside the package.
  • With exports, only the listed paths work. Others get ERR_PACKAGE_PATH_NOT_EXPORTED.
  • Conditional exports = give import users one file and require users another.
  • Subpath imports (#) = short private names inside your own package, set in "imports".
  • "." with import and require: this is a dual package. It serves both module systems.
  • "./grades": a public subpath.
  • secret.js: the file exists, but exports does not list it, so Node.js blocks it.

Simple lesson: exports is the front gate of your package. Only listed paths get in.

Workspaces (Optional)

Workspaces = several packages in one repository, managed by one npm install.

  • Add "workspaces": ["packages/*"] to the root package.json.
  • Shared dependencies are installed once, in the root node_modules.
  • npm run test --workspaces runs test in every package.
  • npm run build -w packages/reports runs it in one package.

Publishing (Optional)

  1. Remove "private": true and pick a unique name.
  2. List the files to share in "files".
  3. Run npm pack --dry-run to see what would be uploaded.
  4. Run npm version patch to raise the version number.
  5. Run npm publish (after npm login).

package.json

{
  "name": "@greenfield/grades",
  "version": "1.0.0",
  "main": "index.js",
  "files": ["index.js"]
}

index.js

module.exports.grade = (m) => (m >= 90 ? "A" : "B");

notes.txt

These notes stay private.
$ npm pack --dry-run
npm notice Tarball Contents
npm notice 53B index.js
npm notice 104B package.json
npm notice total files: 2
  • notes.txt is not in the list, because "files" only allows index.js.
  • Scoped package = a name like @greenfield/grades. The scope groups a team's packages.
  • npm deprecate <name>@<version> "message" warns people away from an old version.

Other Package Managers (Optional)

  • pnpm = saves disk space by storing each package version once and linking it. Fast installs.
  • Yarn = another package manager with its own lockfile, yarn.lock.
  • Both read the same package.json. Use the one your team already uses.
  • Do not mix them in one project. Each writes its own lockfile.

Common mistakes

MistakeWrong ideaCorrect ideaWhat goes wrong
Not committing package-lock.jsonThe lockfile is temporaryCommit itTeammates get different versions
Editing files in node_modulesIt is my code nowChange your own code, or fork the packageThe next install wipes your change
Putting typescript in dependenciesAll packages are the sameBuild tools go in devDependenciesProduction installs carry extra weight
npm run greet Ashanpm passes words to the scriptUse npm run greet -- Ashanpm may treat the word as its own option
Installing project tools with -gGlobal is easierInstall locally and use npxDifferent projects need different versions
Installing a package with a look-alike nameNames are checked by npmCheck the exact name and its downloadsA harmful package may run on install
Using npm install on a build serverBoth commands are the sameUse npm ciBuilds may get different versions

TL;DR

  1. npm = the package tool. Registry = the online store of packages.
  2. package.json = name, version, type, scripts, engines and dependencies.
  3. dependencies = needed to run. devDependencies = needed to build and test.
  4. SemVer = MAJOR.MINOR.PATCH.

    • ^ same major, ~ same minor
  5. package-lock.json = exact versions. Commit it. Use npm ci on servers.
  6. npm scripts = named commands. -- passes your own arguments.
  7. npx = run a tool from node_modules/.bin.
  8. npm audit = check packages for known security problems.
  9. exports = the list of paths a package allows.

Check Yourself

Exercises

What to Remember

  • npm downloads packages from the registry into node_modules.
  • package.json lists what you need. package-lock.json pins exact versions.
  • ^ allows new minor and patch versions, never a new major.
  • Use npm ci on servers, and npm run name -- words to pass arguments.
  • Check a package before installing it, and run npm audit.
  • exports decides which files of a package others may import.

Next: Event Loop and Timers. You will see how Node.js keeps working while slow jobs finish.