import fs from 'node:fs/promises'; import { URL } from 'node:url'; import { Transform } from 'node:stream'; import { Readable } from 'node:stream'; import { pipeline } from 'node:stream/promises'; import { serve } from '@hono/node-server'; import { Hono } from 'hono'; import { createServer as createNodeServer } from 'node:http'; import process from 'node:process'; // 创建 Hono 应用 const app = new Hono();// API路由 import api from './src/server/api.ts'; app.route('/', api); // Constants const isProduction = process.env.NODE_ENV === 'production'; const port = process.env.PORT || 8080; const base = process.env.BASE || '/'; const ABORT_DELAY = 10000; // 解析基础路径为 URL 对象,方便后续处理 const baseUrl = new URL(base, `http://localhost:${port}`); // 使用 Hono 的 serve 启动服务器 const parentServer = serve({ fetch: app.fetch, createServer: createNodeServer, port: port, }, () => { console.log(`Server started at http://localhost:${port}`); }); // Cached production assets let templateHtml = ''; if (isProduction) { templateHtml = await fs.readFile('./dist/client/index.html', 'utf-8'); } // 生产环境中间件 let compressionMiddleware; let sirvMiddleware; if (isProduction) { compressionMiddleware = (await import('compression')).default(); sirvMiddleware = (await import('sirv')).default('./dist/client', { extensions: [], baseUrl: base }); } // Vite 开发服务器 /** @type {import('vite').ViteDevServer | undefined} */ let vite; if (!isProduction) { const { createServer } = await import('vite'); vite = await createServer({ server: { middlewareMode: { server: parentServer }, hmr: { port: 8081, clientPort: 443, // 或其他可用端口 path: 'vite-hmr' }, proxy: { '/vite-hmr': { target: 'ws://localhost:8081', // Proxying WebSocket ws: true, }, }, }, appType: 'custom', base, }); } // 将请求处理逻辑适配为 Hono 中间件 app.use(async (c) => { try { // 使用 c.env 获取原生请求响应对象(关键修复) const req = c.env.incoming; const res = c.env.outgoing; const url = new URL(req.url, `http://${req.headers.host}`); const path = url.pathname; // 检查是否匹配基础路径 if (!path.startsWith(baseUrl.pathname)) { return c.text('Not found', 404); } // 处理基础路径 const normalizedUrl = path.replace(baseUrl.pathname, '/') || '/'; // 开发环境:使用 Vite 中间件 if (!isProduction && vite) { // 使用 Vite 中间件处理请求 const handled = await new Promise((resolve) => { vite.middlewares(req, res, () => resolve(false)); }); // 如果 Vite 中间件已经处理了请求,直接返回 if (handled) { return c.body; } } // 生产环境:使用 compression 和 sirv 中间件 else if (isProduction) { // 先尝试 compression 中间件 const compressed = await new Promise((resolve) => { compressionMiddleware(req, res, () => resolve(false)); }); if (compressed) { return c.body; } // 再尝试 sirv 中间件处理静态文件 const served = await new Promise((resolve) => { sirvMiddleware(req, res, () => resolve(false)); }); if (served) { return c.body; } } // 处理所有其他请求的 SSR 逻辑 /** @type {string} */ let template; /** @type {import('./src/server/index.tsx').render} */ let render; if (!isProduction && vite) { // 开发环境:读取最新模板并转换 template = await fs.readFile('./index.html', 'utf-8'); template = await vite.transformIndexHtml(normalizedUrl, template); render = (await vite.ssrLoadModule('/src/server/index.tsx')).render; } else { // 生产环境:使用缓存的模板 template = templateHtml; render = (await import('./dist/server/entry-server.js')).render; } let didError = false; let abortController; // 创建一个可读流用于 SSR 渲染内容 const [htmlStart, htmlEnd] = template.split(``); const ssrStream = new Readable({ read: () => {} }); // 写入 HTML 头部 ssrStream.push(htmlStart); // 设置响应头和状态码 c.header('Content-Type', 'text/html'); // 处理渲染 const { pipe, abort } = render(normalizedUrl, { onShellError() { didError = true; c.status(500); ssrStream.push('

Something went wrong

'); ssrStream.push(null); // 结束流 }, onShellReady() { // 将渲染结果通过管道传入 ssrStream const transformStream = new Transform({ transform(chunk, encoding, callback) { ssrStream.push(chunk, encoding); callback(); } }); pipe(transformStream); // 当 transformStream 完成时,添加 HTML 尾部 transformStream.on('finish', () => { ssrStream.push(htmlEnd); ssrStream.push(null); // 结束流 }); }, onError(error) { didError = true; console.error(error); }, }); // 设置超时中止 abortController = new AbortController(); const abortTimeout = setTimeout(() => { abort(); abortController.abort(); }, ABORT_DELAY); // 将流通过 Hono 响应返回 return c.body(ssrStream, { onEnd: () => { clearTimeout(abortTimeout); } }); } catch (e) { if (!isProduction && vite) { vite.ssrFixStacktrace(e); } console.error(e.stack); return c.text(e.stack, 500); } });