Skip to content

Commit 2557bf8

Browse files
dac09Tobbe
andauthored
Server Rendering & Streaming (#8561)
Co-authored-by: Tobbe Lundberg <[email protected]>
1 parent 8a7e81b commit 2557bf8

35 files changed

Lines changed: 1843 additions & 461 deletions

packages/cli/src/commands/buildHandler.js

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { buildApi } from '@redwoodjs/internal/dist/build/api'
1111
import { loadAndValidateSdls } from '@redwoodjs/internal/dist/validateSchema'
1212
import { detectPrerenderRoutes } from '@redwoodjs/prerender/detection'
1313
import { timedTelemetry } from '@redwoodjs/telemetry'
14+
import { buildFeServer } from '@redwoodjs/vite'
1415

1516
import { getPaths, getConfig } from '../lib'
1617
import { generatePrismaCommand } from '../lib/generatePrismaClient'
@@ -105,19 +106,28 @@ export const handler = async ({
105106
title: 'Building Web...',
106107
task: async () => {
107108
if (getConfig().web.bundler !== 'webpack') {
108-
// @NOTE: we're using the vite build command here, instead of the
109-
// buildWeb function directly because we want the process.cwd to be
110-
// the web directory, not the root of the project.
111-
// This is important for postcss/tailwind to work correctly
112-
// Having a separate binary lets us contain the change of cwd to that
113-
// process only. If we changed cwd here, or in the buildWeb function,
114-
// it could affect other things that run in parallel while building.
115-
// We don't have any parallel tasks right now, but someone might add
116-
// one in the future as a performance optimization.
117-
await execa(`yarn rw-vite-build --webDir="${rwjsPaths.web.base}"`, {
118-
stdio: verbose ? 'inherit' : 'pipe',
119-
shell: true,
120-
})
109+
if (!getConfig().experimental?.streamingSsr?.enabled) {
110+
// @NOTE: we're using the vite build command here, instead of the
111+
// buildWeb function directly because we want the process.cwd to be
112+
// the web directory, not the root of the project.
113+
// This is important for postcss/tailwind to work correctly
114+
// Having a separate binary lets us contain the change of cwd to that
115+
// process only. If we changed cwd here, or in the buildWeb function,
116+
// it could affect other things that run in parallel while building.
117+
// We don't have any parallel tasks right now, but someone might add
118+
// one in the future as a performance optimization.
119+
await execa(`yarn rw-vite-build --webDir="${rwjsPaths.web.base}"`, {
120+
stdio: verbose ? 'inherit' : 'pipe',
121+
shell: true,
122+
})
123+
} else {
124+
// TODO (STREAMING) we need to contain this in a separate binary
125+
process.chdir(rwjsPaths.web.base)
126+
127+
// TODO (STREAMING) we need to use a binary here, so the the cwd is correct
128+
// Should merge this with the existing rw-vite-build binary
129+
await buildFeServer({ verbose })
130+
}
121131
} else {
122132
await execa(
123133
`yarn cross-env NODE_ENV=production webpack --config ${require.resolve(

packages/cli/src/commands/serve.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,22 @@ function hasExperimentalServerFile() {
1818
return fs.existsSync(serverFilePath)
1919
}
2020

21+
const streamServerErrorHandler = () => {
22+
console.error('⚠️ Experimental Render Mode ~ Cannot serve the web side ⚠️')
23+
console.log('~'.repeat(50))
24+
console.log()
25+
console.log()
26+
console.log('You can run the new frontend server with: `yarn rw-serve-fe`')
27+
console.log('You can run the api server with: yarn rw serve api')
28+
console.log()
29+
console.log()
30+
console.log('~'.repeat(50))
31+
32+
throw new Error(
33+
'You will need to run the FE server and API server separately.'
34+
)
35+
}
36+
2137
export const builder = async (yargs) => {
2238
yargs
2339
.usage('usage: $0 <side>')
@@ -41,6 +57,11 @@ export const builder = async (yargs) => {
4157
socket: argv.socket,
4258
})
4359

60+
if (getConfig().experimental?.streamingSsr?.enabled) {
61+
streamServerErrorHandler()
62+
return
63+
}
64+
4465
// Run the experimental server file, if it exists, with web side also
4566
if (hasExperimentalServerFile()) {
4667
console.log(
@@ -145,6 +166,11 @@ export const builder = async (yargs) => {
145166
apiHost: argv.apiHost,
146167
})
147168

169+
if (getConfig().experimental?.streamingSsr?.enabled) {
170+
streamServerErrorHandler()
171+
return
172+
}
173+
148174
const { webServerHandler } = await import('./serveHandler.js')
149175
await webServerHandler(argv)
150176
},

packages/core/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616
"redwood": "./dist/bins/redwood.js",
1717
"rw": "./dist/bins/redwood.js",
1818
"rw-api-server-watch": "./dist/bins/rw-api-server-watch.js",
19+
"rw-dev-fe": "./dist/bins/rw-dev-fe.js",
1920
"rw-gen": "./dist/bins/rw-gen.js",
2021
"rw-gen-watch": "./dist/bins/rw-gen-watch.js",
2122
"rw-log-formatter": "./dist/bins/rw-log-formatter.js",
23+
"rw-serve-api": "./dist/bins/rw-serve-api.js",
24+
"rw-serve-fe": "./dist/bins/rw-serve-fe.js",
2225
"rwfw": "./dist/bins/rwfw.js"
2326
},
2427
"files": [
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env node
2+
import { createRequire } from 'module'
3+
4+
const requireFromRwVite = createRequire(
5+
require.resolve('@redwoodjs/vite/package.json')
6+
)
7+
8+
const bins = requireFromRwVite('./package.json')['bin']
9+
10+
requireFromRwVite(bins['rw-dev-fe'])
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env node
2+
import { createRequire } from 'module'
3+
4+
const requireFromApiServer = createRequire(
5+
require.resolve('@redwoodjs/api-server/package.json')
6+
)
7+
8+
const bins = requireFromApiServer('./package.json')['bin']
9+
10+
requireFromApiServer(bins['rw-serve-api'])
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env node
2+
import { createRequire } from 'module'
3+
4+
const requireFromRwVite = createRequire(
5+
require.resolve('@redwoodjs/vite/package.json')
6+
)
7+
8+
const bins = requireFromRwVite('./package.json')['bin']
9+
10+
requireFromRwVite(bins['rw-serve-fe'])

packages/internal/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"@graphql-codegen/typescript-resolvers": "3.2.1",
4444
"@redwoodjs/graphql-server": "5.0.0",
4545
"@redwoodjs/project-config": "5.0.0",
46+
"@redwoodjs/router": "5.0.0",
4647
"@sdl-codegen/node": "0.0.10",
4748
"babel-plugin-graphql-tag": "3.3.0",
4849
"babel-plugin-polyfill-corejs3": "0.8.1",

packages/internal/src/build/babel/api.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,27 @@ export const prebuildApiFile = (
228228
})
229229
return result
230230
}
231+
232+
// TODO (STREAMING) I changed the prebuildApiFile function in https://github.com/redwoodjs/redwood/pull/7672/files
233+
// but we had to revert. For this branch temporarily, I'm going to add a new function
234+
// This is used in building routeHooks
235+
export const transformWithBabel = (
236+
srcPath: string,
237+
plugins: TransformOptions['plugins']
238+
) => {
239+
const code = fs.readFileSync(srcPath, 'utf-8')
240+
const defaultOptions = getApiSideDefaultBabelConfig()
241+
242+
const result = transform(code, {
243+
...defaultOptions,
244+
cwd: getPaths().api.base,
245+
filename: srcPath,
246+
// we need inline sourcemaps at this level
247+
// because this file will eventually be fed to esbuild
248+
// when esbuild finds an inline sourcemap, it tries to "combine" it
249+
// so the final sourcemap (the one that esbuild generates) combines both mappings
250+
sourceMaps: 'inline',
251+
plugins,
252+
})
253+
return result
254+
}

packages/internal/src/build/babel/common.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { parseConfigFileTextToJson } from 'typescript'
66

77
import { getPaths } from '@redwoodjs/project-config'
88

9+
import { getWebSideBabelPlugins } from './web'
10+
911
const pkgJson = require('../../../package.json')
1012

1113
export interface RegisterHookOptions {
@@ -69,6 +71,28 @@ export const getCommonPlugins = () => {
6971
]
7072
}
7173

74+
// TODO (STREAMING) double check this, think about it more carefully please!
75+
// It's related to yarn workspaces to be or not to be
76+
export const getRouteHookBabelPlugins = () => {
77+
return [
78+
...getWebSideBabelPlugins({
79+
forVite: true,
80+
}),
81+
[
82+
'babel-plugin-module-resolver',
83+
{
84+
alias: {
85+
'api/src': './src',
86+
},
87+
root: [getPaths().api.base],
88+
cwd: 'packagejson',
89+
loglevel: 'silent', // to silence the unnecessary warnings
90+
},
91+
'rwjs-api-module-resolver',
92+
],
93+
]
94+
}
95+
7296
/**
7397
* Finds, reads and parses the [ts|js]config.json file
7498
* @returns The config object

packages/internal/src/build/babel/web.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export const getWebSideBabelPlugins = (
3737
forJest ? rwjsPaths.web.src : './src',
3838
// adds the paths from [ts|js]config.json to the module resolver
3939
...getPathsFromConfig(tsConfigs.web),
40+
$api: rwjsPaths.api.base,
4041
},
4142
root: [rwjsPaths.web.base],
4243
cwd: 'packagejson',

0 commit comments

Comments
 (0)