From adb4e86bde95db9851ce452d151b967e52162eb2 Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Thu, 23 Nov 2023 12:13:17 +0000 Subject: [PATCH] WIP - Cloudflare bindings in dev mode --- packages/remix-dev/config.ts | 11 +++++++++++ packages/remix-dev/vite/node/adapter.ts | 14 ++++++++++---- packages/remix-dev/vite/plugin.ts | 17 +++++++++++++---- packages/remix-server-runtime/server.ts | 14 ++++++++------ templates/cloudflare-pages/remix.config.js | 6 ++++++ 5 files changed, 48 insertions(+), 14 deletions(-) diff --git a/packages/remix-dev/config.ts b/packages/remix-dev/config.ts index b3b2a25fc44..68d125fa789 100644 --- a/packages/remix-dev/config.ts +++ b/packages/remix-dev/config.ts @@ -4,6 +4,7 @@ import { pathToFileURL } from "node:url"; import fse from "fs-extra"; import PackageJson from "@npmcli/package-json"; import type { NodePolyfillsOptions as EsbuildPluginsNodeModulesPolyfillOptions } from "esbuild-plugins-node-modules-polyfill"; +import type { AppLoadContext } from "@remix-run/server-runtime"; import type { RouteManifest, DefineRoutesFunction } from "./config/routes"; import { defineRoutes } from "./config/routes"; @@ -187,6 +188,13 @@ export interface AppConfig { ? // Partial doesn't work when it's empty so just prevent any keys { [key: string]: never } : Partial; + + /** + * The load context to use in dev mode is provided to the request handler. + * + * Adapters could set this to be used at dev time. + */ + devLoadContext?: AppLoadContext; } /** @@ -353,6 +361,8 @@ export interface RemixConfig { tsconfigPath: string | undefined; future: FutureConfig; + + devLoadContext?: AppLoadContext; } /** @@ -652,6 +662,7 @@ export async function resolveConfig( watchPaths, tsconfigPath, future, + devLoadContext: appConfig.devLoadContext, }; } diff --git a/packages/remix-dev/vite/node/adapter.ts b/packages/remix-dev/vite/node/adapter.ts index 2e6d3c57443..ab1d7da406d 100644 --- a/packages/remix-dev/vite/node/adapter.ts +++ b/packages/remix-dev/vite/node/adapter.ts @@ -7,7 +7,10 @@ import { splitCookiesString } from "set-cookie-parser"; import { Readable } from "stream"; import { File, FormData, Headers, Request as BaseNodeRequest } from "undici"; import { type ServerBuild, installGlobals } from "@remix-run/node"; -import { createRequestHandler as createBaseRequestHandler } from "@remix-run/server-runtime"; +import { + type AppLoadContext, + createRequestHandler as createBaseRequestHandler, +} from "@remix-run/server-runtime"; installGlobals(); @@ -202,12 +205,15 @@ export let createRequestHandler = ( }: { mode?: string; criticalCss?: string; - } + }, + devLoadContext?: AppLoadContext ) => { - let handler = createBaseRequestHandler(build, mode); + let handler = createBaseRequestHandler(build, mode, devLoadContext); return async (req: IncomingMessage, res: ServerResponse) => { let request = createRequest(req); - let response = await handler(request, {}, { __criticalCss: criticalCss }); + let response = await handler(request, { + __criticalCss: criticalCss, + }); handleNodeResponse(response, res); }; }; diff --git a/packages/remix-dev/vite/plugin.ts b/packages/remix-dev/vite/plugin.ts index 9180271796d..1ca581a0e4d 100644 --- a/packages/remix-dev/vite/plugin.ts +++ b/packages/remix-dev/vite/plugin.ts @@ -42,6 +42,7 @@ const supportedRemixConfigKeys = [ "routes", "serverBuildPath", "serverModuleFormat", + "devLoadContext", ] as const satisfies ReadonlyArray; type SupportedRemixConfigKey = typeof supportedRemixConfigKeys[number]; type SupportedRemixConfig = Pick; @@ -83,6 +84,7 @@ type ResolvedRemixVitePluginConfig = Pick< | "routes" | "serverBuildPath" | "serverModuleFormat" + | "devLoadContext" >; let serverEntryId = VirtualModule.id("server-entry"); @@ -297,6 +299,7 @@ export const remixVitePlugin: RemixVitePlugin = (options = {}) => { serverBuildPath, serverModuleFormat, relativeAssetsBuildDirectory, + devLoadContext, } = await resolveConfig(config, { rootDirectory }); return { @@ -313,6 +316,7 @@ export const remixVitePlugin: RemixVitePlugin = (options = {}) => { future: { v3_fetcherPersist: options.future?.v3_fetcherPersist === true, }, + devLoadContext, }; }; @@ -757,15 +761,20 @@ export const remixVitePlugin: RemixVitePlugin = (options = {}) => { if (!vite.config.server.middlewareMode) { vite.middlewares.use(async (req, res, next) => { try { + let pluginConfig = await resolvePluginConfig(); let locals = localsByRequest.get(req); invariant(locals, "No Remix locals found for request"); let { build, criticalCss } = locals; - let handle = createRequestHandler(build, { - mode: "development", - criticalCss, - }); + let handle = createRequestHandler( + build, + { + mode: "development", + criticalCss, + }, + pluginConfig.devLoadContext + ); await handle(req, res); } catch (error) { diff --git a/packages/remix-server-runtime/server.ts b/packages/remix-server-runtime/server.ts index 471047a43b1..c7688b56c2f 100644 --- a/packages/remix-server-runtime/server.ts +++ b/packages/remix-server-runtime/server.ts @@ -31,18 +31,19 @@ import { createServerHandoffString } from "./serverHandoff"; export type RequestHandler = ( request: Request, - loadContext?: AppLoadContext, args?: { /** * @private This is an internal API intended for use by the Remix Vite plugin in dev mode */ __criticalCss?: string; - } + }, + loadContext?: AppLoadContext ) => Promise; export type CreateRequestHandlerFunction = ( build: ServerBuild | (() => Promise), - mode?: string + mode?: string, + devLoadContext?: AppLoadContext ) => RequestHandler; function derive(build: ServerBuild, mode?: string) { @@ -72,7 +73,8 @@ function derive(build: ServerBuild, mode?: string) { export const createRequestHandler: CreateRequestHandlerFunction = ( build, - mode + mode, + devLoadContext = {} ) => { let _build: ServerBuild; let routes: ServerRoute[]; @@ -82,8 +84,8 @@ export const createRequestHandler: CreateRequestHandlerFunction = ( return async function requestHandler( request, - loadContext = {}, - { __criticalCss: criticalCss } = {} + { __criticalCss: criticalCss } = {}, + loadContext = devLoadContext ) { _build = typeof build === "function" ? await build() : build; if (typeof build === "function") { diff --git a/templates/cloudflare-pages/remix.config.js b/templates/cloudflare-pages/remix.config.js index e35ffadbb5d..dbe26b6bcfe 100644 --- a/templates/cloudflare-pages/remix.config.js +++ b/templates/cloudflare-pages/remix.config.js @@ -12,4 +12,10 @@ export default { // appDirectory: "app", // assetsBuildDirectory: "public/build", // publicPath: "/build/", + defaultLoadContext: { + // Example Cloudflare binding that could be made available to Remix + env: { + SOME_ENV: "some value", + }, + }, };