Created
June 15, 2024 06:29
-
-
Save arisris/5b035155822849143358f9f133bbc00a to your computer and use it in GitHub Desktop.
fetch based dev server middleware for vite.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { Connect, type Plugin } from "vite" | |
| type DevSeverOptions = { | |
| /** relative path to appDir without trailing slash */ | |
| appDir?: string; | |
| } | |
| export const getHeaders = (req: Connect.IncomingMessage) => { | |
| const h = new Headers() | |
| for (const [key, value] of Object.entries(req.headers)) { | |
| if (value === undefined) continue | |
| if (Array.isArray(value)) { | |
| for (const v of value) { | |
| h.append(key, v) | |
| } | |
| } else { | |
| h.append(key, value) | |
| } | |
| } | |
| return h | |
| } | |
| export const getBody = async (req: Connect.IncomingMessage) => { | |
| let body: BodyInit | undefined | |
| if (req.method !== "GET") { | |
| body = await new Promise((resolve) => { | |
| let b = "" | |
| req.on("data", (chunk) => { | |
| b += chunk | |
| }) | |
| req.on("end", () => { | |
| resolve(b) | |
| }) | |
| }) | |
| } | |
| return body | |
| } | |
| export function devServer({ | |
| appDir = "app" | |
| }: DevSeverOptions = {}): Plugin { | |
| if (!("Bun" in globalThis)) { | |
| throw new Error(`This Plugin Only Works in Bun`); | |
| } | |
| const serverEntry = appDir + "/server.ts", clientEntry = appDir + "/client.ts"; | |
| let isSsrBuild = false, isServeCommand = false; | |
| return { | |
| name: "bun-vite:dev-server", | |
| async config(config, env) { | |
| if (env.command === "serve") { | |
| isServeCommand = true | |
| } | |
| if (env.isSsrBuild) { | |
| isSsrBuild = true | |
| } | |
| if (isServeCommand || isSsrBuild) { | |
| return { | |
| ...config, | |
| build: { | |
| emptyOutDir: false, | |
| ssr: true, | |
| rollupOptions: { | |
| ...config.build?.rollupOptions, | |
| input: [serverEntry] | |
| } | |
| }, | |
| ssr: { | |
| noExternal: true | |
| } | |
| } | |
| } else { | |
| return { | |
| ...config, | |
| build: { | |
| ...config.build, | |
| manifest: true, | |
| emptyOutDir: true, | |
| rollupOptions: { | |
| ...config.build?.rollupOptions, | |
| input: [clientEntry], | |
| output: { | |
| entryFileNames: "static/[name]-[hash].js", | |
| chunkFileNames: "static/[name]-[hash].js", | |
| assetFileNames: "static/[name]-[hash].[ext]" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| async configureServer(server) { | |
| if (isServeCommand) { | |
| const excluded = [ | |
| /.*\.css$/, | |
| /.*\.ts$/, | |
| /.*\.tsx$/, | |
| /^\/@.+$/, | |
| /\?t\=\d+$/, | |
| /^\/favicon\.ico$/, | |
| /^\/static\/.+/, | |
| /^\/node_modules\/.*/, | |
| /^\/app\/.*/, | |
| ] | |
| server.middlewares.use(async (req, res, next) => { | |
| if (excluded.some((r) => r.test(req.url!))) { | |
| return next() | |
| } | |
| const ssrModule = await server.ssrLoadModule(serverEntry, { fixStacktrace: true }) | |
| if (typeof ssrModule.default?.fetch !== "function") { | |
| res.setHeader("Content-Type", "text/html") | |
| return next(new Error("Server entry is not hono app")) | |
| } | |
| const response = await (ssrModule.default?.fetch as typeof fetch)(new Request(new URL(req.url!, `http://${req.headers.host}`), { | |
| method: req.method, | |
| headers: getHeaders(req), | |
| body: await getBody(req), | |
| })) | |
| response.headers.forEach((value, key) => { | |
| res.setHeader(key, value) | |
| }) | |
| res.statusCode = response.status | |
| res.statusMessage = response.statusText | |
| const reader = response.body?.getReader() | |
| if (!reader) { | |
| res.end() | |
| return | |
| } | |
| const decoder = new TextDecoder() | |
| let done = false | |
| while (!done) { | |
| const { value, done: doneReading } = await reader.read() | |
| done = doneReading | |
| const chunkValue = decoder.decode(value) | |
| res.write(chunkValue) | |
| } | |
| res.end() | |
| return | |
| }) | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment