diff --git a/README.md b/README.md index b3d2a7f3..5bb00c1f 100644 --- a/README.md +++ b/README.md @@ -9,3 +9,5 @@ - 开发阶段 CSR 正常运行 - 生产环境构建 SSR 产物 + +- 支持主题组件 HMR diff --git a/src/node/constants/index.ts b/src/node/constants/index.ts index a5a8a12a..0fa3a5eb 100644 --- a/src/node/constants/index.ts +++ b/src/node/constants/index.ts @@ -19,3 +19,5 @@ export const THEME_PATH = join( export const TEMP_PATH = 'node_modules/.island'; export const SERVER_OUTPUT_PATH = join(TEMP_PATH, 'ssr-entry.mjs'); + +export const DEFAULT_HTML_PATH = join(__dirname, '../../../template.html'); diff --git a/src/node/plugin.ts b/src/node/plugin.ts index fa3d08f2..6107c8d5 100644 --- a/src/node/plugin.ts +++ b/src/node/plugin.ts @@ -1,6 +1,13 @@ import { Plugin } from 'vite'; -import { CLIENT_ENTRY_PATH, THEME_PATH } from './constants'; +import { + CLIENT_ENTRY_PATH, + DEFAULT_HTML_PATH, + isProduction, + THEME_PATH +} from './constants'; import reactPlugin from '@vitejs/plugin-react'; +import fs from 'fs-extra'; +import { createContext } from 'react'; export function createIslandPlugins() { const islandPlugin: Plugin = { @@ -14,41 +21,56 @@ export function createIslandPlugins() { } }; }, + transformIndexHtml(html) { + if (isProduction()) { + return html; + } + // Insert client entry script in development + // And in production, we will insert it in ssr render + return { + html, + tags: [ + { + tag: 'script', + attrs: { + type: 'module', + src: `/@fs/${CLIENT_ENTRY_PATH}` + }, + injectTo: 'body' + } + ] + }; + }, configureServer(server) { return () => { - server.middlewares.use((req, res, next) => { - if (req.url?.endsWith('.html')) { - res.statusCode = 200; - res.setHeader('Content-Type', 'text/html'); - res.end(` - - - - - - - - - -
- - - `); - return; + server.middlewares.use(async (req, res, next) => { + if (res.writableEnded) { + return next(); } + if (req.url?.replace(/\?.*/, '').endsWith('.html')) { + let html = fs.readFileSync(DEFAULT_HTML_PATH, 'utf8'); - next(); + try { + html = await server.transformIndexHtml( + req.url, + html, + req.originalUrl + ); + res.statusCode = 200; + res.setHeader('Content-Type', 'text/html'); + res.end(html); + } catch (e) { + return next(e); + } + } }); }; } }; - const reactImportPlugin: Plugin = { - name: 'internal:react-import', - transform(code, id) { - if (id.endsWith('.jsx') || id.endsWith('.tsx')) { - return `import React from 'react';\n${code}`; - } - } - }; - return [islandPlugin]; + return [ + islandPlugin, + reactPlugin({ + jsxRuntime: 'classic' + }) + ]; } diff --git a/template.html b/template.html new file mode 100644 index 00000000..cc17bcbf --- /dev/null +++ b/template.html @@ -0,0 +1,15 @@ + + + + + + + + + + + +
+ + + \ No newline at end of file