From 4ea27ebaefcc33c018901b8aad14fc145bfe3043 Mon Sep 17 00:00:00 2001
From: c0dedance <2627702283@qq.com>
Date: Sun, 22 Oct 2023 13:49:05 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20ssg=20=E5=A4=9A=E8=B7=AF=E7=94=B1?=
=?UTF-8?q?=E6=89=93=E5=8C=85?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/node/build.ts | 75 +++++++++++++++++++++------------
src/node/cli.ts | 2 +
src/node/plugin-routes/index.ts | 7 +++
src/runtime/ssr-entry.tsx | 6 ++-
4 files changed, 61 insertions(+), 29 deletions(-)
diff --git a/src/node/build.ts b/src/node/build.ts
index 47bd93b..e9addca 100644
--- a/src/node/build.ts
+++ b/src/node/build.ts
@@ -4,10 +4,11 @@ import { build as viteBuild } from 'vite'
import fs from 'fs-extra'
// import ora from 'ora'
import { CLIENT_ENTRY_PATH, SERVER_ENTRY_PATH } from './constant'
+import { createVitePlugins } from './vitePlugins'
import type { InlineConfig } from 'vite'
import type { RollupOutput } from 'rollup'
import type { SiteConfig } from 'shared/types'
-import { createVitePlugins } from './vitePlugins'
+import type { Route } from './plugin-routes'
// const spinner = ora()
@@ -18,40 +19,59 @@ export async function build(root: string = process.cwd(), config: SiteConfig) {
const serverEntryPath = pathToFileURL(
path.resolve(root, './.temp/ssr-entry.cjs') // TODO: 统一化打包后缀
).toString()
- const { render } = await import(serverEntryPath)
+ const { render, routes } = await import(serverEntryPath)
// 3. 服务端渲染,产出HTML
- await renderPage(root, render, clientBundle)
+ await renderPage({ root, render, clientBundle, routes })
}
-export async function renderPage(
- root: string,
- render: () => string,
+export async function renderPage({
+ root,
+ render,
+ clientBundle,
+ routes,
+}: {
+ root: string
+ render: (pagePath: string) => string
clientBundle: RollupOutput
-) {
- const appHtml = render()
+ routes: Route[]
+}) {
const clientChunk = clientBundle.output.find(
(chunk) => chunk.type === 'chunk' && chunk.isEntry
)
-
+ const renderIndexHTML = (appHtml: string) =>
+ `\
+
+
+
+
+
+ title
+
+
+
+ ${appHtml}
+
+
+ `.trim()
console.log(`Rendering page in server side...`)
+ await Promise.all(
+ routes.map(async (r) => {
+ // 渲染路由对应的页面
+ const appHtml = render(r.path)
+ // 组件HTML嵌入到模板中
+ const html = renderIndexHTML(appHtml)
+ // htlm文件名处理
+ const outputFilePath = r.path.endsWith('/')
+ ? `${r.path}index.html`
+ : `${r.path}.html`
+
+ const outputPath = path.join(root, 'build', outputFilePath)
+ // ensure build dir
+ await fs.ensureDir(path.dirname(outputPath))
+ // write html file
+ await fs.writeFile(outputPath, html)
+ })
+ )
- const html = `\
-
-
-
-
-
- title
-
-
-
- ${appHtml}
-
-
-`.trim()
- // ensure build dir
- await fs.ensureDir(path.resolve(root, 'build'))
- // write html file
- await fs.writeFile(path.resolve(root, './build/index.html'), html)
// remove .temp
await fs.remove(path.resolve(root, './.temp'))
}
@@ -92,6 +112,7 @@ async function resolveBuildConfig({
build: {
ssr: isSSR,
outDir: isSSR ? '.temp' : 'build',
+ // outDir: isServer ? path.join(root, '.temp') : path.join(root, 'build'),
rollupOptions: {
input: isSSR ? SERVER_ENTRY_PATH : CLIENT_ENTRY_PATH,
output: {
diff --git a/src/node/cli.ts b/src/node/cli.ts
index 31972ca..e2fb0fd 100644
--- a/src/node/cli.ts
+++ b/src/node/cli.ts
@@ -1,3 +1,4 @@
+import path from 'path'
import { cac } from 'cac'
import { build } from './build'
import { version } from '../../package.json'
@@ -27,6 +28,7 @@ cli
cli
.command('build [root]', 'build for production')
.action(async (root: string) => {
+ root = path.resolve(root)
const config = await resolveConfig(root, 'build', 'production')
await build(root, config)
})
diff --git a/src/node/plugin-routes/index.ts b/src/node/plugin-routes/index.ts
index 3c38b48..dd8e79e 100644
--- a/src/node/plugin-routes/index.ts
+++ b/src/node/plugin-routes/index.ts
@@ -1,3 +1,4 @@
+import React from 'react'
import { Plugin } from 'vite'
import { RouteService } from './RouteService'
@@ -5,6 +6,12 @@ interface PluginOptions {
root: string
}
+export interface Route {
+ path: string
+ element: React.ReactElement
+ filePath: string
+}
+
const CONVENTIONAL_ROUTE_ID = 'rpress:routes'
// 本质: 把文件目录结构 -> 路由数据
diff --git a/src/runtime/ssr-entry.tsx b/src/runtime/ssr-entry.tsx
index 9f04f51..6f6c6b8 100644
--- a/src/runtime/ssr-entry.tsx
+++ b/src/runtime/ssr-entry.tsx
@@ -4,10 +4,12 @@ import { StaticRouter } from 'react-router-dom/server'
import App from './App'
// For ssr component render
-export function render() {
+export function render(pagePath: string) {
return renderToString(
-
+
)
}
+
+export { routes } from 'rpress:routes'