| name | package-management |
| description | Use when choosing or configuring JavaScript/TypeScript package managers, managing dependencies, setting up workspaces, or publishing packages. Covers npm, yarn, pnpm, and bun.
USE FOR: package manager selection, npm/yarn/pnpm/bun configuration, monorepo workspaces, dependency management, package.json anatomy, publishing to registries, lockfile strategies
DO NOT USE FOR: tsconfig.json or build tool configuration (use project-system), CLI tool development (use cli), specific library usage (use packages)
|
| license | MIT |
| metadata | {"displayName":"Package Management","author":"Tyler-R-Kendrick"} |
| compatibility | claude, copilot, cursor |
| references | [{"title":"npm Documentation","url":"https://docs.npmjs.com"},{"title":"pnpm Documentation","url":"https://pnpm.io"},{"title":"Yarn Documentation","url":"https://yarnpkg.com"}] |
Package Management
Overview
JavaScript/TypeScript package managers handle dependency installation, version resolution, lockfile management, script execution, and workspace orchestration. Choosing the right one affects install speed, disk usage, monorepo workflow, and CI performance. This skill covers the four major package managers and their ecosystems.
Tool Comparison
| Feature | npm | Yarn Classic (1.x) | Yarn Berry (3+) | pnpm | Bun |
|---|
| Lockfile | package-lock.json | yarn.lock | yarn.lock | pnpm-lock.yaml | bun.lockb (binary) |
| Install Speed | Moderate | Moderate | Fast (PnP) | Fast | Fastest |
| Disk Usage | High (flat node_modules) | High (hoisted) | Low (PnP, no node_modules) | Low (content-addressable store) | Moderate |
| Monorepo Support | Workspaces (basic) | Workspaces | Workspaces + constraints | Workspaces + filtering | Workspaces |
| Plug'n'Play | No | No | Yes (default) | No | No |
| Strictness | Loose (phantom deps) | Loose | Strict (PnP) | Strict (no hoisting) | Loose |
| Patching | overrides (npm 8+) | resolutions | resolutions + yarn patch | pnpm patch | overrides |
| Built-in Runner | npx | N/A | yarn dlx | pnpm dlx / pnpx | bunx |
| Offline Mode | --prefer-offline | --offline | Yes (zero-installs) | --offline | No |
| Corepack | Yes | Yes | Yes | Yes | No |
npm Essentials
npm is the default package manager shipped with Node.js.
Core Commands
npm install
npm ci
npm install express
npm install -D typescript
npm install -g tsx
npm uninstall express
npm run build
npm test
npm start
npx tsx src/index.ts
npx create-next-app@latest
npm ls
npm ls --depth=0
npm audit
npm audit fix
package-lock.json
- Always commit
package-lock.json to version control.
- Use
npm ci in CI pipelines (faster, deterministic, fails on lockfile mismatch).
- Never manually edit the lockfile.
Overrides (npm 8.3+)
Force a transitive dependency version:
{
"overrides": {
"lodash": "4.17.21",
"foo": {
"bar": "1.0.0"
}
}
}
pnpm Deep Dive
pnpm uses a content-addressable store and strict symlink structure to save disk space and prevent phantom dependencies.
How It Works
- All packages are stored in a global content-addressable store (
~/.pnpm-store).
- Each project's
node_modules contains symlinks to the store instead of copies.
- Each package can only access its declared dependencies (strict mode), eliminating phantom dependency bugs.
Core Commands
pnpm install
pnpm add express
pnpm add -D typescript
pnpm add -g tsx
pnpm remove express
pnpm run build
pnpm test
pnpm exec tsx src/index.ts
pnpm dlx create-next-app@latest
pnpm --filter @myorg/api install
pnpm --filter @myorg/api run build
pnpm --filter "./packages/**" run test
pnpm -r run build
pnpm store status
pnpm store prune
pnpm-workspace.yaml
packages:
- "packages/*"
- "apps/*"
- "!**/test/**"
Patching Dependencies
pnpm patch express@4.18.2
pnpm patch-commit ./path-to-modified-package
Patches are stored in a patches/ directory and applied automatically on install.
Configuration (.npmrc for pnpm)
auto-install-peers=true
strict-peer-dependencies=false
shamefully-hoist=false
node-linker=hoisted
Yarn Berry (3+)
Yarn Berry is a complete rewrite of Yarn with Plug'n'Play, constraints, and plugin extensibility.
Plug'n'Play (PnP)
PnP eliminates node_modules entirely. Dependencies are stored as compressed archives in .yarn/cache, and a .pnp.cjs file maps import requests to the correct archive.
yarn set version berry
yarn install
.pnp.cjs
.yarn/cache/
.yarnrc.yml
.yarnrc.yml
nodeLinker: pnp
enableGlobalCache: false
compressionLevel: mixed
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs
spec: "@yarnpkg/plugin-typescript"
npmScopes:
myorg:
npmRegistryServer: "https://npm.pkg.github.com"
npmAlwaysAuth: true
Constraints
Yarn Berry constraints enforce rules across all workspace packages:
% All packages must have a license field
gen_enforced_field(WorkspaceCwd, 'license', 'MIT').
% All packages must use the same TypeScript version
gen_enforced_dependency(WorkspaceCwd, 'typescript', '~5.4.0', 'devDependencies').
yarn constraints
yarn constraints --fix
Zero-Installs
With PnP, the entire .yarn/cache directory can be committed to git, enabling zero-install: cloning the repo gives you a working project with no yarn install needed.
# .gitattributes for zero-installs
.yarn/cache/** binary
.yarn/releases/** binary
.yarn/plugins/** binary
Core Commands
yarn add express
yarn add -D typescript
yarn remove express
yarn dlx create-next-app@latest
yarn workspaces foreach run build
yarn workspaces foreach --topological run build
yarn up express
yarn patch express
Bun as Package Manager
Bun's package manager is built in Zig for maximum speed.
Core Commands
bun install
bun add express
bun add -d typescript
bun add -g tsx
bun remove express
bun run build
bun test
bunx create-next-app@latest
bun install --dry-run
bun.lockb
Bun uses a binary lockfile (bun.lockb) for speed. To inspect it:
bun install --yarn
Workspaces
{
"workspaces": ["packages/*", "apps/*"]
}
bun run --filter @myorg/api build
Key Differences
- Bun is the fastest installer by a significant margin (often 10-30x faster than npm).
- Binary lockfile is not human-readable but is smaller and faster to parse.
- Compatible with
package.json, node_modules, and most npm packages.
- Does not support Plug'n'Play or content-addressable store.
Monorepo Workspace Patterns
Workspace Configuration
All major package managers support workspaces via package.json:
{
"private": true,
"workspaces": [
"packages/*",
"apps/*"
]
}
For pnpm, use pnpm-workspace.yaml instead of package.json workspaces.
Internal Package References
{
"dependencies": {
"@myorg/shared": "workspace:*",
"@myorg/utils": "*"
}
}
Turborepo Integration
Turborepo orchestrates builds across workspaces with caching and parallelism:
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"]
},
"lint": {},
"dev": {
"cache": false,
"persistent": true
}
}
}
turbo run build
turbo run build --filter=@myorg/api
turbo run dev
package.json Anatomy
{
"name": "@myorg/my-package",
"version": "1.2.3",
"description": "A brief description",
"license": "MIT",
"author": "Your Name <you@example.com>",
"repository": {
"type": "git",
"url": "https://github.com/myorg/my-package"
},
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types"
Key Fields Explained
| Field | Purpose |
|---|
type: "module" | Treat .js files as ESM. Without it, Node.js treats them as CJS. |
exports | Modern entry point map. Supports conditional exports for CJS/ESM/types. |
files | Whitelist of files to include in the published package. Reduces package size. |
engines | Declare minimum Node.js version. Enforced by npm with engine-strict=true. |
peerDependencies | Dependencies the consumer must provide. Common for plugins and frameworks. |
overrides / resolutions | Force specific versions of transitive dependencies for security patches or compatibility. |
Publishing Packages
Pre-publish Checklist
- Set
"files" in package.json to include only necessary files.
- Set
"prepublishOnly" script to build and test before publish.
- Verify the package contents:
npm pack --dry-run.
- Ensure
"types" or "exports" types conditions point to valid .d.ts files.
- Test the package locally:
npm link or npm pack && npm install ./my-package-1.0.0.tgz.
Publishing Commands
npm login
npm publish --access public
npm publish --provenance --access public
npm version prerelease --preid=beta
npm publish --tag beta
npm deprecate @myorg/my-package@1.0.0 "Use v2 instead"
npm view @myorg/my-package
Provenance
npm provenance (npm 9.5+) cryptographically links a published package to its source commit and build environment via Sigstore. Enable it in CI:
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Consumers can verify provenance on npmjs.com or with npm audit signatures.
Private Registries
.npmrc Configuration
@myorg:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
@internal:registry=https://artifactory.example.com/api/npm/npm-local/
//artifactory.example.com/api/npm/npm-local/:_authToken=${ARTIFACTORY_TOKEN}
registry=https://registry.npmjs.org/
GitHub Packages
{
"name": "@myorg/my-package",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
}
}
npm login --registry=https://npm.pkg.github.com --scope=@myorg
npm publish
Artifactory / Verdaccio
npm config set @internal:registry https://artifactory.example.com/api/npm/npm-local/
npm login --registry=https://artifactory.example.com/api/npm/npm-local/
Best Practices
-
Choose one package manager per project and commit its lockfile. Do not mix lockfiles.
-
Use npm ci (or pnpm install --frozen-lockfile) in CI pipelines for deterministic installs.
-
Pin exact TypeScript versions (~5.4.0) in devDependencies. TypeScript minor versions can introduce stricter checks that break builds.
-
Use workspace:* protocol (pnpm/yarn) for internal package references so they always resolve to the local version.
-
Enable engine-strict in .npmrc to enforce the engines field and catch Node.js version mismatches early.
-
Use "files" in package.json to explicitly whitelist published files. This prevents accidentally shipping source, tests, or credentials.
-
Set up prepublishOnly to build and test before every publish.
-
Use "exports" instead of "main" for new packages. It provides explicit entry points and prevents deep imports into private modules.
-
Audit dependencies regularly with npm audit or pnpm audit. Configure audit-level=moderate in .npmrc for CI gates.
-
Use Corepack to pin the package manager version across your team:
corepack enable
corepack use pnpm@9
This adds a "packageManager" field to package.json that Corepack enforces.