diff --git a/packages/cli/src/commands/serve.js b/packages/cli/src/commands/serve.js index 57f1518d24be..9d9ca27e68ea 100644 --- a/packages/cli/src/commands/serve.js +++ b/packages/cli/src/commands/serve.js @@ -18,22 +18,6 @@ function hasExperimentalServerFile() { return fs.existsSync(serverFilePath) } -const streamServerErrorHandler = () => { - console.error('⚠️ Experimental Render Mode ~ Cannot serve the web side ⚠️') - console.log('~'.repeat(50)) - console.log() - console.log() - console.log('You can run the new frontend server with: `yarn rw-serve-fe`') - console.log('You can run the api server with: yarn rw serve api') - console.log() - console.log() - console.log('~'.repeat(50)) - - throw new Error( - 'You will need to run the FE server and API server separately.' - ) -} - export const builder = async (yargs) => { yargs .usage('usage: $0 ') @@ -57,11 +41,6 @@ export const builder = async (yargs) => { socket: argv.socket, }) - if (getConfig().experimental?.streamingSsr?.enabled) { - streamServerErrorHandler() - return - } - // Run the experimental server file, if it exists, with web side also if (hasExperimentalServerFile()) { console.log( @@ -73,20 +52,52 @@ export const builder = async (yargs) => { separator, ].join('\n') ) - await execa( - 'yarn', - ['node', path.join('dist', 'server.js'), '--enable-web'], - { - cwd: getPaths().api.base, + if (getConfig().experimental?.streamingSsr?.enabled) { + console.warn('') + console.warn('⚠️ Skipping Fastify web server ⚠️') + console.warn('⚠️ Using new Streaming FE server instead ⚠️') + console.warn('') + await execa('yarn', ['rw-serve-fe'], { + cwd: getPaths().web.base, stdio: 'inherit', shell: true, - } - ) + }) + } else { + await execa( + 'yarn', + ['node', path.join('dist', 'server.js'), '--enable-web'], + { + cwd: getPaths().api.base, + stdio: 'inherit', + shell: true, + } + ) + } return } - const { bothServerHandler } = await import('./serveHandler.js') - await bothServerHandler(argv) + if (getConfig().experimental?.streamingSsr?.enabled) { + const { apiServerHandler } = await import('./serveHandler.js') + // TODO (STREAMING) Allow specifying port, socket and apiRootPath + const apiPromise = apiServerHandler({ + ...argv, + port: 8911, + apiRootPath: '/', + }) + + // TODO (STREAMING) More gracefully handle Ctrl-C + // Right now you get a big red error box when you kill the process + const fePromise = execa('yarn', ['rw-serve-fe'], { + cwd: getPaths().web.base, + stdio: 'inherit', + shell: true, + }) + + await Promise.all([apiPromise, fePromise]) + } else { + const { bothServerHandler } = await import('./serveHandler.js') + await bothServerHandler(argv) + } }, }) .command({ @@ -167,12 +178,15 @@ export const builder = async (yargs) => { }) if (getConfig().experimental?.streamingSsr?.enabled) { - streamServerErrorHandler() - return + await execa('yarn', ['rw-serve-fe'], { + cwd: getPaths().web.base, + stdio: 'inherit', + shell: true, + }) + } else { + const { webServerHandler } = await import('./serveHandler.js') + await webServerHandler(argv) } - - const { webServerHandler } = await import('./serveHandler.js') - await webServerHandler(argv) }, }) .middleware((argv) => { diff --git a/packages/vite/src/devFeServer.ts b/packages/vite/src/devFeServer.ts index 81ce742eef94..4b1235f55d3b 100644 --- a/packages/vite/src/devFeServer.ts +++ b/packages/vite/src/devFeServer.ts @@ -106,7 +106,7 @@ async function createServer() { // 3. Load the server entry. vite.ssrLoadModule automatically transforms // your ESM source code to be usable in Node.js! There is no bundling // required, and provides efficient invalidation similar to HMR. - const { serverEntry } = await vite.ssrLoadModule(rwPaths.web.entryServer) + const { ServerEntry } = await vite.ssrLoadModule(rwPaths.web.entryServer) // TODO (STREAMING) CSS is handled by Vite in dev mode, we don't need to // worry about it in dev but..... it causes a flash of unstyled content. @@ -132,7 +132,7 @@ async function createServer() { } const { pipe } = renderToPipeableStream( - serverEntry({ + ServerEntry({ url: currentPathName, css: FIXME_HardcodedIndexCss, meta: metaTags, diff --git a/packages/vite/src/runFeServer.ts b/packages/vite/src/runFeServer.ts index bc5deb487785..60022416d056 100644 --- a/packages/vite/src/runFeServer.ts +++ b/packages/vite/src/runFeServer.ts @@ -95,7 +95,7 @@ export async function runFeServer() { const currentPathName = stripQueryStringAndHashFromPath(req.originalUrl) try { - const { serverEntry } = await import(rwPaths.web.distEntryServer) + const { ServerEntry } = await import(rwPaths.web.distEntryServer) // TODO (STREAMING) should we generate individual express Routes for each Route? // This would make handling 404s and favicons / public assets etc. easier @@ -124,7 +124,7 @@ export async function runFeServer() { const assetMap = JSON.stringify({ css: indexEntry.css }) const { pipe } = renderToPipeableStream( - serverEntry({ + ServerEntry({ url: currentPathName, routeContext: null, css: indexEntry.css, @@ -157,10 +157,22 @@ export async function runFeServer() { } if (currentRoute) { + // TODO (STREAMING) hardcoded JS file, watchout if we switch to ESM! + const appRouteHooksPath = path.join( + rwPaths.web.distRouteHooks, + 'App.routeHooks.js' + ) + + let appRouteHooksExists = false + try { + appRouteHooksExists = (await fs.stat(appRouteHooksPath)).isFile() + } catch { + // noop + } + // Make sure we access the dist routeHooks! const routeHookPaths = [ - // TODO (STREAMING) hardcoded JS file, watchout if we switch to ESM! - path.join(rwPaths.web.distRouteHooks, 'App.routeHooks.js'), + appRouteHooksExists ? appRouteHooksPath : null, currentRoute.routeHooks ? path.join(rwPaths.web.distRouteHooks, currentRoute.routeHooks) : null, @@ -192,7 +204,7 @@ export async function runFeServer() { const { pipe, abort } = renderToPipeableStream( // we should use the same shape as Remix or Next for the meta object - serverEntry({ + ServerEntry({ url: currentPathName, css: indexEntry.css, meta: metaTags,