Skip to content

Commit 5060305

Browse files
committed
feat: migrate env-runner to 0.2 with explicit deps
1 parent a6378fb commit 5060305

7 files changed

Lines changed: 87 additions & 37 deletions

File tree

docs/2.deploy/20.providers/cloudflare.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,8 @@ defineHandler(async (event) => {
311311

312312
In development mode, Nitro emulates the Cloudflare environment using [Miniflare](https://miniflare.dev/) (the same [`workerd`](https://github.com/cloudflare/workerd) runtime used by Wrangler and cloudflare workers in production). This means bindings are available natively from the request event — no separate proxy or `wrangler` installation is required.
313313

314+
The [`miniflare`](https://www.npmjs.com/package/miniflare) package is owned by your project: Nitro resolves it from your `node_modules` and offers to install it on first use.
315+
314316
To access bindings in dev mode, we first define them. You can do this in a `wrangler.jsonc`/`wrangler.json`/`wrangler.toml` file:
315317

316318
::code-group

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
"consola": "^3.4.2",
7373
"crossws": "^0.4.12",
7474
"db0": "^0.3.4",
75-
"env-runner": "^0.1.16",
75+
"env-runner": "^0.2.0",
7676
"h3": "^2.0.1-rc.29",
7777
"hookable": "^6.1.1",
7878
"nf3": "^0.3.23",

pnpm-lock.yaml

Lines changed: 11 additions & 33 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/build/vite/env.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { join, resolve } from "node:path";
77
import { runtimeDependencies, runtimeDir } from "nitro/meta";
88
import { resolveModulePath } from "exsolve";
99
import { isAbsolute } from "pathe";
10+
import { resolveMiniflareDeps, resolveRunnerDeps } from "../../dev/runner-deps.ts";
1011

1112
export function createNitroEnvironment(ctx: NitroPluginContext): EnvironmentOptions {
1213
const isWorkerdRunner = _isWorkerdRunner(ctx);
@@ -170,8 +171,11 @@ async function _loadRunner(ctx: NitroPluginContext, manager: RunnerManager) {
170171
let runner;
171172
if (runnerName === "miniflare") {
172173
const { MiniflareEnvRunner } = await import("env-runner/runners/miniflare");
174+
const { miniflare, wranglerModule } = await resolveMiniflareDeps(ctx.nitro!);
173175
runner = new MiniflareEnvRunner({
174176
name: "nitro-vite",
177+
miniflare,
178+
wranglerModule,
175179
wrangler: {
176180
...ctx.nitro!.options.cloudflare?.wrangler,
177181
},
@@ -180,6 +184,7 @@ async function _loadRunner(ctx: NitroPluginContext, manager: RunnerManager) {
180184
});
181185
} else {
182186
runner = await loadRunner(runnerName, {
187+
...(await resolveRunnerDeps(ctx.nitro!, runnerName)),
183188
name: "nitro-vite",
184189
data: { entry },
185190
});

src/dev/runner-deps.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import type { RunnerName } from "env-runner";
2+
import type { Nitro } from "nitro/types";
3+
4+
import { pathToFileURL } from "node:url";
5+
import { resolveModulePath } from "exsolve";
6+
import { ensureDep } from "../utils/dep.ts";
7+
8+
export interface MiniflareRunnerDeps {
9+
miniflare?: URL;
10+
wranglerModule?: URL;
11+
[key: string]: unknown;
12+
}
13+
14+
/**
15+
* Resolve the platform packages a dev runner needs from the user project.
16+
*
17+
* Runners do not import these packages themselves: each one is passed as an
18+
* explicit option so the version installed next to the app is the version that
19+
* runs. Unresolved entries are left out and the runner falls back to its own
20+
* optional import (or a degraded mode).
21+
*/
22+
export async function resolveRunnerDeps(
23+
nitro: Nitro,
24+
runner: RunnerName
25+
): Promise<Record<string, unknown>> {
26+
switch (runner) {
27+
case "miniflare": {
28+
return resolveMiniflareDeps(nitro);
29+
}
30+
case "netlify": {
31+
// `@netlify/runtime` is instantiated inside the worker thread, so it can
32+
// only be handed over as a specifier, never as an imported module.
33+
return { netlifyRuntime: _resolve("@netlify/runtime", nitro.options.rootDir) };
34+
}
35+
default: {
36+
return {};
37+
}
38+
}
39+
}
40+
41+
export async function resolveMiniflareDeps(nitro: Nitro): Promise<MiniflareRunnerDeps> {
42+
const miniflare = await ensureDep({
43+
id: "miniflare",
44+
dir: nitro.options.rootDir,
45+
reason: "the `miniflare` dev runner",
46+
version: "^4",
47+
});
48+
return {
49+
miniflare: miniflare ? pathToFileURL(miniflare) : undefined,
50+
// Optional: without it, a built-in minimal reader handles plain JSON
51+
// wrangler configs and inline objects.
52+
wranglerModule: _resolve("wrangler", nitro.options.rootDir),
53+
};
54+
}
55+
56+
function _resolve(id: string, dir: string): URL | undefined {
57+
const path = resolveModulePath(id, { from: [dir, import.meta.url], try: true });
58+
return path ? pathToFileURL(path) : undefined;
59+
}

src/dev/server.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { serve } from "srvx/node";
1616
import { debounce } from "perfect-debounce";
1717
import { isTest, isCI } from "std-env";
1818
import { NitroDevApp } from "./app.ts";
19+
import { resolveRunnerDeps } from "./runner-deps.ts";
1920
import { writeDevBuildInfo } from "../build/info.ts";
2021

2122
const SHUTDOWN_TIMEOUT = 5000;
@@ -189,9 +190,11 @@ export class NitroDevServer extends NitroDevApp implements RunnerRPCHooks {
189190

190191
async #reload() {
191192
await this.#shutdownWorker();
192-
const runnerName =
193-
this.nitro.options.devServer.runner || process.env.NITRO_DEV_RUNNER || "node-worker";
194-
const runner = await loadRunner(runnerName as RunnerName, {
193+
const runnerName = (this.nitro.options.devServer.runner ||
194+
process.env.NITRO_DEV_RUNNER ||
195+
"node-worker") as RunnerName;
196+
const runner = await loadRunner(runnerName, {
197+
...(await resolveRunnerDeps(this.nitro, runnerName)),
195198
name: `Nitro_${this.#workerIdCtr++}`,
196199
data: { entry: this.#entry, ...this.#workerData },
197200
});

src/presets/vercel/runtime/queue.dev.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as queueSdk from "@vercel/queue";
12
import { send } from "@vercel/queue";
23
import { useRuntimeConfig } from "nitro/runtime-config";
34
import { registerVercelQueueConsumer } from "env-runner/runners/vercel/queue-dev";
@@ -25,6 +26,8 @@ const queueDevPlugin: NitroAppPlugin = (nitroApp) => {
2526
const ready = Promise.all(
2627
triggers.map((trigger) =>
2728
registerVercelQueueConsumer({
29+
// The SDK is owned by the app, so hand env-runner the instance we bundled.
30+
sdk: queueSdk,
2831
topic: trigger.topic,
2932
retryAfterSeconds: trigger.retryAfterSeconds,
3033
handler: async (message: unknown, metadata: unknown) => {

0 commit comments

Comments
 (0)