package.json Deep Dive
The project manifest
Section titled “The project manifest”package.json is the manifest for every Node.js project. It tells Node and npm who owns the package, what version it is, how to run it, which files are public API, and which packages it depends on. Every npm install, npm run, and npm publish command reads from this file.
Key fields
Section titled “Key fields”| Field | Purpose |
|---|---|
name | The package identifier on the npm registry (lowercase, URL-safe) |
version | The current release, following Semantic Versioning (MAJOR.MINOR.PATCH) |
type | Module system default: "module" for ESM, "commonjs" (or absent) for CJS |
main | Legacy entry point for CJS consumers (require('your-pkg')) |
exports | Modern entry point map — controls what consumers can import and from which subpaths |
scripts | Named shell commands run with npm run <name> (plus lifecycle hooks like start, test) |
Annotated example
Section titled “Annotated example”{ "name": "my-library", "version": "1.2.0", "description": "A small utility library", "type": "module", "main": "./dist/index.cjs", "exports": { ".": { "import": "./dist/index.js", "require": "./dist/index.cjs" }, "./utils": "./dist/utils.js" }, "scripts": { "build": "tsc", "test": "node --test", "start": "node dist/index.js", "lint": "eslint src" }, "dependencies": { "zod": "^3.22.4" }, "devDependencies": { "typescript": "~5.4.0" }}The exports field takes precedence over main in modern Node.js. It lets you expose multiple subpaths and serve different files to ESM and CJS consumers from the same package.
Semver ranges
Section titled “Semver ranges”npm uses Semantic Versioning for dependency ranges. The range syntax in package.json controls which versions npm install considers compatible.
| Range | Meaning | Example |
|---|---|---|
^1.2.3 | Compatible — allows MINOR and PATCH bumps, locks MAJOR | >=1.2.3 <2.0.0 |
~1.2.3 | Patch-only — allows only PATCH bumps | >=1.2.3 <1.3.0 |
1.2.3 | Exact — only this version | 1.2.3 |
* | Any version — no constraint | >=0.0.0 |
Dependencies block examples
Section titled “Dependencies block examples”{ "dependencies": { "express": "^4.18.2", "dotenv": "~16.3.1", "uuid": "9.0.0", "some-experimental-pkg": "*" }}In practice, ^ is the most common choice for dependencies: it keeps you on the latest compatible release while protecting against breaking changes introduced by a new major version.
Initialising a project
Section titled “Initialising a project”npm init walks you through an interactive prompt to generate a package.json. npm init -y skips all prompts and accepts the defaults — useful when you want a minimal file fast.
# Interactive wizardnpm init
# Accept all defaults immediatelynpm init -yAfter running npm init -y you get a minimal package.json in the current directory:
{ "name": "my-project", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC"}Edit the file directly or use npm pkg set name="my-library" type="module" to update individual fields from the terminal.