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

Streaming SSR: Fix build, serve and dev #8811

Merged
merged 1 commit into from
Jul 2, 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
84 changes: 49 additions & 35 deletions packages/cli/src/commands/serve.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 <side>')
Expand All @@ -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(
Expand All @@ -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({
Expand Down Expand Up @@ -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) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/vite/src/devFeServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -132,7 +132,7 @@ async function createServer() {
}

const { pipe } = renderToPipeableStream(
serverEntry({
ServerEntry({
url: currentPathName,
css: FIXME_HardcodedIndexCss,
meta: metaTags,
Expand Down
22 changes: 17 additions & 5 deletions packages/vite/src/runFeServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down