Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions packages/plugin-rsc/examples/ssg/src/framework/entry.rsc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,27 @@ export default async function handler(request: Request): Promise<Response> {
},
})
}

// return both rsc and html streams at once for ssg
export async function handleSsg(request: Request): Promise<{
html: ReadableStream<Uint8Array>
rsc: ReadableStream<Uint8Array>
}> {
const url = new URL(request.url)
const rscPayload: RscPayload = { root: <Root url={url} /> }
const rscStream = ReactServer.renderToReadableStream<RscPayload>(rscPayload)
const [rscStream1, rscStream2] = rscStream.tee()

const ssr = await import.meta.viteRsc.loadModule<
typeof import('./entry.ssr')
>('ssr', 'index')
const htmlStream = await ssr.renderHtml(rscStream1, {
ssg: true,
})

return { html: htmlStream, rsc: rscStream2 }
}

if (import.meta.hot) {
import.meta.hot.accept()
}
12 changes: 9 additions & 3 deletions packages/plugin-rsc/examples/ssg/src/framework/entry.ssr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ import * as ReactDomServer from 'react-dom/server.edge'
import { injectRSCPayload } from 'rsc-html-stream/server'
import type { RscPayload } from './shared'

export async function renderHtml(rscStream: ReadableStream<Uint8Array>) {
export async function renderHtml(
rscStream: ReadableStream<Uint8Array>,
options?: {
ssg?: boolean
},
) {
const [rscStream1, rscStream2] = rscStream.tee()

let payload: Promise<RscPayload>
Expand All @@ -20,8 +25,9 @@ export async function renderHtml(rscStream: ReadableStream<Uint8Array>) {
const htmlStream = await ReactDomServer.renderToReadableStream(<SsrRoot />, {
bootstrapScriptContent,
})
// for SSG
await htmlStream.allReady
if (options?.ssg) {
await htmlStream.allReady
}

let responseStream: ReadableStream<Uint8Array> = htmlStream
responseStream = responseStream.pipeThrough(injectRSCPayload(rscStream2))
Expand Down
32 changes: 13 additions & 19 deletions packages/plugin-rsc/examples/ssg/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import assert from 'node:assert'
import fs from 'node:fs'
import path from 'node:path'
import { Readable } from 'node:stream'
Expand Down Expand Up @@ -60,29 +59,24 @@ async function renderStatic(config: ResolvedConfig) {

// render rsc and html
const baseDir = config.environments.client.build.outDir
for (const htmlPath of staticPaths) {
config.logger.info('[vite-rsc:ssg] -> ' + htmlPath)
const rscPath = htmlPath + RSC_POSTFIX
const htmlResponse = await entry.default(
new Request(new URL(htmlPath, 'http://ssg.local')),
for (const staticPatch of staticPaths) {
config.logger.info('[vite-rsc:ssg] -> ' + staticPatch)
const { html, rsc } = await entry.handleSsg(
new Request(new URL(staticPatch, 'http://ssg.local')),
)
assert.equal(htmlResponse.status, 200)
await fs.promises.writeFile(
path.join(baseDir, normalizeHtmlFilePath(htmlPath)),
Readable.fromWeb(htmlResponse.body as any),
)

const rscResponse = await entry.default(
new Request(new URL(rscPath, 'http://ssg.local')),
)
assert.equal(rscResponse.status, 200)
await fs.promises.writeFile(
path.join(baseDir, rscPath),
Readable.fromWeb(rscResponse.body as any),
await writeFileStream(
path.join(baseDir, normalizeHtmlFilePath(staticPatch)),
html,
)
await writeFileStream(path.join(baseDir, staticPatch + RSC_POSTFIX), rsc)
}
}

async function writeFileStream(filePath: string, stream: ReadableStream) {
await fs.promises.mkdir(path.dirname(filePath), { recursive: true })
await fs.promises.writeFile(filePath, Readable.fromWeb(stream as any))
}

function normalizeHtmlFilePath(p: string) {
if (p.endsWith('/')) {
return p + 'index.html'
Expand Down
Loading