66import fs from 'fs/promises'
77import path from 'path'
88
9- import busboy from 'busboy'
109// @ts -expect-error We will remove dotenv-defaults from this package anyway
1110import { config as loadDotEnv } from 'dotenv-defaults'
1211import express from 'express'
1312import { createProxyMiddleware } from 'http-proxy-middleware'
14- import isbot from 'isbot'
15- import RSDWServer from 'react-server-dom-webpack/server.node.unbundled'
1613import type { Manifest as ViteBuildManifest } from 'vite'
1714
1815import { getConfig , getPaths } from '@redwoodjs/project-config'
1916
20- import { hasStatusCode } from './lib/StatusError '
17+ import { createRscRequestHandler } from './rsc/rscRequestHandler '
2118import { registerFwGlobals } from './streaming/registerGlobals'
22- import { renderRSC , setClientEntries } from './waku-lib/rsc-handler-worker'
23-
24- const { decodeReply, decodeReplyFromBusboy } = RSDWServer
19+ import { setClientEntries } from './waku-lib/rsc-handler-worker'
2520
2621/**
2722 * TODO (STREAMING)
@@ -39,10 +34,7 @@ loadDotEnv({
3934 defaults : path . join ( getPaths ( ) . base , '.env.defaults' ) ,
4035 multiline : true ,
4136} )
42- //------------------------------------------------
43-
44- const checkUaForSeoCrawler = isbot . spawn ( )
45- checkUaForSeoCrawler . exclude ( [ 'chrome-lighthouse' ] )
37+ // ------------------------------------------------
4638
4739export async function runFeServer ( ) {
4840 const app = express ( )
@@ -72,10 +64,7 @@ export async function runFeServer() {
7264 // const routeManifest: RWRouteManifest = JSON.parse(routeManifestStr)
7365
7466 // TODO See above about using `import { with: { type: 'json' } }` instead
75- const manifestPath = path . join (
76- getPaths ( ) . web . dist ,
77- 'client-build-manifest.json'
78- )
67+ const manifestPath = path . join ( rwPaths . web . dist , 'client-build-manifest.json' )
7968 const buildManifestStr = await fs . readFile ( manifestPath , 'utf-8' )
8069 const buildManifest : ViteBuildManifest = JSON . parse ( buildManifestStr )
8170
@@ -91,11 +80,11 @@ export async function runFeServer() {
9180 throw new Error ( 'Could not find index.html in build manifest' )
9281 }
9382
94- // 👉 1. Use static handler for assets
83+ // 1. Use static handler for assets
9584 // For CF workers, we'd need an equivalent of this
9685 app . use ( '/assets' , express . static ( rwPaths . web . dist + '/assets' ) )
9786
98- // 👉 2. Proxy the api server
87+ // 2. Proxy the api server
9988 // TODO (STREAMING) we need to be able to specify whether proxying is required or not
10089 // e.g. deploying to Netlify, we don't need to proxy but configure it in Netlify
10190 // Also be careful of differences between v2 and v3 of the server
@@ -114,91 +103,8 @@ export async function runFeServer() {
114103 } )
115104 )
116105
117- app . use ( ( req , _res , next ) => {
118- console . log ( 'req.url' , req . url )
119- next ( )
120- } )
121-
122106 // Mounting middleware at /RSC will strip /RSC from req.url
123- app . use ( '/RSC' , async ( req , res ) => {
124- const basePath = '/RSC/'
125- console . log ( 'basePath' , basePath )
126- console . log ( 'req.originalUrl' , req . originalUrl , 'req.url' , req . url )
127- console . log ( 'req.headers.host' , req . headers . host )
128-
129- const url = new URL ( req . originalUrl || '' , 'http://' + req . headers . host )
130- let rscId : string | undefined
131- let props = { }
132- let rsfId : string | undefined
133- let args : unknown [ ] = [ ]
134-
135- console . log ( 'url.pathname' , url . pathname )
136-
137- if ( url . pathname . startsWith ( basePath ) ) {
138- const index = url . pathname . lastIndexOf ( '/' )
139- const params = new URLSearchParams ( url . pathname . slice ( index + 1 ) )
140- rscId = url . pathname . slice ( basePath . length , index )
141- rsfId = params . get ( 'action_id' ) || undefined
142-
143- console . log ( 'rscId' , rscId )
144- console . log ( 'rsfId' , rsfId )
145-
146- if ( rscId && rscId !== '_' ) {
147- res . setHeader ( 'Content-Type' , 'text/x-component' )
148- props = JSON . parse ( params . get ( 'props' ) || '{}' )
149- } else {
150- rscId = undefined
151- }
152-
153- if ( rsfId ) {
154- if ( req . headers [ 'content-type' ] ?. startsWith ( 'multipart/form-data' ) ) {
155- const bb = busboy ( { headers : req . headers } )
156- const reply = decodeReplyFromBusboy ( bb )
157-
158- req . pipe ( bb )
159- args = await reply
160- } else {
161- let body = ''
162-
163- for await ( const chunk of req ) {
164- body += chunk
165- }
166-
167- if ( body ) {
168- args = await decodeReply ( body )
169- }
170- }
171- }
172- }
173-
174- if ( rscId || rsfId ) {
175- const handleError = ( err : unknown ) => {
176- if ( hasStatusCode ( err ) ) {
177- res . statusCode = err . statusCode
178- } else {
179- console . info ( 'Cannot render RSC' , err )
180- res . statusCode = 500
181- }
182-
183- res . end ( String ( err ) )
184- // TODO (RSC): When we have `yarn rw dev` support we should do this:
185- // if (options.command === 'dev') {
186- // res.end(String(err))
187- // } else {
188- // res.end()
189- // }
190- }
191-
192- try {
193- const pipeable = await renderRSC ( { rscId, props, rsfId, args } )
194- // TODO (RSC): See if we can/need to do more error handling here
195- // pipeable.on(handleError)
196- pipeable . pipe ( res )
197- } catch ( e ) {
198- handleError ( e )
199- }
200- }
201- } )
107+ app . use ( '/RSC' , createRscRequestHandler ( ) )
202108
203109 app . use ( express . static ( rwPaths . web . dist ) )
204110
0 commit comments