Skip to main content
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:

pnpm-workspace.yaml (before)
packages:
  - "packages/*"
  - "apps/*"
deno.json (after)
{
  "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), and packages/*/* 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/excluded negation has no equivalent in the workspace field, 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:

deno.json
{
  "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 the npm: 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

Last updated on

Did you find what you needed?

Edit this page
Privacy policy