#4618·nitro

Resolve `npm:`/`jsr:` versions from the installed tree for Deno-based presets

Author: sandros94Created Sep 13, 2026Updated Sep 15, 2026
Labelspending triage

Describe the feature

While fixing something on the Bunny preset (#4025) I've made the Deno-based presets keep npm: and jsr: specifiers external, so import { escape } from "npm:[email protected]" survives the bundle and gets resolved by the runtime instead of failing to resolve at build time.

That part works, but pinning the version is currently entirely on the user. What I would actually like is to mark a package as external and have Nitro emit the version that is installed, so a plain import { decodeJwt } from "unjwt" comes out as npm:[email protected] even when package.json says ^0.7.0.

The clearest win is on Bunny, where the script is a single file with a 10MB cap, so keeping deps out of the bundle actually matters. Tho it would apply to deno_server and deno_deploy just as well, for custom implementations and runtimes.

Most of the machinery seems to already be there: nf3 hands us the exact resolved versions per package (TracedPackage.versions), and we already consume that hook in externals.ts only to log them.

Two things I got stuck on though:

  1. the trace only runs when nitro.options.node is true, and the presets that would want this (deno_deploy, bunny) set node: false. So the version resolution would need to be independent from the file-copying path
  2. it only pins the direct dependency. Deno then resolves the transitive ones from the registry on its side, so it is a partial pin and not really a lockfile equivalent. I am not sure whether that is acceptable or if it defeats the point

I also have no strong opinion on the config surface, an opt-in list of packages feels right but that is about as far as I got.

Additional information

  • Would you be willing to help implement this feature?