diff --git a/packages/cli/src/commands/__tests__/dev.test.js b/packages/cli/src/commands/__tests__/dev.test.js index 85c5c8d94709..d34f4452eaf4 100644 --- a/packages/cli/src/commands/__tests__/dev.test.js +++ b/packages/cli/src/commands/__tests__/dev.test.js @@ -81,6 +81,11 @@ describe('yarn rw dev', () => { port: 8911, debugPort: 18911, }, + experimental: { + streamingSsr: { + enabled: false, + }, + }, }) await handler({ @@ -106,6 +111,45 @@ describe('yarn rw dev', () => { expect(generateCommand.command).toEqual('yarn rw-gen-watch') }) + it('Should run api and FE dev server, when streaming experimental flag enabled', async () => { + getConfig.mockReturnValue({ + web: { + port: 8910, + }, + api: { + port: 8911, + debugPort: 18911, + }, + experimental: { + streamingSsr: { + enabled: true, // <-- enable SSR/Streaming + }, + }, + }) + + await handler({ + side: ['api', 'web'], + }) + + expect(generatePrismaClient).toHaveBeenCalledTimes(1) + const concurrentlyArgs = concurrently.mock.lastCall[0] + + const webCommand = find(concurrentlyArgs, { name: 'web' }) + const apiCommand = find(concurrentlyArgs, { name: 'api' }) + const generateCommand = find(concurrentlyArgs, { name: 'gen' }) + + // Uses absolute path, so not doing a snapshot + expect(webCommand.command).toContain( + 'yarn cross-env NODE_ENV=development rw-dev-fe' + ) + + expect(apiCommand.command).toMatchInlineSnapshot( + `"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""` + ) + + expect(generateCommand.command).toEqual('yarn rw-gen-watch') + }) + it('Debug port passed in command line overrides TOML', async () => { getConfig.mockReturnValue({ web: { @@ -115,6 +159,11 @@ describe('yarn rw dev', () => { port: 8911, debugPort: 505050, }, + experimental: { + streamingSsr: { + enabled: false, + }, + }, }) await handler({ @@ -140,6 +189,11 @@ describe('yarn rw dev', () => { port: 8911, debugPort: false, }, + experimental: { + streamingSsr: { + enabled: false, + }, + }, }) await handler({ @@ -162,6 +216,11 @@ describe('yarn rw dev', () => { api: { port: 8911, }, + experimental: { + streamingSsr: { + enabled: false, + }, + }, }) await handler({ diff --git a/packages/cli/src/commands/devHandler.js b/packages/cli/src/commands/devHandler.js index edd551aac409..9d4369e1abbe 100644 --- a/packages/cli/src/commands/devHandler.js +++ b/packages/cli/src/commands/devHandler.js @@ -186,12 +186,31 @@ export const handler = async ({ const redwoodConfigPath = getConfigPath() - const webCommand = - getConfig().web.bundler !== 'webpack' // @NOTE: can't use enums, not TS - ? `yarn cross-env NODE_ENV=development rw-vite-dev ${forward}` - : `yarn cross-env NODE_ENV=development RWJS_WATCH_NODE_MODULES=${ - watchNodeModules ? '1' : '' - } webpack serve --config "${webpackDevConfig}" ${forward}` + const streamingSsrEnabled = getConfig().experimental.streamingSsr?.enabled + + // @TODO (Streaming) Lot of temporary feature flags for started dev server. + // Written this way to make it easier to read + + // 1. default: Vite (SPA) + let webCommand = `yarn cross-env NODE_ENV=development rw-vite-dev ${forward}` + + // 2. Vite with SSR + if (streamingSsrEnabled) { + webCommand = `yarn cross-env NODE_ENV=development rw-dev-fe ${forward}` + } + + // 3. Webpack (SPA): we will remove this override after v7 + if (getConfig().web.bundler === 'webpack') { + if (streamingSsrEnabled) { + throw new Error( + 'Webpack does not support SSR. Please switch your bundler to Vite in redwood.toml first' + ) + } else { + webCommand = `yarn cross-env NODE_ENV=development RWJS_WATCH_NODE_MODULES=${ + watchNodeModules ? '1' : '' + } webpack serve --config "${webpackDevConfig}" ${forward}` + } + } /** @type {Record} */ const jobs = { diff --git a/packages/cli/src/commands/experimental/templates/streamingSsr/entry.client.tsx.template b/packages/cli/src/commands/experimental/templates/streamingSsr/entry.client.tsx.template index 7d2b297c6034..38020749d89d 100644 --- a/packages/cli/src/commands/experimental/templates/streamingSsr/entry.client.tsx.template +++ b/packages/cli/src/commands/experimental/templates/streamingSsr/entry.client.tsx.template @@ -1,10 +1,5 @@ import { hydrateRoot, createRoot } from 'react-dom/client' -// TODO (STREAMING) This was marked "temporary workaround" -// Need to figure out why it's a temporary workaround and what we -// should do instead. -import { ServerContextProvider } from '@redwoodjs/web/dist/serverContext' - import App from './App' import { Document } from './Document' @@ -19,20 +14,15 @@ const redwoodAppElement = document.getElementById('redwood-app') if (redwoodAppElement.children?.length > 0) { hydrateRoot( document, - - - - - + + + ) } else { - console.log('Rendering from scratch') const root = createRoot(document) root.render( - - - - - + + + ) } diff --git a/packages/cli/src/commands/experimental/templates/streamingSsr/entry.server.tsx.template b/packages/cli/src/commands/experimental/templates/streamingSsr/entry.server.tsx.template index dac1b959b928..fda9b9b6a01c 100644 --- a/packages/cli/src/commands/experimental/templates/streamingSsr/entry.server.tsx.template +++ b/packages/cli/src/commands/experimental/templates/streamingSsr/entry.server.tsx.template @@ -1,29 +1,20 @@ import { LocationProvider } from '@redwoodjs/router' -import { ServerContextProvider } from '@redwoodjs/web/dist/serverContext' import App from './App' import { Document } from './Document' interface Props { - routeContext: any url: string css: string[] meta?: any[] } -export const ServerEntry: React.FC = ({ - routeContext, - url, - css, - meta, -}) => { +export const ServerEntry: React.FC = ({ url, css, meta }) => { return ( - - - - - - - + + + + + ) } diff --git a/packages/vite/src/devFeServer.ts b/packages/vite/src/devFeServer.ts index 4b1235f55d3b..9adc2519d8e4 100644 --- a/packages/vite/src/devFeServer.ts +++ b/packages/vite/src/devFeServer.ts @@ -20,13 +20,26 @@ globalThis.RWJS_ENV = {} globalThis.__REDWOOD__PRERENDER_PAGES = {} async function createServer() { + // Check CWD: make sure its the web/ directory + // Without this Postcss can misbehave, and its hard to trace why. + if (process.cwd() !== getPaths().web.base) { + console.error('⚠️ Warning: CWD is ', process.cwd()) + console.warn('~'.repeat(50)) + console.warn( + 'The FE dev server cwd must be web/. Please use `yarn rw dev` or start the server from the web/ directory.' + ) + console.log(`Changing cwd to ${getPaths().web.base}....`) + console.log() + + process.chdir(getPaths().web.base) + } + const app = express() const rwPaths = getPaths() // TODO (STREAMING) When Streaming is released Vite will be the only bundler, // and this file should always exist. So the error message needs to change // (or be removed perhaps) - // @MARK: Vite is still experimental, and opt-in if (!rwPaths.web.viteConfig) { throw new Error( 'Vite config not found. You need to setup your project with Vite using `yarn rw setup vite`' @@ -45,7 +58,6 @@ async function createServer() { }) // use vite's connect instance as middleware - // if you use your own express router (express.Router()), you should use router.use app.use(vite.middlewares) app.use('*', async (req, res, next) => { diff --git a/packages/web/src/components/DevFatalErrorPage.tsx b/packages/web/src/components/DevFatalErrorPage.tsx index 9e8dc7cba558..9c27fc0058ff 100644 --- a/packages/web/src/components/DevFatalErrorPage.tsx +++ b/packages/web/src/components/DevFatalErrorPage.tsx @@ -3,14 +3,16 @@ // making it fine for embedding inside this project. // Stacktracey requires buffer, which Vite does not polyfill by default -window.Buffer = window.Buffer || require('buffer').Buffer +if (typeof window !== 'undefined') { + window.Buffer = window.Buffer || require('buffer').Buffer +} import { useState } from 'react' import StackTracey from 'stacktracey' // RWJS_SRC_ROOT is defined and defaulted in webpack to the base path -const srcRoot = RWJS_DEBUG_ENV?.RWJS_SRC_ROOT || '' +const srcRoot = globalThis.RWJS_DEBUG_ENV?.RWJS_SRC_ROOT || '' let appRoot: string diff --git a/packages/web/src/serverContext.tsx b/packages/web/src/serverContext.tsx deleted file mode 100644 index f2ac6ac7a698..000000000000 --- a/packages/web/src/serverContext.tsx +++ /dev/null @@ -1,9 +0,0 @@ -// TODO (STREAMING) is this even used anymore? -import React from 'react' - -const ServerContext = React.createContext({}) - -export const { - Provider: ServerContextProvider, - Consumer: ServerContextConsumer, -} = ServerContext