On this page
Migrate from pnpm
pnpm is a package manager, not a runtime, so most of what you are "migrating" is
configuration rather than code. Deno reads your existing package.json,
installs the same npm dependencies, and runs your scripts, so a single-package
pnpm project usually needs no changes at all.
The one place pnpm differs from npm, Yarn, and Bun is where it stores workspace
configuration. npm, Yarn, and Bun keep their workspace globs in package.json
under workspaces, which Deno reads directly. pnpm keeps them in a separate
pnpm-workspace.yaml file. Deno does not read that file during resolution, but
it migrates it for you automatically the first time it is needed, covered below.
Run your project Jump to heading
Install dependencies and run your entrypoint:
cd my-pnpm-app
deno install
deno run main.ts
deno install reads your existing package.json and resolves the same npm
packages, like pnpm install. It writes a node_modules directory and its own
deno.lock.
On your first deno install, when there is no deno.lock yet, Deno seeds it
from your existing pnpm-lock.yaml: the versions and integrity hashes you
already had pinned carry over, so you don't get a surprise round of upgrades on
the way in. The node_modules layout will feel familiar too: like pnpm, Deno
uses an isolated layout where each package only sees its declared dependencies.
Scripts defined in package.json run with
deno task, the equivalent of pnpm run:
deno task dev
If you also switch from running your app on Node to running it on Deno, expect
one immediate difference: Deno is sandboxed by default. The first time your
program touches the network, file system, or environment, Deno prompts for
permission. Grant everything up front with deno run -A main.ts to match Node's
behavior, then tighten the flags later. See
Security and permissions.
pnpm to Deno cheatsheet Jump to heading
Dependencies Jump to heading
| pnpm | Deno |
|---|---|
pnpm install |
deno install |
pnpm add <pkg> |
deno add <pkg> |
pnpm add -D <pkg> |
deno add -D <pkg> |
pnpm remove <pkg> |
deno remove <pkg> |
pnpm update |
deno update |
pnpm outdated |
deno outdated |
pnpm install --frozen-lockfile |
deno ci |
pnpm audit |
deno audit |
pnpm why <pkg> |
deno why <pkg> |
Run and execute Jump to heading
| pnpm | Deno |
|---|---|
pnpm <script> |
deno task <script> |
pnpm run <script> |
deno task <script> |
pnpm dlx <pkg> |
dx <pkg> |
pnpm exec <cmd> |
deno task <cmd> |
Unlike pnpm, Deno also ships a formatter and linter in the box, so
deno fmt and
deno lint replace Prettier, ESLint, or Biome
with no extra dependencies.
Workspaces and pnpm-workspace.yaml Jump to heading
npm, Yarn, and Bun store their workspace globs in package.json, but pnpm keeps
them in a separate pnpm-workspace.yaml, which Deno does not read during
resolution. You don't have to convert that file by hand: the first time a
workspace member or catalog version fails to resolve because of it, Deno finds
the nearby pnpm-workspace.yaml, converts its packages, catalog, and
catalogs into the equivalent Deno config fields, and prints a hint:
warning: Found pnpm-workspace.yaml nearby, which Deno does not read directly.
hint: Migrated its workspace configuration into package.json. Run the command again.
Run the same command again and it resolves normally. After the migration your
workspace globs live in a workspace array, the equivalent of pnpm's packages
list:
packages:
- "packages/*"
- "apps/*"
{
"workspace": ["packages/*", "apps/*"]
}
A couple of differences are worth knowing when you read the migrated globs:
- Depth is explicit in Deno.
packages/*matches one level (packages/foo), andpackages/*/*matches two levels. There is no**recursive glob; add a/*segment per level instead. See Workspace path patterns. - Exclusions are not supported. pnpm's
!packages/excludednegation has no equivalent in theworkspacefield, so list the members you want explicitly rather than excluding a few from a wildcard.
Catalogs Jump to heading
pnpm catalogs share dependency versions across
members, and Deno supports the same catalog: protocol (added in Deno 2.8). The
auto-migration moves your catalog definitions into the root deno.json for you,
using the same field names:
{
"workspace": ["packages/*"],
"catalog": {
"react": "^19.0.0"
},
"catalogs": {
"react18": {
"react": "^18.3.0"
}
}
}
The catalog: references inside each member's package.json stay exactly as
they are: "react": "catalog:" resolves to the default catalog, and
"react": "catalog:react18" resolves to a named entry in catalogs. See
Centralized dependency versions with catalog:.
What has no direct equivalent Jump to heading
A few pnpm-workspace.yaml settings are pnpm-specific and have no Deno
counterpart. Plan around them rather than translating them:
overrides. Force a transitive dependency to a specific version through thenpm:resolution your project already uses, or with an import map entry.patchedDependencies. Deno has no built-in patch-package mechanism. Vendor the dependency or maintain the patch in your own fork.registries,packageExtensions, and similar tuning. These configure pnpm's resolver specifically and do not carry over.
Keep going Jump to heading
- Migrate from npm. The npm and pnpm CLI workflows map to the same Deno commands.
- Migrate from Node.js. If you are also moving the runtime, this covers CommonJS and ES module resolution and the Node built-ins Deno supports.
- Workspaces. How Deno resolves members, shared imports, and catalogs in detail.
- Dependency management. npm, JSR, and
package.jsonworkflows. - Supply chain management.
deno audit, lockfile discipline, and minimum dependency age.