Skip to content

Commit abf229b

Browse files
authored
fix(ssr): Get experimental ssr setup working properly (#8922)
As title, addresses the following issues: - fixes: no css styles - fixes: DevFatalErrorPage breaks with “window” not defined during ssr - fixes: ReferenceError: RWJS_DEBUG_ENV is not defined - sets up `yarn rw dev` to use fe server when streaming experimen enabled - Also removes server context, no longer needed. Note: Not needed for v6!
1 parent b13caec commit abf229b

7 files changed

Lines changed: 114 additions & 50 deletions

File tree

packages/cli/src/commands/__tests__/dev.test.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ describe('yarn rw dev', () => {
8181
port: 8911,
8282
debugPort: 18911,
8383
},
84+
experimental: {
85+
streamingSsr: {
86+
enabled: false,
87+
},
88+
},
8489
})
8590

8691
await handler({
@@ -106,6 +111,45 @@ describe('yarn rw dev', () => {
106111
expect(generateCommand.command).toEqual('yarn rw-gen-watch')
107112
})
108113

114+
it('Should run api and FE dev server, when streaming experimental flag enabled', async () => {
115+
getConfig.mockReturnValue({
116+
web: {
117+
port: 8910,
118+
},
119+
api: {
120+
port: 8911,
121+
debugPort: 18911,
122+
},
123+
experimental: {
124+
streamingSsr: {
125+
enabled: true, // <-- enable SSR/Streaming
126+
},
127+
},
128+
})
129+
130+
await handler({
131+
side: ['api', 'web'],
132+
})
133+
134+
expect(generatePrismaClient).toHaveBeenCalledTimes(1)
135+
const concurrentlyArgs = concurrently.mock.lastCall[0]
136+
137+
const webCommand = find(concurrentlyArgs, { name: 'web' })
138+
const apiCommand = find(concurrentlyArgs, { name: 'api' })
139+
const generateCommand = find(concurrentlyArgs, { name: 'gen' })
140+
141+
// Uses absolute path, so not doing a snapshot
142+
expect(webCommand.command).toContain(
143+
'yarn cross-env NODE_ENV=development rw-dev-fe'
144+
)
145+
146+
expect(apiCommand.command).toMatchInlineSnapshot(
147+
`"yarn cross-env NODE_ENV=development NODE_OPTIONS=--enable-source-maps yarn nodemon --quiet --watch "/mocked/project/redwood.toml" --exec "yarn rw-api-server-watch --port 8911 --debug-port 18911 | rw-log-formatter""`
148+
)
149+
150+
expect(generateCommand.command).toEqual('yarn rw-gen-watch')
151+
})
152+
109153
it('Debug port passed in command line overrides TOML', async () => {
110154
getConfig.mockReturnValue({
111155
web: {
@@ -115,6 +159,11 @@ describe('yarn rw dev', () => {
115159
port: 8911,
116160
debugPort: 505050,
117161
},
162+
experimental: {
163+
streamingSsr: {
164+
enabled: false,
165+
},
166+
},
118167
})
119168

120169
await handler({
@@ -140,6 +189,11 @@ describe('yarn rw dev', () => {
140189
port: 8911,
141190
debugPort: false,
142191
},
192+
experimental: {
193+
streamingSsr: {
194+
enabled: false,
195+
},
196+
},
143197
})
144198

145199
await handler({
@@ -162,6 +216,11 @@ describe('yarn rw dev', () => {
162216
api: {
163217
port: 8911,
164218
},
219+
experimental: {
220+
streamingSsr: {
221+
enabled: false,
222+
},
223+
},
165224
})
166225

167226
await handler({

packages/cli/src/commands/devHandler.js

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,31 @@ export const handler = async ({
186186

187187
const redwoodConfigPath = getConfigPath()
188188

189-
const webCommand =
190-
getConfig().web.bundler !== 'webpack' // @NOTE: can't use enums, not TS
191-
? `yarn cross-env NODE_ENV=development rw-vite-dev ${forward}`
192-
: `yarn cross-env NODE_ENV=development RWJS_WATCH_NODE_MODULES=${
193-
watchNodeModules ? '1' : ''
194-
} webpack serve --config "${webpackDevConfig}" ${forward}`
189+
const streamingSsrEnabled = getConfig().experimental.streamingSsr?.enabled
190+
191+
// @TODO (Streaming) Lot of temporary feature flags for started dev server.
192+
// Written this way to make it easier to read
193+
194+
// 1. default: Vite (SPA)
195+
let webCommand = `yarn cross-env NODE_ENV=development rw-vite-dev ${forward}`
196+
197+
// 2. Vite with SSR
198+
if (streamingSsrEnabled) {
199+
webCommand = `yarn cross-env NODE_ENV=development rw-dev-fe ${forward}`
200+
}
201+
202+
// 3. Webpack (SPA): we will remove this override after v7
203+
if (getConfig().web.bundler === 'webpack') {
204+
if (streamingSsrEnabled) {
205+
throw new Error(
206+
'Webpack does not support SSR. Please switch your bundler to Vite in redwood.toml first'
207+
)
208+
} else {
209+
webCommand = `yarn cross-env NODE_ENV=development RWJS_WATCH_NODE_MODULES=${
210+
watchNodeModules ? '1' : ''
211+
} webpack serve --config "${webpackDevConfig}" ${forward}`
212+
}
213+
}
195214

196215
/** @type {Record<string, import('concurrently').CommandObj>} */
197216
const jobs = {
Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import { hydrateRoot, createRoot } from 'react-dom/client'
22

3-
// TODO (STREAMING) This was marked "temporary workaround"
4-
// Need to figure out why it's a temporary workaround and what we
5-
// should do instead.
6-
import { ServerContextProvider } from '@redwoodjs/web/dist/serverContext'
7-
83
import App from './App'
94
import { Document } from './Document'
105

@@ -19,20 +14,15 @@ const redwoodAppElement = document.getElementById('redwood-app')
1914
if (redwoodAppElement.children?.length > 0) {
2015
hydrateRoot(
2116
document,
22-
<ServerContextProvider value={{}}>
23-
<Document css={window.__assetMap?.()?.css}>
24-
<App />
25-
</Document>
26-
</ServerContextProvider>
17+
<Document css={window.__assetMap?.()?.css}>
18+
<App />
19+
</Document>
2720
)
2821
} else {
29-
console.log('Rendering from scratch')
3022
const root = createRoot(document)
3123
root.render(
32-
<ServerContextProvider value={{}}>
33-
<Document css={window.__assetMap?.()?.css}>
34-
<App />
35-
</Document>
36-
</ServerContextProvider>
24+
<Document css={window.__assetMap?.()?.css}>
25+
<App />
26+
</Document>
3727
)
3828
}
Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,20 @@
11
import { LocationProvider } from '@redwoodjs/router'
2-
import { ServerContextProvider } from '@redwoodjs/web/dist/serverContext'
32

43
import App from './App'
54
import { Document } from './Document'
65

76
interface Props {
8-
routeContext: any
97
url: string
108
css: string[]
119
meta?: any[]
1210
}
1311

14-
export const ServerEntry: React.FC<Props> = ({
15-
routeContext,
16-
url,
17-
css,
18-
meta,
19-
}) => {
12+
export const ServerEntry: React.FC<Props> = ({ url, css, meta }) => {
2013
return (
21-
<ServerContextProvider value={routeContext}>
22-
<LocationProvider location={{ pathname: url }}>
23-
<Document css={css} meta={meta}>
24-
<App />
25-
</Document>
26-
</LocationProvider>
27-
</ServerContextProvider>
14+
<LocationProvider location={{ pathname: url }}>
15+
<Document css={css} meta={meta}>
16+
<App />
17+
</Document>
18+
</LocationProvider>
2819
)
2920
}

packages/vite/src/devFeServer.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,26 @@ globalThis.RWJS_ENV = {}
2020
globalThis.__REDWOOD__PRERENDER_PAGES = {}
2121

2222
async function createServer() {
23+
// Check CWD: make sure its the web/ directory
24+
// Without this Postcss can misbehave, and its hard to trace why.
25+
if (process.cwd() !== getPaths().web.base) {
26+
console.error('⚠️ Warning: CWD is ', process.cwd())
27+
console.warn('~'.repeat(50))
28+
console.warn(
29+
'The FE dev server cwd must be web/. Please use `yarn rw dev` or start the server from the web/ directory.'
30+
)
31+
console.log(`Changing cwd to ${getPaths().web.base}....`)
32+
console.log()
33+
34+
process.chdir(getPaths().web.base)
35+
}
36+
2337
const app = express()
2438
const rwPaths = getPaths()
2539

2640
// TODO (STREAMING) When Streaming is released Vite will be the only bundler,
2741
// and this file should always exist. So the error message needs to change
2842
// (or be removed perhaps)
29-
// @MARK: Vite is still experimental, and opt-in
3043
if (!rwPaths.web.viteConfig) {
3144
throw new Error(
3245
'Vite config not found. You need to setup your project with Vite using `yarn rw setup vite`'
@@ -45,7 +58,6 @@ async function createServer() {
4558
})
4659

4760
// use vite's connect instance as middleware
48-
// if you use your own express router (express.Router()), you should use router.use
4961
app.use(vite.middlewares)
5062

5163
app.use('*', async (req, res, next) => {

packages/web/src/components/DevFatalErrorPage.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33
// making it fine for embedding inside this project.
44

55
// Stacktracey requires buffer, which Vite does not polyfill by default
6-
window.Buffer = window.Buffer || require('buffer').Buffer
6+
if (typeof window !== 'undefined') {
7+
window.Buffer = window.Buffer || require('buffer').Buffer
8+
}
79

810
import { useState } from 'react'
911

1012
import StackTracey from 'stacktracey'
1113

1214
// RWJS_SRC_ROOT is defined and defaulted in webpack to the base path
13-
const srcRoot = RWJS_DEBUG_ENV?.RWJS_SRC_ROOT || ''
15+
const srcRoot = globalThis.RWJS_DEBUG_ENV?.RWJS_SRC_ROOT || ''
1416

1517
let appRoot: string
1618

packages/web/src/serverContext.tsx

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)