Open-Source Wikis

/

Deno

/

Features

/

npm support

denoland/deno

npm support

Active contributors: David Sherret, Bartek Iwańczuk, Marvin Hagemeister

Purpose

Deno can import directly from the npm registry. import express from "npm:express@4" works without a package.json, without a build step, and without a separate install command. Behind the scenes this involves: parsing the specifier, querying the npm registry, resolving the dependency graph, downloading and extracting tarballs, and serving the resulting modules to the runtime through Node-style resolution rules.

This page maps the layers and points you at the specific code for each.

The layers

graph TD
    User["import 'npm:express@4'"] --> ML["cli/module_loader.rs"]
    ML --> Resolver["libs/resolver"]
    Resolver --> NpmRes["NpmResolver<br/>libs/npm"]
    NpmRes -->|registry call| Registry["registry.npmjs.org<br/>(or .npmrc-configured registry)"]
    NpmRes -->|version solve| Graph["libs/npm/resolution/graph.rs<br/>(9,478 lines)"]
    Graph --> Lockfile["libs/lockfile<br/>(deno.lock)"]
    NpmRes --> Cache["libs/npm_cache"]
    Cache --> Tarball["DENO_DIR/npm/registry.npmjs.org/.../tarball.tgz"]
    Cache --> Installer["libs/npm_installer"]
    Installer --> Layout{"layout?"}
    Layout -->|auto| GlobalCache["DENO_DIR/npm/<br/>(unique paths)"]
    Layout -->|BYONM| LocalNM["./node_modules/"]
    GlobalCache --> NodeRes["libs/node_resolver"]
    LocalNM --> NodeRes
    NodeRes -->|file URL| ML

Crate roles

libs/npm

The npm client. Handles:

  • npm registry HTTP queries (with the .npmrc-configured registries and auth)
  • semver constraint parsing
  • the dependency-graph solver — libs/npm/resolution/graph.rs (9,478 lines) — implementing peer-dependency hoisting, optional deps, npm's particular conflict rules
  • Lockfile interaction (read pinned versions, write new resolutions)

libs/npm_cache

On-disk caching of registry metadata and package tarballs. Lives under DENO_DIR/npm/registry.npmjs.org/<package>/. Implements the cache invalidation: registry metadata is refreshed on deno install or when a new version is requested; tarballs are immutable once extracted.

libs/npm_installer

Actual installation: tarball extraction, file layout, optional dependency handling. The --allow-scripts flag is enforced here — npm packages with postinstall scripts only run those scripts when explicitly allowed by name (deno install --allow-scripts=esbuild,sharp).

libs/npmrc

Parser for .npmrc files. Supports per-scope registries (@my-scope:registry=https://npm.pkg.github.com), auth tokens (//registry.example.com:_authToken=...), and the env-var interpolation (${NPM_TOKEN}) standard.

libs/node_resolver

Once a package is on disk, this crate handles require('foo') and ESM imports per Node's algorithm: walking node_modules/ directories, applying package.json exports/imports, conditional exports (browser, node, import, require, default), CJS/ESM extension rules.

cli/npm.rs

The CLI-side glue (~736 lines): wires the above crates into cli/factory.rs, drives deno install/deno add/deno remove, and implements the BYONM detection logic.

Two layouts

Auto-managed (default)

With no nodeModulesDir in deno.json (or with "nodeModulesDir": "auto"), Deno stores npm packages in DENO_DIR/npm/. Each package version lives in its own directory; the resolver maps import 'foo' to that path directly. There's no project-local node_modules/ directory.

Pros: shared cache across projects, no duplication, no install step.

BYONM ("bring your own node_modules")

With "nodeModulesDir": "manual" in deno.json, Deno expects a project-local node_modules/ directory that the user manages with npm install (or pnpm, yarn, etc.). Deno reads from it but doesn't write to it.

Pros: compatibility with tools that walk node_modules/ directly (some bundlers, IDEs, type tools), familiar workflow for npm-using teams. Cons: gives up Deno's automatic caching.

The detection and switching logic lives in cli/npm.rs and cli/factory.rs.

Subcommand surface

The npm-related subcommands are implemented under cli/tools/pm/:

Subcommand What it does
deno add <pkg> Add a dependency to deno.json imports (or package.json in BYONM)
deno remove <pkg> Remove a dependency
deno install Install all dependencies; populates node_modules/ in BYONM, or warms the cache otherwise
deno install <pkg> Install a single package
deno uninstall Reverse of install
deno outdated List packages with newer versions available
deno audit Run npm audit-equivalent vulnerability check
deno approve-scripts Approve postinstall scripts that were blocked

deno install recently grew --prod (skip dev deps and @types/*) and --os/--arch for cross-platform installs (commits in late April 2026).

Lockfile interaction

deno.lock records the resolved version and integrity hash for every npm package. When you do deno run script.ts:

  • If the lockfile has an entry for an npm: specifier, the recorded version is used and the integrity hash is checked against the cache.
  • If a specifier isn't in the lockfile, npm is queried, the result is recorded, and the lockfile is updated (unless --frozen-lockfile is set).

The recent commit test(npm): add regression test for update breaking peer dep lockfile entries reflects the kind of subtle interactions that show up — peer dep changes can invalidate lockfile entries even when no direct dep changed.

package.json support

When a project has a package.json, Deno respects:

  • dependencies, devDependencies, peerDependencies, optionalDependencies
  • imports (the package.json field, not Deno's separate imports)
  • exports
  • The scripts field is read by deno task to expose npm scripts as Deno tasks

Implementation: libs/package_json parses the file; libs/node_resolver honors exports/imports; cli/tools/task.rs exposes scripts.

Tests

  • Spec tests under tests/specs/install/, tests/specs/npm/, tests/specs/node/.
  • Registry fixtures live in tests/registry/.
  • Big chunks of integration logic are exercised by the LSP suite tests/integration/lsp_tests.rs.

Entry points for modification

  • Resolution algorithm changelibs/npm/resolution/ (most likely graph.rs).
  • Cache layout changelibs/npm_cache/ plus libs/cache_dir.
  • Installation behaviorlibs/npm_installer/.
  • CLI behavior (e.g., new deno install flag) — cli/args/flags.rs for the flag, cli/tools/pm/ for the handler.
  • Node-style resolution semanticslibs/node_resolver/.

Key source files

File Purpose
cli/npm.rs CLI-side npm glue
cli/tools/pm/ deno add/remove/install/outdated/audit handlers
libs/npm/lib.rs Crate root — public API
libs/npm/resolution/graph.rs Dependency-graph solver (9,478 lines)
libs/npm_cache/ Tarball/registry-response cache
libs/npm_installer/ Package installation
libs/npmrc/ .npmrc parser
libs/node_resolver/ Node-style resolution
libs/lockfile/ deno.lock parser/writer
libs/package_json/ package.json parser

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

npm support – Deno wiki | Factory