UNPKG

@mcansh/remix-fastify

Version:

Fastify server request handler for Remix and React Router

127 lines (124 loc) 4.32 kB
import { Readable } from "node:stream"; import path from "node:path"; import url from "node:url"; import fastifyStatic from "@fastify/static"; import { cacheHeader } from "pretty-cache-header"; //#region src/shared.ts function createHeaders(requestHeaders) { let headers = new Headers(); for (let [key, values] of Object.entries(requestHeaders)) if (values) if (Array.isArray(values)) for (let value of values) headers.append(key, value); else headers.set(key, values); return headers; } function getUrl(request) { let origin = `${request.protocol}://${request.host}`; let url$1 = `${origin}${request.originalUrl}`; return url$1; } function createRequest(request, reply, createReadableStreamFromReadable) { let url$1 = getUrl(request); let controller = new AbortController(); let init = { method: request.method, headers: createHeaders(request.headers), signal: controller.signal }; reply.raw.on("finish", () => controller = null); reply.raw.on("close", () => controller?.abort()); if (request.method !== "GET" && request.method !== "HEAD") { init.body = createReadableStreamFromReadable(request.raw); init.duplex = "half"; } return new Request(url$1, init); } async function sendResponse(reply, nodeResponse) { reply.status(nodeResponse.status); for (let [key, values] of nodeResponse.headers.entries()) reply.headers({ [key]: values }); if (nodeResponse.body) { let stream = responseToReadable(nodeResponse); return reply.send(stream); } return reply.send(await nodeResponse.text()); } function responseToReadable(response) { if (!response.body) return null; let reader = response.body.getReader(); let readable = new Readable(); readable._read = async () => { try { let result = await reader.read(); if (!result.done) readable.push(Buffer.from(result.value)); else readable.push(null); } catch (error) { readable.destroy(error); throw error; } }; return readable; } //#endregion //#region src/plugins/index.ts function createPlugin(fastify, { basename = "/", buildDirectory = "build", serverBuildFile = "index.js", getLoadContext, mode = process.env.NODE_ENV, viteOptions, fastifyStaticOptions, assetCacheControl = { public: true, maxAge: "1 year", immutable: true }, defaultCacheControl = { public: true, maxAge: "1 hour" }, productionServerBuild, childServerOptions }, virtualModule, createRequestHandler) { return async () => { let cwd = process.env.REMIX_ROOT ?? process.cwd(); let vite; if (mode !== "production") vite = await import("vite").then((mod) => { return mod.createServer({ ...viteOptions, server: { ...viteOptions?.server, middlewareMode: true } }); }); let resolvedBuildDirectory = path.resolve(cwd, buildDirectory); let SERVER_BUILD = path.join(resolvedBuildDirectory, "server", serverBuildFile); let SERVER_BUILD_URL = url.pathToFileURL(SERVER_BUILD).href; let handler = createRequestHandler({ mode, getLoadContext, build: vite ? () => vite.ssrLoadModule(virtualModule) : productionServerBuild ?? await import(SERVER_BUILD_URL) }); if (vite) { let middie = await import("@fastify/middie").then((mod) => mod.default); await fastify.register(middie); fastify.use(vite.middlewares); } else { let BUILD_DIR = path.join(resolvedBuildDirectory, "client"); let ASSET_DIR = path.join(BUILD_DIR, "assets"); await fastify.register(fastifyStatic, { root: BUILD_DIR, prefix: basename, wildcard: false, cacheControl: false, dotfiles: "allow", etag: true, serveDotFiles: true, lastModified: true, setHeaders(res, filepath) { let isAsset = filepath.startsWith(ASSET_DIR); res.setHeader("cache-control", isAsset ? cacheHeader(assetCacheControl) : cacheHeader(defaultCacheControl)); }, ...fastifyStaticOptions }); } fastify.register(async function createRemixRequestHandler(childServer) { childServer.removeAllContentTypeParsers(); childServer.addContentTypeParser("*", (_request, payload, done) => { done(null, payload); }); if (childServerOptions) childServer.all("*", childServerOptions, handler); else childServer.all("*", handler); }, { prefix: basename }); }; } //#endregion export { createPlugin, createRequest, sendResponse }; //# sourceMappingURL=plugins-CszfyhUI.js.map