diff --git a/examples/api/next.config.mjs b/examples/api/next.config.mjs index 4678774e..895f52c4 100644 --- a/examples/api/next.config.mjs +++ b/examples/api/next.config.mjs @@ -1,4 +1,13 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {}; +const nextConfig = { + webpack: ( + config, + ) => { + config.externals = [ + 'wrangler', + ]; + return config; + }, +}; export default nextConfig; diff --git a/examples/api/package.json b/examples/api/package.json index 54bb2149..c9f8ebfc 100644 --- a/examples/api/package.json +++ b/examples/api/package.json @@ -16,12 +16,12 @@ "dependencies": { "next": "catalog:", "react": "catalog:", - "react-dom": "catalog:" + "react-dom": "catalog:", + "wrangler": "catalog:" }, "devDependencies": { "@opennextjs/cloudflare": "workspace:*", "@playwright/test": "catalog:", - "@types/node": "catalog:", - "wrangler": "catalog:" + "@types/node": "catalog:" } } diff --git a/packages/cloudflare/package.json b/packages/cloudflare/package.json index 439af866..a78d963d 100644 --- a/packages/cloudflare/package.json +++ b/packages/cloudflare/package.json @@ -48,5 +48,8 @@ }, "dependencies": { "ts-morph": "catalog:" + }, + "peerDependencies": { + "wrangler": "catalog:" } } diff --git a/packages/cloudflare/src/api/get-cloudflare-context.ts b/packages/cloudflare/src/api/get-cloudflare-context.ts index b9afec87..3edc19cd 100644 --- a/packages/cloudflare/src/api/get-cloudflare-context.ts +++ b/packages/cloudflare/src/api/get-cloudflare-context.ts @@ -37,17 +37,46 @@ export async function getCloudflareContext< CfProperties extends Record = IncomingRequestCfProperties, Context = ExecutionContext, >(): Promise> { - const cloudflareContext = ( - globalThis as unknown as { - [cloudflareContextSymbol]: CloudflareContext | undefined; - } - )[cloudflareContextSymbol]; + const global = globalThis as unknown as { + [cloudflareContextSymbol]: CloudflareContext | undefined; + }; + + const cloudflareContext = global[cloudflareContextSymbol]; if (!cloudflareContext) { - // TODO: cloudflareContext should always be present in production/preview, if not it means that this - // is running under `next dev`, in this case use `getPlatformProxy` to return local proxies - throw new Error("Cloudflare context is not defined!"); + // the cloudflare context is initialized by the worker and is always present in production/preview, + // so, it not being present means that the application is running under `next dev` + return getCloudflareContextInNextDev(); } return cloudflareContext; } + +const cloudflareContextInNextDevSymbol = Symbol.for("__next-dev/cloudflare-context__"); + +/** + * Gets a local proxy version of the cloudflare context (created using `getPlatformProxy`) when + * running in the standard next dev server (via `next dev`) + * + * @returns the local proxy version of the cloudflare context + */ +async function getCloudflareContextInNextDev< + CfProperties extends Record = IncomingRequestCfProperties, + Context = ExecutionContext, +>(): Promise> { + const global = globalThis as unknown as { + [cloudflareContextInNextDevSymbol]: CloudflareContext | undefined; + }; + + if (!global[cloudflareContextInNextDevSymbol]) { + const { getPlatformProxy } = await import("wrangler"); + const { env, cf, ctx } = await getPlatformProxy(); + global[cloudflareContextInNextDevSymbol] = { + env, + cf: cf as unknown as CfProperties, + ctx: ctx as Context, + }; + } + + return global[cloudflareContextInNextDevSymbol]!; +} diff --git a/packages/cloudflare/tsup.config.ts b/packages/cloudflare/tsup.config.ts index 367faabe..e69b049b 100644 --- a/packages/cloudflare/tsup.config.ts +++ b/packages/cloudflare/tsup.config.ts @@ -8,6 +8,7 @@ const cliConfig = defineConfig({ format: ["esm"], platform: "node", external: ["esbuild"], + clean: true, onSuccess: async () => { await cp(`${__dirname}/src/cli/templates`, `${__dirname}/dist/cli/templates`, { recursive: true, @@ -22,6 +23,7 @@ const apiConfig = defineConfig({ format: ["esm"], platform: "node", external: ["server-only"], + clean: true, }); export default [cliConfig, apiConfig]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 335379d9..c56d6d63 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -80,6 +80,9 @@ importers: react-dom: specifier: 'catalog:' version: 18.3.1(react@18.3.1) + wrangler: + specifier: 'catalog:' + version: 3.78.6(@cloudflare/workers-types@4.20240919.0) devDependencies: '@opennextjs/cloudflare': specifier: workspace:* @@ -90,9 +93,6 @@ importers: '@types/node': specifier: 'catalog:' version: 22.2.0 - wrangler: - specifier: 'catalog:' - version: 3.78.6(@cloudflare/workers-types@4.20240919.0) examples/create-next-app: dependencies: @@ -145,6 +145,9 @@ importers: ts-morph: specifier: 'catalog:' version: 23.0.0 + wrangler: + specifier: 'catalog:' + version: 3.78.6(@cloudflare/workers-types@4.20240919.0) devDependencies: '@cloudflare/workers-types': specifier: 'catalog:'