Node.js Package Management
Overview
IVPM provides first-class support for Node.js packages, managing both npm
registry packages and source packages (linked via npm link) within a
project-local Node.js environment.
Note
The Node handler performs the work described on this page. For handler-level details (activation conditions, phase ordering, how it fits into the update pipeline), see Node Handler (node) in Package Handlers.
Node Environment Structure
IVPM creates a project-local Node.js environment in packages/node/
on demand: it is only created when at least one dependency (direct or
transitive) is a Node.js package. Projects that contain no Node packages
never create this directory, keeping the workspace lean.
When a Node environment is created it is placed at:
packages/
└── node/
├── package.json # Generated by IVPM — do not edit manually
├── package-lock.json # Committed for reproducible builds
├── node_modules/ # Installed packages (git-ignored)
├── export.envrc # direnv activation snippet
└── .nvmrc # Node version pin (when version: is set)
Package Manager Selection
IVPM supports three Node.js package managers:
npm — default, requires Node.js to be installed
pnpm — faster, disk-efficient alternative
yarn — alternative package manager
Specify the manager in ivpm.yaml:
package:
name: my-project
with:
node:
manager: pnpm # npm (default) | pnpm | yarn
Configuring the Node Handler
A project can permanently configure the Node handler by adding a with.node
section inside package: in ivpm.yaml.
package:
name: my-project
with:
node:
manager: npm # npm (default) | pnpm | yarn
version: "20" # Node version to pin (.nvmrc)
env: true # Write packages.envrc entry (default: true)
dep-sets:
- name: default-dev
deps:
- name: lodash
src: npm
manager key
Selects the package manager for all node install / node link operations.
Value |
Behaviour |
|---|---|
|
Use |
|
Use |
|
Use |
version key
When set, writes a .nvmrc file to packages/node/ containing the
specified version string. Tools such as nvm use and volta read this
file to select the correct Node runtime automatically.
with:
node:
version: "20" # writes packages/node/.nvmrc
env key
Controls whether the handler writes an activation entry into
packages/packages.envrc. Defaults to true. Set to false to
suppress environment modification (useful for CI or projects that manage their
own PATH).
with:
node:
env: false # Don't patch packages.envrc
npm Registry Packages (src: npm)
Install a package from the npm registry by specifying src: npm.
deps:
# Latest version
- name: lodash
src: npm
# Specific version
- name: react
src: npm
version: "^18.0.0"
# Dev-only dependency
- name: jest
src: npm
dev: true
# Optional dependency
- name: fsevents
src: npm
optional: true
Attributes:
versionnpm semver range (e.g.,
^18.0.0,>=1.0.0 <2.0.0). Defaults to*(latest).devWhen
true, the package is placed indevDependenciesin the generatedpackages/node/package.json. Defaults tofalse.optionalWhen
true, marks the package as optional. Defaults tofalse.
Install behavior: All npm packages are installed into
packages/node/node_modules/ by running the configured package manager.
Importing deps from an Existing package.json (src: package.json)
If your project already has a package.json (e.g., in the repo root or in a
sub-package), you can import all its dependencies into the IVPM-managed
environment without duplicating them:
deps:
- name: root-pkg-json
src: package.json
url: file://${PROJECT_ROOT}/package.json
IVPM reads the dependencies and devDependencies sections of the
referenced file and synthesises PackageNpm entries for each one. An
explicit src: npm entry in the same dep-set always overrides a same-named
entry from src: package.json (explicit entries win).
Attributes:
urlPath to the
package.jsonfile. Supports:file://prefix for absolute paths${VAR}environment variable substitution
Source Package Linking (type: node)
Source packages (git, dir) that provide a Node.js library can be linked into
the managed environment using npm link so that other packages in the
project can require() them directly:
deps:
# Git-hosted TypeScript library
- name: my-ts-lib
url: https://github.com/org/my-ts-lib.git
type: node
# Link but treat as dev-only
- name: shared-utils
url: https://github.com/org/shared-utils.git
type: { node: { dev: true, link: true } }
# Source present but do NOT link (just track)
- name: build-tool
url: https://github.com/org/build-tool.git
type: { node: { link: false } }
Per-package options (``type: { node: { … } }``):
devTreat the linked package as a dev dependency. Defaults to
false.linkWhen
true(default), runsnpm link <path>for the package after install. Set tofalseto track the package without linking.
Auto-detection: IVPM automatically applies type: node (with
link: true) to any source package (git/dir/http/gh-rls) that contains a
package.json but has no explicit type: set. This mirrors the Python
handler’s pyproject.toml auto-detection.
Generated packages/node/package.json
IVPM synthesises a packages/node/package.json from all collected npm
packages. The file is written during ivpm update and should be committed
to your repository (together with package-lock.json) for reproducible
builds.
Example generated file:
{
"name": "ivpm-node-env",
"version": "1.0.0",
"private": true,
"dependencies": {
"lodash": "^4.17.21",
"axios": "^1.0.0"
},
"devDependencies": {
"jest": "^29.0.0",
"typescript": "^5.0.0"
}
}
Note
Do not edit packages/node/package.json manually — IVPM regenerates
it on every ivpm update run. Put all dependency declarations in
ivpm.yaml.
Hash-based skip: IVPM stores a SHA-256 hash of the generated
package.json in packages/ivpm.json. On subsequent runs, if the hash
matches and node_modules/ already exists, the expensive npm install
step is skipped automatically, making repeated ivpm update calls fast.
Environment Activation
direnv (recommended)
The Node handler patches packages/packages.envrc to source the Node
activation snippet automatically when you cd into the project:
packages/
├── packages.envrc # Patched to source node/export.envrc
└── node/
└── export.envrc # Sets PATH and NODE_PATH
Contents of packages/node/export.envrc:
# Generated by IVPM node handler — do not edit manually
PATH_add node_modules/.bin
export NODE_PATH="$PWD/node_modules"
With direnv installed and .envrc sourcing packages/packages.envrc,
all executables in packages/node/node_modules/.bin are on your PATH
automatically (e.g., jest, tsc, eslint).
Manual activation
Without direnv, source the export file manually:
$ source packages/node/export.envrc
Windows
On Windows, IVPM additionally writes:
packages/node/activate_node.bat— addsnode_modules/.binto%PATH%packages/node/activate_node.ps1— PowerShell equivalentpackages/packages_activate.bat/.ps1— top-level wrappers
:: In a .bat script or CMD session
call packages\packages_activate.bat
:: In PowerShell
. packages\packages_activate.ps1
Node Version Management
When version: is configured, IVPM writes a .nvmrc file:
with:
node:
version: "20"
# .nvmrc written to packages/node/.nvmrc
$ cat packages/node/.nvmrc
20
# nvm picks up the version automatically
$ cd packages/node && nvm use
Now using node v20.x.x
# volta also reads .nvmrc
$ volta pin node@$(cat packages/node/.nvmrc)
To ensure the correct Node.js version is active before running IVPM:
$ nvm use 20 && ivpm update
Lockfile Recommendations
Commit packages/node/package-lock.json (or pnpm-lock.yaml /
yarn.lock for other managers) to version control. This ensures every
developer and CI system installs the exact same package versions.
IVPM will warn if package-lock.json is present but git-ignored:
WARNING: packages/node/package-lock.json is gitignored.
Consider committing it for reproducible builds.
Add to .gitignore (node_modules only, not the lockfile):
packages/node/node_modules/
Complete Examples
Example 1: Front-end Tooling
package:
name: my-webapp
default-dep-set: default-dev
with:
node:
manager: npm
version: "20"
dep-sets:
- name: default-dev
deps:
# Runtime
- name: react
src: npm
version: "^18.0.0"
- name: react-dom
src: npm
version: "^18.0.0"
# Dev tools
- name: typescript
src: npm
version: "^5.0.0"
dev: true
- name: jest
src: npm
version: "^29.0.0"
dev: true
- name: "@types/react"
src: npm
version: "^18.0.0"
dev: true
Usage:
$ ivpm update
$ tsc --noEmit # type-check (tsc on PATH via direnv)
$ jest # run tests
$ node -e "require('react')" # verify install
Example 2: Mixed Python + Node Project
package:
name: fullstack-app
default-dep-set: default-dev
with:
python:
venv: uv
node:
manager: npm
dep-sets:
- name: default-dev
deps:
# Python backend
- name: fastapi
src: pypi
- name: uvicorn
src: pypi
# Node frontend build
- name: webpack
src: npm
dev: true
- name: babel-loader
src: npm
dev: true
# Shared TypeScript library (source)
- name: shared-types
url: https://github.com/org/shared-types.git
type: node
Usage:
$ ivpm update
$ uvicorn app:main & # Python backend
$ webpack --config webpack.config.js # Node build
Example 3: Importing from Existing package.json
package:
name: existing-project
default-dep-set: default-dev
dep-sets:
- name: default-dev
deps:
# Import all deps from the repo's root package.json
- name: root-deps
src: package.json
url: file://${PROJECT_ROOT}/package.json
# Override one specific version
- name: lodash
src: npm
version: "^4.17.21" # Overrides whatever is in package.json
Best Practices
Commit lockfiles (
package-lock.json,pnpm-lock.yaml,yarn.lock) for reproducible builds; never commitnode_modules/.Pin Node.js version using
with.node.versionand.nvmrcfor consistent environments.Separate dev deps with
dev: trueto keep production installs lean.Use auto-detection — source packages with
package.jsonare detected automatically; only addtype: nodewhen you need to change options.Let IVPM own
packages/node/package.json— declare all deps inivpm.yamland never edit the generated file directly.Use pnpm for monorepos with many shared sub-packages; it handles symlink-based deduplication better than npm.
See Also
Package Handlers - Node handler details and other built-in handlers
Getting Started with IVPM - Basic project setup
Dependency Sets - Organizing Node.js and other deps together
Package Types & Sources - npm and package.json source types
Python Package Management - Python package management (similar patterns)