Skip to content

Commit

Permalink
Split RSC and RSA handling in rscWorker (redwoodjs#10565)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tobbe committed May 12, 2024
1 parent f77331a commit 9bf9ada
Showing 1 changed file with 65 additions and 42 deletions.
107 changes: 65 additions & 42 deletions packages/vite/src/rsc/rscWorker.ts
Expand Up @@ -66,7 +66,9 @@ const handleRender = async ({ id, input }: MessageReq & { type: 'render' }) => {
console.log('handleRender', id, input)

try {
const pipeable = await renderRsc(input)
const pipeable = input.rscId
? await renderRsc(input)
: await handleRsa(input)

const writable = new Writable({
write(chunk, encoding, callback) {
Expand Down Expand Up @@ -305,19 +307,9 @@ async function setClientEntries(): Promise<void> {
)
}

interface SerializedFormData {
__formData__: boolean
state: Record<string, string | string[]>
}

function isSerializedFormData(data?: unknown): data is SerializedFormData {
return !!data && (data as SerializedFormData)?.__formData__
}

async function renderRsc(input: RenderInput): Promise<PipeableStream> {
function setRootInConfig(config: ConfigType) {
const rwPaths = getPaths()

const config = await configPromise
// TODO (RSC): Should root be configurable by the user? We probably need it
// to be different values in different contexts. Should we introduce more
// config options?
Expand All @@ -331,7 +323,9 @@ async function renderRsc(input: RenderInput): Promise<PipeableStream> {
: rwPaths.base
console.log('config.root', config.root)
console.log('rwPaths.base', rwPaths.base)
}

function getBundlerConfig(config: ConfigType) {
// TODO (RSC): Try removing the proxy here and see if it's really necessary.
// Looks like it'd work to just have a regular object with a getter.
// Remove the proxy and see what breaks.
Expand All @@ -351,6 +345,7 @@ async function renderRsc(input: RenderInput): Promise<PipeableStream> {
if (isDev) {
id = resolveClientEntryForDev(filePath, config)
} else {
// Needs config.root to be set properly
id = resolveClientEntryForProd(filePath, config)
}

Expand All @@ -361,47 +356,75 @@ async function renderRsc(input: RenderInput): Promise<PipeableStream> {
},
)

return bundlerConfig
}

async function renderRsc(input: RenderInput): Promise<PipeableStream> {
if (input.rsfId || !input.args) {
throw new Error(
"Unexpected input. Can't request both RSCs and execute RSAs at the same time.",
)
}

if (!input.rscId || !input.props) {
throw new Error('Unexpected input. Missing rscId or props.')
}

console.log('renderRsc input', input)

if (input.rsfId && input.args) {
const [fileId, name] = input.rsfId.split('#')
const fname = path.join(config.root, fileId)
console.log('Server Action, fileId', fileId, 'name', name, 'fname', fname)
const module = await loadServerFile(fname)
const config = await configPromise
setRootInConfig(config)

if (isSerializedFormData(input.args[0])) {
const formData = new FormData()
const component = await getFunctionComponent(input.rscId)

Object.entries(input.args[0].state).forEach(([key, value]) => {
if (Array.isArray(value)) {
value.forEach((v) => {
formData.append(key, v)
})
} else {
formData.append(key, value)
}
})
return renderToPipeableStream(
createElement(component, input.props),
getBundlerConfig(config),
).pipe(transformRsfId(config.root))
}

input.args[0] = formData
}
interface SerializedFormData {
__formData__: boolean
state: Record<string, string | string[]>
}

const data = await (module[name] || module)(...input.args)
if (!input.rscId) {
return renderToPipeableStream(data, bundlerConfig)
}
// continue for mutation mode
function isSerializedFormData(data?: unknown): data is SerializedFormData {
return !!data && (data as SerializedFormData)?.__formData__
}

async function handleRsa(input: RenderInput): Promise<PipeableStream> {
const config = await configPromise
setRootInConfig(config)

console.log('handleRsa input', input)

if (!input.rsfId || !input.args) {
throw new Error('Unexpected input')
}

if (input.rscId && input.props) {
const component = await getFunctionComponent(input.rscId)
const [fileId, name] = input.rsfId.split('#')
const fname = path.join(config.root, fileId)
console.log('Server Action, fileId', fileId, 'name', name, 'fname', fname)
const module = await loadServerFile(fname)

if (isSerializedFormData(input.args[0])) {
const formData = new FormData()

Object.entries(input.args[0].state).forEach(([key, value]) => {
if (Array.isArray(value)) {
value.forEach((v) => {
formData.append(key, v)
})
} else {
formData.append(key, value)
}
})

return renderToPipeableStream(
createElement(component, input.props),
bundlerConfig,
).pipe(transformRsfId(config.root))
input.args[0] = formData
}

throw new Error('Unexpected input')
const data = await (module[name] || module)(...input.args)
return renderToPipeableStream(data, getBundlerConfig(config))
}

// HACK Patching stream is very fragile.
Expand Down

0 comments on commit 9bf9ada

Please sign in to comment.