Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSC: Make code and execution easier to follow. Improve error handling. #9154

Merged
merged 3 commits into from
Sep 11, 2023
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
3 changes: 3 additions & 0 deletions packages/vite/src/buildRscFeServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ export const buildRscFeServer = async ({
webDistEntries,
webRouteManifest,
}: Args) => {
// Step 1: Analyze all files and generate a list of RSCs and RSFs
const { clientEntryFiles, serverEntryFiles } = await rscBuild(viteConfigPath)

// Step 2: Generate the client bundle
const clientBuildOutput = await viteBuild({
// configFile: viteConfigPath,
root: webSrc,
Expand Down Expand Up @@ -64,6 +66,7 @@ export const buildRscFeServer = async ({
throw new Error('Unexpected vite client build output')
}

// Step 3: Generate the server output
const serverBuildOutput = await serverBuild(
entries,
clientEntryFiles,
Expand Down
29 changes: 25 additions & 4 deletions packages/vite/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,20 @@ import type { ReactElement } from 'react'

import { createFromFetch, encodeReply } from 'react-server-dom-webpack/client'

import { StatusError } from './lib/StatusError'

const checkStatus = async (
responsePromise: Promise<Response>
): Promise<Response> => {
const response = await responsePromise

if (!response.ok) {
throw new StatusError(response.statusText, response.status)
}

return response
}

export function serve<Props>(rscId: string, basePath = '/RSC/') {
type SetRerender = (
rerender: (next: [ReactElement, string]) => void
Expand All @@ -24,24 +38,30 @@ export function serve<Props>(rscId: string, basePath = '/RSC/') {

const options = {
async callServer(rsfId: string, args: unknown[]) {
console.log('client.ts :: callServer rsfId', rsfId, 'args', args)
const isMutating = !!mutationMode
const searchParams = new URLSearchParams()
searchParams.set('action_id', rsfId)
let id: string

if (isMutating) {
id = rscId
searchParams.set('props', serializedProps)
} else {
id = '_'
}

const response = fetch(basePath + id + '/' + searchParams, {
method: 'POST',
body: await encodeReply(args),
})

const data = createFromFetch(response, options)

if (isMutating) {
rerender?.([data, serializedProps])
}

return data
},
}
Expand All @@ -54,11 +74,12 @@ export function serve<Props>(rscId: string, basePath = '/RSC/') {
'fetchRSC before createFromFetch',
basePath + rscId + '/' + searchParams
)
const data = createFromFetch(
prefetched || fetch(basePath + rscId + '/' + searchParams),
options
)

const response =
prefetched || fetch(basePath + rscId + '/' + searchParams)
const data = createFromFetch(checkStatus(response), options)
console.log('fetchRSC after createFromFetch. data:', data)

return [data, setRerender]
}
)
Expand Down
5 changes: 5 additions & 0 deletions packages/vite/src/lib/StatusError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export class StatusError extends Error {
constructor(message: string, public statusCode: number) {
super(message)
}
}
2 changes: 1 addition & 1 deletion packages/vite/src/rscBuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { onWarn } from './lib/onWarn'
import { rscAnalyzePlugin } from './waku-lib/vite-plugin-rsc'

/**
* RSC build
* RSC build. Step 1 of 3.
* Uses rscAnalyzePlugin to collect client and server entry points
* Starts building the AST in entries.ts
* Doesn't output any files, only collects a list of RSCs and RSFs
Expand Down
6 changes: 5 additions & 1 deletion packages/vite/src/waku-lib/rsc-handler-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { createServer } from 'vite'
import { getPaths } from '@redwoodjs/project-config'

import type { defineEntries } from '../entries'
import { StatusError } from '../lib/StatusError'
// import type { unstable_GetCustomModules } from '../waku-server'

import { configFileConfig, resolveConfig } from './config'
Expand Down Expand Up @@ -226,7 +227,8 @@ const getFunctionComponent = async (
if (typeof mod?.default === 'function') {
return mod?.default
}
throw new Error('No function component found')
// TODO (RSC): Making this a 404 error is marked as "HACK" in waku's source
throw new StatusError('No function component found', 404)
}

let absoluteClientEntries: Record<string, string> = {}
Expand Down Expand Up @@ -287,6 +289,8 @@ export async function renderRSC(input: RenderInput): Promise<PipeableStream> {
}
)

console.log('renderRSC input', input)

if (input.rsfId && input.args) {
const [fileId, name] = input.rsfId.split('#')
const fname = path.join(config.root, fileId)
Expand Down
8 changes: 6 additions & 2 deletions packages/vite/src/waku-lib/rsc-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,17 @@ import('${moduleId}');`
}

// HACK Patching stream is very fragile.
export const transformRsfId = (prefixToRemove: string) =>
new Transform({
export const transformRsfId = (prefixToRemove: string) => {
console.log('prefixToRemove', prefixToRemove)

return new Transform({
transform(chunk, encoding, callback) {
if (encoding !== ('buffer' as any)) {
throw new Error('Unknown encoding')
}
const data = chunk.toString()
const lines = data.split('\n')
console.log('lines', lines)
let changed = false
for (let i = 0; i < lines.length; ++i) {
const match = lines[i].match(
Expand All @@ -77,3 +80,4 @@ export const transformRsfId = (prefixToRemove: string) =>
callback(null, changed ? Buffer.from(lines.join('\n')) : chunk)
},
})
}
4 changes: 3 additions & 1 deletion packages/vite/src/waku-lib/vite-plugin-rsc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { ResolveFunction } from '../react-server-dom-webpack/node-loader'

import { codeToInject } from './rsc-utils.js'

// Used in Step 2 of the build process, for the client bundle
export function rscIndexPlugin(): Plugin {
return {
name: 'rsc-index-plugin',
Expand Down Expand Up @@ -118,6 +119,7 @@ export function rscReloadPlugin(fn: (type: 'full-reload') => void): Plugin {
}
return false
}

return {
name: 'reload-plugin',
configResolved(config) {
Expand All @@ -144,7 +146,7 @@ export function rscAnalyzePlugin(
serverEntryCallback: (id: string) => void
): Plugin {
return {
name: 'rsc-bundle-plugin',
name: 'rsc-analyze-plugin',
transform(code, id) {
const ext = path.extname(id)
if (['.ts', '.tsx', '.js', '.jsx'].includes(ext)) {
Expand Down