NPM

Hex.pmCI

npm package management for Elixir — resolve, fetch, cache, and link npm packages from Mix without requiring Node.js for installation.

mix npm.install lodash
mix npm.exec eslint .

npm_ex reads package.json, resolves npm semver with PubGrub, writes package-lock.json, and links packages into node_modules/.

Why npm_ex

Elixir projects increasingly need JavaScript packages for assets, formatters, linters, browser libraries, and runtime integrations. npm_ex keeps that workflow inside Mix:

Installation

def deps do
[{:duskmoon_npm, "~> 9.5.1"}]
end
mix npm.init
mix npm.install lodash

Elixir umbrella projects

For umbrella projects, prefer npm workspaces for frontend assets:

Example root manifest:

{
"name": "my_umbrella",
"private": true,
"workspaces": ["apps/*"],
"dependencies": {
"tailwindcss": "4.3.0"
}
}

Example Phoenix web app manifest:

{
"name": "my_app_web",
"private": true,
"dependencies": {
"phoenix_html": "file:../../deps/phoenix_html"
},
"devDependencies": {
"postcss": "8.5.15"
}
}

npm_ex links workspace packages and local directory file: packages into the root node_modules/ and resolves registry dependencies into package-lock.json. When mix npm.install <package> is run from a workspace package, the dependency is saved to that package's package.json while installation still happens at the workspace root.

Common workflows

# Install and maintain dependencies
mix npm.install
mix npm.install lodash@^4.0
mix npm.install eslint --save-dev
mix npm.update
mix npm.remove lodash
# CI / reproducibility
mix npm.install --frozen
mix npm.ci
mix npm.verify
# Inspect dependency state
mix npm.list
mix npm.tree
mix npm.why accepts
mix npm.outdated
# Run scripts and binaries
mix npm.run build
mix npm.exec eslint .
# Registry, cache, and config
mix npm.info express
mix npm.search react
mix npm.cache status
mix npm.config

How installs work

  1. Read root package.json dependencies, workspace package dependencies, dev dependencies, optional dependencies, and root overrides.
  2. Resolve the full dependency tree using duskmoon_hex_solver and the bundled npm semver parser.
  3. Fetch registry packuments and tarballs with integrity verification.
  4. Store package contents in the global cache.
  5. Link registry packages plus local workspace packages into node_modules/ and write package-lock.json.

npm_ex writes npm package-lock.json v3 and stores npm_ex-specific security policy metadata under x-npm-ex. Existing legacy npm.lock files are migrated automatically when package-lock.json is missing.

Supply-chain safety

npm_ex is intentionally conservative around install-time code execution:

This blocks common install-time credential stealers that rely on postinstall hooks reading files like .env and exfiltrating secrets during dependency installation.

Auditing malicious packages

mix npm.audit supports npm vulnerability checks and OSV/OpenSSF malicious-package intelligence:

# npm registry vulnerability audit
mix npm.audit
# Strict online OSV malicious-package gate
mix npm.audit --osv
# Refresh the shared local malicious-package cache for the current lockfile
mix npm.audit --osv --write-cache --policy warn
# Deterministic offline gate using the shared cache or configured DB
mix npm.audit --compromised

--write-cache merges matching OSV advisories into ~/.npm_ex/security/compromised_packages.json by default. mix npm.audit --osv fails closed when OSV cannot be queried; mix npm.audit --compromised is offline and deterministic.

OpenSSF/OSV is the default-compatible open data source. Socket, Snyk, and Phylum provide valuable proprietary intelligence or install-time firewall workflows; they fit best as external scanners/proxies or future optional integrations rather than default npm_ex install dependencies.

Configuration

Most projects only need the defaults. Use mix npm.config to inspect effective settings.

Common environment variables:

Elixir application config is also supported:

config :duskmoon_npm,
registry: "https://registry.npmjs.org",
token: System.get_env("NPM_TOKEN"),
cache_dir: Path.expand("~/.npm_ex"),
block_exotic_subdeps: true,
exotic_deps: [],
allowed_registries: ["https://registry.npmjs.org"],
allow_registry_redirects: false,
package_age_warning_days: 7,
version_age_warning_days: 3,
compromised_db_path: Path.expand("~/.npm_ex/security/compromised_packages.json"),
compromised_policy: :error

API organization

The main public API is NPM. Supporting modules are grouped by domain: NPM.Package.*, NPM.Dependency.*, NPM.Lockfile.*, NPM.Security.*, NPM.Registry.*, NPM.Config.*, NPM.Install.*, NPM.Node.*, NPM.NodeModules.*, and NPM.Diagnostics.*.

See CHANGELOG.md for the 0.7 migration map from older pre-namespace module names.

Documentation

Full guides and API documentation are available on HexDocs:

License

MIT © 2026 Danila Poyarkov