From e5f7606188321a7f6e6eae36f26d9fd2eb1f687b Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Wed, 14 Feb 2024 19:55:57 +0700 Subject: [PATCH 01/75] just adding some comments for my understanding --- packages/vite/src/entries.ts | 2 +- packages/vite/src/rsc/rscWorker.ts | 6 ++++++ packages/vite/src/rsc/rscWorkerCommunication.ts | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/entries.ts b/packages/vite/src/entries.ts index b0195d10253b..1cdea07b2de3 100644 --- a/packages/vite/src/entries.ts +++ b/packages/vite/src/entries.ts @@ -19,7 +19,7 @@ export type GetBuilder = ( /** * Used to look up the component to import when calling `serve('App')` in - * entry.client.tsx + * Routes.tsx */ export function defineEntries(getEntry: GetEntry, getBuilder?: GetBuilder) { return { getEntry, getBuilder } diff --git a/packages/vite/src/rsc/rscWorker.ts b/packages/vite/src/rsc/rscWorker.ts index e4e2a741b794..fc06ab0bcfb9 100644 --- a/packages/vite/src/rsc/rscWorker.ts +++ b/packages/vite/src/rsc/rscWorker.ts @@ -192,6 +192,7 @@ const shutdown = async () => { const loadServerFile = async (fname: string) => { const vite = await vitePromise + // @MARK: in prod we need to switch to import return vite.ssrLoadModule(fname) } @@ -283,6 +284,7 @@ async function setClientEntries( absoluteClientEntries = value return } + // Vite config const config = await configPromise const entriesFile = await getEntriesFile(config, false) console.log('setClientEntries :: entriesFile', entriesFile) @@ -292,6 +294,8 @@ async function setClientEntries( throw new Error('Failed to load clientEntries') } const baseDir = path.dirname(entriesFile) + + // Convert it to absolute paths absoluteClientEntries = Object.fromEntries( Object.entries(clientEntries).map(([key, val]) => { let fullKey = path.join(baseDir, key) @@ -335,6 +339,8 @@ async function renderRsc(input: RenderInput): Promise { : rwPaths.base console.log('config.root', config.root) console.log('rwPaths.base', rwPaths.base) + + // @MARK: Why proxy? Remove this and see what breaks. const bundlerConfig = new Proxy( {}, { diff --git a/packages/vite/src/rsc/rscWorkerCommunication.ts b/packages/vite/src/rsc/rscWorkerCommunication.ts index 1405a8661d2c..ac13688e891f 100644 --- a/packages/vite/src/rsc/rscWorkerCommunication.ts +++ b/packages/vite/src/rsc/rscWorkerCommunication.ts @@ -86,9 +86,13 @@ export function shutdown() { let nextId = 1 +/** + * Set the client entries to worker for the server build. + */ export function setClientEntries( value: 'load' | Record ): Promise { + // Just making this function async instead of callback return new Promise((resolve, reject) => { const id = nextId++ From b7870ee681720329e2a8d945c87915ea7ee31c8e Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Wed, 14 Feb 2024 19:57:50 +0700 Subject: [PATCH 02/75] Add some TODOS --- packages/vite/src/buildRscFeServer.ts | 2 ++ packages/vite/src/rsc/rscBuildAnalyze.ts | 2 ++ packages/vite/src/rsc/rscBuildClient.ts | 2 ++ packages/vite/src/rsc/rscBuildClientEntriesFile.ts | 1 + packages/vite/src/rsc/rscBuildRwEnvVars.ts | 1 + packages/vite/src/rsc/rscBuildServer.ts | 3 ++- packages/vite/src/rsc/rscWorker.ts | 2 ++ 7 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/buildRscFeServer.ts b/packages/vite/src/buildRscFeServer.ts index 625a5372afad..7eb42a360ac9 100644 --- a/packages/vite/src/buildRscFeServer.ts +++ b/packages/vite/src/buildRscFeServer.ts @@ -43,6 +43,8 @@ export const buildRscFeServer = async ({ ) // Copy CSS assets from server to client + // TODO(RSC_DC): I think not required, the clientBuild just doesn't + // have postcss configured) await rscBuildCopyCssAssets(serverBuildOutput, webDist, webDistServer) // Mappings from server to client asset file names diff --git a/packages/vite/src/rsc/rscBuildAnalyze.ts b/packages/vite/src/rsc/rscBuildAnalyze.ts index 7bc4b364ee40..05361ac96e1a 100644 --- a/packages/vite/src/rsc/rscBuildAnalyze.ts +++ b/packages/vite/src/rsc/rscBuildAnalyze.ts @@ -14,6 +14,8 @@ import { rscAnalyzePlugin } from './rscVitePlugins' * Starts building the AST in entries.ts * Doesn't output any files, only collects a list of RSCs and RSFs */ +// @TODO(RSC_DC): Can we skip actually building here? +// only needed to trigger the rscAnalyzePlugin export async function rscBuildAnalyze(viteConfigPath: string) { const rwPaths = getPaths() const clientEntryFileSet = new Set() diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index 3f82297565c6..2b7f08a3c211 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -15,6 +15,8 @@ import { rscIndexPlugin } from './rscVitePlugins' * buildFeServer -> buildRscFeServer -> rscBuildClient * Generate the client bundle */ +// @TODO(RSC_DC): no redwood-vite plugin +// integrate the rw-v plugin here export async function rscBuildClient( webHtml: string, webDist: string, diff --git a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts index 4c65f2a9d455..f35c7f287413 100644 --- a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts +++ b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts @@ -8,6 +8,7 @@ import type { rscBuildServer } from './rscBuildServer' * Append a mapping of server asset names to client asset names to the * `web/dist/server/entries.js` file. */ +// TODO(RSC_DC) : Understand how this gets used, we can probably use a vite plugin to do this export function rscBuildClientEntriesMappings( clientBuildOutput: Awaited>, serverBuildOutput: Awaited>, diff --git a/packages/vite/src/rsc/rscBuildRwEnvVars.ts b/packages/vite/src/rsc/rscBuildRwEnvVars.ts index d0c767eacea2..b625dca48fb8 100644 --- a/packages/vite/src/rsc/rscBuildRwEnvVars.ts +++ b/packages/vite/src/rsc/rscBuildRwEnvVars.ts @@ -8,6 +8,7 @@ import fs from 'fs/promises' * The import of entries.js that we're adding this to is handled by the * RSC worker we've got set up */ +// TODO(RSC_DC): See if we can inject into bundle as well export async function rscBuildRwEnvVars(webDistServerEntries: string) { await fs.appendFile( webDistServerEntries, diff --git a/packages/vite/src/rsc/rscBuildServer.ts b/packages/vite/src/rsc/rscBuildServer.ts index 9da0dd62182d..c2d4f8ecb43d 100644 --- a/packages/vite/src/rsc/rscBuildServer.ts +++ b/packages/vite/src/rsc/rscBuildServer.ts @@ -13,6 +13,7 @@ import { onWarn } from '../lib/onWarn' * buildFeServer -> buildRscFeServer -> rscBuildClient * Generate the client bundle */ +// @TODO(RSC_DC): no redwood-vite plugin, add it back in here export async function rscBuildServer( entriesFile: string, clientEntryFiles: Record, @@ -141,7 +142,7 @@ export async function rscBuildServer( // TODO (RSC) Change output dir to just dist. We should be "server // first". Client components are the "special case" and should be output // to dist/client - outDir: rwPaths.web.distServer, + outDir: rwPaths.web.distServer + '/rsc', manifest: 'server-build-manifest.json', rollupOptions: { onwarn: onWarn, diff --git a/packages/vite/src/rsc/rscWorker.ts b/packages/vite/src/rsc/rscWorker.ts index fc06ab0bcfb9..ea1aa86d8d36 100644 --- a/packages/vite/src/rsc/rscWorker.ts +++ b/packages/vite/src/rsc/rscWorker.ts @@ -32,6 +32,8 @@ import type { // import type { RenderInput, MessageReq, MessageRes } from './rsc-handler' // import { transformRsfId, generatePrefetchCode } from './rsc-utils' +// TODO(RSC_DC): Use the sekret renderToReadableStream one +// so that we can respond with web streams const { renderToPipeableStream } = RSDWServer type Entries = { default: ReturnType } From a6809a9973c8ebc5b0a45bb5ac36775e7a4579ac Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 15 Feb 2024 18:00:14 +0700 Subject: [PATCH 03/75] Combine builds - rename some functions --- packages/vite/src/buildFeServer.ts | 95 ++++++------------- packages/vite/src/buildRouteHooks.ts | 53 +++++++++++ packages/vite/src/buildRscFeServer.ts | 13 ++- .../vite/src/rsc/rscBuildClientEntriesFile.ts | 4 +- .../vite/src/rsc/rscBuildCopyCssAssets.ts | 4 +- packages/vite/src/rsc/rscBuildServer.ts | 12 ++- 6 files changed, 104 insertions(+), 77 deletions(-) create mode 100644 packages/vite/src/buildRouteHooks.ts diff --git a/packages/vite/src/buildFeServer.ts b/packages/vite/src/buildFeServer.ts index 5d0eeb0ea5de..6a62d06e42fd 100644 --- a/packages/vite/src/buildFeServer.ts +++ b/packages/vite/src/buildFeServer.ts @@ -1,17 +1,12 @@ -import type { PluginBuild } from 'esbuild' -import { build as esbuildBuild } from 'esbuild' import { build as viteBuild } from 'vite' -import { - getRouteHookBabelPlugins, - transformWithBabel, -} from '@redwoodjs/babel-config' import { buildWeb } from '@redwoodjs/internal/dist/build/web' -import { findRouteHooksSrc } from '@redwoodjs/internal/dist/files' +import type { Paths } from '@redwoodjs/project-config' import { getConfig, getPaths } from '@redwoodjs/project-config' +import { buildRouteHooks } from './buildRouteHooks' import { buildRouteManifest } from './buildRouteManifest' -import { buildRscFeServer } from './buildRscFeServer' +import { buildRscClientAndWorker } from './buildRscFeServer' import { ensureProcessDirWeb } from './utils' export interface BuildOptions { @@ -19,6 +14,8 @@ export interface BuildOptions { webDir?: string } +// const SKIP = true + export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { ensureProcessDirWeb(webDir) @@ -26,6 +23,9 @@ export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { const rwConfig = getConfig() const viteConfigPath = rwPaths.web.viteConfig + const rscBuild = rwConfig.experimental?.rsc?.enabled + const streamingBuild = rwConfig.experimental?.streamingSsr?.enabled + if (!viteConfigPath) { throw new Error( 'Vite config not found. You need to setup your project with Vite ' + @@ -41,36 +41,41 @@ export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { ) } - if (rwConfig.experimental?.rsc?.enabled) { + if (rscBuild) { if (!rwPaths.web.entries) { throw new Error('RSC entries file not found') } - await buildRscFeServer({ + await buildRscClientAndWorker({ viteConfigPath, webHtml: rwPaths.web.html, entries: rwPaths.web.entries, webDist: rwPaths.web.dist, webDistServer: rwPaths.web.distServer, - webDistServerEntries: rwPaths.web.distServerEntries, + webDistServerEntries: rwPaths.web.dist + '/rsc/entries.js', }) + } - // Write a route manifest - return await buildRouteManifest() - - // - // RSC specific code ends here - // + // We generate the RSC client bundle in the buildRscFeServer function + // Streaming and RSC client bundles are **not** the same + if (streamingBuild && !rscBuild) { + await buildWeb({ verbose }) } - // - // SSR Specific code below - // + // Generates the output used for the server (streaming/ssr but NOT rsc) + await buildForServer(viteConfigPath, rwPaths, verbose) - // Step 1A: Generate the client bundle - await buildWeb({ verbose }) + await buildRouteHooks(verbose, rwPaths) - // Step 1B: Generate the server output + // Write a route manifest + await buildRouteManifest() +} + +async function buildForServer( + viteConfigPath: string, + rwPaths: Paths, + verbose: boolean | undefined +) { await viteBuild({ configFile: viteConfigPath, build: { @@ -78,48 +83,10 @@ export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { ssr: true, // use boolean here, instead of string. // rollup inputs are defined in the vite plugin }, + legacy: { + buildSsrCjsExternalHeuristics: true, // @MARK @TODO: this gets picked up by the RSC build if its in the index.js..... + }, envFile: false, logLevel: verbose ? 'info' : 'warn', }) - - const allRouteHooks = findRouteHooksSrc() - - const runRwBabelTransformsPlugin = { - name: 'rw-esbuild-babel-transform', - setup(build: PluginBuild) { - build.onLoad({ filter: /\.(js|ts|tsx|jsx)$/ }, async (args) => { - const transformedCode = await transformWithBabel(args.path, [ - ...getRouteHookBabelPlugins(), - ]) - - if (transformedCode?.code) { - return { - contents: transformedCode.code, - loader: 'js', - } - } - - throw new Error(`Could not transform file: ${args.path}`) - }) - }, - } - - await esbuildBuild({ - absWorkingDir: getPaths().web.base, - entryPoints: allRouteHooks, - platform: 'node', - target: 'node16', - // @MARK Disable splitting and esm, because Redwood web modules don't support esm yet - // outExtension: { '.js': '.mjs' }, - // format: 'esm', - // splitting: true, - bundle: true, - plugins: [runRwBabelTransformsPlugin], - packages: 'external', - logLevel: verbose ? 'info' : 'error', - outdir: rwPaths.web.distRouteHooks, - }) - - // Write a route manifest - await buildRouteManifest() } diff --git a/packages/vite/src/buildRouteHooks.ts b/packages/vite/src/buildRouteHooks.ts new file mode 100644 index 000000000000..683d55f26e1f --- /dev/null +++ b/packages/vite/src/buildRouteHooks.ts @@ -0,0 +1,53 @@ +import type { PluginBuild } from 'esbuild' +import { build as esbuildBuild } from 'esbuild' + +import { + getRouteHookBabelPlugins, + transformWithBabel, +} from '@redwoodjs/babel-config' +import { findRouteHooksSrc } from '@redwoodjs/internal/dist/files' +import type { Paths } from '@redwoodjs/project-config' +import { getPaths } from '@redwoodjs/project-config' + +export async function buildRouteHooks( + verbose: boolean | undefined, + rwPaths: Paths +) { + const allRouteHooks = findRouteHooksSrc() + + const runRwBabelTransformsPlugin = { + name: 'rw-esbuild-babel-transform', + setup(build: PluginBuild) { + build.onLoad({ filter: /\.(js|ts|tsx|jsx)$/ }, async (args) => { + const transformedCode = await transformWithBabel(args.path, [ + ...getRouteHookBabelPlugins(), + ]) + + if (transformedCode?.code) { + return { + contents: transformedCode.code, + loader: 'js', + } + } + + throw new Error(`Could not transform file: ${args.path}`) + }) + }, + } + + await esbuildBuild({ + absWorkingDir: getPaths().web.base, + entryPoints: allRouteHooks, + platform: 'node', + target: 'node16', + // @MARK Disable splitting and esm, because Redwood web modules don't support esm yet + // outExtension: { '.js': '.mjs' }, + // format: 'esm', + // splitting: true, + bundle: true, + plugins: [runRwBabelTransformsPlugin], + packages: 'external', + logLevel: verbose ? 'info' : 'error', + outdir: rwPaths.web.distRouteHooks, + }) +} diff --git a/packages/vite/src/buildRscFeServer.ts b/packages/vite/src/buildRscFeServer.ts index 7eb42a360ac9..c2d125967ca9 100644 --- a/packages/vite/src/buildRscFeServer.ts +++ b/packages/vite/src/buildRscFeServer.ts @@ -3,7 +3,7 @@ import { rscBuildClient } from './rsc/rscBuildClient' import { rscBuildClientEntriesMappings } from './rsc/rscBuildClientEntriesFile' import { rscBuildCopyCssAssets } from './rsc/rscBuildCopyCssAssets' import { rscBuildRwEnvVars } from './rsc/rscBuildRwEnvVars' -import { rscBuildServer } from './rsc/rscBuildServer' +import { rscBuildForWorker } from './rsc/rscBuildServer' interface Args { viteConfigPath: string @@ -14,12 +14,11 @@ interface Args { webDistServerEntries: string } -export const buildRscFeServer = async ({ +export const buildRscClientAndWorker = async ({ viteConfigPath, webHtml, entries, webDist, - webDistServer, webDistServerEntries, }: Args) => { // Analyze all files and generate a list of RSCs and RSFs @@ -35,7 +34,7 @@ export const buildRscFeServer = async ({ ) // Generate the server output - const serverBuildOutput = await rscBuildServer( + const serverBuildOutput = await rscBuildForWorker( entries, clientEntryFiles, serverEntryFiles, @@ -45,7 +44,11 @@ export const buildRscFeServer = async ({ // Copy CSS assets from server to client // TODO(RSC_DC): I think not required, the clientBuild just doesn't // have postcss configured) - await rscBuildCopyCssAssets(serverBuildOutput, webDist, webDistServer) + await rscBuildCopyCssAssets( + serverBuildOutput, + webDist + '/client', + webDist + '/rsc' + ) // Mappings from server to client asset file names await rscBuildClientEntriesMappings( diff --git a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts index f35c7f287413..bbac6d71be82 100644 --- a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts +++ b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts @@ -1,7 +1,7 @@ import fs from 'fs/promises' import type { rscBuildClient } from './rscBuildClient' -import type { rscBuildServer } from './rscBuildServer' +import type { rscBuildForWorker } from './rscBuildServer' /** * RSC build. Step 5. @@ -11,7 +11,7 @@ import type { rscBuildServer } from './rscBuildServer' // TODO(RSC_DC) : Understand how this gets used, we can probably use a vite plugin to do this export function rscBuildClientEntriesMappings( clientBuildOutput: Awaited>, - serverBuildOutput: Awaited>, + serverBuildOutput: Awaited>, clientEntryFiles: Record, webDistServerEntries: string ) { diff --git a/packages/vite/src/rsc/rscBuildCopyCssAssets.ts b/packages/vite/src/rsc/rscBuildCopyCssAssets.ts index 37bf0927246f..a61fe58c13c5 100644 --- a/packages/vite/src/rsc/rscBuildCopyCssAssets.ts +++ b/packages/vite/src/rsc/rscBuildCopyCssAssets.ts @@ -1,14 +1,14 @@ import fs from 'fs/promises' import path from 'path' -import type { rscBuildServer } from './rscBuildServer' +import type { rscBuildForWorker } from './rscBuildServer' /** * RSC build. Step 4. * Copy CSS assets from server to client */ export function rscBuildCopyCssAssets( - serverBuildOutput: Awaited>, + serverBuildOutput: Awaited>, webDist: string, webDistServer: string ) { diff --git a/packages/vite/src/rsc/rscBuildServer.ts b/packages/vite/src/rsc/rscBuildServer.ts index c2d4f8ecb43d..74be26e3ad04 100644 --- a/packages/vite/src/rsc/rscBuildServer.ts +++ b/packages/vite/src/rsc/rscBuildServer.ts @@ -10,11 +10,11 @@ import { onWarn } from '../lib/onWarn' /** * RSC build. Step 3. - * buildFeServer -> buildRscFeServer -> rscBuildClient - * Generate the client bundle + * buildFeServer -> buildRscFeServer -> rscBuildForWorker + * Generate the output to be used on the rsc worker (not the actual server!) */ // @TODO(RSC_DC): no redwood-vite plugin, add it back in here -export async function rscBuildServer( +export async function rscBuildForWorker( entriesFile: string, clientEntryFiles: Record, serverEntryFiles: Record, @@ -43,6 +43,10 @@ export async function rscBuildServer( envPrefix: 'REDWOOD_ENV_', publicDir: path.join(rwPaths.web.base, 'public'), envFile: false, + legacy: { + // @MARK: for the worker, we're building ESM! (not CJS) + buildSsrCjsExternalHeuristics: false, + }, define: { RWJS_ENV: { // @NOTE we're avoiding process.env here, unlike webpack @@ -142,7 +146,7 @@ export async function rscBuildServer( // TODO (RSC) Change output dir to just dist. We should be "server // first". Client components are the "special case" and should be output // to dist/client - outDir: rwPaths.web.distServer + '/rsc', + outDir: rwPaths.web.dist + '/rsc', manifest: 'server-build-manifest.json', rollupOptions: { onwarn: onWarn, From 3147a638455b9eb02de3567307416f7d468f3198 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 15 Feb 2024 18:04:47 +0700 Subject: [PATCH 04/75] [Paths] Update paths to build into separate client, server and rsc folders --- packages/vite/src/buildRouteManifest.ts | 2 +- .../src/fully-react/ProdRwRscServerGlobal.ts | 2 +- packages/vite/src/index.ts | 9 +-- packages/vite/src/rsc/rscBuildClient.ts | 4 +- packages/vite/src/rsc/rscWorker.ts | 4 +- packages/vite/src/runFeServer.ts | 58 +++++++------------ .../streaming/createReactStreamingHandler.ts | 23 ++------ 7 files changed, 36 insertions(+), 66 deletions(-) diff --git a/packages/vite/src/buildRouteManifest.ts b/packages/vite/src/buildRouteManifest.ts index 818c501966e0..9f3d0b0b004f 100644 --- a/packages/vite/src/buildRouteManifest.ts +++ b/packages/vite/src/buildRouteManifest.ts @@ -15,7 +15,7 @@ import type { RWRouteManifest } from './types' */ export async function buildRouteManifest() { const buildManifestUrl = url.pathToFileURL( - path.join(getPaths().web.dist, 'client-build-manifest.json') + path.join(getPaths().web.dist + '/client', 'client-build-manifest.json') ).href const clientBuildManifest: ViteBuildManifest = ( await import(buildManifestUrl, { with: { type: 'json' } }) diff --git a/packages/vite/src/fully-react/ProdRwRscServerGlobal.ts b/packages/vite/src/fully-react/ProdRwRscServerGlobal.ts index 8260bd6f869b..5e4e5fc9c2b0 100644 --- a/packages/vite/src/fully-react/ProdRwRscServerGlobal.ts +++ b/packages/vite/src/fully-react/ProdRwRscServerGlobal.ts @@ -21,7 +21,7 @@ export class ProdRwRscServerGlobal extends RwRscServerGlobal { const rwPaths = getPaths() this.serverManifest = readJSON( - join(rwPaths.web.distServer, 'server-build-manifest.json') + join(rwPaths.web.dist + '/rsc', 'server-build-manifest.json') ) } diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 4de0a64da24e..8e5ee82f71fc 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -248,7 +248,7 @@ export default function redwoodPluginVite(): PluginOption[] { }, }, build: { - outDir: options.build?.outDir || rwPaths.web.dist, + outDir: options.build?.outDir || rwPaths.web.dist + '/client', emptyOutDir: true, manifest: !env.ssrBuild ? 'client-build-manifest.json' : undefined, sourcemap: !env.ssrBuild && rwConfig.web.sourceMap, // Note that this can be boolean or 'inline' @@ -256,11 +256,8 @@ export default function redwoodPluginVite(): PluginOption[] { input: getRollupInput(!!env.ssrBuild), }, }, - legacy: { - buildSsrCjsExternalHeuristics: rwConfig.experimental?.rsc?.enabled - ? false - : env.ssrBuild, - }, + // @MARK: do not set buildSsrCjsExternalHeuristics here + // because rsc builds want false, client and server build wants true optimizeDeps: { esbuildOptions: { // @MARK this is because JS projects in Redwood don't have .jsx extensions diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index 2b7f08a3c211..17c8652b7195 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -85,10 +85,12 @@ export async function rscBuildClient( }), }, }), + + // @TODO(RSC_DC): this plugin modifies index.html but in streaming there's not index.html!! rscIndexPlugin(), ], build: { - outDir: webDist, + outDir: webDist + '/client', emptyOutDir: true, // Needed because `outDir` is not inside `root` // TODO (RSC) Enable this when we switch to a server-first approach // emptyOutDir: false, // Already done when building server diff --git a/packages/vite/src/rsc/rscWorker.ts b/packages/vite/src/rsc/rscWorker.ts index ea1aa86d8d36..1694a93f196c 100644 --- a/packages/vite/src/rsc/rscWorker.ts +++ b/packages/vite/src/rsc/rscWorker.ts @@ -234,7 +234,7 @@ const getEntriesFile = async ( return path.join(config.root, config.build.outDir, 'entries.js') } - return rwPaths.web.distServerEntries + return rwPaths.web.dist + '/rsc/entries.js' } const getFunctionComponent = async ( @@ -342,7 +342,7 @@ async function renderRsc(input: RenderInput): Promise { console.log('config.root', config.root) console.log('rwPaths.base', rwPaths.base) - // @MARK: Why proxy? Remove this and see what breaks. + // TODO(RSC_DC): Why proxy? Remove this and see what breaks. const bundlerConfig = new Proxy( {}, { diff --git a/packages/vite/src/runFeServer.ts b/packages/vite/src/runFeServer.ts index 0e7bb767ff00..b8e17dc404d8 100644 --- a/packages/vite/src/runFeServer.ts +++ b/packages/vite/src/runFeServer.ts @@ -49,13 +49,11 @@ export async function runFeServer() { registerFwGlobals() - try { - // This will fail if we're not running in RSC mode (i.e. for Streaming SSR) - // TODO (RSC) Remove the try/catch, or at least the if-statement in there - // once RSC is always enabled - await setClientEntries('load') - } catch (e) { - if (rwConfig.experimental?.rsc?.enabled) { + if (rwConfig.experimental?.rsc?.enabled) { + try { + // This will fail if we're not running in RSC mode (i.e. for Streaming SSR) + await setClientEntries('load') + } catch (e) { console.error('Failed to load client entries') console.error(e) process.exit(1) @@ -68,7 +66,7 @@ export async function runFeServer() { ).default const buildManifestUrl = url.pathToFileURL( - path.join(rwPaths.web.dist, 'client-build-manifest.json') + path.join(rwPaths.web.dist + '/client', 'client-build-manifest.json') ).href const buildManifest: ViteBuildManifest = ( await import(buildManifestUrl, { with: { type: 'json' } }) @@ -80,8 +78,10 @@ export async function runFeServer() { console.log('='.repeat(80)) } + // @MARK: @TODO(RSC_DC): Because of the way we pass everything as an input during rsc build + // it's hard to determine what the true entry is. Compare with SSR-only build. const indexEntry = Object.values(buildManifest).find((manifestItem) => { - return manifestItem.isEntry + return manifestItem.isEntry && manifestItem.src?.includes('index.html') }) if (!indexEntry) { @@ -92,7 +92,7 @@ export async function runFeServer() { // For CF workers, we'd need an equivalent of this app.use( '/assets', - express.static(rwPaths.web.dist + '/assets', { index: false }) + express.static(rwPaths.web.dist + '/client/assets', { index: false }) ) // 2. Proxy the api server @@ -129,33 +129,17 @@ export async function runFeServer() { ? route.matchRegexString : route.pathDefinition - if (!getConfig().experimental?.rsc?.enabled) { - const routeHandler = await createReactStreamingHandler({ - route, - clientEntryPath: clientEntry, - getStylesheetLinks, - }) - - // Wrap with whatg/server adapter. Express handler -> Fetch API handler - app.get(expressPathDef, createServerAdapter(routeHandler)) - } else { - console.log('expressPathDef', expressPathDef) - - // This is for RSC only. And only for now, until we have SSR working we - // with RSC. This maps /, /about, etc to index.html - app.get(expressPathDef, (req, res, next) => { - // Serve index.html for all routes, to let client side routing take - // over - req.url = '/' - // Without this, we get a flash of a url with a trailing slash. Still - // works, but doesn't look nice - // For example, if we navigate to /about we'll see a flash of /about/ - // before returning to /about - req.originalUrl = '/' - - return express.static(rwPaths.web.dist)(req, res, next) - }) - } + // TODO(RSC_DC): RSC is rendering blank page, try using this function for initial render + const routeHandler = await createReactStreamingHandler({ + route, + clientEntryPath: clientEntry, + getStylesheetLinks, + }) + + console.log('Attatching streaming handler for route', route.pathDefinition) + + // Wrap with whatg/server adapter. Express handler -> Fetch API handler + app.get(expressPathDef, createServerAdapter(routeHandler)) } // Mounting middleware at /rw-rsc will strip /rw-rsc from req.url diff --git a/packages/vite/src/streaming/createReactStreamingHandler.ts b/packages/vite/src/streaming/createReactStreamingHandler.ts index e01768193610..44d280042ae7 100644 --- a/packages/vite/src/streaming/createReactStreamingHandler.ts +++ b/packages/vite/src/streaming/createReactStreamingHandler.ts @@ -6,7 +6,7 @@ import type { ViteDevServer } from 'vite' import { defaultAuthProviderState } from '@redwoodjs/auth' import type { RWRouteManifestItem } from '@redwoodjs/internal' -import { getAppRouteHook, getConfig, getPaths } from '@redwoodjs/project-config' +import { getAppRouteHook, getPaths } from '@redwoodjs/project-config' import { matchPath } from '@redwoodjs/router' import type { TagDescriptor } from '@redwoodjs/web' @@ -42,23 +42,10 @@ export const createReactStreamingHandler = async ( if (isProd) { // TODO (RSC) Consolidate paths, so we can have the same code for SSR and RSC - if (getConfig().experimental?.rsc?.enabled) { - entryServerImport = await import( - makeFilePath( - path.join(rwPaths.web.distServer, 'assets', 'entry.server.js') - ) - ) - fallbackDocumentImport = await import( - makeFilePath(path.join(rwPaths.web.distServer, 'assets', 'Document.js')) - ) - } else { - entryServerImport = await import( - makeFilePath(rwPaths.web.distEntryServer) - ) - fallbackDocumentImport = await import( - makeFilePath(rwPaths.web.distDocumentServer) - ) - } + entryServerImport = await import(makeFilePath(rwPaths.web.distEntryServer)) + fallbackDocumentImport = await import( + makeFilePath(rwPaths.web.distDocumentServer) + ) } // @NOTE: we are returning a FetchAPI handler From 391a145b171eb89779d92347281e3c2f7f6ebe11 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 15 Feb 2024 18:33:43 +0700 Subject: [PATCH 05/75] Rename rscBuildServer -> rscBuildForWorker --- packages/vite/src/buildRscFeServer.ts | 2 +- packages/vite/src/rsc/rscBuildClientEntriesFile.ts | 2 +- packages/vite/src/rsc/rscBuildCopyCssAssets.ts | 2 +- .../vite/src/rsc/{rscBuildServer.ts => rscBuildForWorker.ts} | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename packages/vite/src/rsc/{rscBuildServer.ts => rscBuildForWorker.ts} (100%) diff --git a/packages/vite/src/buildRscFeServer.ts b/packages/vite/src/buildRscFeServer.ts index c2d125967ca9..44ae4bf3f604 100644 --- a/packages/vite/src/buildRscFeServer.ts +++ b/packages/vite/src/buildRscFeServer.ts @@ -2,8 +2,8 @@ import { rscBuildAnalyze } from './rsc/rscBuildAnalyze' import { rscBuildClient } from './rsc/rscBuildClient' import { rscBuildClientEntriesMappings } from './rsc/rscBuildClientEntriesFile' import { rscBuildCopyCssAssets } from './rsc/rscBuildCopyCssAssets' +import { rscBuildForWorker } from './rsc/rscBuildForWorker' import { rscBuildRwEnvVars } from './rsc/rscBuildRwEnvVars' -import { rscBuildForWorker } from './rsc/rscBuildServer' interface Args { viteConfigPath: string diff --git a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts index bbac6d71be82..6bbbbba8423d 100644 --- a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts +++ b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts @@ -1,7 +1,7 @@ import fs from 'fs/promises' import type { rscBuildClient } from './rscBuildClient' -import type { rscBuildForWorker } from './rscBuildServer' +import type { rscBuildForWorker } from './rscBuildForWorker' /** * RSC build. Step 5. diff --git a/packages/vite/src/rsc/rscBuildCopyCssAssets.ts b/packages/vite/src/rsc/rscBuildCopyCssAssets.ts index a61fe58c13c5..0307af068e45 100644 --- a/packages/vite/src/rsc/rscBuildCopyCssAssets.ts +++ b/packages/vite/src/rsc/rscBuildCopyCssAssets.ts @@ -1,7 +1,7 @@ import fs from 'fs/promises' import path from 'path' -import type { rscBuildForWorker } from './rscBuildServer' +import type { rscBuildForWorker } from './rscBuildForWorker' /** * RSC build. Step 4. diff --git a/packages/vite/src/rsc/rscBuildServer.ts b/packages/vite/src/rsc/rscBuildForWorker.ts similarity index 100% rename from packages/vite/src/rsc/rscBuildServer.ts rename to packages/vite/src/rsc/rscBuildForWorker.ts From c43d08a8afd9123366ed81ea8c56e761084f68e0 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 15 Feb 2024 23:32:50 +0700 Subject: [PATCH 06/75] WIP: getDefines --- ...babel-plugin-redwood-routes-auto-loader.ts | 11 ++++ packages/vite/src/buildFeServer.ts | 2 + packages/vite/src/index.ts | 14 ++++- packages/vite/src/lib/getViteDefines.ts | 55 +++++++++++++++++ packages/vite/src/rsc/rscBuildClient.ts | 61 ++++--------------- packages/vite/src/rsc/rscBuildForWorker.ts | 57 ++--------------- 6 files changed, 98 insertions(+), 102 deletions(-) create mode 100644 packages/vite/src/lib/getViteDefines.ts diff --git a/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts b/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts index 4b2d0405afab..c408125b5fc4 100644 --- a/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts +++ b/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts @@ -64,6 +64,8 @@ export default function ( ) } + // @MARK @TODO: + // we should change this so that it automatically imports "serve" instead of skipping entirely! if (getConfig().experimental?.rsc?.enabled) { // TODO (RSC): Enable auto-loader for RSC return { @@ -137,6 +139,15 @@ export default function ( Program: { enter() { pages = processPagesDir().map(withRelativeImports) + console.log('xxxxxxxxx') + console.log('xxxxxxxxx') + console.log('xxxxxxxxx') + console.log('xxxxxxxxx') + console.log('xxxxxxxxx') + console.log('xxxxxxxxx') + console.log('xxxxxxxxx') + console.log('xxxxxxxxx') + console.log('xxxxxxxxx') }, exit(p) { if (pages.length === 0) { diff --git a/packages/vite/src/buildFeServer.ts b/packages/vite/src/buildFeServer.ts index 6a62d06e42fd..174a3b0c264e 100644 --- a/packages/vite/src/buildFeServer.ts +++ b/packages/vite/src/buildFeServer.ts @@ -76,6 +76,8 @@ async function buildForServer( rwPaths: Paths, verbose: boolean | undefined ) { + console.log('Starting server build.... \n') + await viteBuild({ configFile: viteConfigPath, build: { diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 8e5ee82f71fc..8cf66983565a 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -29,7 +29,9 @@ export default function redwoodPluginVite(): PluginOption[] { } const relativeEntryPath = path.relative(rwPaths.web.src, clientEntryPath) - + console.log('Definitely using the redwood-vite-plugin') + console.log('Definitely using the redwood-vite-plugin') + console.log('Definitely using the redwood-vite-plugin') return [ { name: 'redwood-plugin-vite-html-env', @@ -120,6 +122,13 @@ export default function redwoodPluginVite(): PluginOption[] { // ---------- End Bundle injection ---------- config: (options: UserConfig, env: ConfigEnv): UserConfig => { + console.log('Definite in this config function') + console.log('Definite in this config function') + console.log('Definite in this config function') + console.log('Definite in this config function') + console.log('Definite in this config function') + console.log('Definite in this config function') + console.log('Definite in this config function') let apiHost = process.env.REDWOOD_API_HOST apiHost ??= rwConfig.api.host apiHost ??= process.env.NODE_ENV === 'production' ? '0.0.0.0' : '[::]' @@ -153,8 +162,7 @@ export default function redwoodPluginVite(): PluginOption[] { __REDWOOD__APP_TITLE: rwConfig.web.title || path.basename(rwPaths.base), RWJS_EXP_STREAMING_SSR: - rwConfig.experimental.streamingSsr && - rwConfig.experimental.streamingSsr.enabled, + rwConfig.experimental?.streamingSsr?.enabled, RWJS_EXP_RSC: rwConfig.experimental?.rsc?.enabled, }, RWJS_DEBUG_ENV: { diff --git a/packages/vite/src/lib/getViteDefines.ts b/packages/vite/src/lib/getViteDefines.ts new file mode 100644 index 000000000000..d04b9832c3c2 --- /dev/null +++ b/packages/vite/src/lib/getViteDefines.ts @@ -0,0 +1,55 @@ +import path from 'node:path' + +import { getConfig, getPaths } from '@redwoodjs/project-config' + +export function getViteDefines(): Record | undefined { + const rwConfig = getConfig() + const rwPaths = getPaths() + + const graphQlUrl = + rwConfig.web.apiGraphQLUrl ?? rwConfig.web.apiUrl + '/graphql' + + return { + RWJS_ENV: { + __REDWOOD__APP_TITLE: rwConfig.web.title || path.basename(rwPaths.base), + RWJS_API_GRAPHQL_URL: graphQlUrl, + RWJS_API_URL: rwConfig.web.apiUrl, + RWJS_EXP_STREAMING_SSR: rwConfig.experimental?.streamingSsr?.enabled, + RWJS_EXP_RSC: rwConfig.experimental?.rsc?.enabled, + }, + RWJS_DEBUG_ENV: { + RWJS_SRC_ROOT: rwPaths.web.src, + REDWOOD_ENV_EDITOR: JSON.stringify(process.env.REDWOOD_ENV_EDITOR), + }, + // Vite can automatically expose environment variables, but we + // disable that in `buildFeServer.ts` by setting `envFile: false` + // because we want to use our own logic for loading .env, + // .env.defaults, etc + // The two object spreads below will expose all environment + // variables listed in redwood.toml and all environment variables + // prefixed with REDWOOD_ENV_ + ...Object.fromEntries( + rwConfig.web.includeEnvironmentVariables.flatMap((envName) => [ + // TODO (RSC): Figure out if/why we need to disable eslint here. + // Re-enable if possible + // eslint-disable-next-line + [`import.meta.env.${envName}`, JSON.stringify(process.env[envName])], + // TODO (RSC): Figure out if/why we need to disable eslint here + // Re-enable if possible + // eslint-disable-next-line + [`process.env.${envName}`, JSON.stringify(process.env[envName])], + ]) + ), + ...Object.entries(process.env).reduce>( + (acc, [key, value]) => { + if (key.startsWith('REDWOOD_ENV_')) { + acc[`import.meta.env.${key}`] = JSON.stringify(value) + acc[`process.env.${key}`] = JSON.stringify(value) + } + + return acc + }, + {} + ), + } +} diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index 17c8652b7195..f4bd379ed009 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -4,8 +4,9 @@ import react from '@vitejs/plugin-react' import { build as viteBuild } from 'vite' import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' -import { getConfig, getPaths } from '@redwoodjs/project-config' +import { getPaths } from '@redwoodjs/project-config' +import { getViteDefines } from '../lib/getViteDefines' import { onWarn } from '../lib/onWarn' import { rscIndexPlugin } from './rscVitePlugins' @@ -16,17 +17,22 @@ import { rscIndexPlugin } from './rscVitePlugins' * Generate the client bundle */ // @TODO(RSC_DC): no redwood-vite plugin -// integrate the rw-v plugin here +// @MARK: I can't seem to remove the duplicated defines here - while it builds +// the output doesn't run anymore (RWJS_ENV undefined etc.) +// why? It's definitely using the vite plugin, but the defines don't come through? export async function rscBuildClient( webHtml: string, webDist: string, clientEntryFiles: Record ) { + console.log('Starting RSC client build.... \n') const rwPaths = getPaths() - const rwConfig = getConfig() - const graphQlUrl = - rwConfig.web.apiGraphQLUrl ?? rwConfig.web.apiUrl + '/graphql' + if (process.cwd() !== rwPaths.web.base) { + throw new Error( + 'Looks like you are running the command from the wrong dir, this can lead to unintended consequences on CSS processing' + ) + } const clientBuildOutput = await viteBuild({ // configFile: viteConfigPath, @@ -34,49 +40,8 @@ export async function rscBuildClient( envPrefix: 'REDWOOD_ENV_', publicDir: path.join(rwPaths.web.base, 'public'), envFile: false, - define: { - RWJS_ENV: { - __REDWOOD__APP_TITLE: rwConfig.web.title || path.basename(rwPaths.base), - RWJS_API_GRAPHQL_URL: graphQlUrl, - RWJS_API_URL: rwConfig.web.apiUrl, - RWJS_EXP_STREAMING_SSR: rwConfig.experimental?.streamingSsr?.enabled, - RWJS_EXP_RSC: rwConfig.experimental?.rsc?.enabled, - }, - RWJS_DEBUG_ENV: { - RWJS_SRC_ROOT: rwPaths.web.src, - REDWOOD_ENV_EDITOR: JSON.stringify(process.env.REDWOOD_ENV_EDITOR), - }, - // Vite can automatically expose environment variables, but we - // disable that in `buildFeServer.ts` by setting `envFile: false` - // because we want to use our own logic for loading .env, - // .env.defaults, etc - // The two object spreads below will expose all environment - // variables listed in redwood.toml and all environment variables - // prefixed with REDWOOD_ENV_ - ...Object.fromEntries( - rwConfig.web.includeEnvironmentVariables.flatMap((envName) => [ - // TODO (RSC): Figure out if/why we need to disable eslint here. - // Re-enable if possible - // eslint-disable-next-line - [`import.meta.env.${envName}`, JSON.stringify(process.env[envName])], - // TODO (RSC): Figure out if/why we need to disable eslint here - // Re-enable if possible - // eslint-disable-next-line - [`process.env.${envName}`, JSON.stringify(process.env[envName])], - ]) - ), - ...Object.entries(process.env).reduce>( - (acc, [key, value]) => { - if (key.startsWith('REDWOOD_ENV_')) { - acc[`import.meta.env.${key}`] = JSON.stringify(value) - acc[`process.env.${key}`] = JSON.stringify(value) - } - - return acc - }, - {} - ), - }, + // @MARK: We need to duplicate the defines here. + define: getViteDefines(), plugins: [ react({ babel: { diff --git a/packages/vite/src/rsc/rscBuildForWorker.ts b/packages/vite/src/rsc/rscBuildForWorker.ts index 74be26e3ad04..bf013b58d171 100644 --- a/packages/vite/src/rsc/rscBuildForWorker.ts +++ b/packages/vite/src/rsc/rscBuildForWorker.ts @@ -20,6 +20,8 @@ export async function rscBuildForWorker( serverEntryFiles: Record, customModules: Record ) { + console.log('Starting RSC worker build.... \n') + const input = { entries: entriesFile, ...clientEntryFiles, @@ -37,61 +39,14 @@ export async function rscBuildForWorker( rwConfig.experimental?.rsc?.enabled ) - const serverBuildOutput = await viteBuild({ + const workerBuildOutput = await viteBuild({ // ...configFileConfig, - root: rwPaths.web.base, - envPrefix: 'REDWOOD_ENV_', - publicDir: path.join(rwPaths.web.base, 'public'), + root: rwPaths.web.base, // 👈 @MARK: watch out, this is different from the main build! envFile: false, legacy: { // @MARK: for the worker, we're building ESM! (not CJS) buildSsrCjsExternalHeuristics: false, }, - define: { - RWJS_ENV: { - // @NOTE we're avoiding process.env here, unlike webpack - RWJS_API_GRAPHQL_URL: - rwConfig.web.apiGraphQLUrl ?? rwConfig.web.apiUrl + '/graphql', - RWJS_API_URL: rwConfig.web.apiUrl, - __REDWOOD__APP_TITLE: rwConfig.web.title || path.basename(rwPaths.base), - RWJS_EXP_STREAMING_SSR: rwConfig.experimental?.streamingSsr?.enabled, - RWJS_EXP_RSC: rwConfig.experimental?.rsc?.enabled, - }, - RWJS_DEBUG_ENV: { - RWJS_SRC_ROOT: rwPaths.web.src, - REDWOOD_ENV_EDITOR: JSON.stringify(process.env.REDWOOD_ENV_EDITOR), - }, - // Vite can automatically expose environment variables, but we - // disable that in `buildFeServer.ts` by setting `envFile: false` - // because we want to use our own logic for loading .env, - // .env.defaults, etc - // The two object spreads below will expose all environment - // variables listed in redwood.toml and all environment variables - // prefixed with REDWOOD_ENV_ - ...Object.fromEntries( - rwConfig.web.includeEnvironmentVariables.flatMap((envName) => [ - // TODO (RSC): Figure out if/why we need to disable eslint here. - // Re-enable if possible - // eslint-disable-next-line - [`import.meta.env.${envName}`, JSON.stringify(process.env[envName])], - // TODO (RSC): Figure out if/why we need to disable eslint here - // Re-enable if possible - // eslint-disable-next-line - [`process.env.${envName}`, JSON.stringify(process.env[envName])], - ]) - ), - ...Object.entries(process.env).reduce>( - (acc, [key, value]) => { - if (key.startsWith('REDWOOD_ENV_')) { - acc[`import.meta.env.${key}`] = JSON.stringify(value) - acc[`process.env.${key}`] = JSON.stringify(value) - } - - return acc - }, - {} - ), - }, ssr: { // Externalize everything except packages with files that have // 'use client' in them (which are the files in `clientEntryFiles`) @@ -190,9 +145,9 @@ export async function rscBuildForWorker( }, }) - if (!('output' in serverBuildOutput)) { + if (!('output' in workerBuildOutput)) { throw new Error('Unexpected vite server build output') } - return serverBuildOutput.output + return workerBuildOutput.output } From 86a4e41440eb67d66864e1678cb6351fe3b3f1c0 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Fri, 16 Feb 2024 17:06:26 +0700 Subject: [PATCH 07/75] Re-enable routes autoloader Make it import rsc "serve" for rsc client builds Clean up and make notes on rscBuild process --- ...babel-plugin-redwood-routes-auto-loader.ts | 190 ++++++++++-------- packages/babel-config/src/web.ts | 25 ++- .../internal/src/__tests__/build_web.test.ts | 2 +- .../src/__tests__/nestedPages.test.ts | 2 +- .../src/__tests__/viteNestedPages.test.mts | 2 +- packages/vite/src/index.ts | 51 +---- .../src/middleware/invokeMiddleware.test.ts | 6 +- packages/vite/src/rsc/rscBuildClient.ts | 5 +- packages/vite/src/rsc/rscBuildForWorker.ts | 17 +- 9 files changed, 143 insertions(+), 157 deletions(-) diff --git a/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts b/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts index c408125b5fc4..86752bcfc897 100644 --- a/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts +++ b/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts @@ -4,16 +4,16 @@ import type { PluginObj, types } from '@babel/core' import type { PagesDependency } from '@redwoodjs/project-config' import { + ensurePosixPath, + getPaths, importStatementPath, processPagesDir, - getPaths, - ensurePosixPath, - getConfig, } from '@redwoodjs/project-config' -interface PluginOptions { - prerender?: boolean - vite?: boolean +export interface PluginOptions { + forPrerender?: boolean + forVite?: boolean + forRscClient?: boolean } /** @@ -39,7 +39,7 @@ const withRelativeImports = (page: PagesDependency) => { export default function ( { types: t }: { types: typeof types }, - { prerender = false, vite = false }: PluginOptions + { forPrerender = false, forVite = false, forRscClient = false }: PluginOptions ): PluginObj { // @NOTE: This var gets mutated inside the visitors let pages = processPagesDir().map(withRelativeImports) @@ -66,13 +66,14 @@ export default function ( // @MARK @TODO: // we should change this so that it automatically imports "serve" instead of skipping entirely! - if (getConfig().experimental?.rsc?.enabled) { - // TODO (RSC): Enable auto-loader for RSC - return { - name: 'babel-plugin-redwood-routes-auto-loader', - visitor: {}, - } - } + // But we need to configure the plugin + // if (getConfig().experimental?.rsc?.enabled) { + // // TODO (RSC): Enable auto-loader for RSC + // return { + // name: 'babel-plugin-redwood-routes-auto-loader', + // visitor: {}, + // } + // } return { name: 'babel-plugin-redwood-routes-auto-loader', @@ -104,7 +105,7 @@ export default function ( // This is to make sure that all the imported "Page modules" are normal // imports and not asynchronous ones. // Note that jest in a user's project does not enter this block, but our tests do - if (prerender) { + if (forPrerender) { // Match import paths, const name could be different const pageThatUserImported = pages.find((page) => { @@ -139,15 +140,6 @@ export default function ( Program: { enter() { pages = processPagesDir().map(withRelativeImports) - console.log('xxxxxxxxx') - console.log('xxxxxxxxx') - console.log('xxxxxxxxx') - console.log('xxxxxxxxx') - console.log('xxxxxxxxx') - console.log('xxxxxxxxx') - console.log('xxxxxxxxx') - console.log('xxxxxxxxx') - console.log('xxxxxxxxx') }, exit(p) { if (pages.length === 0) { @@ -163,67 +155,103 @@ export default function ( ) ) + // For RSC Client builds add + // import { serve } from '@redwoodjs/vite/client' + if (forRscClient) { + nodes.unshift( + t.importDeclaration( + [ + t.importSpecifier( + t.identifier('serve'), + t.identifier('serve') + ), + ], + t.stringLiteral('@redwoodjs/vite/client') + ) + ) + } + // Prepend all imports to the top of the file for (const { importName, relativeImport } of pages) { - // const = { - // name: , - // prerenderLoader: (name) => prerenderLoaderImpl - // LazyComponent: lazy(() => import(/* webpackChunkName: "..." */ ) - // } - - /** - * Real example - * const LoginPage = { - * name: "LoginPage", - * prerenderLoader: () => __webpack_require__(require.resolveWeak("./pages/LoginPage/LoginPage")), */ - // LazyComponent: lazy(() => import("/* webpackChunkName: "LoginPage" *//pages/LoginPage/LoginPage.tsx")) - /* - * } - */ - const importArgument = t.stringLiteral(relativeImport) - importArgument.leadingComments = [ - { - type: 'CommentBlock', - value: ` webpackChunkName: "${importName}" `, - }, - ] + if (forRscClient) { + // rsc CLIENT want this format + // const AboutPage = serve('AboutPage') + // this basically allows the page to be rendered via flight response + nodes.push( + t.variableDeclaration('const', [ + t.variableDeclarator( + t.identifier(importName), + t.callExpression(t.identifier('serve'), [ + t.stringLiteral(importName), + ]) + ), + ]) + ) + } else { + // const = { + // name: , + // prerenderLoader: (name) => prerenderLoaderImpl + // LazyComponent: lazy(() => import(/* webpackChunkName: "..." */ ) + // } + + /** + * Real example + * const LoginPage = { + * name: "LoginPage", + * prerenderLoader: () => __webpack_require__(require.resolveWeak("./pages/LoginPage/LoginPage")), */ + // LazyComponent: lazy(() => import("/* webpackChunkName: "LoginPage" *//pages/LoginPage/LoginPage.tsx")) + /* + * } + */ + importArgument.leadingComments = [ + { + type: 'CommentBlock', + value: ` webpackChunkName: "${importName}" `, + }, + ] - nodes.push( - t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(importName), - t.objectExpression([ - t.objectProperty( - t.identifier('name'), - t.stringLiteral(importName) - ), - // prerenderLoader for ssr/prerender and first load of - // prerendered pages in browser (csr) - // prerenderLoader: (name) => { prerenderLoaderImpl } - t.objectProperty( - t.identifier('prerenderLoader'), - t.arrowFunctionExpression( - [t.identifier('name')], - prerenderLoaderImpl(prerender, vite, relativeImport, t) - ) - ), - t.objectProperty( - t.identifier('LazyComponent'), - t.callExpression(t.identifier('lazy'), [ + nodes.push( + t.variableDeclaration('const', [ + t.variableDeclarator( + t.identifier(importName), + t.objectExpression([ + t.objectProperty( + t.identifier('name'), + t.stringLiteral(importName) + ), + // prerenderLoader for ssr/prerender and first load of + // prerendered pages in browser (csr) + // prerenderLoader: (name) => { prerenderLoaderImpl } + t.objectProperty( + t.identifier('prerenderLoader'), t.arrowFunctionExpression( - [], - t.callExpression(t.identifier('import'), [ - importArgument, - ]) - ), - ]) - ), - ]) - ), - ]) - ) + [t.identifier('name')], + prerenderLoaderImpl( + forPrerender, + forVite, + relativeImport, + t + ) + ) + ), + t.objectProperty( + t.identifier('LazyComponent'), + t.callExpression(t.identifier('lazy'), [ + t.arrowFunctionExpression( + [], + t.callExpression(t.identifier('import'), [ + importArgument, + ]) + ), + ]) + ), + ]) + ), + ]) + ) + } } // Insert at the top of the file @@ -236,7 +264,7 @@ export default function ( function prerenderLoaderImpl( prerender: boolean, - vite: boolean, + forVite: boolean, relativeImport: string, t: typeof types ) { @@ -254,7 +282,7 @@ function prerenderLoaderImpl( // Manually imported pages will be bundled in the main bundle and will be // loaded by the code in `normalizePage` in util.ts let implForBuild - if (vite) { + if (forVite) { implForBuild = t.objectExpression([ t.objectProperty( t.identifier('default'), diff --git a/packages/babel-config/src/web.ts b/packages/babel-config/src/web.ts index df4b3efe51e1..f38ccfeb8edf 100644 --- a/packages/babel-config/src/web.ts +++ b/packages/babel-config/src/web.ts @@ -4,6 +4,8 @@ import path from 'path' import * as babel from '@babel/core' import type { TransformOptions } from '@babel/core' +// Weird import, but just doing this for typesafety. Its just a type, no harm importing from src. +import type { PluginOptions as RoutesAutoloaderOptions } from '@redwoodjs/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader' import { getConfig, getPaths } from '@redwoodjs/project-config' import type { RegisterHookOptions } from './common' @@ -99,11 +101,20 @@ export const getWebSideBabelPlugins = ( } export const getWebSideOverrides = ( - { prerender, forVite }: Flags = { - prerender: false, + { forPrerender, forVite, forRscClient }: Flags = { + forPrerender: false, forVite: false, + forRscClient: false, } ) => { + // Bit of complexity here! + // The plugin will modify the Routes file differently based on what context we're building for + const routeLoaderOptions: RoutesAutoloaderOptions = { + forPrerender, + forVite, + forRscClient, + } + const overrides = [ { test: /.+Cell.(js|tsx|jsx)$/, @@ -116,10 +127,7 @@ export const getWebSideOverrides = ( plugins: [ [ require('./plugins/babel-plugin-redwood-routes-auto-loader').default, - { - prerender, - vite: forVite, - }, + routeLoaderOptions, ], ], }, @@ -199,8 +207,9 @@ export const getWebSideBabelConfigPath = () => { // These flags toggle on/off certain features export interface Flags { forJest?: boolean // will change the alias for module-resolver plugin - prerender?: boolean // changes what babel-plugin-redwood-routes-auto-loader does + forPrerender?: boolean // changes what babel-plugin-redwood-routes-auto-loader does forVite?: boolean + forRscClient?: boolean } export const getWebSideDefaultBabelConfig = (options: Flags = {}) => { @@ -234,7 +243,7 @@ export const registerWebSideBabelHook = ({ // We only register for prerender currently // Static importing pages makes sense overrides: [ - ...getWebSideOverrides({ prerender: true, forVite }), + ...getWebSideOverrides({ forPrerender: true, forVite }), ...overrides, ], }) diff --git a/packages/internal/src/__tests__/build_web.test.ts b/packages/internal/src/__tests__/build_web.test.ts index ef949b5f8664..9ff804130fe3 100644 --- a/packages/internal/src/__tests__/build_web.test.ts +++ b/packages/internal/src/__tests__/build_web.test.ts @@ -64,7 +64,7 @@ test('Check routes are imported with require when staticImports flag is enabled' const routesFile = getPaths().web.routes const prerendered = prebuildWebFile(routesFile, { - prerender: true, + forPrerender: true, forJest: true, })?.code diff --git a/packages/internal/src/__tests__/nestedPages.test.ts b/packages/internal/src/__tests__/nestedPages.test.ts index fa4746fcb23e..bc36517ce71d 100644 --- a/packages/internal/src/__tests__/nestedPages.test.ts +++ b/packages/internal/src/__tests__/nestedPages.test.ts @@ -35,7 +35,7 @@ describe('User specified imports, with static imports', () => { const routesFile = getPaths().web.routes outputWithStaticImports = prebuildWebFile(routesFile, { - prerender: true, + forPrerender: true, forJest: true, })?.code outputWithStaticImports &&= normalizeStr(outputWithStaticImports) diff --git a/packages/vite/src/__tests__/viteNestedPages.test.mts b/packages/vite/src/__tests__/viteNestedPages.test.mts index 27805d40a1b5..983ec22b3d3c 100644 --- a/packages/vite/src/__tests__/viteNestedPages.test.mts +++ b/packages/vite/src/__tests__/viteNestedPages.test.mts @@ -71,7 +71,7 @@ describe('User specified imports, with static imports', () => { const routesFile = getPaths().web.routes const prerenderResult = await vitePrebuildWebFile(routesFile, { - prerender: true, + forPrerender: true, forJest: true, forVite: true, }) diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 8cf66983565a..48110c569b4c 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -9,6 +9,7 @@ import { normalizePath } from 'vite' import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getConfig, getPaths } from '@redwoodjs/project-config' +import { getViteDefines } from './lib/getViteDefines' import handleJsAsJsx from './plugins/vite-plugin-jsx-loader' import removeFromBundle from './plugins/vite-plugin-remove-from-bundle' import swapApolloProvider from './plugins/vite-plugin-swap-apollo-provider' @@ -153,55 +154,7 @@ export default function redwoodPluginVite(): PluginOption[] { // }, envPrefix: 'REDWOOD_ENV_', publicDir: path.join(rwPaths.web.base, 'public'), - define: { - RWJS_ENV: { - // @NOTE we're avoiding process.env here, unlike webpack - RWJS_API_GRAPHQL_URL: - rwConfig.web.apiGraphQLUrl ?? rwConfig.web.apiUrl + '/graphql', - RWJS_API_URL: rwConfig.web.apiUrl, - __REDWOOD__APP_TITLE: - rwConfig.web.title || path.basename(rwPaths.base), - RWJS_EXP_STREAMING_SSR: - rwConfig.experimental?.streamingSsr?.enabled, - RWJS_EXP_RSC: rwConfig.experimental?.rsc?.enabled, - }, - RWJS_DEBUG_ENV: { - RWJS_SRC_ROOT: rwPaths.web.src, - REDWOOD_ENV_EDITOR: JSON.stringify( - process.env.REDWOOD_ENV_EDITOR - ), - }, - // Vite can automatically expose environment variables, but we - // disable that in `buildFeServer.ts` by setting `envFile: false` - // because we want to use our own logic for loading .env, - // .env.defaults, etc - // The two object spreads below will expose all environment - // variables listed in redwood.toml and all environment variables - // prefixed with REDWOOD_ENV_ - ...Object.fromEntries( - rwConfig.web.includeEnvironmentVariables.flatMap((envName) => [ - [ - `import.meta.env.${envName}`, - JSON.stringify(process.env[envName]), - ], - [ - `process.env.${envName}`, - JSON.stringify(process.env[envName]), - ], - ]) - ), - ...Object.entries(process.env).reduce>( - (acc, [key, value]) => { - if (key.startsWith('REDWOOD_ENV_')) { - acc[`import.meta.env.${key}`] = JSON.stringify(value) - acc[`process.env.${key}`] = JSON.stringify(value) - } - - return acc - }, - {} - ), - }, + define: getViteDefines(), css: { // @NOTE config path is relative to where vite.config.js is if you use relative path // postcss: './config/', diff --git a/packages/vite/src/middleware/invokeMiddleware.test.ts b/packages/vite/src/middleware/invokeMiddleware.test.ts index 7547f5a14f60..91d724c0541e 100644 --- a/packages/vite/src/middleware/invokeMiddleware.test.ts +++ b/packages/vite/src/middleware/invokeMiddleware.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, test } from 'vitest' +import { describe, expect, test, vi } from 'vitest' import { defaultAuthProviderState } from '@redwoodjs/auth' @@ -33,6 +33,9 @@ describe('Invoke middleware', () => { }) test('returns a MiddlewareResponse, even if middleware throws', async () => { + const consoleErrorSpy = vi + .spyOn(console, 'error') + .mockImplementation(() => {}) const throwingMiddleware = () => { throw new Error('I want to break free') } @@ -44,6 +47,7 @@ describe('Invoke middleware', () => { expect(mwRes).toBeInstanceOf(MiddlewareResponse) expect(authState).toEqual(defaultAuthProviderState) + consoleErrorSpy.mockRestore() }) test('returns a MiddlewareResponse, even if middleware returns a Response', async () => { diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index f4bd379ed009..ffa901e48b00 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -35,7 +35,7 @@ export async function rscBuildClient( } const clientBuildOutput = await viteBuild({ - // configFile: viteConfigPath, + // @MARK This runs on TOP of the settings in rw-vite-plugin, because we don't set configFile: false root: rwPaths.web.src, envPrefix: 'REDWOOD_ENV_', publicDir: path.join(rwPaths.web.base, 'public'), @@ -43,10 +43,13 @@ export async function rscBuildClient( // @MARK: We need to duplicate the defines here. define: getViteDefines(), plugins: [ + // @MARK We need to duplicate the plugins here.... otherwise builds fail I don't understand why react({ babel: { ...getWebSideDefaultBabelConfig({ forVite: true, + // @MARK 👇 This flag is different for RSC Client builds + forRscClient: true, }), }, }), diff --git a/packages/vite/src/rsc/rscBuildForWorker.ts b/packages/vite/src/rsc/rscBuildForWorker.ts index bf013b58d171..ce79cc44eecc 100644 --- a/packages/vite/src/rsc/rscBuildForWorker.ts +++ b/packages/vite/src/rsc/rscBuildForWorker.ts @@ -4,7 +4,7 @@ import react from '@vitejs/plugin-react' import { build as viteBuild } from 'vite' import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' -import { getConfig, getPaths } from '@redwoodjs/project-config' +import { getPaths } from '@redwoodjs/project-config' import { onWarn } from '../lib/onWarn' @@ -29,19 +29,11 @@ export async function rscBuildForWorker( ...customModules, } - console.log('input', input) - const rwPaths = getPaths() - const rwConfig = getConfig() - - console.log( - 'rscBuildServer.ts RWJS_EXP_RSC', - rwConfig.experimental?.rsc?.enabled - ) const workerBuildOutput = await viteBuild({ - // ...configFileConfig, - root: rwPaths.web.base, // 👈 @MARK: watch out, this is different from the main build! + configFile: false, // @MARK disable loading the original plugin, only use settings in this file. This prevents issues with the routes-auto-loader + root: rwPaths.web.src, // @MARK this used to base, not sure if intentional or not!!! envFile: false, legacy: { // @MARK: for the worker, we're building ESM! (not CJS) @@ -98,9 +90,6 @@ export async function rscBuildForWorker( build: { ssr: true, ssrEmitAssets: true, - // TODO (RSC) Change output dir to just dist. We should be "server - // first". Client components are the "special case" and should be output - // to dist/client outDir: rwPaths.web.dist + '/rsc', manifest: 'server-build-manifest.json', rollupOptions: { From 979bdd859c5e254b8cdabdc74cbc5501284e6449 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Fri, 16 Feb 2024 18:43:35 +0700 Subject: [PATCH 08/75] Build manifest log --- packages/vite/src/runFeServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/runFeServer.ts b/packages/vite/src/runFeServer.ts index b8e17dc404d8..4dcf57e58ebf 100644 --- a/packages/vite/src/runFeServer.ts +++ b/packages/vite/src/runFeServer.ts @@ -74,7 +74,7 @@ export async function runFeServer() { if (rwConfig.experimental?.rsc?.enabled) { console.log('='.repeat(80)) - console.log('buildManifest', buildManifest.default) + console.log('buildManifest', buildManifest) console.log('='.repeat(80)) } From a59b10c37b07ffb5452988e2d6a0f236372e5236 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Fri, 16 Feb 2024 18:43:49 +0700 Subject: [PATCH 09/75] Remove viteclient.serve from fixtures and template for RSC --- __fixtures__/test-project-rsa/web/src/Routes.tsx | 4 ---- .../commands/experimental/templates/rsc/Routes.tsx.template | 4 ---- 2 files changed, 8 deletions(-) diff --git a/__fixtures__/test-project-rsa/web/src/Routes.tsx b/__fixtures__/test-project-rsa/web/src/Routes.tsx index 89a1df33eef0..595c80207883 100644 --- a/__fixtures__/test-project-rsa/web/src/Routes.tsx +++ b/__fixtures__/test-project-rsa/web/src/Routes.tsx @@ -8,14 +8,10 @@ // 'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage import { Router, Route, Set } from '@redwoodjs/router' -import { serve } from '@redwoodjs/vite/client' import NavigationLayout from './layouts/NavigationLayout/NavigationLayout' import NotFoundPage from './pages/NotFoundPage/NotFoundPage' -const AboutPage = serve('AboutPage') -const HomePage = serve('HomePage') - const Routes = () => { return ( diff --git a/packages/cli/src/commands/experimental/templates/rsc/Routes.tsx.template b/packages/cli/src/commands/experimental/templates/rsc/Routes.tsx.template index 89a1df33eef0..595c80207883 100644 --- a/packages/cli/src/commands/experimental/templates/rsc/Routes.tsx.template +++ b/packages/cli/src/commands/experimental/templates/rsc/Routes.tsx.template @@ -8,14 +8,10 @@ // 'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage import { Router, Route, Set } from '@redwoodjs/router' -import { serve } from '@redwoodjs/vite/client' import NavigationLayout from './layouts/NavigationLayout/NavigationLayout' import NotFoundPage from './pages/NotFoundPage/NotFoundPage' -const AboutPage = serve('AboutPage') -const HomePage = serve('HomePage') - const Routes = () => { return ( From b283f051e7b5d7643fc524ec836e12cafdc17770 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Fri, 16 Feb 2024 18:45:23 +0700 Subject: [PATCH 10/75] Serve all static files in the dist.client folder --- packages/vite/src/runFeServer.ts | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/vite/src/runFeServer.ts b/packages/vite/src/runFeServer.ts index c6672bd47609..35d18c4c3e7c 100644 --- a/packages/vite/src/runFeServer.ts +++ b/packages/vite/src/runFeServer.ts @@ -159,17 +159,7 @@ export async function runFeServer() { }) ) - // Serve static assets that aren't covered by any of the above routes or middleware - // Note: That the order here is important and that we are explicitly preventing access - // to the server dist folder - // TODO: In the future, we should explicitly serve `web/dist/client` and `web/dist/rsc` - // and simply not serve the `web/dist/server` folder - app.use(`/${path.basename(rwPaths.web.distServer)}/*`, (_req, res, _next) => { - return res - .status(403) - .end('403 Forbidden: Access to server dist is forbidden') - }) - app.use(express.static(rwPaths.web.dist, { index: false })) + app.use(express.static(rwPaths.web.dist + '/client', { index: false })) app.listen(rwConfig.web.port) console.log( From 93a3e68c9ff795f45f5c8ee966b4af2ce8fe5fee Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Mon, 19 Feb 2024 17:45:31 +0700 Subject: [PATCH 11/75] Remove console logs --- packages/vite/src/index.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 976e6fd5ebad..98d615c9566c 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -127,13 +127,6 @@ export default function redwoodPluginVite(): PluginOption[] { // ---------- End Bundle injection ---------- config: (options: UserConfig, env: ConfigEnv): UserConfig => { - console.log('Definite in this config function') - console.log('Definite in this config function') - console.log('Definite in this config function') - console.log('Definite in this config function') - console.log('Definite in this config function') - console.log('Definite in this config function') - console.log('Definite in this config function') let apiHost = process.env.REDWOOD_API_HOST apiHost ??= rwConfig.api.host apiHost ??= process.env.NODE_ENV === 'production' ? '0.0.0.0' : '[::]' From ec3925bd6cc13ff524acd7c31714b596b8cfff40 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Mon, 19 Feb 2024 18:09:55 +0700 Subject: [PATCH 12/75] Rename server -> renderFromRscServer Add tests for routes auto-loader --- ...-plugin-redwood-routes-auto-loader.test.ts | 63 ++++++++++++++++++- ...babel-plugin-redwood-routes-auto-loader.ts | 23 ++----- packages/vite/src/client.ts | 5 +- 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/packages/babel-config/src/plugins/__tests__/babel-plugin-redwood-routes-auto-loader.test.ts b/packages/babel-config/src/plugins/__tests__/babel-plugin-redwood-routes-auto-loader.test.ts index 9c27d7c93800..48857411a662 100644 --- a/packages/babel-config/src/plugins/__tests__/babel-plugin-redwood-routes-auto-loader.test.ts +++ b/packages/babel-config/src/plugins/__tests__/babel-plugin-redwood-routes-auto-loader.test.ts @@ -6,13 +6,17 @@ import * as babel from '@babel/core' import { getPaths } from '@redwoodjs/project-config' import babelRoutesAutoLoader from '../babel-plugin-redwood-routes-auto-loader' +import type { PluginOptions as RoutesAutoLoaderOptions } from '../babel-plugin-redwood-routes-auto-loader' -const transform = (filename: string) => { +const transform = ( + filename: string, + pluginOptions?: RoutesAutoLoaderOptions +) => { const code = fs.readFileSync(filename, 'utf-8') return babel.transform(code, { filename, presets: ['@babel/preset-react'], - plugins: [babelRoutesAutoLoader], + plugins: [[babelRoutesAutoLoader, pluginOptions]], }) } @@ -67,4 +71,59 @@ describe('page auto loader correctly imports pages', () => { test('Already imported pages are left alone.', () => { expect(result?.code).toContain(`import FooPage from 'src/pages/FooPage'`) }) + + test('RSC specific code should not be added', () => { + expect(result?.code).not.toContain( + 'import { renderFromRscServer } from "@redwoodjs/vite/client"' + ) + }) +}) + +describe('page auto loader handles imports for RSC', () => { + const FIXTURE_PATH = path.resolve( + __dirname, + '../../../../../__fixtures__/example-todo-main/' + ) + + let result: babel.BabelFileResult | null + + beforeAll(() => { + process.env.RWJS_CWD = FIXTURE_PATH + result = transform(getPaths().web.routes, { forRscClient: true }) + }) + + afterAll(() => { + delete process.env.RWJS_CWD + }) + + test('Pages are loaded with renderFromRscServer', () => { + const codeOutput = result?.code + expect(codeOutput).not.toContain(`const HomePage = { + name: "HomePage", + prerenderLoader: name => __webpack_require__(require.resolveWeak("./pages/HomePage/HomePage")), + LazyComponent: lazy(() => import( /* webpackChunkName: "HomePage" */"./pages/HomePage/HomePage")) +`) + + expect(codeOutput).toContain( + 'import { renderFromRscServer } from "@redwoodjs/vite/client"' + ) + + expect(codeOutput).toContain( + 'const HomePage = renderFromRscServer("HomePage")' + ) + + // Un-imported pages get added with renderFromRscServer + // so it calls the RSC worker to get a flight response + expect(codeOutput).toContain( + 'const HomePage = renderFromRscServer("HomePage")' + ) + expect(codeOutput).toContain( + 'const BarPage = renderFromRscServer("BarPage")' + ) + }) + + // Not sure about this 👇 - what should the behaviour be? + test('Already imported pages are left alone.', () => { + expect(result?.code).toContain(`import FooPage from 'src/pages/FooPage'`) + }) }) diff --git a/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts b/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts index 86752bcfc897..40bf7aee2dea 100644 --- a/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts +++ b/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts @@ -64,17 +64,6 @@ export default function ( ) } - // @MARK @TODO: - // we should change this so that it automatically imports "serve" instead of skipping entirely! - // But we need to configure the plugin - // if (getConfig().experimental?.rsc?.enabled) { - // // TODO (RSC): Enable auto-loader for RSC - // return { - // name: 'babel-plugin-redwood-routes-auto-loader', - // visitor: {}, - // } - // } - return { name: 'babel-plugin-redwood-routes-auto-loader', visitor: { @@ -156,14 +145,14 @@ export default function ( ) // For RSC Client builds add - // import { serve } from '@redwoodjs/vite/client' + // import { renderFromRscServer } from '@redwoodjs/vite/client' if (forRscClient) { nodes.unshift( t.importDeclaration( [ t.importSpecifier( - t.identifier('serve'), - t.identifier('serve') + t.identifier('renderFromRscServer'), + t.identifier('renderFromRscServer') ), ], t.stringLiteral('@redwoodjs/vite/client') @@ -176,14 +165,14 @@ export default function ( const importArgument = t.stringLiteral(relativeImport) if (forRscClient) { - // rsc CLIENT want this format - // const AboutPage = serve('AboutPage') + // rsc CLIENT wants this format + // const AboutPage = renderFromRscServer('AboutPage') // this basically allows the page to be rendered via flight response nodes.push( t.variableDeclaration('const', [ t.variableDeclarator( t.identifier(importName), - t.callExpression(t.identifier('serve'), [ + t.callExpression(t.identifier('renderFromRscServer'), [ t.stringLiteral(importName), ]) ), diff --git a/packages/vite/src/client.ts b/packages/vite/src/client.ts index e25fd25af950..8ad1159ffacf 100644 --- a/packages/vite/src/client.ts +++ b/packages/vite/src/client.ts @@ -17,7 +17,10 @@ const checkStatus = async ( return response } -export function serve(rscId: string, basePath = '/rw-rsc/') { +export function renderFromRscServer( + rscId: string, + basePath = '/rw-rsc/' +) { type SetRerender = ( rerender: (next: [ReactElement, string]) => void ) => () => void From 5e767b2f77dde4f321ab5ca6275507a37419dbfc Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Tue, 20 Feb 2024 01:26:59 +0700 Subject: [PATCH 13/75] Remove index.html requirement Move webpack shims into streaming server (and remove vite plugin) --- packages/vite/src/buildFeServer.ts | 2 +- packages/vite/src/buildRouteManifest.ts | 5 +++- packages/vite/src/buildRscFeServer.ts | 7 +++-- packages/vite/src/rsc/rscBuildAnalyze.ts | 8 ++---- packages/vite/src/rsc/rscBuildClient.ts | 14 ++++------ .../vite/src/rsc/rscBuildClientEntriesFile.ts | 8 ++++-- packages/vite/src/rsc/rscVitePlugins.ts | 28 +------------------ packages/vite/src/rsc/rscWebpackShims.ts | 9 ++++++ packages/vite/src/runFeServer.ts | 13 ++++----- .../streaming/createReactStreamingHandler.ts | 3 ++ packages/vite/src/streaming/streamHelpers.ts | 3 +- 11 files changed, 44 insertions(+), 56 deletions(-) create mode 100644 packages/vite/src/rsc/rscWebpackShims.ts diff --git a/packages/vite/src/buildFeServer.ts b/packages/vite/src/buildFeServer.ts index 174a3b0c264e..82f25bb14fc7 100644 --- a/packages/vite/src/buildFeServer.ts +++ b/packages/vite/src/buildFeServer.ts @@ -48,7 +48,7 @@ export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { await buildRscClientAndWorker({ viteConfigPath, - webHtml: rwPaths.web.html, + entryClient: rwPaths.web.entryClient, entries: rwPaths.web.entries, webDist: rwPaths.web.dist, webDistServer: rwPaths.web.distServer, diff --git a/packages/vite/src/buildRouteManifest.ts b/packages/vite/src/buildRouteManifest.ts index 9f3d0b0b004f..bbd8e0c520b9 100644 --- a/packages/vite/src/buildRouteManifest.ts +++ b/packages/vite/src/buildRouteManifest.ts @@ -27,7 +27,10 @@ export async function buildRouteManifest() { acc[route.pathDefinition] = { name: route.name, bundle: route.relativeFilePath - ? clientBuildManifest[route.relativeFilePath]?.file ?? null + ? // @TODO(RSC_DC): this no longer resolves to anything i.e. its always null + // Because the clientBuildManifest has no pages, because all pages are Server-components? + // This may be a non-issue, because RSC pages don't need a client bundle per page (or atleast not the same bundle) + clientBuildManifest[route.relativeFilePath]?.file ?? null : null, matchRegexString: route.matchRegexString, // NOTE this is the path definition, not the actual path diff --git a/packages/vite/src/buildRscFeServer.ts b/packages/vite/src/buildRscFeServer.ts index 44ae4bf3f604..bab8d1bba27a 100644 --- a/packages/vite/src/buildRscFeServer.ts +++ b/packages/vite/src/buildRscFeServer.ts @@ -7,7 +7,7 @@ import { rscBuildRwEnvVars } from './rsc/rscBuildRwEnvVars' interface Args { viteConfigPath: string - webHtml: string + entryClient: string entries: string webDist: string webDistServer: string @@ -16,7 +16,7 @@ interface Args { export const buildRscClientAndWorker = async ({ viteConfigPath, - webHtml, + entryClient, entries, webDist, webDistServerEntries, @@ -28,7 +28,7 @@ export const buildRscClientAndWorker = async ({ // Generate the client bundle const clientBuildOutput = await rscBuildClient( - webHtml, + entryClient, webDist, clientEntryFiles ) @@ -51,6 +51,7 @@ export const buildRscClientAndWorker = async ({ ) // Mappings from server to client asset file names + // Used by the RSC worker await rscBuildClientEntriesMappings( clientBuildOutput, serverBuildOutput, diff --git a/packages/vite/src/rsc/rscBuildAnalyze.ts b/packages/vite/src/rsc/rscBuildAnalyze.ts index 05361ac96e1a..4bdf2e62e6cf 100644 --- a/packages/vite/src/rsc/rscBuildAnalyze.ts +++ b/packages/vite/src/rsc/rscBuildAnalyze.ts @@ -16,6 +16,7 @@ import { rscAnalyzePlugin } from './rscVitePlugins' */ // @TODO(RSC_DC): Can we skip actually building here? // only needed to trigger the rscAnalyzePlugin + export async function rscBuildAnalyze(viteConfigPath: string) { const rwPaths = getPaths() const clientEntryFileSet = new Set() @@ -30,12 +31,6 @@ export async function rscBuildAnalyze(viteConfigPath: string) { root: rwPaths.base, plugins: [ react(), - // { - // name: 'rsc-test-plugin', - // transform(_code, id) { - // console.log('rsc-test-plugin id', id) - // }, - // }, rscAnalyzePlugin( (id) => clientEntryFileSet.add(id), (id) => serverEntryFileSet.add(id) @@ -58,6 +53,7 @@ export async function rscBuildAnalyze(viteConfigPath: string) { rollupOptions: { onwarn: onWarn, input: { + // @TODO(RSC_DC): We could generate this entries file from the analyzedRoutes entries: rwPaths.web.entries, }, }, diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index 9a3daa623ce9..629cc1c53caf 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -9,19 +9,16 @@ import { getPaths } from '@redwoodjs/project-config' import { getViteDefines } from '../lib/getViteDefines' import { onWarn } from '../lib/onWarn' -import { rscIndexPlugin } from './rscVitePlugins' - /** * RSC build. Step 2. * buildFeServer -> buildRscFeServer -> rscBuildClient * Generate the client bundle */ -// @TODO(RSC_DC): no redwood-vite plugin // @MARK: I can't seem to remove the duplicated defines here - while it builds // the output doesn't run anymore (RWJS_ENV undefined etc.) // why? It's definitely using the vite plugin, but the defines don't come through? export async function rscBuildClient( - webHtml: string, + entryClient: string, webDist: string, clientEntryFiles: Record ) { @@ -36,6 +33,7 @@ export async function rscBuildClient( const clientBuildOutput = await viteBuild({ // @MARK This runs on TOP of the settings in rw-vite-plugin, because we don't set configFile: false + // but if you actually set the config file, it runs the transforms twice root: rwPaths.web.src, envPrefix: 'REDWOOD_ENV_', publicDir: path.join(rwPaths.web.base, 'public'), @@ -53,9 +51,6 @@ export async function rscBuildClient( }), }, }), - - // @TODO(RSC_DC): this plugin modifies index.html but in streaming there's not index.html!! - rscIndexPlugin(), ], build: { outDir: webDist + '/client', @@ -65,7 +60,10 @@ export async function rscBuildClient( rollupOptions: { onwarn: onWarn, input: { - main: webHtml, + // @MARK: temporary hack to find the entry client so we can get the index.css bundle + // but we don't actually want this on an rsc page! + 'rwjs-client-entry': entryClient, + // we need this, so that files with "use client" aren't bundled. I **think** RSC wants an unbundled build ...clientEntryFiles, }, preserveEntrySignatures: 'exports-only', diff --git a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts index 6bbbbba8423d..47ae214b1b4e 100644 --- a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts +++ b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts @@ -6,9 +6,10 @@ import type { rscBuildForWorker } from './rscBuildForWorker' /** * RSC build. Step 5. * Append a mapping of server asset names to client asset names to the - * `web/dist/server/entries.js` file. + * `web/dist/rsc/entries.js` file. Only used by the RSC worker. */ -// TODO(RSC_DC) : Understand how this gets used, we can probably use a vite plugin to do this +// TODO(RSC_DC) : We could probably do this in rscBuildForWorker +// using the `writeBundle` hook or similar. export function rscBuildClientEntriesMappings( clientBuildOutput: Awaited>, serverBuildOutput: Awaited>, @@ -18,6 +19,9 @@ export function rscBuildClientEntriesMappings( const clientEntries: Record = {} for (const item of clientBuildOutput) { const { name, fileName } = item + + // @MARK: Doesn't refer to Vite entry... + // this is file that uses one or more of the clientEntries const entryFile = name && // TODO (RSC) Can't we just compare the names? `item.name === name` diff --git a/packages/vite/src/rsc/rscVitePlugins.ts b/packages/vite/src/rsc/rscVitePlugins.ts index b2bdd5bf920c..6c3fa75a1875 100644 --- a/packages/vite/src/rsc/rscVitePlugins.ts +++ b/packages/vite/src/rsc/rscVitePlugins.ts @@ -6,33 +6,6 @@ import type { Plugin } from 'vite' import * as RSDWNodeLoader from '../react-server-dom-webpack/node-loader' import type { ResolveFunction } from '../react-server-dom-webpack/node-loader' -// Used in Step 2 of the build process, for the client bundle -export function rscIndexPlugin(): Plugin { - const codeToInject = ` - globalThis.__rw_module_cache__ = new Map(); - - globalThis.__webpack_chunk_load__ = (id) => { - return import(id).then((m) => globalThis.__rw_module_cache__.set(id, m)) - }; - - globalThis.__webpack_require__ = (id) => { - return globalThis.__rw_module_cache__.get(id) - };\n ` - - return { - name: 'rsc-index-plugin', - async transformIndexHtml() { - return [ - { - tag: 'script', - children: codeToInject, - injectTo: 'body', - }, - ] - }, - } -} - export function rscTransformPlugin(): Plugin { return { name: 'rsc-transform-plugin', @@ -160,6 +133,7 @@ export function rscAnalyzePlugin( transform(code, id) { const ext = path.extname(id) if (['.ts', '.tsx', '.js', '.jsx'].includes(ext)) { + // @MARK: We're using swc here, that's cool but another dependency! const mod = swc.parseSync(code, { syntax: ext === '.ts' || ext === '.tsx' ? 'typescript' : 'ecmascript', tsx: ext === '.tsx', diff --git a/packages/vite/src/rsc/rscWebpackShims.ts b/packages/vite/src/rsc/rscWebpackShims.ts new file mode 100644 index 000000000000..a62cd0958f72 --- /dev/null +++ b/packages/vite/src/rsc/rscWebpackShims.ts @@ -0,0 +1,9 @@ +export const rscWebpackShims = `globalThis.__rw_module_cache__ = new Map(); + +globalThis.__webpack_chunk_load__ = (id) => { + return import(id).then((m) => globalThis.__rw_module_cache__.set(id, m)) +}; + +globalThis.__webpack_require__ = (id) => { + return globalThis.__rw_module_cache__.get(id) +};\n` diff --git a/packages/vite/src/runFeServer.ts b/packages/vite/src/runFeServer.ts index 35d18c4c3e7c..f81ce1418291 100644 --- a/packages/vite/src/runFeServer.ts +++ b/packages/vite/src/runFeServer.ts @@ -68,24 +68,23 @@ export async function runFeServer() { const buildManifestUrl = url.pathToFileURL( path.join(rwPaths.web.dist + '/client', 'client-build-manifest.json') ).href - const buildManifest: ViteBuildManifest = ( + const clientBuildManifest: ViteBuildManifest = ( await import(buildManifestUrl, { with: { type: 'json' } }) ).default if (rwConfig.experimental?.rsc?.enabled) { console.log('='.repeat(80)) - console.log('buildManifest', buildManifest) + console.log('buildManifest', clientBuildManifest) console.log('='.repeat(80)) } - // @MARK: @TODO(RSC_DC): Because of the way we pass everything as an input during rsc build - // it's hard to determine what the true entry is. Compare with SSR-only build. - const indexEntry = Object.values(buildManifest).find((manifestItem) => { - return manifestItem.isEntry && manifestItem.src?.includes('index.html') + // @MARK: Surely there's a better way than this! + const indexEntry = Object.values(clientBuildManifest).find((manifestItem) => { + return manifestItem.file.includes('rwjs-client-entry-') }) if (!indexEntry) { - throw new Error('Could not find index.html in build manifest') + throw new Error('Could not find client entry in build manifest') } // 1. Use static handler for assets diff --git a/packages/vite/src/streaming/createReactStreamingHandler.ts b/packages/vite/src/streaming/createReactStreamingHandler.ts index 44d280042ae7..73a7ed889e30 100644 --- a/packages/vite/src/streaming/createReactStreamingHandler.ts +++ b/packages/vite/src/streaming/createReactStreamingHandler.ts @@ -116,6 +116,9 @@ export const createReactStreamingHandler = async ( metaTags = routeHookOutput.meta + // @MARK @TODO(RSC_DC): the entry path for RSC will be different, + // because we don't want to inject a full bundle, just a slice of it + // I'm not sure what though.... const jsBundles = [ clientEntryPath, // @NOTE: must have slash in front bundle && '/' + bundle, diff --git a/packages/vite/src/streaming/streamHelpers.ts b/packages/vite/src/streaming/streamHelpers.ts index 53f9082cf5f6..47a6e847fccd 100644 --- a/packages/vite/src/streaming/streamHelpers.ts +++ b/packages/vite/src/streaming/streamHelpers.ts @@ -18,6 +18,7 @@ import { } from '@redwoodjs/web/dist/components/ServerInject' import type { MiddlewareResponse } from '../middleware/MiddlewareResponse' +import { rscWebpackShims } from '../rsc/rscWebpackShims' import { createBufferedTransformStream } from './transforms/bufferedTransform' import { createTimeoutTransform } from './transforms/cancelTimeoutTransform' @@ -126,7 +127,7 @@ export async function reactRenderToStreamResponse( bootstrapScriptContent: // Only insert assetMap if clientside JS will be loaded jsBundles.length > 0 - ? `window.__REDWOOD__ASSET_MAP = ${assetMap}` + ? `window.__REDWOOD__ASSET_MAP = ${assetMap}; ${rscWebpackShims}` : undefined, bootstrapModules: jsBundles, } From 310f8ebd2b2ed68135c1da86fe840a770018ed41 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Tue, 20 Feb 2024 12:04:44 +0700 Subject: [PATCH 14/75] Only change client path when ssr or rsc enabled --- packages/vite/src/buildFeServer.ts | 1 + packages/vite/src/index.ts | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/buildFeServer.ts b/packages/vite/src/buildFeServer.ts index 82f25bb14fc7..629b16d51147 100644 --- a/packages/vite/src/buildFeServer.ts +++ b/packages/vite/src/buildFeServer.ts @@ -59,6 +59,7 @@ export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { // We generate the RSC client bundle in the buildRscFeServer function // Streaming and RSC client bundles are **not** the same if (streamingBuild && !rscBuild) { + console.log('Building client for streaming SSR') await buildWeb({ verbose }) } diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 98d615c9566c..68e7ef8a80fd 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -131,6 +131,10 @@ export default function redwoodPluginVite(): PluginOption[] { apiHost ??= rwConfig.api.host apiHost ??= process.env.NODE_ENV === 'production' ? '0.0.0.0' : '[::]' + const streamingBuild = rwConfig.experimental.streamingSsr?.enabled + // @MARK: note that most RSC settings sit in their individual build functions + const rscBuild = rwConfig.experimental.rsc?.enabled + let apiPort if (process.env.REDWOOD_API_PORT) { apiPort = parseInt(process.env.REDWOOD_API_PORT) @@ -206,7 +210,12 @@ export default function redwoodPluginVite(): PluginOption[] { }, }, build: { - outDir: options.build?.outDir || rwPaths.web.dist + '/client', + outDir: + options.build?.outDir || + // @MARK: For RSC and Streaming, we build to dist/client directory + (streamingBuild || rscBuild + ? rwPaths.web.dist + '/client' + : rwPaths.web.dist), emptyOutDir: true, manifest: !env.ssrBuild ? 'client-build-manifest.json' : undefined, sourcemap: !env.ssrBuild && rwConfig.web.sourceMap, // Note that this can be boolean or 'inline' From dcade00cf64a09f363e1c41202ec120b24575c03 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Tue, 20 Feb 2024 13:07:01 +0700 Subject: [PATCH 15/75] FIx entry lookup in fe server --- packages/vite/src/runFeServer.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/vite/src/runFeServer.ts b/packages/vite/src/runFeServer.ts index f81ce1418291..58c509feaebe 100644 --- a/packages/vite/src/runFeServer.ts +++ b/packages/vite/src/runFeServer.ts @@ -46,10 +46,10 @@ export async function runFeServer() { const app = express() const rwPaths = getPaths() const rwConfig = getConfig() - + const rscBuild = rwConfig.experimental?.rsc?.enabled registerFwGlobals() - if (rwConfig.experimental?.rsc?.enabled) { + if (rscBuild) { try { // This will fail if we're not running in RSC mode (i.e. for Streaming SSR) await setClientEntries('load') @@ -80,7 +80,10 @@ export async function runFeServer() { // @MARK: Surely there's a better way than this! const indexEntry = Object.values(clientBuildManifest).find((manifestItem) => { - return manifestItem.file.includes('rwjs-client-entry-') + // For RSC builds, we pass in many Vite entries, so we need to find it differently. + return rscBuild + ? manifestItem.file.includes('rwjs-client-entry-') + : manifestItem.isEntry }) if (!indexEntry) { From 29250e0009d8ad2ab2b30ce29aabba3b72742d7f Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Tue, 20 Feb 2024 13:07:12 +0700 Subject: [PATCH 16/75] Add apollo dependency to RSC fixture --- __fixtures__/test-project-rsa/web/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/__fixtures__/test-project-rsa/web/package.json b/__fixtures__/test-project-rsa/web/package.json index 6dac2b3ab8c2..780cb2cd1652 100644 --- a/__fixtures__/test-project-rsa/web/package.json +++ b/__fixtures__/test-project-rsa/web/package.json @@ -11,6 +11,7 @@ ] }, "dependencies": { + "@apollo/experimental-nextjs-app-support": "0.0.0-commit-b8a73fe", "@redwoodjs/forms": "7.0.0-canary.717", "@redwoodjs/router": "7.0.0-canary.717", "@redwoodjs/web": "7.0.0-canary.717", From def1dd92771c6d63ce011f4305a5380458fd5dfa Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Tue, 20 Feb 2024 18:04:10 +0700 Subject: [PATCH 17/75] Unhardcode new paths, add it to rwPaths package Stop passing around paths unnecessarily! --- packages/project-config/src/paths.ts | 15 +++++-- packages/vite/src/buildFeServer.ts | 23 +++------- packages/vite/src/buildRouteManifest.ts | 2 +- packages/vite/src/buildRscFeServer.ts | 43 ++++++------------- .../src/fully-react/ProdRwRscServerGlobal.ts | 2 +- packages/vite/src/index.ts | 2 +- packages/vite/src/rsc/rscBuildAnalyze.ts | 4 +- packages/vite/src/rsc/rscBuildClient.ts | 10 ++--- packages/vite/src/rsc/rscBuildForWorker.ts | 8 ++-- packages/vite/src/runFeServer.ts | 4 +- 10 files changed, 43 insertions(+), 70 deletions(-) diff --git a/packages/project-config/src/paths.ts b/packages/project-config/src/paths.ts index 62902ef16fa4..6e69621887eb 100644 --- a/packages/project-config/src/paths.ts +++ b/packages/project-config/src/paths.ts @@ -49,10 +49,12 @@ export interface WebPaths { storybookManagerConfig: string dist: string distServer: string + distClient: string + distRsc: string distEntryServer: string distDocumentServer: string distRouteHooks: string - distServerEntries: string + distRscEntries: string routeManifest: string types: string graphql: string @@ -122,12 +124,17 @@ const PATH_WEB_DIR_CONFIG_STORYBOOK_CONFIG = 'web/config/storybook.config.js' const PATH_WEB_DIR_CONFIG_STORYBOOK_PREVIEW = 'web/config/storybook.preview' // .js, .tsx const PATH_WEB_DIR_CONFIG_STORYBOOK_MANAGER = 'web/config/storybook.manager.js' const PATH_WEB_DIR_DIST = 'web/dist' + +// Used by Streaming & RSC builds to output to their individual folders const PATH_WEB_DIR_DIST_SERVER = 'web/dist/server' +const PATH_WEB_DIR_DIST_RSC = 'web/dist/rsc' +const PATH_WEB_DIR_DIST_CLIENT = 'web/dist/client' + const PATH_WEB_DIR_DIST_SERVER_ENTRY_SERVER = 'web/dist/server/entry.server.js' const PATH_WEB_DIR_DIST_DOCUMENT = 'web/dist/server/Document.js' const PATH_WEB_DIR_DIST_SERVER_ROUTEHOOKS = 'web/dist/server/routeHooks' -const PATH_WEB_DIR_DIST_SERVER_ENTRIES = 'web/dist/server/entries.js' +const PATH_WEB_DIR_DIST_RSC_ENTRIES = 'web/dist/rsc/entries.js' const PATH_WEB_DIR_ROUTE_MANIFEST = 'web/dist/server/route-manifest.json' /** @@ -237,13 +244,15 @@ export const getPaths = (BASE_DIR: string = getBaseDir()): Paths => { ), dist: path.join(BASE_DIR, PATH_WEB_DIR_DIST), distServer: path.join(BASE_DIR, PATH_WEB_DIR_DIST_SERVER), + distClient: path.join(BASE_DIR, PATH_WEB_DIR_DIST_CLIENT), + distRsc: path.join(BASE_DIR, PATH_WEB_DIR_DIST_RSC), distEntryServer: path.join( BASE_DIR, PATH_WEB_DIR_DIST_SERVER_ENTRY_SERVER ), distDocumentServer: path.join(BASE_DIR, PATH_WEB_DIR_DIST_DOCUMENT), distRouteHooks: path.join(BASE_DIR, PATH_WEB_DIR_DIST_SERVER_ROUTEHOOKS), - distServerEntries: path.join(BASE_DIR, PATH_WEB_DIR_DIST_SERVER_ENTRIES), + distRscEntries: path.join(BASE_DIR, PATH_WEB_DIR_DIST_RSC_ENTRIES), routeManifest: path.join(BASE_DIR, PATH_WEB_DIR_ROUTE_MANIFEST), types: path.join(BASE_DIR, 'web/types'), entryClient: resolveFile(path.join(BASE_DIR, PATH_WEB_DIR_ENTRY_CLIENT)), // new vite/stream entry point for client diff --git a/packages/vite/src/buildFeServer.ts b/packages/vite/src/buildFeServer.ts index 629b16d51147..59d1ac449d59 100644 --- a/packages/vite/src/buildFeServer.ts +++ b/packages/vite/src/buildFeServer.ts @@ -1,7 +1,6 @@ import { build as viteBuild } from 'vite' import { buildWeb } from '@redwoodjs/internal/dist/build/web' -import type { Paths } from '@redwoodjs/project-config' import { getConfig, getPaths } from '@redwoodjs/project-config' import { buildRouteHooks } from './buildRouteHooks' @@ -46,25 +45,18 @@ export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { throw new Error('RSC entries file not found') } - await buildRscClientAndWorker({ - viteConfigPath, - entryClient: rwPaths.web.entryClient, - entries: rwPaths.web.entries, - webDist: rwPaths.web.dist, - webDistServer: rwPaths.web.distServer, - webDistServerEntries: rwPaths.web.dist + '/rsc/entries.js', - }) + await buildRscClientAndWorker() } // We generate the RSC client bundle in the buildRscFeServer function // Streaming and RSC client bundles are **not** the same if (streamingBuild && !rscBuild) { - console.log('Building client for streaming SSR') + console.log('Building client for streaming SSR...\n') await buildWeb({ verbose }) } // Generates the output used for the server (streaming/ssr but NOT rsc) - await buildForServer(viteConfigPath, rwPaths, verbose) + await buildForServer({ verbose }) await buildRouteHooks(verbose, rwPaths) @@ -72,15 +64,12 @@ export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { await buildRouteManifest() } -async function buildForServer( - viteConfigPath: string, - rwPaths: Paths, - verbose: boolean | undefined -) { +async function buildForServer({ verbose = false }: { verbose?: boolean }) { console.log('Starting server build.... \n') + const rwPaths = getPaths() await viteBuild({ - configFile: viteConfigPath, + configFile: rwPaths.web.viteConfig as string, build: { outDir: rwPaths.web.distServer, ssr: true, // use boolean here, instead of string. diff --git a/packages/vite/src/buildRouteManifest.ts b/packages/vite/src/buildRouteManifest.ts index bbd8e0c520b9..a7a93a024165 100644 --- a/packages/vite/src/buildRouteManifest.ts +++ b/packages/vite/src/buildRouteManifest.ts @@ -15,7 +15,7 @@ import type { RWRouteManifest } from './types' */ export async function buildRouteManifest() { const buildManifestUrl = url.pathToFileURL( - path.join(getPaths().web.dist + '/client', 'client-build-manifest.json') + path.join(getPaths().web.distClient, 'client-build-manifest.json') ).href const clientBuildManifest: ViteBuildManifest = ( await import(buildManifestUrl, { with: { type: 'json' } }) diff --git a/packages/vite/src/buildRscFeServer.ts b/packages/vite/src/buildRscFeServer.ts index bab8d1bba27a..041505f600b1 100644 --- a/packages/vite/src/buildRscFeServer.ts +++ b/packages/vite/src/buildRscFeServer.ts @@ -1,3 +1,5 @@ +import { getPaths } from '@redwoodjs/project-config' + import { rscBuildAnalyze } from './rsc/rscBuildAnalyze' import { rscBuildClient } from './rsc/rscBuildClient' import { rscBuildClientEntriesMappings } from './rsc/rscBuildClientEntriesFile' @@ -5,49 +7,28 @@ import { rscBuildCopyCssAssets } from './rsc/rscBuildCopyCssAssets' import { rscBuildForWorker } from './rsc/rscBuildForWorker' import { rscBuildRwEnvVars } from './rsc/rscBuildRwEnvVars' -interface Args { - viteConfigPath: string - entryClient: string - entries: string - webDist: string - webDistServer: string - webDistServerEntries: string -} - -export const buildRscClientAndWorker = async ({ - viteConfigPath, - entryClient, - entries, - webDist, - webDistServerEntries, -}: Args) => { +export const buildRscClientAndWorker = async () => { + const rwPaths = getPaths() // Analyze all files and generate a list of RSCs and RSFs - const { clientEntryFiles, serverEntryFiles } = await rscBuildAnalyze( - viteConfigPath - ) + const { clientEntryFiles, serverEntryFiles } = await rscBuildAnalyze() // Generate the client bundle - const clientBuildOutput = await rscBuildClient( - entryClient, - webDist, - clientEntryFiles - ) + const clientBuildOutput = await rscBuildClient(clientEntryFiles) // Generate the server output const serverBuildOutput = await rscBuildForWorker( - entries, clientEntryFiles, serverEntryFiles, {} ) // Copy CSS assets from server to client - // TODO(RSC_DC): I think not required, the clientBuild just doesn't - // have postcss configured) + // TODO(RSC_DC): Unsure why we're having to do this still. + // Need to understand the thinking behind this, and how CSS assets get injected await rscBuildCopyCssAssets( serverBuildOutput, - webDist + '/client', - webDist + '/rsc' + rwPaths.web.distClient, + rwPaths.web.distRsc ) // Mappings from server to client asset file names @@ -56,9 +37,9 @@ export const buildRscClientAndWorker = async ({ clientBuildOutput, serverBuildOutput, clientEntryFiles, - webDistServerEntries + rwPaths.web.distRscEntries ) // Make RW specific env vars, like RWJS_ENV, available to server components - await rscBuildRwEnvVars(webDistServerEntries) + await rscBuildRwEnvVars(rwPaths.web.distRscEntries) } diff --git a/packages/vite/src/fully-react/ProdRwRscServerGlobal.ts b/packages/vite/src/fully-react/ProdRwRscServerGlobal.ts index 5e4e5fc9c2b0..326284e0b6e1 100644 --- a/packages/vite/src/fully-react/ProdRwRscServerGlobal.ts +++ b/packages/vite/src/fully-react/ProdRwRscServerGlobal.ts @@ -21,7 +21,7 @@ export class ProdRwRscServerGlobal extends RwRscServerGlobal { const rwPaths = getPaths() this.serverManifest = readJSON( - join(rwPaths.web.dist + '/rsc', 'server-build-manifest.json') + join(rwPaths.web.distRsc, 'server-build-manifest.json') ) } diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 68e7ef8a80fd..36c491bb5015 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -214,7 +214,7 @@ export default function redwoodPluginVite(): PluginOption[] { options.build?.outDir || // @MARK: For RSC and Streaming, we build to dist/client directory (streamingBuild || rscBuild - ? rwPaths.web.dist + '/client' + ? rwPaths.web.distClient : rwPaths.web.dist), emptyOutDir: true, manifest: !env.ssrBuild ? 'client-build-manifest.json' : undefined, diff --git a/packages/vite/src/rsc/rscBuildAnalyze.ts b/packages/vite/src/rsc/rscBuildAnalyze.ts index 4bdf2e62e6cf..304ef6f61456 100644 --- a/packages/vite/src/rsc/rscBuildAnalyze.ts +++ b/packages/vite/src/rsc/rscBuildAnalyze.ts @@ -17,7 +17,7 @@ import { rscAnalyzePlugin } from './rscVitePlugins' // @TODO(RSC_DC): Can we skip actually building here? // only needed to trigger the rscAnalyzePlugin -export async function rscBuildAnalyze(viteConfigPath: string) { +export async function rscBuildAnalyze() { const rwPaths = getPaths() const clientEntryFileSet = new Set() const serverEntryFileSet = new Set() @@ -27,7 +27,7 @@ export async function rscBuildAnalyze(viteConfigPath: string) { } await viteBuild({ - configFile: viteConfigPath, + configFile: rwPaths.web.viteConfig as string, root: rwPaths.base, plugins: [ react(), diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index 629cc1c53caf..ed40c99f6270 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -17,11 +17,7 @@ import { onWarn } from '../lib/onWarn' // @MARK: I can't seem to remove the duplicated defines here - while it builds // the output doesn't run anymore (RWJS_ENV undefined etc.) // why? It's definitely using the vite plugin, but the defines don't come through? -export async function rscBuildClient( - entryClient: string, - webDist: string, - clientEntryFiles: Record -) { +export async function rscBuildClient(clientEntryFiles: Record) { console.log('Starting RSC client build.... \n') const rwPaths = getPaths() @@ -53,7 +49,7 @@ export async function rscBuildClient( }), ], build: { - outDir: webDist + '/client', + outDir: rwPaths.web.distClient, emptyOutDir: true, // Needed because `outDir` is not inside `root` // TODO (RSC) Enable this when we switch to a server-first approach // emptyOutDir: false, // Already done when building server @@ -62,7 +58,7 @@ export async function rscBuildClient( input: { // @MARK: temporary hack to find the entry client so we can get the index.css bundle // but we don't actually want this on an rsc page! - 'rwjs-client-entry': entryClient, + 'rwjs-client-entry': rwPaths.web.entryClient as string, // we need this, so that files with "use client" aren't bundled. I **think** RSC wants an unbundled build ...clientEntryFiles, }, diff --git a/packages/vite/src/rsc/rscBuildForWorker.ts b/packages/vite/src/rsc/rscBuildForWorker.ts index ce79cc44eecc..747c8f24a82b 100644 --- a/packages/vite/src/rsc/rscBuildForWorker.ts +++ b/packages/vite/src/rsc/rscBuildForWorker.ts @@ -15,22 +15,20 @@ import { onWarn } from '../lib/onWarn' */ // @TODO(RSC_DC): no redwood-vite plugin, add it back in here export async function rscBuildForWorker( - entriesFile: string, clientEntryFiles: Record, serverEntryFiles: Record, customModules: Record ) { console.log('Starting RSC worker build.... \n') + const rwPaths = getPaths() const input = { - entries: entriesFile, + entries: rwPaths.web.entries as string, ...clientEntryFiles, ...serverEntryFiles, ...customModules, } - const rwPaths = getPaths() - const workerBuildOutput = await viteBuild({ configFile: false, // @MARK disable loading the original plugin, only use settings in this file. This prevents issues with the routes-auto-loader root: rwPaths.web.src, // @MARK this used to base, not sure if intentional or not!!! @@ -90,7 +88,7 @@ export async function rscBuildForWorker( build: { ssr: true, ssrEmitAssets: true, - outDir: rwPaths.web.dist + '/rsc', + outDir: rwPaths.web.distRsc, manifest: 'server-build-manifest.json', rollupOptions: { onwarn: onWarn, diff --git a/packages/vite/src/runFeServer.ts b/packages/vite/src/runFeServer.ts index 58c509feaebe..429d35c1e221 100644 --- a/packages/vite/src/runFeServer.ts +++ b/packages/vite/src/runFeServer.ts @@ -66,7 +66,7 @@ export async function runFeServer() { ).default const buildManifestUrl = url.pathToFileURL( - path.join(rwPaths.web.dist + '/client', 'client-build-manifest.json') + path.join(rwPaths.web.distClient, 'client-build-manifest.json') ).href const clientBuildManifest: ViteBuildManifest = ( await import(buildManifestUrl, { with: { type: 'json' } }) @@ -161,7 +161,7 @@ export async function runFeServer() { }) ) - app.use(express.static(rwPaths.web.dist + '/client', { index: false })) + app.use(express.static(rwPaths.web.distClient, { index: false })) app.listen(rwConfig.web.port) console.log( From 46f94998380ae7cca265e3fc30702c4ed46fec0e Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 23 Feb 2024 16:46:20 +0530 Subject: [PATCH 18/75] Apply suggestions from code review --- .../babel-plugin-redwood-routes-auto-loader.test.ts | 2 +- packages/vite/src/entries.ts | 2 +- packages/vite/src/rsc/rscBuildForWorker.ts | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/babel-config/src/plugins/__tests__/babel-plugin-redwood-routes-auto-loader.test.ts b/packages/babel-config/src/plugins/__tests__/babel-plugin-redwood-routes-auto-loader.test.ts index 48857411a662..69cda21e9399 100644 --- a/packages/babel-config/src/plugins/__tests__/babel-plugin-redwood-routes-auto-loader.test.ts +++ b/packages/babel-config/src/plugins/__tests__/babel-plugin-redwood-routes-auto-loader.test.ts @@ -122,7 +122,7 @@ describe('page auto loader handles imports for RSC', () => { ) }) - // Not sure about this 👇 - what should the behaviour be? + // Not sure about this 👇 - what should the behavior be? test('Already imported pages are left alone.', () => { expect(result?.code).toContain(`import FooPage from 'src/pages/FooPage'`) }) diff --git a/packages/vite/src/entries.ts b/packages/vite/src/entries.ts index 1cdea07b2de3..a33dc0539764 100644 --- a/packages/vite/src/entries.ts +++ b/packages/vite/src/entries.ts @@ -18,7 +18,7 @@ export type GetBuilder = ( }> /** - * Used to look up the component to import when calling `serve('App')` in + * Used to look up the component to import when calling `renderFromRscServer('MyPage')` in * Routes.tsx */ export function defineEntries(getEntry: GetEntry, getBuilder?: GetBuilder) { diff --git a/packages/vite/src/rsc/rscBuildForWorker.ts b/packages/vite/src/rsc/rscBuildForWorker.ts index ef21fd2da1c7..bb2e8ab93355 100644 --- a/packages/vite/src/rsc/rscBuildForWorker.ts +++ b/packages/vite/src/rsc/rscBuildForWorker.ts @@ -14,7 +14,7 @@ import { rscTransformPlugin } from './rscVitePlugins' /** * RSC build. Step 3. * buildFeServer -> buildRscFeServer -> rscBuildForWorker - * Generate the output to be used on the rsc worker (not the actual server!) + * Generate the output to be used by the rsc worker (not the actual server!) */ // @TODO(RSC_DC): no redwood-vite plugin, add it back in here export async function rscBuildForWorker( @@ -22,7 +22,7 @@ export async function rscBuildForWorker( serverEntryFiles: Record, customModules: Record ) { - console.log('Starting RSC worker build.... \n') + console.log('Starting RSC worker build...\n') const rwPaths = getPaths() const input = { @@ -34,7 +34,7 @@ export async function rscBuildForWorker( const workerBuildOutput = await viteBuild({ configFile: false, // @MARK disable loading the original plugin, only use settings in this file. This prevents issues with the routes-auto-loader - root: rwPaths.web.src, // @MARK this used to base, not sure if intentional or not!!! + root: rwPaths.web.src, // @MARK this used to be `rwPaths.web.base`, not sure if intentional or not!!! envFile: false, legacy: { // @MARK: for the worker, we're building ESM! (not CJS) From 051decf1184fd623519d07bde37e1d97c0c5d378 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 23 Feb 2024 16:58:10 +0530 Subject: [PATCH 19/75] Tobbe review changes --- .../babel-plugin-redwood-routes-auto-loader.ts | 17 ++++++++--------- packages/babel-config/src/web.ts | 2 +- packages/vite/src/buildFeServer.ts | 9 +++++++-- packages/vite/src/rsc/rscBuildAnalyze.ts | 6 +++++- packages/vite/src/rsc/rscBuildClient.ts | 1 - packages/vite/src/rsc/rscBuildForWorker.ts | 6 +++++- packages/vite/src/rsc/rscWorker.ts | 2 +- packages/vite/src/rsc/rscWorkerCommunication.ts | 4 +--- 8 files changed, 28 insertions(+), 19 deletions(-) diff --git a/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts b/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts index af05146e5bed..bd31b9b914b4 100644 --- a/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts +++ b/packages/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader.ts @@ -185,15 +185,14 @@ export default function ( // LazyComponent: lazy(() => import(/* webpackChunkName: "..." */ ) // } - /** - * Real example - * const LoginPage = { - * name: "LoginPage", - * prerenderLoader: () => __webpack_require__(require.resolveWeak("./pages/LoginPage/LoginPage")), */ - // LazyComponent: lazy(() => import("/* webpackChunkName: "LoginPage" *//pages/LoginPage/LoginPage.tsx")) - /* - * } - */ + // + // Real example + // const LoginPage = { + // name: "LoginPage", + // prerenderLoader: () => __webpack_require__(require.resolveWeak("./pages/LoginPage/LoginPage")), + // LazyComponent: lazy(() => import("/* webpackChunkName: "LoginPage" *//pages/LoginPage/LoginPage.tsx")) + // } + // importArgument.leadingComments = [ { type: 'CommentBlock', diff --git a/packages/babel-config/src/web.ts b/packages/babel-config/src/web.ts index ea7f0649ee28..87924e84f01f 100644 --- a/packages/babel-config/src/web.ts +++ b/packages/babel-config/src/web.ts @@ -4,7 +4,7 @@ import path from 'path' import * as babel from '@babel/core' import type { TransformOptions } from '@babel/core' -// Weird import, but just doing this for typesafety. Its just a type, no harm importing from src. +// This import is for types safety. Its just a type, no harm importing from src. import type { PluginOptions as RoutesAutoLoaderOptions } from '@redwoodjs/babel-config/src/plugins/babel-plugin-redwood-routes-auto-loader' import { getConfig, getPaths } from '@redwoodjs/project-config' diff --git a/packages/vite/src/buildFeServer.ts b/packages/vite/src/buildFeServer.ts index 59d1ac449d59..4f279cf187f6 100644 --- a/packages/vite/src/buildFeServer.ts +++ b/packages/vite/src/buildFeServer.ts @@ -68,15 +68,20 @@ async function buildForServer({ verbose = false }: { verbose?: boolean }) { console.log('Starting server build.... \n') const rwPaths = getPaths() + if (!rwPaths.web.viteConfig) { + throw new Error('Vite config not found') + } + await viteBuild({ - configFile: rwPaths.web.viteConfig as string, + configFile: rwPaths.web.viteConfig, build: { outDir: rwPaths.web.distServer, ssr: true, // use boolean here, instead of string. // rollup inputs are defined in the vite plugin }, legacy: { - buildSsrCjsExternalHeuristics: true, // @MARK @TODO: this gets picked up by the RSC build if its in the index.js..... + // @MARK @TODO: this gets picked up by the RSC build if its in the index.js... + buildSsrCjsExternalHeuristics: true, }, envFile: false, logLevel: verbose ? 'info' : 'warn', diff --git a/packages/vite/src/rsc/rscBuildAnalyze.ts b/packages/vite/src/rsc/rscBuildAnalyze.ts index 304ef6f61456..63e9d648cbb0 100644 --- a/packages/vite/src/rsc/rscBuildAnalyze.ts +++ b/packages/vite/src/rsc/rscBuildAnalyze.ts @@ -26,8 +26,12 @@ export async function rscBuildAnalyze() { throw new Error('RSC entries file not found') } + if (!rwPaths.web.viteConfig) { + throw new Error('Vite config not found') + } + await viteBuild({ - configFile: rwPaths.web.viteConfig as string, + configFile: rwPaths.web.viteConfig, root: rwPaths.base, plugins: [ react(), diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index d7dfd6e94e69..d4635e5ab1fa 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -41,7 +41,6 @@ export async function rscBuildClient(clientEntryFiles: Record) { babel: { ...getWebSideDefaultBabelConfig({ forVite: true, - // @MARK 👇 This flag is important for RSC Client builds forRscClient: true, }), }, diff --git a/packages/vite/src/rsc/rscBuildForWorker.ts b/packages/vite/src/rsc/rscBuildForWorker.ts index bb2e8ab93355..523f93edd13f 100644 --- a/packages/vite/src/rsc/rscBuildForWorker.ts +++ b/packages/vite/src/rsc/rscBuildForWorker.ts @@ -25,8 +25,12 @@ export async function rscBuildForWorker( console.log('Starting RSC worker build...\n') const rwPaths = getPaths() + if (!rwPaths.web.entries) { + throw new Error('RSC entries file not found') + } + const input = { - entries: rwPaths.web.entries as string, + entries: rwPaths.web.entries, ...clientEntryFiles, ...serverEntryFiles, ...customModules, diff --git a/packages/vite/src/rsc/rscWorker.ts b/packages/vite/src/rsc/rscWorker.ts index 8405ed620d9e..ca43677ab50f 100644 --- a/packages/vite/src/rsc/rscWorker.ts +++ b/packages/vite/src/rsc/rscWorker.ts @@ -146,7 +146,7 @@ const shutdown = async () => { const loadServerFile = async (fname: string) => { const vite = await vitePromise - // @MARK: in prod we need to switch to import + // TODO(RSC): in prod we need to switch to import return vite.ssrLoadModule(fname) } diff --git a/packages/vite/src/rsc/rscWorkerCommunication.ts b/packages/vite/src/rsc/rscWorkerCommunication.ts index ac13688e891f..c9bbed3edeba 100644 --- a/packages/vite/src/rsc/rscWorkerCommunication.ts +++ b/packages/vite/src/rsc/rscWorkerCommunication.ts @@ -86,9 +86,7 @@ export function shutdown() { let nextId = 1 -/** - * Set the client entries to worker for the server build. - */ +/** Set the client entries to worker for the server build */ export function setClientEntries( value: 'load' | Record ): Promise { From cc4024e6f592e04137f5ea700e2d1497954f84ac Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Fri, 23 Feb 2024 19:17:27 +0700 Subject: [PATCH 20/75] Rename build functions --- packages/vite/src/buildFeServer.ts | 29 ++--------------- packages/vite/src/buildRscFeServer.ts | 4 +-- ...BuildForWorker.ts => buildForRscServer.ts} | 2 +- .../vite/src/rsc/rscBuildClientEntriesFile.ts | 4 +-- .../vite/src/rsc/rscBuildCopyCssAssets.ts | 4 +-- .../src/streaming/buildForStreamingServer.ts | 31 +++++++++++++++++++ 6 files changed, 40 insertions(+), 34 deletions(-) rename packages/vite/src/rsc/{rscBuildForWorker.ts => buildForRscServer.ts} (99%) create mode 100644 packages/vite/src/streaming/buildForStreamingServer.ts diff --git a/packages/vite/src/buildFeServer.ts b/packages/vite/src/buildFeServer.ts index 4f279cf187f6..6cda2aeed6fd 100644 --- a/packages/vite/src/buildFeServer.ts +++ b/packages/vite/src/buildFeServer.ts @@ -1,11 +1,10 @@ -import { build as viteBuild } from 'vite' - import { buildWeb } from '@redwoodjs/internal/dist/build/web' import { getConfig, getPaths } from '@redwoodjs/project-config' import { buildRouteHooks } from './buildRouteHooks' import { buildRouteManifest } from './buildRouteManifest' import { buildRscClientAndWorker } from './buildRscFeServer' +import { buildForStreamingServer } from './streaming/buildForStreamingServer' import { ensureProcessDirWeb } from './utils' export interface BuildOptions { @@ -56,34 +55,10 @@ export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { } // Generates the output used for the server (streaming/ssr but NOT rsc) - await buildForServer({ verbose }) + await buildForStreamingServer({ verbose }) await buildRouteHooks(verbose, rwPaths) // Write a route manifest await buildRouteManifest() } - -async function buildForServer({ verbose = false }: { verbose?: boolean }) { - console.log('Starting server build.... \n') - const rwPaths = getPaths() - - if (!rwPaths.web.viteConfig) { - throw new Error('Vite config not found') - } - - await viteBuild({ - configFile: rwPaths.web.viteConfig, - build: { - outDir: rwPaths.web.distServer, - ssr: true, // use boolean here, instead of string. - // rollup inputs are defined in the vite plugin - }, - legacy: { - // @MARK @TODO: this gets picked up by the RSC build if its in the index.js... - buildSsrCjsExternalHeuristics: true, - }, - envFile: false, - logLevel: verbose ? 'info' : 'warn', - }) -} diff --git a/packages/vite/src/buildRscFeServer.ts b/packages/vite/src/buildRscFeServer.ts index 041505f600b1..bda6c6524666 100644 --- a/packages/vite/src/buildRscFeServer.ts +++ b/packages/vite/src/buildRscFeServer.ts @@ -1,10 +1,10 @@ import { getPaths } from '@redwoodjs/project-config' +import { buildForRscServer } from './rsc/buildForRscServer' import { rscBuildAnalyze } from './rsc/rscBuildAnalyze' import { rscBuildClient } from './rsc/rscBuildClient' import { rscBuildClientEntriesMappings } from './rsc/rscBuildClientEntriesFile' import { rscBuildCopyCssAssets } from './rsc/rscBuildCopyCssAssets' -import { rscBuildForWorker } from './rsc/rscBuildForWorker' import { rscBuildRwEnvVars } from './rsc/rscBuildRwEnvVars' export const buildRscClientAndWorker = async () => { @@ -16,7 +16,7 @@ export const buildRscClientAndWorker = async () => { const clientBuildOutput = await rscBuildClient(clientEntryFiles) // Generate the server output - const serverBuildOutput = await rscBuildForWorker( + const serverBuildOutput = await buildForRscServer( clientEntryFiles, serverEntryFiles, {} diff --git a/packages/vite/src/rsc/rscBuildForWorker.ts b/packages/vite/src/rsc/buildForRscServer.ts similarity index 99% rename from packages/vite/src/rsc/rscBuildForWorker.ts rename to packages/vite/src/rsc/buildForRscServer.ts index 523f93edd13f..2c7b99496a7a 100644 --- a/packages/vite/src/rsc/rscBuildForWorker.ts +++ b/packages/vite/src/rsc/buildForRscServer.ts @@ -17,7 +17,7 @@ import { rscTransformPlugin } from './rscVitePlugins' * Generate the output to be used by the rsc worker (not the actual server!) */ // @TODO(RSC_DC): no redwood-vite plugin, add it back in here -export async function rscBuildForWorker( +export async function buildForRscServer( clientEntryFiles: Record, serverEntryFiles: Record, customModules: Record diff --git a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts index 47ae214b1b4e..cb3974ee6add 100644 --- a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts +++ b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts @@ -1,7 +1,7 @@ import fs from 'fs/promises' import type { rscBuildClient } from './rscBuildClient' -import type { rscBuildForWorker } from './rscBuildForWorker' +import type { buildForRscServer } from './buildForRscServer' /** * RSC build. Step 5. @@ -12,7 +12,7 @@ import type { rscBuildForWorker } from './rscBuildForWorker' // using the `writeBundle` hook or similar. export function rscBuildClientEntriesMappings( clientBuildOutput: Awaited>, - serverBuildOutput: Awaited>, + serverBuildOutput: Awaited>, clientEntryFiles: Record, webDistServerEntries: string ) { diff --git a/packages/vite/src/rsc/rscBuildCopyCssAssets.ts b/packages/vite/src/rsc/rscBuildCopyCssAssets.ts index 0307af068e45..8a2ad5020fc4 100644 --- a/packages/vite/src/rsc/rscBuildCopyCssAssets.ts +++ b/packages/vite/src/rsc/rscBuildCopyCssAssets.ts @@ -1,14 +1,14 @@ import fs from 'fs/promises' import path from 'path' -import type { rscBuildForWorker } from './rscBuildForWorker' +import type { buildForRscServer } from './buildForRscServer' /** * RSC build. Step 4. * Copy CSS assets from server to client */ export function rscBuildCopyCssAssets( - serverBuildOutput: Awaited>, + serverBuildOutput: Awaited>, webDist: string, webDistServer: string ) { diff --git a/packages/vite/src/streaming/buildForStreamingServer.ts b/packages/vite/src/streaming/buildForStreamingServer.ts new file mode 100644 index 000000000000..7cffcacd9c4d --- /dev/null +++ b/packages/vite/src/streaming/buildForStreamingServer.ts @@ -0,0 +1,31 @@ +import { build as viteBuild } from 'vite' + +import { getPaths } from '@redwoodjs/project-config' + +export async function buildForStreamingServer({ + verbose = false, +}: { + verbose?: boolean +}) { + console.log('Starting streaming server build.... \n') + const rwPaths = getPaths() + + if (!rwPaths.web.viteConfig) { + throw new Error('Vite config not found') + } + + await viteBuild({ + configFile: rwPaths.web.viteConfig, + build: { + outDir: rwPaths.web.distServer, + ssr: true, // use boolean here, instead of string. + // rollup inputs are defined in the vite plugin + }, + legacy: { + // @MARK @TODO: this gets picked up by the RSC build if its in the index.js... + buildSsrCjsExternalHeuristics: true, + }, + envFile: false, + logLevel: verbose ? 'info' : 'warn', + }) +} From 5a7c550ce3bf94191cf06b11156fb9633bd3ee4f Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Fri, 23 Feb 2024 19:35:31 +0700 Subject: [PATCH 21/75] Fix paths test with new paths --- .../src/__tests__/paths.test.ts | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/project-config/src/__tests__/paths.test.ts b/packages/project-config/src/__tests__/paths.test.ts index 99e34e484f84..963deb5f180d 100644 --- a/packages/project-config/src/__tests__/paths.test.ts +++ b/packages/project-config/src/__tests__/paths.test.ts @@ -141,6 +141,7 @@ describe('paths', () => { 'storybook.manager.js' ), dist: path.join(FIXTURE_BASEDIR, 'web', 'dist'), + distClient: path.join(FIXTURE_BASEDIR, 'web', 'dist', 'client'), distEntryServer: path.join( FIXTURE_BASEDIR, 'web', @@ -155,6 +156,7 @@ describe('paths', () => { 'server', 'routeHooks' ), + distRsc: path.join(FIXTURE_BASEDIR, 'web', 'dist', 'rsc'), distServer: path.join(FIXTURE_BASEDIR, 'web', 'dist', 'server'), distDocumentServer: path.join( FIXTURE_BASEDIR, @@ -163,11 +165,11 @@ describe('paths', () => { 'server', 'Document.js' ), - distServerEntries: path.join( + distRscEntries: path.join( FIXTURE_BASEDIR, 'web', 'dist', - 'server', + 'rsc', 'entries.js' ), types: path.join(FIXTURE_BASEDIR, 'web', 'types'), @@ -416,6 +418,7 @@ describe('paths', () => { 'storybook.manager.js' ), dist: path.join(FIXTURE_BASEDIR, 'web', 'dist'), + distClient: path.join(FIXTURE_BASEDIR, 'web', 'dist', 'client'), distEntryServer: path.join( FIXTURE_BASEDIR, 'web', @@ -438,11 +441,12 @@ describe('paths', () => { 'routeHooks' ), distServer: path.join(FIXTURE_BASEDIR, 'web', 'dist', 'server'), - distServerEntries: path.join( + distRsc: path.join(FIXTURE_BASEDIR, 'web', 'dist', 'rsc'), + distRscEntries: path.join( FIXTURE_BASEDIR, 'web', 'dist', - 'server', + 'rsc', 'entries.js' ), types: path.join(FIXTURE_BASEDIR, 'web', 'types'), @@ -740,6 +744,7 @@ describe('paths', () => { entryServer: null, entries: null, dist: path.join(FIXTURE_BASEDIR, 'web', 'dist'), + distClient: path.join(FIXTURE_BASEDIR, 'web', 'dist', 'client'), distEntryServer: path.join( FIXTURE_BASEDIR, 'web', @@ -762,11 +767,12 @@ describe('paths', () => { 'routeHooks' ), distServer: path.join(FIXTURE_BASEDIR, 'web', 'dist', 'server'), - distServerEntries: path.join( + distRsc: path.join(FIXTURE_BASEDIR, 'web', 'dist', 'rsc'), + distRscEntries: path.join( FIXTURE_BASEDIR, 'web', 'dist', - 'server', + 'rsc', 'entries.js' ), types: path.join(FIXTURE_BASEDIR, 'web', 'types'), @@ -1015,6 +1021,7 @@ describe('paths', () => { 'storybook.manager.js' ), dist: path.join(FIXTURE_BASEDIR, 'web', 'dist'), + distClient: path.join(FIXTURE_BASEDIR, 'web', 'dist', 'client'), distEntryServer: path.join( FIXTURE_BASEDIR, 'web', @@ -1037,11 +1044,12 @@ describe('paths', () => { 'routeHooks' ), distServer: path.join(FIXTURE_BASEDIR, 'web', 'dist', 'server'), - distServerEntries: path.join( + distRsc: path.join(FIXTURE_BASEDIR, 'web', 'dist', 'rsc'), + distRscEntries: path.join( FIXTURE_BASEDIR, 'web', 'dist', - 'server', + 'rsc', 'entries.js' ), types: path.join(FIXTURE_BASEDIR, 'web', 'types'), From 9be7278a48b7225cd7375879cd8c004aaea34371 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Fri, 23 Feb 2024 19:35:41 +0700 Subject: [PATCH 22/75] Another rename of build functions --- packages/vite/src/buildFeServer.ts | 4 ++-- .../src/{buildRscFeServer.ts => buildRscClientAndServer.ts} | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename packages/vite/src/{buildRscFeServer.ts => buildRscClientAndServer.ts} (96%) diff --git a/packages/vite/src/buildFeServer.ts b/packages/vite/src/buildFeServer.ts index 6cda2aeed6fd..bb656131c98e 100644 --- a/packages/vite/src/buildFeServer.ts +++ b/packages/vite/src/buildFeServer.ts @@ -3,7 +3,7 @@ import { getConfig, getPaths } from '@redwoodjs/project-config' import { buildRouteHooks } from './buildRouteHooks' import { buildRouteManifest } from './buildRouteManifest' -import { buildRscClientAndWorker } from './buildRscFeServer' +import { buildRscClientAndServer } from './buildRscClientAndServer' import { buildForStreamingServer } from './streaming/buildForStreamingServer' import { ensureProcessDirWeb } from './utils' @@ -44,7 +44,7 @@ export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { throw new Error('RSC entries file not found') } - await buildRscClientAndWorker() + await buildRscClientAndServer() } // We generate the RSC client bundle in the buildRscFeServer function diff --git a/packages/vite/src/buildRscFeServer.ts b/packages/vite/src/buildRscClientAndServer.ts similarity index 96% rename from packages/vite/src/buildRscFeServer.ts rename to packages/vite/src/buildRscClientAndServer.ts index bda6c6524666..b0936a575cb2 100644 --- a/packages/vite/src/buildRscFeServer.ts +++ b/packages/vite/src/buildRscClientAndServer.ts @@ -7,7 +7,7 @@ import { rscBuildClientEntriesMappings } from './rsc/rscBuildClientEntriesFile' import { rscBuildCopyCssAssets } from './rsc/rscBuildCopyCssAssets' import { rscBuildRwEnvVars } from './rsc/rscBuildRwEnvVars' -export const buildRscClientAndWorker = async () => { +export const buildRscClientAndServer = async () => { const rwPaths = getPaths() // Analyze all files and generate a list of RSCs and RSFs const { clientEntryFiles, serverEntryFiles } = await rscBuildAnalyze() From 274955de57243e6054c2739c86af383eb0f266de Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Fri, 23 Feb 2024 19:44:20 +0700 Subject: [PATCH 23/75] Lint --- packages/vite/src/rsc/rscBuildClientEntriesFile.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts index cb3974ee6add..56f58e15fe0b 100644 --- a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts +++ b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts @@ -1,7 +1,7 @@ import fs from 'fs/promises' -import type { rscBuildClient } from './rscBuildClient' import type { buildForRscServer } from './buildForRscServer' +import type { rscBuildClient } from './rscBuildClient' /** * RSC build. Step 5. From 8cb6645054f393aab03e34449e48dbd1be4afdc0 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Fri, 23 Feb 2024 20:40:49 +0700 Subject: [PATCH 24/75] Remove duplicated file --- packages/vite/src/lib/getViteDefines.ts | 55 ------------------------- 1 file changed, 55 deletions(-) delete mode 100644 packages/vite/src/lib/getViteDefines.ts diff --git a/packages/vite/src/lib/getViteDefines.ts b/packages/vite/src/lib/getViteDefines.ts deleted file mode 100644 index d04b9832c3c2..000000000000 --- a/packages/vite/src/lib/getViteDefines.ts +++ /dev/null @@ -1,55 +0,0 @@ -import path from 'node:path' - -import { getConfig, getPaths } from '@redwoodjs/project-config' - -export function getViteDefines(): Record | undefined { - const rwConfig = getConfig() - const rwPaths = getPaths() - - const graphQlUrl = - rwConfig.web.apiGraphQLUrl ?? rwConfig.web.apiUrl + '/graphql' - - return { - RWJS_ENV: { - __REDWOOD__APP_TITLE: rwConfig.web.title || path.basename(rwPaths.base), - RWJS_API_GRAPHQL_URL: graphQlUrl, - RWJS_API_URL: rwConfig.web.apiUrl, - RWJS_EXP_STREAMING_SSR: rwConfig.experimental?.streamingSsr?.enabled, - RWJS_EXP_RSC: rwConfig.experimental?.rsc?.enabled, - }, - RWJS_DEBUG_ENV: { - RWJS_SRC_ROOT: rwPaths.web.src, - REDWOOD_ENV_EDITOR: JSON.stringify(process.env.REDWOOD_ENV_EDITOR), - }, - // Vite can automatically expose environment variables, but we - // disable that in `buildFeServer.ts` by setting `envFile: false` - // because we want to use our own logic for loading .env, - // .env.defaults, etc - // The two object spreads below will expose all environment - // variables listed in redwood.toml and all environment variables - // prefixed with REDWOOD_ENV_ - ...Object.fromEntries( - rwConfig.web.includeEnvironmentVariables.flatMap((envName) => [ - // TODO (RSC): Figure out if/why we need to disable eslint here. - // Re-enable if possible - // eslint-disable-next-line - [`import.meta.env.${envName}`, JSON.stringify(process.env[envName])], - // TODO (RSC): Figure out if/why we need to disable eslint here - // Re-enable if possible - // eslint-disable-next-line - [`process.env.${envName}`, JSON.stringify(process.env[envName])], - ]) - ), - ...Object.entries(process.env).reduce>( - (acc, [key, value]) => { - if (key.startsWith('REDWOOD_ENV_')) { - acc[`import.meta.env.${key}`] = JSON.stringify(value) - acc[`process.env.${key}`] = JSON.stringify(value) - } - - return acc - }, - {} - ), - } -} From 81ddb6e87162a372f52ec0ac6773deeed9cf83af Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Fri, 23 Feb 2024 20:42:58 +0700 Subject: [PATCH 25/75] Update some more comments --- packages/vite/src/rsc/buildForRscServer.ts | 2 +- packages/vite/src/rsc/rscBuildClientEntriesFile.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/vite/src/rsc/buildForRscServer.ts b/packages/vite/src/rsc/buildForRscServer.ts index 2c7b99496a7a..d9b7737a752d 100644 --- a/packages/vite/src/rsc/buildForRscServer.ts +++ b/packages/vite/src/rsc/buildForRscServer.ts @@ -13,7 +13,7 @@ import { rscTransformPlugin } from './rscVitePlugins' /** * RSC build. Step 3. - * buildFeServer -> buildRscFeServer -> rscBuildForWorker + * buildFeServer -> buildRscFeServer -> buildForRscServer * Generate the output to be used by the rsc worker (not the actual server!) */ // @TODO(RSC_DC): no redwood-vite plugin, add it back in here diff --git a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts index 56f58e15fe0b..0ab3e8e6d88d 100644 --- a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts +++ b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts @@ -8,8 +8,9 @@ import type { rscBuildClient } from './rscBuildClient' * Append a mapping of server asset names to client asset names to the * `web/dist/rsc/entries.js` file. Only used by the RSC worker. */ -// TODO(RSC_DC) : We could probably do this in rscBuildForWorker -// using the `writeBundle` hook or similar. +// TODO(RSC_DC): This function should eventually be removed. +// The dev server will need this implemented as a Vite plugin, +// so worth waiting till implementation to swap out and just include the plugin for the prod build export function rscBuildClientEntriesMappings( clientBuildOutput: Awaited>, serverBuildOutput: Awaited>, From ffca10337d9cd8cc9ebf1084861fe753490ce8b4 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 23 Feb 2024 22:45:13 +0530 Subject: [PATCH 26/75] Code comment tweaks/fixes --- packages/vite/src/buildFeServer.ts | 2 -- packages/vite/src/rsc/rscBuildClientEntriesFile.ts | 4 ++-- packages/vite/src/streaming/buildForStreamingServer.ts | 6 +++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/vite/src/buildFeServer.ts b/packages/vite/src/buildFeServer.ts index bb656131c98e..236e109ec317 100644 --- a/packages/vite/src/buildFeServer.ts +++ b/packages/vite/src/buildFeServer.ts @@ -12,8 +12,6 @@ export interface BuildOptions { webDir?: string } -// const SKIP = true - export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { ensureProcessDirWeb(webDir) diff --git a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts index 0ab3e8e6d88d..70f0f34e1b26 100644 --- a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts +++ b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts @@ -35,10 +35,10 @@ export function rscBuildClientEntriesMappings( if (entryFile) { console.log('entryFile', entryFile) if (process.platform === 'win32') { - const entryFileSlash = entryFile.replaceAll('\\', '/') - console.log('entryFileSlash', entryFileSlash) // Prevent errors on Windows like // Error: No client entry found for D:/a/redwood/rsc-project/web/dist/server/assets/rsc0.js + const entryFileSlash = entryFile.replaceAll('\\', '/') + console.log('entryFileSlash', entryFileSlash) clientEntries[entryFileSlash] = fileName } else { clientEntries[entryFile] = fileName diff --git a/packages/vite/src/streaming/buildForStreamingServer.ts b/packages/vite/src/streaming/buildForStreamingServer.ts index 7cffcacd9c4d..86f0b720894f 100644 --- a/packages/vite/src/streaming/buildForStreamingServer.ts +++ b/packages/vite/src/streaming/buildForStreamingServer.ts @@ -7,7 +7,7 @@ export async function buildForStreamingServer({ }: { verbose?: boolean }) { - console.log('Starting streaming server build.... \n') + console.log('Starting streaming server build...\n') const rwPaths = getPaths() if (!rwPaths.web.viteConfig) { @@ -18,11 +18,11 @@ export async function buildForStreamingServer({ configFile: rwPaths.web.viteConfig, build: { outDir: rwPaths.web.distServer, - ssr: true, // use boolean here, instead of string. + ssr: true, // rollup inputs are defined in the vite plugin }, legacy: { - // @MARK @TODO: this gets picked up by the RSC build if its in the index.js... + // @MARK @TODO: this gets picked up by the RSC build if it's in index.js buildSsrCjsExternalHeuristics: true, }, envFile: false, From d6247762e93fb7bb5e86a59a0913ce4cb660b949 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 23 Feb 2024 22:54:01 +0530 Subject: [PATCH 27/75] Remove `serve` from test fixture --- .../web/src/Routes.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/__fixtures__/test-project-rsc-external-packages-and-cells/web/src/Routes.tsx b/__fixtures__/test-project-rsc-external-packages-and-cells/web/src/Routes.tsx index 9dfe79a44e72..2f71f3ec9501 100644 --- a/__fixtures__/test-project-rsc-external-packages-and-cells/web/src/Routes.tsx +++ b/__fixtures__/test-project-rsc-external-packages-and-cells/web/src/Routes.tsx @@ -8,21 +8,11 @@ // 'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage import { Router, Route, Set } from '@redwoodjs/router' -import { serve } from '@redwoodjs/vite/client' import NavigationLayout from './layouts/NavigationLayout/NavigationLayout' import ScaffoldLayout from './layouts/ScaffoldLayout/ScaffoldLayout' import NotFoundPage from './pages/NotFoundPage/NotFoundPage' -const AboutPage = serve('AboutPage') -const HomePage = serve('HomePage') -const UserExampleUserExamplesPage = serve('UserExampleUserExamplesPage') -const UserExampleUserExamplePage = serve('UserExampleUserExamplePage') -const UserExampleNewUserExamplePage = serve('UserExampleNewUserExamplePage') -const EmptyUserEmptyUsersPage = serve('EmptyUserEmptyUsersPage') -const EmptyUserNewEmptyUserPage = serve('EmptyUserNewEmptyUserPage') -const MultiCellPage = serve('MultiCellPage') - const Routes = () => { return ( From 246e045945c0d54d4997987abed40faded5475c2 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 23 Feb 2024 23:06:41 +0530 Subject: [PATCH 28/75] node-loader should look in distRsc now, not distServer --- packages/vite/src/react-server-dom-webpack/node-loader.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/react-server-dom-webpack/node-loader.ts b/packages/vite/src/react-server-dom-webpack/node-loader.ts index 11cc33f8cc83..3033702cad96 100644 --- a/packages/vite/src/react-server-dom-webpack/node-loader.ts +++ b/packages/vite/src/react-server-dom-webpack/node-loader.ts @@ -394,7 +394,7 @@ async function transformClientModule( // throw an error if it's undefined const loadId = entryRecord - ? path.join(getPaths().web.distServer, 'assets', entryRecord[0] + '.js') + ? path.join(getPaths().web.distRsc, 'assets', entryRecord[0] + '.js') : url let newSrc = From c4b42ab59566431d20be7b79425aed6a358b0513 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 23 Feb 2024 23:09:29 +0530 Subject: [PATCH 29/75] doc comment formatting --- packages/vite/src/entries.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/entries.ts b/packages/vite/src/entries.ts index a33dc0539764..b7c21e1dc9df 100644 --- a/packages/vite/src/entries.ts +++ b/packages/vite/src/entries.ts @@ -18,8 +18,8 @@ export type GetBuilder = ( }> /** - * Used to look up the component to import when calling `renderFromRscServer('MyPage')` in - * Routes.tsx + * Used to look up the component to import when calling + * `renderFromRscServer('MyPage')` in Routes.tsx */ export function defineEntries(getEntry: GetEntry, getBuilder?: GetBuilder) { return { getEntry, getBuilder } From e81d557b4cafc30a55b46ca86f98be1f508c9688 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 24 Feb 2024 07:59:21 +0530 Subject: [PATCH 30/75] innerText --- tasks/smoke-tests/rsa/tests/rsa.spec.ts | 2 +- .../tests/rsc-external-packages-and-cells.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tasks/smoke-tests/rsa/tests/rsa.spec.ts b/tasks/smoke-tests/rsa/tests/rsa.spec.ts index 006a38043859..2c21529c0f14 100644 --- a/tasks/smoke-tests/rsa/tests/rsa.spec.ts +++ b/tasks/smoke-tests/rsa/tests/rsa.spec.ts @@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test' test('Submitting the form should return a response', async ({ page }) => { await page.goto('/') - const h3 = await page.locator('h1').innerHTML() + const h3 = await page.locator('h1').innerText() expect(h3).toMatch(/Hello Anonymous!!/) const pageText = await page.locator('#redwood-app > div').innerText() diff --git a/tasks/smoke-tests/rsc-external-packages-and-cells/tests/rsc-external-packages-and-cells.spec.ts b/tasks/smoke-tests/rsc-external-packages-and-cells/tests/rsc-external-packages-and-cells.spec.ts index ffa82ab4cecb..868d88b32531 100644 --- a/tasks/smoke-tests/rsc-external-packages-and-cells/tests/rsc-external-packages-and-cells.spec.ts +++ b/tasks/smoke-tests/rsc-external-packages-and-cells/tests/rsc-external-packages-and-cells.spec.ts @@ -18,7 +18,7 @@ test('Client components should work', async ({ page }) => { test('Submitting the form should return a response', async ({ page }) => { await page.goto('/') - const h1 = await page.locator('h1').innerHTML() + const h1 = await page.locator('h1').innerText() expect(h1).toMatch(/Hello Anonymous!!/) const pageText = await page.locator('#redwood-app > div').innerText() From 924d771c6a3e731e1d28e6b7b4f90fd936ff66d4 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 24 Feb 2024 08:47:39 +0530 Subject: [PATCH 31/75] no more 'as string', and startsWith instead of includes --- packages/vite/src/rsc/rscBuildClient.ts | 6 +++++- packages/vite/src/runFeServer.ts | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index d4635e5ab1fa..3a6540362177 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -27,6 +27,10 @@ export async function rscBuildClient(clientEntryFiles: Record) { ) } + if (!rwPaths.web.entryClient) { + throw new Error('Could not find entry.client file') + } + const clientBuildOutput = await viteBuild({ // @MARK This runs on TOP of the settings in rw-vite-plugin, because we don't set configFile: false // but if you actually set the config file, it runs the transforms twice @@ -56,7 +60,7 @@ export async function rscBuildClient(clientEntryFiles: Record) { input: { // @MARK: temporary hack to find the entry client so we can get the index.css bundle // but we don't actually want this on an rsc page! - 'rwjs-client-entry': rwPaths.web.entryClient as string, + 'rwjs-client-entry': rwPaths.web.entryClient, // we need this, so that files with "use client" aren't bundled. I **think** RSC wants an unbundled build ...clientEntryFiles, }, diff --git a/packages/vite/src/runFeServer.ts b/packages/vite/src/runFeServer.ts index 429d35c1e221..272f8b9c305e 100644 --- a/packages/vite/src/runFeServer.ts +++ b/packages/vite/src/runFeServer.ts @@ -82,7 +82,7 @@ export async function runFeServer() { const indexEntry = Object.values(clientBuildManifest).find((manifestItem) => { // For RSC builds, we pass in many Vite entries, so we need to find it differently. return rscBuild - ? manifestItem.file.includes('rwjs-client-entry-') + ? manifestItem.file.startsWith('rwjs-client-entry-') : manifestItem.isEntry }) From 229fe35e77c7c2a0a1cdd552a080aab03608f975 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 24 Feb 2024 08:51:35 +0530 Subject: [PATCH 32/75] Remove outdated todo comment. And other comment formatting --- packages/vite/src/rsc/rscBuildClient.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index 3a6540362177..3dcb9d0dedee 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -32,15 +32,17 @@ export async function rscBuildClient(clientEntryFiles: Record) { } const clientBuildOutput = await viteBuild({ - // @MARK This runs on TOP of the settings in rw-vite-plugin, because we don't set configFile: false - // but if you actually set the config file, it runs the transforms twice + // @MARK This runs on TOP of the settings in rw-vite-plugin, because we + // don't set configFile: false. But if you actually set the config file, + // it runs the transforms twice root: rwPaths.web.src, envPrefix: 'REDWOOD_ENV_', publicDir: path.join(rwPaths.web.base, 'public'), envFile: false, define: getEnvVarDefinitions(), plugins: [ - // @MARK We need to duplicate the plugins here.... otherwise builds fail I don't understand why + // @MARK We need to duplicate the plugins here... otherwise builds fail + // and I don't understand why react({ babel: { ...getWebSideDefaultBabelConfig({ @@ -53,15 +55,14 @@ export async function rscBuildClient(clientEntryFiles: Record) { build: { outDir: rwPaths.web.distClient, emptyOutDir: true, // Needed because `outDir` is not inside `root` - // TODO (RSC) Enable this when we switch to a server-first approach - // emptyOutDir: false, // Already done when building server rollupOptions: { onwarn: onWarn, input: { - // @MARK: temporary hack to find the entry client so we can get the index.css bundle - // but we don't actually want this on an rsc page! + // @MARK: temporary hack to find the entry client so we can get the + // index.css bundle but we don't actually want this on an rsc page! 'rwjs-client-entry': rwPaths.web.entryClient, - // we need this, so that files with "use client" aren't bundled. I **think** RSC wants an unbundled build + // we need this, so that files with "use client" aren't bundled. I + // **think** RSC wants an unbundled build ...clientEntryFiles, }, preserveEntrySignatures: 'exports-only', From c4f59bc1d45148896da31dff1ee022fb1f728a25 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 24 Feb 2024 09:11:21 +0530 Subject: [PATCH 33/75] Revert startsWith change --- packages/vite/src/runFeServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/runFeServer.ts b/packages/vite/src/runFeServer.ts index 272f8b9c305e..429d35c1e221 100644 --- a/packages/vite/src/runFeServer.ts +++ b/packages/vite/src/runFeServer.ts @@ -82,7 +82,7 @@ export async function runFeServer() { const indexEntry = Object.values(clientBuildManifest).find((manifestItem) => { // For RSC builds, we pass in many Vite entries, so we need to find it differently. return rscBuild - ? manifestItem.file.startsWith('rwjs-client-entry-') + ? manifestItem.file.includes('rwjs-client-entry-') : manifestItem.isEntry }) From 4e29a131caaa611e7ec6dc675589b6859cfc4dac Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 25 Feb 2024 16:52:32 +0530 Subject: [PATCH 34/75] @tobbe.dev/rsc-test@0.0.4 --- .../web/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__fixtures__/test-project-rsc-external-packages-and-cells/web/package.json b/__fixtures__/test-project-rsc-external-packages-and-cells/web/package.json index 47ea736552d6..acae4f86a7c1 100644 --- a/__fixtures__/test-project-rsc-external-packages-and-cells/web/package.json +++ b/__fixtures__/test-project-rsc-external-packages-and-cells/web/package.json @@ -15,7 +15,7 @@ "@redwoodjs/forms": "7.0.0-canary.1011", "@redwoodjs/router": "7.0.0-canary.1011", "@redwoodjs/web": "7.0.0-canary.1011", - "@tobbe.dev/rsc-test": "0.0.3", + "@tobbe.dev/rsc-test": "0.0.4", "client-only": "0.0.1", "react": "0.0.0-experimental-e5205658f-20230913", "react-dom": "0.0.0-experimental-e5205658f-20230913", From 881edb686ca93a450e9233fb2ac96a6598387dbc Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 25 Feb 2024 17:13:01 +0530 Subject: [PATCH 35/75] @tobbe.dev/rsc-test@0.0.5 --- .../web/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__fixtures__/test-project-rsc-external-packages-and-cells/web/package.json b/__fixtures__/test-project-rsc-external-packages-and-cells/web/package.json index acae4f86a7c1..9a1bb651ddbd 100644 --- a/__fixtures__/test-project-rsc-external-packages-and-cells/web/package.json +++ b/__fixtures__/test-project-rsc-external-packages-and-cells/web/package.json @@ -15,7 +15,7 @@ "@redwoodjs/forms": "7.0.0-canary.1011", "@redwoodjs/router": "7.0.0-canary.1011", "@redwoodjs/web": "7.0.0-canary.1011", - "@tobbe.dev/rsc-test": "0.0.4", + "@tobbe.dev/rsc-test": "0.0.5", "client-only": "0.0.1", "react": "0.0.0-experimental-e5205658f-20230913", "react-dom": "0.0.0-experimental-e5205658f-20230913", From 142f0b74964c733f18fa225ce0ae109ccac20e41 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Tue, 27 Feb 2024 14:09:05 +0700 Subject: [PATCH 36/75] Update setupRscExternal readme --- .../set-up-rsc-external-packages-and-cells-project/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/set-up-rsc-external-packages-and-cells-project/README.md b/.github/actions/set-up-rsc-external-packages-and-cells-project/README.md index a2e4c2677e07..f260f2a8997e 100644 --- a/.github/actions/set-up-rsc-external-packages-and-cells-project/README.md +++ b/.github/actions/set-up-rsc-external-packages-and-cells-project/README.md @@ -12,7 +12,7 @@ Go into the github actions folder `cd .github/actions` Then run the following command to execute the action -`node set-up-rsc-external-packages-project/setUpRscExternalPackagesProjectLocally.mjs` +`node ./set-up-rsc-external-packages-and-cells-project/setUpRscExternalPackagesProjectLocally.mjs` ## Design From ec6497dd5774a77228a9dc3f83fbc6c12a1b1153 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 28 Feb 2024 10:23:09 +0530 Subject: [PATCH 37/75] emptyOutDir --- packages/vite/src/rsc/buildForRscServer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vite/src/rsc/buildForRscServer.ts b/packages/vite/src/rsc/buildForRscServer.ts index d9b7737a752d..9f9ed94fd0d6 100644 --- a/packages/vite/src/rsc/buildForRscServer.ts +++ b/packages/vite/src/rsc/buildForRscServer.ts @@ -105,6 +105,7 @@ export async function buildForRscServer( ssr: true, ssrEmitAssets: true, outDir: rwPaths.web.distRsc, + emptyOutDir: true, // Needed because `outDir` is not inside `root` manifest: 'server-build-manifest.json', rollupOptions: { onwarn: onWarn, From 25b7edec7d8b6560ed8db18017fd0c0f4511a75a Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Wed, 28 Feb 2024 17:00:17 +0700 Subject: [PATCH 38/75] Consolidate vite settings for rsc build (#262) Enables CSS and Tailwind support with RSC Fixes glaring bug in Vite plugin where we would override passed in settings --- packages/cli/src/index.js | 1 + packages/vite/src/index.ts | 39 +++++++++------- packages/vite/src/rsc/buildForRscServer.ts | 14 ------ packages/vite/src/rsc/rscBuildAnalyze.ts | 4 +- packages/vite/src/rsc/rscBuildClient.ts | 46 +++++-------------- .../vite/src/rsc/rscBuildClientEntriesFile.ts | 1 + 6 files changed, 38 insertions(+), 67 deletions(-) diff --git a/packages/cli/src/index.js b/packages/cli/src/index.js index 9490c763956e..8dafe2a85b31 100644 --- a/packages/cli/src/index.js +++ b/packages/cli/src/index.js @@ -139,6 +139,7 @@ async function main() { recordTelemetryAttributes({ command: '--help' }) } + // @TODO: BIG RED BOX > FIX MEEEEEEE try { // Run the command via yargs await runYargs() diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 732c1f527fd0..d61c3494446f 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -3,8 +3,8 @@ import path from 'path' import react from '@vitejs/plugin-react' import type { InputOption } from 'rollup' -import type { ConfigEnv, UserConfig, PluginOption } from 'vite' -import { normalizePath } from 'vite' +import type { ConfigEnv, PluginOption, UserConfig } from 'vite' +import { mergeConfig, normalizePath } from 'vite' import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getConfig, getPaths } from '@redwoodjs/project-config' @@ -126,7 +126,9 @@ export default function redwoodPluginVite(): PluginOption[] { }, // ---------- End Bundle injection ---------- - config: (options: UserConfig, env: ConfigEnv): UserConfig => { + // @MARK: Using the config hook here let's us modify the config + // but returning plugins will **not** work + config: (userConfig: UserConfig, env: ConfigEnv): UserConfig => { let apiHost = process.env.REDWOOD_API_HOST apiHost ??= rwConfig.api.host apiHost ??= process.env.NODE_ENV === 'production' ? '0.0.0.0' : '[::]' @@ -142,17 +144,16 @@ export default function redwoodPluginVite(): PluginOption[] { apiPort = rwConfig.api.port } - return { + const defaultRwViteConfig: UserConfig = { root: rwPaths.web.src, - // Disabling for now, let babel handle this for consistency - // resolve: { - // alias: [ - // { - // find: 'src', - // replacement: redwoodPaths.web.src, - // }, - // ], - // }, + resolve: { + alias: [ + { + find: 'src', + replacement: rwPaths.web.src, + }, + ], + }, envPrefix: 'REDWOOD_ENV_', publicDir: path.join(rwPaths.web.base, 'public'), define: getEnvVarDefinitions(), @@ -210,12 +211,12 @@ export default function redwoodPluginVite(): PluginOption[] { }, }, build: { + // NOTE this gets overridden when build gets called anyway! outDir: - options.build?.outDir || // @MARK: For RSC and Streaming, we build to dist/client directory - (streamingBuild || rscBuild + streamingBuild || rscBuild ? rwPaths.web.distClient - : rwPaths.web.dist), + : rwPaths.web.dist, emptyOutDir: true, manifest: !env.ssrBuild ? 'client-build-manifest.json' : undefined, sourcemap: !env.ssrBuild && rwConfig.web.sourceMap, // Note that this can be boolean or 'inline' @@ -239,6 +240,7 @@ export default function redwoodPluginVite(): PluginOption[] { }, }, } + return mergeConfig(defaultRwViteConfig, userConfig) }, }, // We can remove when streaming is stable @@ -260,6 +262,11 @@ export default function redwoodPluginVite(): PluginOption[] { babel: { ...getWebSideDefaultBabelConfig({ forVite: true, + // @MARK: Potential issue in the future. We don't want to set react plugins in each build file + // because we should be able to trigger the builds from the vite CLI directly. + // Right now, we can compile to SPA+SSR OR RSCClient+SSR+RSC - once this experimental flag is removed + // we will need to make sure SPA+SSR is still possible + forRscClient: rwConfig.experimental.rsc?.enabled, }), }, }), diff --git a/packages/vite/src/rsc/buildForRscServer.ts b/packages/vite/src/rsc/buildForRscServer.ts index 9f9ed94fd0d6..3112e6f4a60c 100644 --- a/packages/vite/src/rsc/buildForRscServer.ts +++ b/packages/vite/src/rsc/buildForRscServer.ts @@ -1,12 +1,9 @@ import path from 'node:path' -import react from '@vitejs/plugin-react' import { build as viteBuild } from 'vite' -import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getPaths } from '@redwoodjs/project-config' -import { getEnvVarDefinitions } from '../envVarDefinitions' import { onWarn } from '../lib/onWarn' import { rscTransformPlugin } from './rscVitePlugins' @@ -37,15 +34,11 @@ export async function buildForRscServer( } const workerBuildOutput = await viteBuild({ - configFile: false, // @MARK disable loading the original plugin, only use settings in this file. This prevents issues with the routes-auto-loader - root: rwPaths.web.src, // @MARK this used to be `rwPaths.web.base`, not sure if intentional or not!!! envFile: false, legacy: { // @MARK: for the worker, we're building ESM! (not CJS) buildSsrCjsExternalHeuristics: false, }, - // TODO (RSC) (Tobbe): Can this be removed? - define: getEnvVarDefinitions(), ssr: { // Externalize everything except packages with files that have // 'use client' in them (which are the files in `clientEntryFiles`) @@ -86,13 +79,6 @@ export async function buildForRscServer( }, }, plugins: [ - react({ - babel: { - ...getWebSideDefaultBabelConfig({ - forVite: true, - }), - }, - }), // The rscTransformPlugin maps paths like // /Users/tobbe/.../rw-app/node_modules/@tobbe.dev/rsc-test/dist/rsc-test.es.js // to diff --git a/packages/vite/src/rsc/rscBuildAnalyze.ts b/packages/vite/src/rsc/rscBuildAnalyze.ts index 8c1630063066..36723054659a 100644 --- a/packages/vite/src/rsc/rscBuildAnalyze.ts +++ b/packages/vite/src/rsc/rscBuildAnalyze.ts @@ -1,4 +1,3 @@ -import react from '@vitejs/plugin-react' import { build as viteBuild } from 'vite' import { getPaths } from '@redwoodjs/project-config' @@ -22,6 +21,8 @@ export async function rscBuildAnalyze() { const clientEntryFileSet = new Set() const serverEntryFileSet = new Set() + console.log('Starting rscBuildAnalyze........\n') + if (!rwPaths.web.entries) { throw new Error('RSC entries file not found') } @@ -34,7 +35,6 @@ export async function rscBuildAnalyze() { configFile: rwPaths.web.viteConfig, root: rwPaths.base, plugins: [ - react(), rscAnalyzePlugin( (id) => clientEntryFileSet.add(id), (id) => serverEntryFileSet.add(id) diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index 3dcb9d0dedee..3da2b84beb25 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -1,57 +1,27 @@ -import path from 'node:path' - -import react from '@vitejs/plugin-react' import { build as viteBuild } from 'vite' -import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getPaths } from '@redwoodjs/project-config' -import { getEnvVarDefinitions } from '../envVarDefinitions' import { onWarn } from '../lib/onWarn' +import { ensureProcessDirWeb } from '../utils' /** * RSC build. Step 2. * buildFeServer -> buildRscFeServer -> rscBuildClient * Generate the client bundle */ -// @MARK: I can't seem to remove the duplicated defines here - while it builds -// the output doesn't run anymore (RWJS_ENV undefined etc.) -// why? It's definitely using the vite plugin, but the defines don't come through? export async function rscBuildClient(clientEntryFiles: Record) { console.log('Starting RSC client build.... \n') const rwPaths = getPaths() - if (process.cwd() !== rwPaths.web.base) { - throw new Error( - 'Looks like you are running the command from the wrong dir, this can lead to unintended consequences on CSS processing' - ) - } + ensureProcessDirWeb() if (!rwPaths.web.entryClient) { - throw new Error('Could not find entry.client file') + throw new Error('Missing web/src/entry.client') } const clientBuildOutput = await viteBuild({ - // @MARK This runs on TOP of the settings in rw-vite-plugin, because we - // don't set configFile: false. But if you actually set the config file, - // it runs the transforms twice - root: rwPaths.web.src, - envPrefix: 'REDWOOD_ENV_', - publicDir: path.join(rwPaths.web.base, 'public'), envFile: false, - define: getEnvVarDefinitions(), - plugins: [ - // @MARK We need to duplicate the plugins here... otherwise builds fail - // and I don't understand why - react({ - babel: { - ...getWebSideDefaultBabelConfig({ - forVite: true, - forRscClient: true, - }), - }, - }), - ], build: { outDir: rwPaths.web.distClient, emptyOutDir: true, // Needed because `outDir` is not inside `root` @@ -61,8 +31,8 @@ export async function rscBuildClient(clientEntryFiles: Record) { // @MARK: temporary hack to find the entry client so we can get the // index.css bundle but we don't actually want this on an rsc page! 'rwjs-client-entry': rwPaths.web.entryClient, - // we need this, so that files with "use client" aren't bundled. I - // **think** RSC wants an unbundled build + // we need this, so that the output contains rsc-specific bundles + // for the client-only components. They get loaded, once the page is rendered ...clientEntryFiles, }, preserveEntrySignatures: 'exports-only', @@ -83,6 +53,12 @@ export async function rscBuildClient(clientEntryFiles: Record) { }, }) + if (process.cwd() !== rwPaths.web.base) { + throw new Error( + 'Looks like you are running the command from the wrong dir, this can lead to unintended consequences on CSS processing' + ) + } + if (!('output' in clientBuildOutput)) { throw new Error('Unexpected vite client build output') } diff --git a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts index 70f0f34e1b26..643d35e44dce 100644 --- a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts +++ b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts @@ -49,6 +49,7 @@ export function rscBuildClientEntriesMappings( console.log('clientEntries', clientEntries) return fs.appendFile( + // @TODO @MARK: should this be webDistRscEntries? webDistServerEntries, `export const clientEntries=${JSON.stringify(clientEntries)};` ) From 374ac5ec67e07189a7b122cebbbaccc550174aea Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Wed, 28 Feb 2024 17:09:34 +0700 Subject: [PATCH 39/75] Allow mjs entry server and document. As string is OK --- packages/project-config/src/paths.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/project-config/src/paths.ts b/packages/project-config/src/paths.ts index 6e69621887eb..20ad0f8cdcad 100644 --- a/packages/project-config/src/paths.ts +++ b/packages/project-config/src/paths.ts @@ -130,8 +130,9 @@ const PATH_WEB_DIR_DIST_SERVER = 'web/dist/server' const PATH_WEB_DIR_DIST_RSC = 'web/dist/rsc' const PATH_WEB_DIR_DIST_CLIENT = 'web/dist/client' -const PATH_WEB_DIR_DIST_SERVER_ENTRY_SERVER = 'web/dist/server/entry.server.js' -const PATH_WEB_DIR_DIST_DOCUMENT = 'web/dist/server/Document.js' +// Don't specify extension, handled by resolve file +const PATH_WEB_DIR_DIST_SERVER_ENTRY_SERVER = 'web/dist/server/entry.server' +const PATH_WEB_DIR_DIST_DOCUMENT = 'web/dist/server/Document' const PATH_WEB_DIR_DIST_SERVER_ROUTEHOOKS = 'web/dist/server/routeHooks' const PATH_WEB_DIR_DIST_RSC_ENTRIES = 'web/dist/rsc/entries.js' @@ -246,11 +247,15 @@ export const getPaths = (BASE_DIR: string = getBaseDir()): Paths => { distServer: path.join(BASE_DIR, PATH_WEB_DIR_DIST_SERVER), distClient: path.join(BASE_DIR, PATH_WEB_DIR_DIST_CLIENT), distRsc: path.join(BASE_DIR, PATH_WEB_DIR_DIST_RSC), - distEntryServer: path.join( - BASE_DIR, - PATH_WEB_DIR_DIST_SERVER_ENTRY_SERVER - ), - distDocumentServer: path.join(BASE_DIR, PATH_WEB_DIR_DIST_DOCUMENT), + // Allow for the possibility of a .mjs file + distEntryServer: resolveFile( + path.join(BASE_DIR, PATH_WEB_DIR_DIST_SERVER_ENTRY_SERVER), + ['.js', '.mjs'] + ) as string, + distDocumentServer: resolveFile( + path.join(BASE_DIR, PATH_WEB_DIR_DIST_DOCUMENT), + ['.js', '.mjs'] + ) as string, distRouteHooks: path.join(BASE_DIR, PATH_WEB_DIR_DIST_SERVER_ROUTEHOOKS), distRscEntries: path.join(BASE_DIR, PATH_WEB_DIR_DIST_RSC_ENTRIES), routeManifest: path.join(BASE_DIR, PATH_WEB_DIR_ROUTE_MANIFEST), From f88424edff3756a40f01376ff62dfdb388033a70 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Wed, 28 Feb 2024 17:10:59 +0700 Subject: [PATCH 40/75] Empty outdir for ssr build --- packages/vite/src/streaming/buildForStreamingServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/streaming/buildForStreamingServer.ts b/packages/vite/src/streaming/buildForStreamingServer.ts index 86f0b720894f..0154f1a5f3f9 100644 --- a/packages/vite/src/streaming/buildForStreamingServer.ts +++ b/packages/vite/src/streaming/buildForStreamingServer.ts @@ -19,7 +19,7 @@ export async function buildForStreamingServer({ build: { outDir: rwPaths.web.distServer, ssr: true, - // rollup inputs are defined in the vite plugin + emptyOutDir: true, }, legacy: { // @MARK @TODO: this gets picked up by the RSC build if it's in index.js From bb05d58f566b5f427b93b5946d3ba90c20705cc6 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Wed, 28 Feb 2024 18:19:45 +0700 Subject: [PATCH 41/75] Silence rscBuildAnalyze --- packages/vite/src/rsc/rscBuildAnalyze.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/vite/src/rsc/rscBuildAnalyze.ts b/packages/vite/src/rsc/rscBuildAnalyze.ts index 36723054659a..d51377e02036 100644 --- a/packages/vite/src/rsc/rscBuildAnalyze.ts +++ b/packages/vite/src/rsc/rscBuildAnalyze.ts @@ -34,6 +34,8 @@ export async function rscBuildAnalyze() { await viteBuild({ configFile: rwPaths.web.viteConfig, root: rwPaths.base, + // @MARK: We don't want to see any output from this step. It's just for returning the entry names! + logLevel: 'silent', plugins: [ rscAnalyzePlugin( (id) => clientEntryFileSet.add(id), From 92e13bf7e57c15fc6c8e0e8a791b995da1fc84fd Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 29 Feb 2024 19:00:14 +0700 Subject: [PATCH 42/75] Move react plugin into their individual files --- packages/vite/src/getMergedConfig.ts | 155 ++++++++++++++++ packages/vite/src/index.ts | 175 ++---------------- packages/vite/src/rsc/buildForRscServer.ts | 10 + packages/vite/src/rsc/rscBuildAnalyze.ts | 9 + packages/vite/src/rsc/rscBuildClient.ts | 12 ++ packages/vite/src/runFeServer.ts | 3 +- .../src/streaming/buildForStreamingServer.ts | 11 ++ 7 files changed, 211 insertions(+), 164 deletions(-) create mode 100644 packages/vite/src/getMergedConfig.ts diff --git a/packages/vite/src/getMergedConfig.ts b/packages/vite/src/getMergedConfig.ts new file mode 100644 index 000000000000..b95febef387d --- /dev/null +++ b/packages/vite/src/getMergedConfig.ts @@ -0,0 +1,155 @@ +import path from 'path' + +import type { InputOption } from 'rollup' +import type { ConfigEnv, UserConfig } from 'vite' +import { mergeConfig } from 'vite' + +import type { Config, Paths } from '@redwoodjs/project-config' +import { getConfig, getPaths } from '@redwoodjs/project-config' + +import { getEnvVarDefinitions } from './envVarDefinitions' + +export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { + return (userConfig: UserConfig, env: ConfigEnv): UserConfig => { + let apiHost = process.env.REDWOOD_API_HOST + apiHost ??= rwConfig.api.host + apiHost ??= process.env.NODE_ENV === 'production' ? '0.0.0.0' : '[::]' + + const streamingBuild = rwConfig.experimental.streamingSsr?.enabled + // @MARK: note that most RSC settings sit in their individual build functions + const rscBuild = rwConfig.experimental.rsc?.enabled + + let apiPort + if (process.env.REDWOOD_API_PORT) { + apiPort = parseInt(process.env.REDWOOD_API_PORT) + } else { + apiPort = rwConfig.api.port + } + + const defaultRwViteConfig: UserConfig = { + root: rwPaths.web.src, + resolve: { + alias: [ + { + find: 'src', + replacement: rwPaths.web.src, + }, + ], + }, + envPrefix: 'REDWOOD_ENV_', + publicDir: path.join(rwPaths.web.base, 'public'), + define: getEnvVarDefinitions(), + css: { + // @NOTE config path is relative to where vite.config.js is if you use relative path + // postcss: './config/', + postcss: rwPaths.web.config, + }, + server: { + open: rwConfig.browser.open, + port: rwConfig.web.port, + host: true, // Listen to all hosts + proxy: { + [rwConfig.web.apiUrl]: { + target: `http://${apiHost}:${apiPort}`, + changeOrigin: false, + // Remove the `.redwood/functions` part, but leave the `/graphql` + rewrite: (path) => path.replace(rwConfig.web.apiUrl, ''), + configure: (proxy) => { + // @MARK: this is a hack to prevent showing confusing proxy errors on startup + // because Vite launches so much faster than the API server. + let waitingForApiServer = true + + // Wait for 2.5s, then restore regular proxy error logging + setTimeout(() => { + waitingForApiServer = false + }, 2500) + + proxy.on('error', (err, _req, res) => { + if ( + waitingForApiServer && + err.message.includes('ECONNREFUSED') + ) { + err.stack = + '⌛ API Server launching, please refresh your page...' + } + const msg = { + errors: [ + { + message: + 'The RedwoodJS API server is not available or is currently reloading. Please refresh.', + }, + ], + } + + res.writeHead(203, { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache', + }) + res.write(JSON.stringify(msg)) + res.end() + }) + }, + }, + }, + }, + build: { + // NOTE this gets overridden when build gets called anyway! + outDir: + // @MARK: For RSC and Streaming, we build to dist/client directory + streamingBuild || rscBuild + ? rwPaths.web.distClient + : rwPaths.web.dist, + emptyOutDir: true, + manifest: !env.ssrBuild ? 'client-build-manifest.json' : undefined, + sourcemap: !env.ssrBuild && rwConfig.web.sourceMap, // Note that this can be boolean or 'inline' + rollupOptions: { + input: getRollupInput(!!env.ssrBuild), + }, + }, + // @MARK: do not set buildSsrCjsExternalHeuristics here + // because rsc builds want false, client and server build wants true + optimizeDeps: { + esbuildOptions: { + // @MARK this is because JS projects in Redwood don't have .jsx extensions + loader: { + '.js': 'jsx', + }, + // Node.js global to browser globalThis + // @MARK unsure why we need this, but required for DevFatalErrorPage atleast + define: { + global: 'globalThis', + }, + }, + }, + } + return mergeConfig(defaultRwViteConfig, userConfig) + } +} +/** + * + * This function configures how vite (actually Rollup) will bundle. + * + * By default, the entry point is the index.html file - even if you don't specify it in RollupOptions + * + * With streaming SSR, out entrypoint is different - either entry.client.tsx or entry.server.tsx + * and the html file is not used at all, because it is defined in Document.tsx + * + * @param ssr {boolean} Whether to return the SSR inputs or not + * @returns Rollup input Options + */ +function getRollupInput(ssr: boolean): InputOption | undefined { + const rwConfig = getConfig() + const rwPaths = getPaths() + + // @NOTE once streaming ssr is out of experimental, this will become the default + if (rwConfig.experimental.streamingSsr.enabled) { + return ssr + ? { + 'entry.server': rwPaths.web.entryServer as string, + Document: rwPaths.web.document, // We need the document for React's fallback + } + : (rwPaths.web.entryClient as string) + } + + return rwPaths.web.html +} diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index d61c3494446f..81a34ca7af3a 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -1,15 +1,12 @@ import fs from 'fs' import path from 'path' -import react from '@vitejs/plugin-react' -import type { InputOption } from 'rollup' -import type { ConfigEnv, PluginOption, UserConfig } from 'vite' -import { mergeConfig, normalizePath } from 'vite' +import type { PluginOption } from 'vite' +import { normalizePath } from 'vite' -import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getConfig, getPaths } from '@redwoodjs/project-config' -import { getEnvVarDefinitions } from './envVarDefinitions' +import { getMergedConfig } from './getMergedConfig' import handleJsAsJsx from './plugins/vite-plugin-jsx-loader' import removeFromBundle from './plugins/vite-plugin-remove-from-bundle' import swapApolloProvider from './plugins/vite-plugin-swap-apollo-provider' @@ -126,122 +123,10 @@ export default function redwoodPluginVite(): PluginOption[] { }, // ---------- End Bundle injection ---------- - // @MARK: Using the config hook here let's us modify the config - // but returning plugins will **not** work - config: (userConfig: UserConfig, env: ConfigEnv): UserConfig => { - let apiHost = process.env.REDWOOD_API_HOST - apiHost ??= rwConfig.api.host - apiHost ??= process.env.NODE_ENV === 'production' ? '0.0.0.0' : '[::]' - - const streamingBuild = rwConfig.experimental.streamingSsr?.enabled - // @MARK: note that most RSC settings sit in their individual build functions - const rscBuild = rwConfig.experimental.rsc?.enabled - - let apiPort - if (process.env.REDWOOD_API_PORT) { - apiPort = parseInt(process.env.REDWOOD_API_PORT) - } else { - apiPort = rwConfig.api.port - } - - const defaultRwViteConfig: UserConfig = { - root: rwPaths.web.src, - resolve: { - alias: [ - { - find: 'src', - replacement: rwPaths.web.src, - }, - ], - }, - envPrefix: 'REDWOOD_ENV_', - publicDir: path.join(rwPaths.web.base, 'public'), - define: getEnvVarDefinitions(), - css: { - // @NOTE config path is relative to where vite.config.js is if you use relative path - // postcss: './config/', - postcss: rwPaths.web.config, - }, - server: { - open: rwConfig.browser.open, - port: rwConfig.web.port, - host: true, // Listen to all hosts - proxy: { - [rwConfig.web.apiUrl]: { - target: `http://${apiHost}:${apiPort}`, - changeOrigin: false, - // Remove the `.redwood/functions` part, but leave the `/graphql` - rewrite: (path) => path.replace(rwConfig.web.apiUrl, ''), - configure: (proxy) => { - // @MARK: this is a hack to prevent showing confusing proxy errors on startup - // because Vite launches so much faster than the API server. - let waitingForApiServer = true - - // Wait for 2.5s, then restore regular proxy error logging - setTimeout(() => { - waitingForApiServer = false - }, 2500) - - proxy.on('error', (err, _req, res) => { - if ( - waitingForApiServer && - err.message.includes('ECONNREFUSED') - ) { - err.stack = - '⌛ API Server launching, please refresh your page...' - } - const msg = { - errors: [ - { - message: - 'The RedwoodJS API server is not available or is currently reloading. Please refresh.', - }, - ], - } - - res.writeHead(203, { - 'Content-Type': 'application/json', - 'Cache-Control': 'no-cache', - }) - res.write(JSON.stringify(msg)) - res.end() - }) - }, - }, - }, - }, - build: { - // NOTE this gets overridden when build gets called anyway! - outDir: - // @MARK: For RSC and Streaming, we build to dist/client directory - streamingBuild || rscBuild - ? rwPaths.web.distClient - : rwPaths.web.dist, - emptyOutDir: true, - manifest: !env.ssrBuild ? 'client-build-manifest.json' : undefined, - sourcemap: !env.ssrBuild && rwConfig.web.sourceMap, // Note that this can be boolean or 'inline' - rollupOptions: { - input: getRollupInput(!!env.ssrBuild), - }, - }, - // @MARK: do not set buildSsrCjsExternalHeuristics here - // because rsc builds want false, client and server build wants true - optimizeDeps: { - esbuildOptions: { - // @MARK this is because JS projects in Redwood don't have .jsx extensions - loader: { - '.js': 'jsx', - }, - // Node.js global to browser globalThis - // @MARK unsure why we need this, but required for DevFatalErrorPage atleast - define: { - global: 'globalThis', - }, - }, - }, - } - return mergeConfig(defaultRwViteConfig, userConfig) - }, + // @MARK: Using the config hook here let's us merge the config with defaults + // but returning plugins will **not** work, see https://vitejs.dev/guide/api-plugin.html#config + // "User plugins are resolved before running this hook so injecting other plugins inside the config hook will have no effect." + config: getMergedConfig(rwConfig, rwPaths), }, // We can remove when streaming is stable rwConfig.experimental.streamingSsr.enabled && swapApolloProvider(), @@ -258,46 +143,10 @@ export default function redwoodPluginVite(): PluginOption[] { id: /@redwoodjs\/web\/dist\/apollo\/sseLink/, }, ]), - react({ - babel: { - ...getWebSideDefaultBabelConfig({ - forVite: true, - // @MARK: Potential issue in the future. We don't want to set react plugins in each build file - // because we should be able to trigger the builds from the vite CLI directly. - // Right now, we can compile to SPA+SSR OR RSCClient+SSR+RSC - once this experimental flag is removed - // we will need to make sure SPA+SSR is still possible - forRscClient: rwConfig.experimental.rsc?.enabled, - }), - }, - }), - ] -} -/** - * - * This function configures how vite (actually Rollup) will bundle. - * - * By default, the entry point is the index.html file - even if you don't specify it in RollupOptions - * - * With streaming SSR, out entrypoint is different - either entry.client.tsx or entry.server.tsx - * and the html file is not used at all, because it is defined in Document.tsx - * - * @param ssr {boolean} Whether to return the SSR inputs or not - * @returns Rollup input Options - */ -function getRollupInput(ssr: boolean): InputOption | undefined { - const rwConfig = getConfig() - const rwPaths = getPaths() - - // @NOTE once streaming ssr is out of experimental, this will become the default - if (rwConfig.experimental.streamingSsr.enabled) { - return ssr - ? { - 'entry.server': rwPaths.web.entryServer as string, - Document: rwPaths.web.document, // We need the document for React's fallback - } - : (rwPaths.web.entryClient as string) - } - - return rwPaths.web.html + // @MARK: Removed React plugin from this rw-vite plugin + // Because we need to configure it conditionally based on whether + // we are building for SPA/SSR or for RSC + // There must be a better way to do this, but I can't figure it out right now + ] } diff --git a/packages/vite/src/rsc/buildForRscServer.ts b/packages/vite/src/rsc/buildForRscServer.ts index 3112e6f4a60c..0fcf8b4534aa 100644 --- a/packages/vite/src/rsc/buildForRscServer.ts +++ b/packages/vite/src/rsc/buildForRscServer.ts @@ -1,7 +1,9 @@ import path from 'node:path' +import react from '@vitejs/plugin-react' import { build as viteBuild } from 'vite' +import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getPaths } from '@redwoodjs/project-config' import { onWarn } from '../lib/onWarn' @@ -86,6 +88,14 @@ export async function buildForRscServer( // That's why it needs the `clientEntryFiles` data // (It does other things as well, but that's why it needs clientEntryFiles) rscTransformPlugin(clientEntryFiles), + react({ + babel: { + ...getWebSideDefaultBabelConfig({ + forVite: true, + forRscClient: false, + }), + }, + }), ], build: { ssr: true, diff --git a/packages/vite/src/rsc/rscBuildAnalyze.ts b/packages/vite/src/rsc/rscBuildAnalyze.ts index d51377e02036..fe572b9bacb2 100644 --- a/packages/vite/src/rsc/rscBuildAnalyze.ts +++ b/packages/vite/src/rsc/rscBuildAnalyze.ts @@ -1,5 +1,7 @@ +import react from '@vitejs/plugin-react' import { build as viteBuild } from 'vite' +import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getPaths } from '@redwoodjs/project-config' import { onWarn } from '../lib/onWarn' @@ -41,6 +43,13 @@ export async function rscBuildAnalyze() { (id) => clientEntryFileSet.add(id), (id) => serverEntryFileSet.add(id) ), + react({ + babel: { + ...getWebSideDefaultBabelConfig({ + forVite: true, + }), + }, + }), ], ssr: { // We can ignore everything that starts with `node:` because it's not going to be RSCs diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index 3da2b84beb25..4869e3f4ed0b 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -1,5 +1,7 @@ +import react from '@vitejs/plugin-react' import { build as viteBuild } from 'vite' +import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getPaths } from '@redwoodjs/project-config' import { onWarn } from '../lib/onWarn' @@ -22,6 +24,16 @@ export async function rscBuildClient(clientEntryFiles: Record) { const clientBuildOutput = await viteBuild({ envFile: false, + plugins: [ + react({ + babel: { + ...getWebSideDefaultBabelConfig({ + forVite: true, + forRscClient: true, // 👈 👈 👈 + }), + }, + }), + ], build: { outDir: rwPaths.web.distClient, emptyOutDir: true, // Needed because `outDir` is not inside `root` diff --git a/packages/vite/src/runFeServer.ts b/packages/vite/src/runFeServer.ts index 429d35c1e221..c4d10bbe4f7e 100644 --- a/packages/vite/src/runFeServer.ts +++ b/packages/vite/src/runFeServer.ts @@ -106,7 +106,8 @@ export async function runFeServer() { // @WARN! Be careful, between v2 and v3 of http-proxy-middleware // the syntax has changed https://github.com/chimurai/http-proxy-middleware createProxyMiddleware({ - changeOrigin: true, + // See https://github.com/redwoodjs/redwood/issues/7811 + changeOrigin: false, pathRewrite: { [`^${rwConfig.web.apiUrl}`]: '', // remove base path }, diff --git a/packages/vite/src/streaming/buildForStreamingServer.ts b/packages/vite/src/streaming/buildForStreamingServer.ts index 0154f1a5f3f9..f2b3c2689666 100644 --- a/packages/vite/src/streaming/buildForStreamingServer.ts +++ b/packages/vite/src/streaming/buildForStreamingServer.ts @@ -1,5 +1,7 @@ +import react from '@vitejs/plugin-react' import { build as viteBuild } from 'vite' +import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getPaths } from '@redwoodjs/project-config' export async function buildForStreamingServer({ @@ -21,6 +23,15 @@ export async function buildForStreamingServer({ ssr: true, emptyOutDir: true, }, + plugins: [ + react({ + babel: { + ...getWebSideDefaultBabelConfig({ + forVite: true, + }), + }, + }), + ], legacy: { // @MARK @TODO: this gets picked up by the RSC build if it's in index.js buildSsrCjsExternalHeuristics: true, From e7173971bfbb68e2b4f152131b8045c37892261d Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 29 Feb 2024 19:05:22 +0700 Subject: [PATCH 43/75] Ok --- .vscode/settings.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e3fc6a2c092c..89d70d0ff17e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,7 +13,7 @@ "search.exclude": { "**/dist": true, "**/node_modules": true, - "**/__fixtures__": true, + "/packages/**/__fixtures__": true, ".yarn-packages-cache": true }, "[markdown][html][mjml]": { @@ -23,8 +23,10 @@ "peacock.color": "#b85833", "cSpell.words": [ "autoplay", + "corepack", "execa", "Fastify", + "Flightcontrol", "graphiql", "opentelemetry", "pino", From 73961bac0cec16e328d258b9e45aea615dd94e65 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 29 Feb 2024 19:24:32 +0700 Subject: [PATCH 44/75] Also change SPA web build --- packages/internal/src/build/web.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/internal/src/build/web.ts b/packages/internal/src/build/web.ts index 8cf087ae2478..e668beb450af 100644 --- a/packages/internal/src/build/web.ts +++ b/packages/internal/src/build/web.ts @@ -6,6 +6,8 @@ import { removeSync } from 'fs-extra' import type { Flags } from '@redwoodjs/babel-config' import { prebuildWebFile } from '@redwoodjs/babel-config' import { getPaths } from '@redwoodjs/project-config' +import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' +import react from '@vitejs/plugin-react' // @MARK // This whole file is currently only used in testing @@ -70,6 +72,16 @@ export const buildWeb = async ({ verbose }: BuildOptions) => { return build({ configFile: viteConfig, envFile: false, + // @MARK Doing this because we need to modify the babel settings per build + plugins: [ + react({ + babel: { + ...getWebSideDefaultBabelConfig({ + forVite: true, + }), + }, + }), + ], logLevel: verbose ? 'info' : 'warn', }) } From afb02cebc9fbb7eb271f3db4e9a91d6aea7d8920 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 29 Feb 2024 19:30:33 +0700 Subject: [PATCH 45/75] Lint --- packages/internal/src/build/web.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/internal/src/build/web.ts b/packages/internal/src/build/web.ts index e668beb450af..d1be15b3ab2b 100644 --- a/packages/internal/src/build/web.ts +++ b/packages/internal/src/build/web.ts @@ -1,13 +1,15 @@ import fs from 'fs' import path from 'path' +import react from '@vitejs/plugin-react' import { removeSync } from 'fs-extra' import type { Flags } from '@redwoodjs/babel-config' -import { prebuildWebFile } from '@redwoodjs/babel-config' +import { + prebuildWebFile, + getWebSideDefaultBabelConfig, +} from '@redwoodjs/babel-config' import { getPaths } from '@redwoodjs/project-config' -import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' -import react from '@vitejs/plugin-react' // @MARK // This whole file is currently only used in testing From b4a7661ea0a89a088b2999756db1b0e0c80dba2e Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 29 Feb 2024 19:40:10 +0700 Subject: [PATCH 46/75] Revert "Ok" This reverts commit e7173971bfbb68e2b4f152131b8045c37892261d. --- .vscode/settings.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 89d70d0ff17e..e3fc6a2c092c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,7 +13,7 @@ "search.exclude": { "**/dist": true, "**/node_modules": true, - "/packages/**/__fixtures__": true, + "**/__fixtures__": true, ".yarn-packages-cache": true }, "[markdown][html][mjml]": { @@ -23,10 +23,8 @@ "peacock.color": "#b85833", "cSpell.words": [ "autoplay", - "corepack", "execa", "Fastify", - "Flightcontrol", "graphiql", "opentelemetry", "pino", From 20488fb677ec4fee3678767e0146638336353a6f Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Thu, 29 Feb 2024 18:03:29 +0530 Subject: [PATCH 47/75] RSC: Make babel insert `renderFromRscServer` calls into Routes.tsx (#10074) --- .../__tests__/babel-plugin-redwood-routes-auto-loader.test.ts | 2 +- packages/babel-config/src/web.ts | 2 ++ packages/cli/src/commands/experimental/setupRscHandler.js | 2 -- packages/vite/src/rsc/rscBuildClient.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/babel-config/src/plugins/__tests__/babel-plugin-redwood-routes-auto-loader.test.ts b/packages/babel-config/src/plugins/__tests__/babel-plugin-redwood-routes-auto-loader.test.ts index 69cda21e9399..be625d841ffc 100644 --- a/packages/babel-config/src/plugins/__tests__/babel-plugin-redwood-routes-auto-loader.test.ts +++ b/packages/babel-config/src/plugins/__tests__/babel-plugin-redwood-routes-auto-loader.test.ts @@ -122,7 +122,7 @@ describe('page auto loader handles imports for RSC', () => { ) }) - // Not sure about this 👇 - what should the behavior be? + // TODO(RSC): Figure out what the behavior should be? test('Already imported pages are left alone.', () => { expect(result?.code).toContain(`import FooPage from 'src/pages/FooPage'`) }) diff --git a/packages/babel-config/src/web.ts b/packages/babel-config/src/web.ts index 87924e84f01f..a36b3f23f132 100644 --- a/packages/babel-config/src/web.ts +++ b/packages/babel-config/src/web.ts @@ -150,6 +150,8 @@ export const getWebSideOverrides = ( }, ] + console.log('babel - How many times does this get called?') + return overrides.filter( (override: false | TransformOptions): override is TransformOptions => { return !!override diff --git a/packages/cli/src/commands/experimental/setupRscHandler.js b/packages/cli/src/commands/experimental/setupRscHandler.js index 8a8f5fad08b1..19c6475043b1 100644 --- a/packages/cli/src/commands/experimental/setupRscHandler.js +++ b/packages/cli/src/commands/experimental/setupRscHandler.js @@ -326,8 +326,6 @@ export const handler = async ({ force, verbose }) => { ) }, }, - // TODO (RSC): Remove this once we have a better way to handle routes. - // This is a total hack right now { title: 'Overwriting routes...', task: async () => { diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index 4869e3f4ed0b..1f1839e3319d 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -29,7 +29,7 @@ export async function rscBuildClient(clientEntryFiles: Record) { babel: { ...getWebSideDefaultBabelConfig({ forVite: true, - forRscClient: true, // 👈 👈 👈 + forRscClient: true, }), }, }), From f22679d6bd5ec6e78f7b90c5cf01912e43b66055 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Thu, 29 Feb 2024 18:21:12 +0530 Subject: [PATCH 48/75] Consistent rsc build logging --- packages/vite/src/rsc/buildForRscServer.ts | 8 ++++---- packages/vite/src/rsc/rscBuildAnalyze.ts | 6 ++++-- packages/vite/src/rsc/rscBuildClient.ts | 5 ++++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/vite/src/rsc/buildForRscServer.ts b/packages/vite/src/rsc/buildForRscServer.ts index 186d0cb09199..7496ae01e2bd 100644 --- a/packages/vite/src/rsc/buildForRscServer.ts +++ b/packages/vite/src/rsc/buildForRscServer.ts @@ -21,16 +21,16 @@ export async function buildForRscServer( serverEntryFiles: Record, customModules: Record ) { + console.log('\n') + console.log('3. buildForRscServer') + console.log('====================\n') + const rwPaths = getPaths() if (!rwPaths.web.entries) { throw new Error('RSC entries file not found') } - console.log('\n') - console.log('3. rscBuildServer') - console.log('=================\n') - const input = { entries: rwPaths.web.entries, ...clientEntryFiles, diff --git a/packages/vite/src/rsc/rscBuildAnalyze.ts b/packages/vite/src/rsc/rscBuildAnalyze.ts index fe572b9bacb2..2c538a8aec3c 100644 --- a/packages/vite/src/rsc/rscBuildAnalyze.ts +++ b/packages/vite/src/rsc/rscBuildAnalyze.ts @@ -19,12 +19,14 @@ import { rscAnalyzePlugin } from './rscVitePlugins' // only needed to trigger the rscAnalyzePlugin export async function rscBuildAnalyze() { + console.log('\n') + console.log('1. rscBuildAnalyze') + console.log('==================\n') + const rwPaths = getPaths() const clientEntryFileSet = new Set() const serverEntryFileSet = new Set() - console.log('Starting rscBuildAnalyze........\n') - if (!rwPaths.web.entries) { throw new Error('RSC entries file not found') } diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index 1f1839e3319d..5e575401417e 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -13,7 +13,10 @@ import { ensureProcessDirWeb } from '../utils' * Generate the client bundle */ export async function rscBuildClient(clientEntryFiles: Record) { - console.log('Starting RSC client build.... \n') + console.log('\n') + console.log('2. rscBuildClient') + console.log('=================\n') + const rwPaths = getPaths() ensureProcessDirWeb() From f9799ffcd42aef2e0a9cb520a53eedf314eafa94 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 29 Feb 2024 20:20:22 +0700 Subject: [PATCH 49/75] Revert "Move react plugin into their individual files" This reverts commit 92e13bf7e57c15fc6c8e0e8a791b995da1fc84fd. --- packages/vite/src/getMergedConfig.ts | 155 ---------------- packages/vite/src/index.ts | 175 ++++++++++++++++-- packages/vite/src/rsc/buildForRscServer.ts | 10 - packages/vite/src/rsc/rscBuildAnalyze.ts | 9 - packages/vite/src/rsc/rscBuildClient.ts | 12 -- packages/vite/src/runFeServer.ts | 3 +- .../src/streaming/buildForStreamingServer.ts | 11 -- 7 files changed, 164 insertions(+), 211 deletions(-) delete mode 100644 packages/vite/src/getMergedConfig.ts diff --git a/packages/vite/src/getMergedConfig.ts b/packages/vite/src/getMergedConfig.ts deleted file mode 100644 index b95febef387d..000000000000 --- a/packages/vite/src/getMergedConfig.ts +++ /dev/null @@ -1,155 +0,0 @@ -import path from 'path' - -import type { InputOption } from 'rollup' -import type { ConfigEnv, UserConfig } from 'vite' -import { mergeConfig } from 'vite' - -import type { Config, Paths } from '@redwoodjs/project-config' -import { getConfig, getPaths } from '@redwoodjs/project-config' - -import { getEnvVarDefinitions } from './envVarDefinitions' - -export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { - return (userConfig: UserConfig, env: ConfigEnv): UserConfig => { - let apiHost = process.env.REDWOOD_API_HOST - apiHost ??= rwConfig.api.host - apiHost ??= process.env.NODE_ENV === 'production' ? '0.0.0.0' : '[::]' - - const streamingBuild = rwConfig.experimental.streamingSsr?.enabled - // @MARK: note that most RSC settings sit in their individual build functions - const rscBuild = rwConfig.experimental.rsc?.enabled - - let apiPort - if (process.env.REDWOOD_API_PORT) { - apiPort = parseInt(process.env.REDWOOD_API_PORT) - } else { - apiPort = rwConfig.api.port - } - - const defaultRwViteConfig: UserConfig = { - root: rwPaths.web.src, - resolve: { - alias: [ - { - find: 'src', - replacement: rwPaths.web.src, - }, - ], - }, - envPrefix: 'REDWOOD_ENV_', - publicDir: path.join(rwPaths.web.base, 'public'), - define: getEnvVarDefinitions(), - css: { - // @NOTE config path is relative to where vite.config.js is if you use relative path - // postcss: './config/', - postcss: rwPaths.web.config, - }, - server: { - open: rwConfig.browser.open, - port: rwConfig.web.port, - host: true, // Listen to all hosts - proxy: { - [rwConfig.web.apiUrl]: { - target: `http://${apiHost}:${apiPort}`, - changeOrigin: false, - // Remove the `.redwood/functions` part, but leave the `/graphql` - rewrite: (path) => path.replace(rwConfig.web.apiUrl, ''), - configure: (proxy) => { - // @MARK: this is a hack to prevent showing confusing proxy errors on startup - // because Vite launches so much faster than the API server. - let waitingForApiServer = true - - // Wait for 2.5s, then restore regular proxy error logging - setTimeout(() => { - waitingForApiServer = false - }, 2500) - - proxy.on('error', (err, _req, res) => { - if ( - waitingForApiServer && - err.message.includes('ECONNREFUSED') - ) { - err.stack = - '⌛ API Server launching, please refresh your page...' - } - const msg = { - errors: [ - { - message: - 'The RedwoodJS API server is not available or is currently reloading. Please refresh.', - }, - ], - } - - res.writeHead(203, { - 'Content-Type': 'application/json', - 'Cache-Control': 'no-cache', - }) - res.write(JSON.stringify(msg)) - res.end() - }) - }, - }, - }, - }, - build: { - // NOTE this gets overridden when build gets called anyway! - outDir: - // @MARK: For RSC and Streaming, we build to dist/client directory - streamingBuild || rscBuild - ? rwPaths.web.distClient - : rwPaths.web.dist, - emptyOutDir: true, - manifest: !env.ssrBuild ? 'client-build-manifest.json' : undefined, - sourcemap: !env.ssrBuild && rwConfig.web.sourceMap, // Note that this can be boolean or 'inline' - rollupOptions: { - input: getRollupInput(!!env.ssrBuild), - }, - }, - // @MARK: do not set buildSsrCjsExternalHeuristics here - // because rsc builds want false, client and server build wants true - optimizeDeps: { - esbuildOptions: { - // @MARK this is because JS projects in Redwood don't have .jsx extensions - loader: { - '.js': 'jsx', - }, - // Node.js global to browser globalThis - // @MARK unsure why we need this, but required for DevFatalErrorPage atleast - define: { - global: 'globalThis', - }, - }, - }, - } - return mergeConfig(defaultRwViteConfig, userConfig) - } -} -/** - * - * This function configures how vite (actually Rollup) will bundle. - * - * By default, the entry point is the index.html file - even if you don't specify it in RollupOptions - * - * With streaming SSR, out entrypoint is different - either entry.client.tsx or entry.server.tsx - * and the html file is not used at all, because it is defined in Document.tsx - * - * @param ssr {boolean} Whether to return the SSR inputs or not - * @returns Rollup input Options - */ -function getRollupInput(ssr: boolean): InputOption | undefined { - const rwConfig = getConfig() - const rwPaths = getPaths() - - // @NOTE once streaming ssr is out of experimental, this will become the default - if (rwConfig.experimental.streamingSsr.enabled) { - return ssr - ? { - 'entry.server': rwPaths.web.entryServer as string, - Document: rwPaths.web.document, // We need the document for React's fallback - } - : (rwPaths.web.entryClient as string) - } - - return rwPaths.web.html -} diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 81a34ca7af3a..d61c3494446f 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -1,12 +1,15 @@ import fs from 'fs' import path from 'path' -import type { PluginOption } from 'vite' -import { normalizePath } from 'vite' +import react from '@vitejs/plugin-react' +import type { InputOption } from 'rollup' +import type { ConfigEnv, PluginOption, UserConfig } from 'vite' +import { mergeConfig, normalizePath } from 'vite' +import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getConfig, getPaths } from '@redwoodjs/project-config' -import { getMergedConfig } from './getMergedConfig' +import { getEnvVarDefinitions } from './envVarDefinitions' import handleJsAsJsx from './plugins/vite-plugin-jsx-loader' import removeFromBundle from './plugins/vite-plugin-remove-from-bundle' import swapApolloProvider from './plugins/vite-plugin-swap-apollo-provider' @@ -123,10 +126,122 @@ export default function redwoodPluginVite(): PluginOption[] { }, // ---------- End Bundle injection ---------- - // @MARK: Using the config hook here let's us merge the config with defaults - // but returning plugins will **not** work, see https://vitejs.dev/guide/api-plugin.html#config - // "User plugins are resolved before running this hook so injecting other plugins inside the config hook will have no effect." - config: getMergedConfig(rwConfig, rwPaths), + // @MARK: Using the config hook here let's us modify the config + // but returning plugins will **not** work + config: (userConfig: UserConfig, env: ConfigEnv): UserConfig => { + let apiHost = process.env.REDWOOD_API_HOST + apiHost ??= rwConfig.api.host + apiHost ??= process.env.NODE_ENV === 'production' ? '0.0.0.0' : '[::]' + + const streamingBuild = rwConfig.experimental.streamingSsr?.enabled + // @MARK: note that most RSC settings sit in their individual build functions + const rscBuild = rwConfig.experimental.rsc?.enabled + + let apiPort + if (process.env.REDWOOD_API_PORT) { + apiPort = parseInt(process.env.REDWOOD_API_PORT) + } else { + apiPort = rwConfig.api.port + } + + const defaultRwViteConfig: UserConfig = { + root: rwPaths.web.src, + resolve: { + alias: [ + { + find: 'src', + replacement: rwPaths.web.src, + }, + ], + }, + envPrefix: 'REDWOOD_ENV_', + publicDir: path.join(rwPaths.web.base, 'public'), + define: getEnvVarDefinitions(), + css: { + // @NOTE config path is relative to where vite.config.js is if you use relative path + // postcss: './config/', + postcss: rwPaths.web.config, + }, + server: { + open: rwConfig.browser.open, + port: rwConfig.web.port, + host: true, // Listen to all hosts + proxy: { + [rwConfig.web.apiUrl]: { + target: `http://${apiHost}:${apiPort}`, + changeOrigin: false, + // Remove the `.redwood/functions` part, but leave the `/graphql` + rewrite: (path) => path.replace(rwConfig.web.apiUrl, ''), + configure: (proxy) => { + // @MARK: this is a hack to prevent showing confusing proxy errors on startup + // because Vite launches so much faster than the API server. + let waitingForApiServer = true + + // Wait for 2.5s, then restore regular proxy error logging + setTimeout(() => { + waitingForApiServer = false + }, 2500) + + proxy.on('error', (err, _req, res) => { + if ( + waitingForApiServer && + err.message.includes('ECONNREFUSED') + ) { + err.stack = + '⌛ API Server launching, please refresh your page...' + } + const msg = { + errors: [ + { + message: + 'The RedwoodJS API server is not available or is currently reloading. Please refresh.', + }, + ], + } + + res.writeHead(203, { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache', + }) + res.write(JSON.stringify(msg)) + res.end() + }) + }, + }, + }, + }, + build: { + // NOTE this gets overridden when build gets called anyway! + outDir: + // @MARK: For RSC and Streaming, we build to dist/client directory + streamingBuild || rscBuild + ? rwPaths.web.distClient + : rwPaths.web.dist, + emptyOutDir: true, + manifest: !env.ssrBuild ? 'client-build-manifest.json' : undefined, + sourcemap: !env.ssrBuild && rwConfig.web.sourceMap, // Note that this can be boolean or 'inline' + rollupOptions: { + input: getRollupInput(!!env.ssrBuild), + }, + }, + // @MARK: do not set buildSsrCjsExternalHeuristics here + // because rsc builds want false, client and server build wants true + optimizeDeps: { + esbuildOptions: { + // @MARK this is because JS projects in Redwood don't have .jsx extensions + loader: { + '.js': 'jsx', + }, + // Node.js global to browser globalThis + // @MARK unsure why we need this, but required for DevFatalErrorPage atleast + define: { + global: 'globalThis', + }, + }, + }, + } + return mergeConfig(defaultRwViteConfig, userConfig) + }, }, // We can remove when streaming is stable rwConfig.experimental.streamingSsr.enabled && swapApolloProvider(), @@ -143,10 +258,46 @@ export default function redwoodPluginVite(): PluginOption[] { id: /@redwoodjs\/web\/dist\/apollo\/sseLink/, }, ]), - - // @MARK: Removed React plugin from this rw-vite plugin - // Because we need to configure it conditionally based on whether - // we are building for SPA/SSR or for RSC - // There must be a better way to do this, but I can't figure it out right now + react({ + babel: { + ...getWebSideDefaultBabelConfig({ + forVite: true, + // @MARK: Potential issue in the future. We don't want to set react plugins in each build file + // because we should be able to trigger the builds from the vite CLI directly. + // Right now, we can compile to SPA+SSR OR RSCClient+SSR+RSC - once this experimental flag is removed + // we will need to make sure SPA+SSR is still possible + forRscClient: rwConfig.experimental.rsc?.enabled, + }), + }, + }), ] } + +/** + * + * This function configures how vite (actually Rollup) will bundle. + * + * By default, the entry point is the index.html file - even if you don't specify it in RollupOptions + * + * With streaming SSR, out entrypoint is different - either entry.client.tsx or entry.server.tsx + * and the html file is not used at all, because it is defined in Document.tsx + * + * @param ssr {boolean} Whether to return the SSR inputs or not + * @returns Rollup input Options + */ +function getRollupInput(ssr: boolean): InputOption | undefined { + const rwConfig = getConfig() + const rwPaths = getPaths() + + // @NOTE once streaming ssr is out of experimental, this will become the default + if (rwConfig.experimental.streamingSsr.enabled) { + return ssr + ? { + 'entry.server': rwPaths.web.entryServer as string, + Document: rwPaths.web.document, // We need the document for React's fallback + } + : (rwPaths.web.entryClient as string) + } + + return rwPaths.web.html +} diff --git a/packages/vite/src/rsc/buildForRscServer.ts b/packages/vite/src/rsc/buildForRscServer.ts index 7496ae01e2bd..dae32a81b75e 100644 --- a/packages/vite/src/rsc/buildForRscServer.ts +++ b/packages/vite/src/rsc/buildForRscServer.ts @@ -1,9 +1,7 @@ import path from 'node:path' -import react from '@vitejs/plugin-react' import { build as viteBuild } from 'vite' -import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getPaths } from '@redwoodjs/project-config' import { onWarn } from '../lib/onWarn' @@ -91,14 +89,6 @@ export async function buildForRscServer( // That's why it needs the `clientEntryFiles` data // (It does other things as well, but that's why it needs clientEntryFiles) rscTransformPlugin(clientEntryFiles), - react({ - babel: { - ...getWebSideDefaultBabelConfig({ - forVite: true, - forRscClient: false, - }), - }, - }), ], build: { ssr: true, diff --git a/packages/vite/src/rsc/rscBuildAnalyze.ts b/packages/vite/src/rsc/rscBuildAnalyze.ts index 2c538a8aec3c..26af90628ff9 100644 --- a/packages/vite/src/rsc/rscBuildAnalyze.ts +++ b/packages/vite/src/rsc/rscBuildAnalyze.ts @@ -1,7 +1,5 @@ -import react from '@vitejs/plugin-react' import { build as viteBuild } from 'vite' -import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getPaths } from '@redwoodjs/project-config' import { onWarn } from '../lib/onWarn' @@ -45,13 +43,6 @@ export async function rscBuildAnalyze() { (id) => clientEntryFileSet.add(id), (id) => serverEntryFileSet.add(id) ), - react({ - babel: { - ...getWebSideDefaultBabelConfig({ - forVite: true, - }), - }, - }), ], ssr: { // We can ignore everything that starts with `node:` because it's not going to be RSCs diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index 5e575401417e..595462b6bade 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -1,7 +1,5 @@ -import react from '@vitejs/plugin-react' import { build as viteBuild } from 'vite' -import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getPaths } from '@redwoodjs/project-config' import { onWarn } from '../lib/onWarn' @@ -27,16 +25,6 @@ export async function rscBuildClient(clientEntryFiles: Record) { const clientBuildOutput = await viteBuild({ envFile: false, - plugins: [ - react({ - babel: { - ...getWebSideDefaultBabelConfig({ - forVite: true, - forRscClient: true, - }), - }, - }), - ], build: { outDir: rwPaths.web.distClient, emptyOutDir: true, // Needed because `outDir` is not inside `root` diff --git a/packages/vite/src/runFeServer.ts b/packages/vite/src/runFeServer.ts index c4d10bbe4f7e..429d35c1e221 100644 --- a/packages/vite/src/runFeServer.ts +++ b/packages/vite/src/runFeServer.ts @@ -106,8 +106,7 @@ export async function runFeServer() { // @WARN! Be careful, between v2 and v3 of http-proxy-middleware // the syntax has changed https://github.com/chimurai/http-proxy-middleware createProxyMiddleware({ - // See https://github.com/redwoodjs/redwood/issues/7811 - changeOrigin: false, + changeOrigin: true, pathRewrite: { [`^${rwConfig.web.apiUrl}`]: '', // remove base path }, diff --git a/packages/vite/src/streaming/buildForStreamingServer.ts b/packages/vite/src/streaming/buildForStreamingServer.ts index f2b3c2689666..0154f1a5f3f9 100644 --- a/packages/vite/src/streaming/buildForStreamingServer.ts +++ b/packages/vite/src/streaming/buildForStreamingServer.ts @@ -1,7 +1,5 @@ -import react from '@vitejs/plugin-react' import { build as viteBuild } from 'vite' -import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getPaths } from '@redwoodjs/project-config' export async function buildForStreamingServer({ @@ -23,15 +21,6 @@ export async function buildForStreamingServer({ ssr: true, emptyOutDir: true, }, - plugins: [ - react({ - babel: { - ...getWebSideDefaultBabelConfig({ - forVite: true, - }), - }, - }), - ], legacy: { // @MARK @TODO: this gets picked up by the RSC build if it's in index.js buildSsrCjsExternalHeuristics: true, From 9e2c3f74aaaa0e06ab594cb5a1cdd79259baffaf Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 29 Feb 2024 20:20:35 +0700 Subject: [PATCH 50/75] Revert "Also change SPA web build" This reverts commit 73961bac0cec16e328d258b9e45aea615dd94e65. --- packages/internal/src/build/web.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/internal/src/build/web.ts b/packages/internal/src/build/web.ts index d1be15b3ab2b..0a8e295fc790 100644 --- a/packages/internal/src/build/web.ts +++ b/packages/internal/src/build/web.ts @@ -74,16 +74,6 @@ export const buildWeb = async ({ verbose }: BuildOptions) => { return build({ configFile: viteConfig, envFile: false, - // @MARK Doing this because we need to modify the babel settings per build - plugins: [ - react({ - babel: { - ...getWebSideDefaultBabelConfig({ - forVite: true, - }), - }, - }), - ], logLevel: verbose ? 'info' : 'warn', }) } From 8e435b4c87ded2b255e66466cfe6f3cfe18440f1 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 29 Feb 2024 20:22:30 +0700 Subject: [PATCH 51/75] More bad merging --- packages/internal/src/build/web.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/internal/src/build/web.ts b/packages/internal/src/build/web.ts index 0a8e295fc790..8cf087ae2478 100644 --- a/packages/internal/src/build/web.ts +++ b/packages/internal/src/build/web.ts @@ -1,14 +1,10 @@ import fs from 'fs' import path from 'path' -import react from '@vitejs/plugin-react' import { removeSync } from 'fs-extra' import type { Flags } from '@redwoodjs/babel-config' -import { - prebuildWebFile, - getWebSideDefaultBabelConfig, -} from '@redwoodjs/babel-config' +import { prebuildWebFile } from '@redwoodjs/babel-config' import { getPaths } from '@redwoodjs/project-config' // @MARK From 6eb5ca9d1dce6c57babc5fe53872324b0e868385 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 29 Feb 2024 20:22:43 +0700 Subject: [PATCH 52/75] MjsOrJs Resolution of paths --- packages/project-config/src/paths.ts | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/packages/project-config/src/paths.ts b/packages/project-config/src/paths.ts index 20ad0f8cdcad..0c750837e5f4 100644 --- a/packages/project-config/src/paths.ts +++ b/packages/project-config/src/paths.ts @@ -166,6 +166,15 @@ export const resolveFile = ( return null } +/** Default to JS path, but if MJS exists, use it instead **/ +const mjsOrJs = (filePath: string) => { + const mjsPath = resolveFile(filePath, ['.mjs']) + if (mjsPath) { + return mjsPath + } + return filePath + '.js' +} + /** * Path constants that are relevant to a Redwood project. */ @@ -248,14 +257,12 @@ export const getPaths = (BASE_DIR: string = getBaseDir()): Paths => { distClient: path.join(BASE_DIR, PATH_WEB_DIR_DIST_CLIENT), distRsc: path.join(BASE_DIR, PATH_WEB_DIR_DIST_RSC), // Allow for the possibility of a .mjs file - distEntryServer: resolveFile( - path.join(BASE_DIR, PATH_WEB_DIR_DIST_SERVER_ENTRY_SERVER), - ['.js', '.mjs'] - ) as string, - distDocumentServer: resolveFile( - path.join(BASE_DIR, PATH_WEB_DIR_DIST_DOCUMENT), - ['.js', '.mjs'] - ) as string, + distEntryServer: mjsOrJs( + path.join(BASE_DIR, PATH_WEB_DIR_DIST_SERVER_ENTRY_SERVER) + ), + distDocumentServer: mjsOrJs( + path.join(BASE_DIR, PATH_WEB_DIR_DIST_DOCUMENT) + ), distRouteHooks: path.join(BASE_DIR, PATH_WEB_DIR_DIST_SERVER_ROUTEHOOKS), distRscEntries: path.join(BASE_DIR, PATH_WEB_DIR_DIST_RSC_ENTRIES), routeManifest: path.join(BASE_DIR, PATH_WEB_DIR_ROUTE_MANIFEST), From 91cf5af7a63be1fb1c4cd6b0da5384cb71987858 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 29 Feb 2024 20:23:09 +0700 Subject: [PATCH 53/75] Remove log --- packages/babel-config/src/web.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/babel-config/src/web.ts b/packages/babel-config/src/web.ts index a36b3f23f132..87924e84f01f 100644 --- a/packages/babel-config/src/web.ts +++ b/packages/babel-config/src/web.ts @@ -150,8 +150,6 @@ export const getWebSideOverrides = ( }, ] - console.log('babel - How many times does this get called?') - return overrides.filter( (override: false | TransformOptions): override is TransformOptions => { return !!override From 350fadcb53b852b56c5b7ae383dd4abfcfa7ed9d Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 29 Feb 2024 20:26:42 +0700 Subject: [PATCH 54/75] getMergedConfig --- packages/vite/src/getMergedConfig.ts | 161 +++++++++++++++++++++++++++ packages/vite/src/index.ts | 151 +------------------------ 2 files changed, 165 insertions(+), 147 deletions(-) create mode 100644 packages/vite/src/getMergedConfig.ts diff --git a/packages/vite/src/getMergedConfig.ts b/packages/vite/src/getMergedConfig.ts new file mode 100644 index 000000000000..891cb539ef8f --- /dev/null +++ b/packages/vite/src/getMergedConfig.ts @@ -0,0 +1,161 @@ +import path from 'path' + +import type { InputOption } from 'rollup' +import type { ConfigEnv, UserConfig } from 'vite' +import { mergeConfig } from 'vite' + +import type { Config, Paths } from '@redwoodjs/project-config' +import { getConfig, getPaths } from '@redwoodjs/project-config' + +import { getEnvVarDefinitions } from './envVarDefinitions' + +/** + * + * This function will merge in the default Redwood Vite config passed into the build function (or in Vite.config.xxx) + * + * Note that returning plugins in this function will have no effect on the build + */ +export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { + return (userConfig: UserConfig, env: ConfigEnv): UserConfig => { + let apiHost = process.env.REDWOOD_API_HOST + apiHost ??= rwConfig.api.host + apiHost ??= process.env.NODE_ENV === 'production' ? '0.0.0.0' : '[::]' + + const streamingBuild = rwConfig.experimental.streamingSsr?.enabled + // @MARK: note that most RSC settings sit in their individual build functions + const rscBuild = rwConfig.experimental.rsc?.enabled + + let apiPort + if (process.env.REDWOOD_API_PORT) { + apiPort = parseInt(process.env.REDWOOD_API_PORT) + } else { + apiPort = rwConfig.api.port + } + + const defaultRwViteConfig: UserConfig = { + root: rwPaths.web.src, + resolve: { + alias: [ + { + find: 'src', + replacement: rwPaths.web.src, + }, + ], + }, + envPrefix: 'REDWOOD_ENV_', + publicDir: path.join(rwPaths.web.base, 'public'), + define: getEnvVarDefinitions(), + css: { + // @NOTE config path is relative to where vite.config.js is if you use relative path + // postcss: './config/', + postcss: rwPaths.web.config, + }, + server: { + open: rwConfig.browser.open, + port: rwConfig.web.port, + host: true, // Listen to all hosts + proxy: { + [rwConfig.web.apiUrl]: { + target: `http://${apiHost}:${apiPort}`, + changeOrigin: false, + // Remove the `.redwood/functions` part, but leave the `/graphql` + rewrite: (path) => path.replace(rwConfig.web.apiUrl, ''), + configure: (proxy) => { + // @MARK: this is a hack to prevent showing confusing proxy errors on startup + // because Vite launches so much faster than the API server. + let waitingForApiServer = true + + // Wait for 2.5s, then restore regular proxy error logging + setTimeout(() => { + waitingForApiServer = false + }, 2500) + + proxy.on('error', (err, _req, res) => { + if ( + waitingForApiServer && + err.message.includes('ECONNREFUSED') + ) { + err.stack = + '⌛ API Server launching, please refresh your page...' + } + const msg = { + errors: [ + { + message: + 'The RedwoodJS API server is not available or is currently reloading. Please refresh.', + }, + ], + } + + res.writeHead(203, { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache', + }) + res.write(JSON.stringify(msg)) + res.end() + }) + }, + }, + }, + }, + build: { + // NOTE this gets overridden when build gets called anyway! + outDir: + // @MARK: For RSC and Streaming, we build to dist/client directory + streamingBuild || rscBuild + ? rwPaths.web.distClient + : rwPaths.web.dist, + emptyOutDir: true, + manifest: !env.ssrBuild ? 'client-build-manifest.json' : undefined, + sourcemap: !env.ssrBuild && rwConfig.web.sourceMap, // Note that this can be boolean or 'inline' + rollupOptions: { + input: getRollupInput(!!env.ssrBuild), + }, + }, + // @MARK: do not set buildSsrCjsExternalHeuristics here + // because rsc builds want false, client and server build wants true + optimizeDeps: { + esbuildOptions: { + // @MARK this is because JS projects in Redwood don't have .jsx extensions + loader: { + '.js': 'jsx', + }, + // Node.js global to browser globalThis + // @MARK unsure why we need this, but required for DevFatalErrorPage atleast + define: { + global: 'globalThis', + }, + }, + }, + } + return mergeConfig(defaultRwViteConfig, userConfig) + } +} +/** + * + * This function configures how vite (actually Rollup) will bundle. + * + * By default, the entry point is the index.html file - even if you don't specify it in RollupOptions + * + * With streaming SSR, out entrypoint is different - either entry.client.tsx or entry.server.tsx + * and the html file is not used at all, because it is defined in Document.tsx + * + * @param ssr {boolean} Whether to return the SSR inputs or not + * @returns Rollup input Options + */ +function getRollupInput(ssr: boolean): InputOption | undefined { + const rwConfig = getConfig() + const rwPaths = getPaths() + + // @NOTE once streaming ssr is out of experimental, this will become the default + if (rwConfig.experimental.streamingSsr.enabled) { + return ssr + ? { + 'entry.server': rwPaths.web.entryServer as string, + Document: rwPaths.web.document, // We need the document for React's fallback + } + : (rwPaths.web.entryClient as string) + } + + return rwPaths.web.html +} diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index d61c3494446f..026766d6fb31 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -2,14 +2,13 @@ import fs from 'fs' import path from 'path' import react from '@vitejs/plugin-react' -import type { InputOption } from 'rollup' -import type { ConfigEnv, PluginOption, UserConfig } from 'vite' -import { mergeConfig, normalizePath } from 'vite' +import type { PluginOption } from 'vite' +import { normalizePath } from 'vite' import { getWebSideDefaultBabelConfig } from '@redwoodjs/babel-config' import { getConfig, getPaths } from '@redwoodjs/project-config' -import { getEnvVarDefinitions } from './envVarDefinitions' +import { getMergedConfig } from './getMergedConfig' import handleJsAsJsx from './plugins/vite-plugin-jsx-loader' import removeFromBundle from './plugins/vite-plugin-remove-from-bundle' import swapApolloProvider from './plugins/vite-plugin-swap-apollo-provider' @@ -128,120 +127,7 @@ export default function redwoodPluginVite(): PluginOption[] { // @MARK: Using the config hook here let's us modify the config // but returning plugins will **not** work - config: (userConfig: UserConfig, env: ConfigEnv): UserConfig => { - let apiHost = process.env.REDWOOD_API_HOST - apiHost ??= rwConfig.api.host - apiHost ??= process.env.NODE_ENV === 'production' ? '0.0.0.0' : '[::]' - - const streamingBuild = rwConfig.experimental.streamingSsr?.enabled - // @MARK: note that most RSC settings sit in their individual build functions - const rscBuild = rwConfig.experimental.rsc?.enabled - - let apiPort - if (process.env.REDWOOD_API_PORT) { - apiPort = parseInt(process.env.REDWOOD_API_PORT) - } else { - apiPort = rwConfig.api.port - } - - const defaultRwViteConfig: UserConfig = { - root: rwPaths.web.src, - resolve: { - alias: [ - { - find: 'src', - replacement: rwPaths.web.src, - }, - ], - }, - envPrefix: 'REDWOOD_ENV_', - publicDir: path.join(rwPaths.web.base, 'public'), - define: getEnvVarDefinitions(), - css: { - // @NOTE config path is relative to where vite.config.js is if you use relative path - // postcss: './config/', - postcss: rwPaths.web.config, - }, - server: { - open: rwConfig.browser.open, - port: rwConfig.web.port, - host: true, // Listen to all hosts - proxy: { - [rwConfig.web.apiUrl]: { - target: `http://${apiHost}:${apiPort}`, - changeOrigin: false, - // Remove the `.redwood/functions` part, but leave the `/graphql` - rewrite: (path) => path.replace(rwConfig.web.apiUrl, ''), - configure: (proxy) => { - // @MARK: this is a hack to prevent showing confusing proxy errors on startup - // because Vite launches so much faster than the API server. - let waitingForApiServer = true - - // Wait for 2.5s, then restore regular proxy error logging - setTimeout(() => { - waitingForApiServer = false - }, 2500) - - proxy.on('error', (err, _req, res) => { - if ( - waitingForApiServer && - err.message.includes('ECONNREFUSED') - ) { - err.stack = - '⌛ API Server launching, please refresh your page...' - } - const msg = { - errors: [ - { - message: - 'The RedwoodJS API server is not available or is currently reloading. Please refresh.', - }, - ], - } - - res.writeHead(203, { - 'Content-Type': 'application/json', - 'Cache-Control': 'no-cache', - }) - res.write(JSON.stringify(msg)) - res.end() - }) - }, - }, - }, - }, - build: { - // NOTE this gets overridden when build gets called anyway! - outDir: - // @MARK: For RSC and Streaming, we build to dist/client directory - streamingBuild || rscBuild - ? rwPaths.web.distClient - : rwPaths.web.dist, - emptyOutDir: true, - manifest: !env.ssrBuild ? 'client-build-manifest.json' : undefined, - sourcemap: !env.ssrBuild && rwConfig.web.sourceMap, // Note that this can be boolean or 'inline' - rollupOptions: { - input: getRollupInput(!!env.ssrBuild), - }, - }, - // @MARK: do not set buildSsrCjsExternalHeuristics here - // because rsc builds want false, client and server build wants true - optimizeDeps: { - esbuildOptions: { - // @MARK this is because JS projects in Redwood don't have .jsx extensions - loader: { - '.js': 'jsx', - }, - // Node.js global to browser globalThis - // @MARK unsure why we need this, but required for DevFatalErrorPage atleast - define: { - global: 'globalThis', - }, - }, - }, - } - return mergeConfig(defaultRwViteConfig, userConfig) - }, + config: getMergedConfig(rwConfig, rwPaths), }, // We can remove when streaming is stable rwConfig.experimental.streamingSsr.enabled && swapApolloProvider(), @@ -272,32 +158,3 @@ export default function redwoodPluginVite(): PluginOption[] { }), ] } - -/** - * - * This function configures how vite (actually Rollup) will bundle. - * - * By default, the entry point is the index.html file - even if you don't specify it in RollupOptions - * - * With streaming SSR, out entrypoint is different - either entry.client.tsx or entry.server.tsx - * and the html file is not used at all, because it is defined in Document.tsx - * - * @param ssr {boolean} Whether to return the SSR inputs or not - * @returns Rollup input Options - */ -function getRollupInput(ssr: boolean): InputOption | undefined { - const rwConfig = getConfig() - const rwPaths = getPaths() - - // @NOTE once streaming ssr is out of experimental, this will become the default - if (rwConfig.experimental.streamingSsr.enabled) { - return ssr - ? { - 'entry.server': rwPaths.web.entryServer as string, - Document: rwPaths.web.document, // We need the document for React's fallback - } - : (rwPaths.web.entryClient as string) - } - - return rwPaths.web.html -} From 089daa9fd15a3b1a85c4d582e7538cbd99acb2dd Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 29 Feb 2024 20:27:48 +0700 Subject: [PATCH 55/75] Undo accidentally changing vscode settings --- .vscode/settings.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e3fc6a2c092c..89d70d0ff17e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,7 +13,7 @@ "search.exclude": { "**/dist": true, "**/node_modules": true, - "**/__fixtures__": true, + "/packages/**/__fixtures__": true, ".yarn-packages-cache": true }, "[markdown][html][mjml]": { @@ -23,8 +23,10 @@ "peacock.color": "#b85833", "cSpell.words": [ "autoplay", + "corepack", "execa", "Fastify", + "Flightcontrol", "graphiql", "opentelemetry", "pino", From 8e05e7f39753dee5123028a9caccce02f41696e7 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 29 Feb 2024 20:32:56 +0700 Subject: [PATCH 56/75] Remove comment on entries --- packages/vite/src/rsc/rscBuildClientEntriesFile.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts index 9127198ed751..eed6a293b4a4 100644 --- a/packages/vite/src/rsc/rscBuildClientEntriesFile.ts +++ b/packages/vite/src/rsc/rscBuildClientEntriesFile.ts @@ -25,8 +25,6 @@ export function rscBuildClientEntriesMappings( for (const item of clientBuildOutput) { const { name, fileName } = item - // @MARK: Doesn't refer to Vite entry... - // this is file that uses one or more of the clientEntries const entryFile = name && // TODO (RSC) Can't we just compare the names? `item.name === name` From 8db7877541d59380c2606bae2fe0fc4350190107 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 29 Feb 2024 20:33:56 +0700 Subject: [PATCH 57/75] Clarify comment on entries vs inputs --- packages/vite/src/runFeServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/runFeServer.ts b/packages/vite/src/runFeServer.ts index 429d35c1e221..3d08d16313c2 100644 --- a/packages/vite/src/runFeServer.ts +++ b/packages/vite/src/runFeServer.ts @@ -80,7 +80,7 @@ export async function runFeServer() { // @MARK: Surely there's a better way than this! const indexEntry = Object.values(clientBuildManifest).find((manifestItem) => { - // For RSC builds, we pass in many Vite entries, so we need to find it differently. + // For RSC builds, we pass in many rollUp inputs, so we need to find it differently. return rscBuild ? manifestItem.file.includes('rwjs-client-entry-') : manifestItem.isEntry From 69abdd8110f13a6af42feb72d1b952a89e43de9a Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Thu, 29 Feb 2024 20:34:41 +0700 Subject: [PATCH 58/75] Remove TODO --- packages/vite/src/streaming/createReactStreamingHandler.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/vite/src/streaming/createReactStreamingHandler.ts b/packages/vite/src/streaming/createReactStreamingHandler.ts index 73a7ed889e30..b3fbcf73ce1e 100644 --- a/packages/vite/src/streaming/createReactStreamingHandler.ts +++ b/packages/vite/src/streaming/createReactStreamingHandler.ts @@ -41,7 +41,6 @@ export const createReactStreamingHandler = async ( let fallbackDocumentImport: any if (isProd) { - // TODO (RSC) Consolidate paths, so we can have the same code for SSR and RSC entryServerImport = await import(makeFilePath(rwPaths.web.distEntryServer)) fallbackDocumentImport = await import( makeFilePath(rwPaths.web.distDocumentServer) From e19ed76ce6654b269287d693c3b96454057edc68 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Thu, 29 Feb 2024 22:04:49 +0530 Subject: [PATCH 59/75] chore(refactor): vite - extract into buildRouteHooks.ts (#10080) --- packages/vite/src/buildFeServer.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/vite/src/buildFeServer.ts b/packages/vite/src/buildFeServer.ts index 236e109ec317..269326b8dc4d 100644 --- a/packages/vite/src/buildFeServer.ts +++ b/packages/vite/src/buildFeServer.ts @@ -1,3 +1,8 @@ +<<<<<<< HEAD +======= +import { build as viteBuild } from 'vite' + +>>>>>>> 6e40545cc (chore(refactor): vite - extract into buildRouteHooks.ts (#10080)) import { buildWeb } from '@redwoodjs/internal/dist/build/web' import { getConfig, getPaths } from '@redwoodjs/project-config' From 68f34efc7da16828659f2aa4044a173ae622c172 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 2 Mar 2024 16:38:29 +0100 Subject: [PATCH 60/75] rscEnabled naming everywhere --- packages/vite/src/buildFeServer.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/vite/src/buildFeServer.ts b/packages/vite/src/buildFeServer.ts index 236e109ec317..984f16036ea3 100644 --- a/packages/vite/src/buildFeServer.ts +++ b/packages/vite/src/buildFeServer.ts @@ -19,8 +19,8 @@ export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { const rwConfig = getConfig() const viteConfigPath = rwPaths.web.viteConfig - const rscBuild = rwConfig.experimental?.rsc?.enabled - const streamingBuild = rwConfig.experimental?.streamingSsr?.enabled + const rscEnabled = rwConfig.experimental?.rsc?.enabled + const streamingSsrEnabled = rwConfig.experimental?.streamingSsr?.enabled if (!viteConfigPath) { throw new Error( @@ -37,7 +37,7 @@ export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { ) } - if (rscBuild) { + if (rscEnabled) { if (!rwPaths.web.entries) { throw new Error('RSC entries file not found') } @@ -47,7 +47,7 @@ export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { // We generate the RSC client bundle in the buildRscFeServer function // Streaming and RSC client bundles are **not** the same - if (streamingBuild && !rscBuild) { + if (streamingSsrEnabled && !rscEnabled) { console.log('Building client for streaming SSR...\n') await buildWeb({ verbose }) } From 0b3a49cc7ad134b702f468bd241f105f7a8c71ac Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 2 Mar 2024 16:41:50 +0100 Subject: [PATCH 61/75] getMergedConfig comment format --- packages/vite/src/getMergedConfig.ts | 43 +++++++++++++++++----------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/packages/vite/src/getMergedConfig.ts b/packages/vite/src/getMergedConfig.ts index 891cb539ef8f..642801c675db 100644 --- a/packages/vite/src/getMergedConfig.ts +++ b/packages/vite/src/getMergedConfig.ts @@ -10,10 +10,11 @@ import { getConfig, getPaths } from '@redwoodjs/project-config' import { getEnvVarDefinitions } from './envVarDefinitions' /** + * This function will merge in the default Redwood Vite config passed into the + * build function (or in Vite.config.xxx) * - * This function will merge in the default Redwood Vite config passed into the build function (or in Vite.config.xxx) - * - * Note that returning plugins in this function will have no effect on the build + * Note that returning plugins in this function will have no effect on the + * build */ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { return (userConfig: UserConfig, env: ConfigEnv): UserConfig => { @@ -46,8 +47,8 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { publicDir: path.join(rwPaths.web.base, 'public'), define: getEnvVarDefinitions(), css: { - // @NOTE config path is relative to where vite.config.js is if you use relative path - // postcss: './config/', + // @NOTE config path is relative to where vite.config.js is if you use + // a relative path postcss: rwPaths.web.config, }, server: { @@ -61,8 +62,9 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { // Remove the `.redwood/functions` part, but leave the `/graphql` rewrite: (path) => path.replace(rwConfig.web.apiUrl, ''), configure: (proxy) => { - // @MARK: this is a hack to prevent showing confusing proxy errors on startup - // because Vite launches so much faster than the API server. + // @MARK: this is a hack to prevent showing confusing proxy + // errors on startup because Vite launches so much faster than + // the API server. let waitingForApiServer = true // Wait for 2.5s, then restore regular proxy error logging @@ -82,7 +84,8 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { errors: [ { message: - 'The RedwoodJS API server is not available or is currently reloading. Please refresh.', + 'The RedwoodJS API server is not available or is ' + + 'currently reloading. Please refresh.', }, ], } @@ -107,7 +110,8 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { : rwPaths.web.dist, emptyOutDir: true, manifest: !env.ssrBuild ? 'client-build-manifest.json' : undefined, - sourcemap: !env.ssrBuild && rwConfig.web.sourceMap, // Note that this can be boolean or 'inline' + // Note that sourcemap can be boolean or 'inline' + sourcemap: !env.ssrBuild && rwConfig.web.sourceMap, rollupOptions: { input: getRollupInput(!!env.ssrBuild), }, @@ -116,12 +120,14 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { // because rsc builds want false, client and server build wants true optimizeDeps: { esbuildOptions: { - // @MARK this is because JS projects in Redwood don't have .jsx extensions + // @MARK this is because JS projects in Redwood don't have .jsx + // extensions loader: { '.js': 'jsx', }, // Node.js global to browser globalThis - // @MARK unsure why we need this, but required for DevFatalErrorPage atleast + // @MARK unsure why we need this, but required for DevFatalErrorPage + // at least define: { global: 'globalThis', }, @@ -132,13 +138,14 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { } } /** - * * This function configures how vite (actually Rollup) will bundle. * - * By default, the entry point is the index.html file - even if you don't specify it in RollupOptions + * By default, the entry point is the index.html file - even if you don't + * specify it in RollupOptions * - * With streaming SSR, out entrypoint is different - either entry.client.tsx or entry.server.tsx - * and the html file is not used at all, because it is defined in Document.tsx + * With streaming SSR, out entrypoint is different - either entry.client.tsx + * or entry.server.tsx and the html file is not used at all, because it is + * defined in Document.tsx * * @param ssr {boolean} Whether to return the SSR inputs or not * @returns Rollup input Options @@ -147,12 +154,14 @@ function getRollupInput(ssr: boolean): InputOption | undefined { const rwConfig = getConfig() const rwPaths = getPaths() - // @NOTE once streaming ssr is out of experimental, this will become the default + // @NOTE once streaming ssr is out of experimental, this will become the + // default if (rwConfig.experimental.streamingSsr.enabled) { return ssr ? { 'entry.server': rwPaths.web.entryServer as string, - Document: rwPaths.web.document, // We need the document for React's fallback + // We need the document for React's fallback + Document: rwPaths.web.document, } : (rwPaths.web.entryClient as string) } From 18b6fd7792e61695fab44fd3fd63e8513fa9368e Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 2 Mar 2024 16:42:11 +0100 Subject: [PATCH 62/75] More comment formatting --- packages/vite/src/getMergedConfig.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/vite/src/getMergedConfig.ts b/packages/vite/src/getMergedConfig.ts index 642801c675db..d8e1fc25bfcc 100644 --- a/packages/vite/src/getMergedConfig.ts +++ b/packages/vite/src/getMergedConfig.ts @@ -137,6 +137,7 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { return mergeConfig(defaultRwViteConfig, userConfig) } } + /** * This function configures how vite (actually Rollup) will bundle. * From d5eb5d95985cb7b6672b8cd864011e96ddff2c5e Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 2 Mar 2024 16:59:53 +0100 Subject: [PATCH 63/75] paths.ts: Move helper to esm section --- packages/project-config/src/paths.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/project-config/src/paths.ts b/packages/project-config/src/paths.ts index 856b48d684af..407483e729c7 100644 --- a/packages/project-config/src/paths.ts +++ b/packages/project-config/src/paths.ts @@ -166,15 +166,6 @@ export const resolveFile = ( return null } -/** Default to JS path, but if MJS exists, use it instead **/ -const mjsOrJs = (filePath: string) => { - const mjsPath = resolveFile(filePath, ['.mjs']) - if (mjsPath) { - return mjsPath - } - return filePath + '.js' -} - /** * Path constants that are relevant to a Redwood project. */ @@ -442,3 +433,9 @@ export function projectIsEsm() { return true } + +/** Default to JS path, but if MJS exists, use it instead */ +const mjsOrJs = (filePath: string) => { + const mjsPath = resolveFile(filePath, ['.mjs']) + return mjsPath ? mjsPath : filePath + '.js' +} From 22d4065fb9947fc3615d3f2b260e5dec46a6f6a6 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 3 Mar 2024 22:26:43 +0100 Subject: [PATCH 64/75] Remove duplicated comment (exists further down) --- packages/vite/src/rsc/rscBuildRwEnvVars.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/vite/src/rsc/rscBuildRwEnvVars.ts b/packages/vite/src/rsc/rscBuildRwEnvVars.ts index 0bf55f369d75..838ae8ec646d 100644 --- a/packages/vite/src/rsc/rscBuildRwEnvVars.ts +++ b/packages/vite/src/rsc/rscBuildRwEnvVars.ts @@ -10,7 +10,6 @@ import { getPaths } from '@redwoodjs/project-config' * The import of entries.js that we're adding this to is handled by the * RSC worker we've got set up */ -// TODO(RSC_DC): See if we can inject into bundle as well export async function rscBuildRwEnvVars() { console.log('\n') console.log('6. rscBuildRwEnvVars') From 14b1f47742547fede552e009efe7cb1e04e871d0 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 3 Mar 2024 22:28:28 +0100 Subject: [PATCH 65/75] Only comment about swc in one place --- packages/vite/src/rsc/rscVitePlugins.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/vite/src/rsc/rscVitePlugins.ts b/packages/vite/src/rsc/rscVitePlugins.ts index a09437064c02..a9e4323938f2 100644 --- a/packages/vite/src/rsc/rscVitePlugins.ts +++ b/packages/vite/src/rsc/rscVitePlugins.ts @@ -156,7 +156,6 @@ export function rscAnalyzePlugin( transform(code, id) { const ext = path.extname(id) if (['.ts', '.tsx', '.js', '.jsx'].includes(ext)) { - // @MARK: We're using swc here, that's cool but another dependency! const mod = swc.parseSync(code, { syntax: ext === '.ts' || ext === '.tsx' ? 'typescript' : 'ecmascript', tsx: ext === '.tsx', From e9beff0c7859c28a2e08e0296b7a58ce6e4bdde3 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 3 Mar 2024 23:01:07 +0100 Subject: [PATCH 66/75] Remove handled TODO --- packages/vite/src/streaming/createReactStreamingHandler.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/vite/src/streaming/createReactStreamingHandler.ts b/packages/vite/src/streaming/createReactStreamingHandler.ts index 73a7ed889e30..b3fbcf73ce1e 100644 --- a/packages/vite/src/streaming/createReactStreamingHandler.ts +++ b/packages/vite/src/streaming/createReactStreamingHandler.ts @@ -41,7 +41,6 @@ export const createReactStreamingHandler = async ( let fallbackDocumentImport: any if (isProd) { - // TODO (RSC) Consolidate paths, so we can have the same code for SSR and RSC entryServerImport = await import(makeFilePath(rwPaths.web.distEntryServer)) fallbackDocumentImport = await import( makeFilePath(rwPaths.web.distDocumentServer) From 71851bbd62e87cd0d823ebcdde8c802ee4a45ae5 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 4 Mar 2024 09:37:29 +0100 Subject: [PATCH 67/75] Remove redundant cwd check --- packages/vite/src/rsc/rscBuildClient.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index bad88fd56d2b..a37693e697a5 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -57,12 +57,6 @@ export async function rscBuildClient(clientEntryFiles: Record) { }, }) - if (process.cwd() !== rwPaths.web.base) { - throw new Error( - 'Looks like you are running the command from the wrong dir, this can lead to unintended consequences on CSS processing' - ) - } - if (!('output' in clientBuildOutput)) { throw new Error('Unexpected vite client build output') } From 72f56a0f4276b254c3d0c7ebc2252dc0f1c28391 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 4 Mar 2024 09:44:26 +0100 Subject: [PATCH 68/75] Comment on ensureProcessDirWeb() --- packages/vite/src/rsc/rscBuildClient.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/vite/src/rsc/rscBuildClient.ts b/packages/vite/src/rsc/rscBuildClient.ts index a37693e697a5..230a25c64bab 100644 --- a/packages/vite/src/rsc/rscBuildClient.ts +++ b/packages/vite/src/rsc/rscBuildClient.ts @@ -17,6 +17,11 @@ export async function rscBuildClient(clientEntryFiles: Record) { const rwPaths = getPaths() + // Safe-guard for the future, if someone tries to include this function in + // code that gets executed by running `vite build` or some other bin from the + // cli + // Running the web build in the wrong working directory can lead to + // unintended consequences on CSS processing ensureProcessDirWeb() if (!rwPaths.web.entryClient) { From 99ca8dc0b5ba0046fd975ff4c8fda00e341c9e21 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Mon, 4 Mar 2024 16:41:31 +0700 Subject: [PATCH 69/75] Hack: Skip calling renderFromRscServer during SSR --- .vscode/settings.json | 16 +++++++++++++++- packages/vite/src/client.ts | 6 ++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 89d70d0ff17e..462e796938e0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,7 +8,21 @@ "workbench.colorCustomizations": { "statusBar.background": "#b85833", "statusBarItem.hoverBackground": "#ce7350", - "statusBar.foreground": "#e7e7e7" + "statusBar.foreground": "#e7e7e7", + "activityBar.activeBackground": "#ce7350", + "activityBar.background": "#ce7350", + "activityBar.foreground": "#15202b", + "activityBar.inactiveForeground": "#15202b99", + "activityBarBadge.background": "#82dc9b", + "activityBarBadge.foreground": "#15202b", + "commandCenter.border": "#e7e7e799", + "sash.hoverBorder": "#ce7350", + "statusBarItem.remoteBackground": "#b85833", + "statusBarItem.remoteForeground": "#e7e7e7", + "titleBar.activeBackground": "#b85833", + "titleBar.activeForeground": "#e7e7e7", + "titleBar.inactiveBackground": "#b8583399", + "titleBar.inactiveForeground": "#e7e7e799" }, "search.exclude": { "**/dist": true, diff --git a/packages/vite/src/client.ts b/packages/vite/src/client.ts index c91b26ac6e9a..8d98276040af 100644 --- a/packages/vite/src/client.ts +++ b/packages/vite/src/client.ts @@ -22,6 +22,12 @@ const BASE_PATH = '/rw-rsc/' export function renderFromRscServer(rscId: string) { console.log('serve rscId', rscId) + // Temporarily skip rendering this component during SSR + // I don't know what we actually should do during SSR yet + if (typeof window === 'undefined') { + return null + } + type SetRerender = ( rerender: (next: [ReactElement, string]) => void ) => () => void From ab9713e1a43872844c54a78342ccfa6e4c2bae2a Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Mon, 4 Mar 2024 16:54:48 +0700 Subject: [PATCH 70/75] Undo settings.json Undo path aliasing in Vite settings --- .vscode/settings.json | 16 +--------------- packages/vite/src/getMergedConfig.ts | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 462e796938e0..89d70d0ff17e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,21 +8,7 @@ "workbench.colorCustomizations": { "statusBar.background": "#b85833", "statusBarItem.hoverBackground": "#ce7350", - "statusBar.foreground": "#e7e7e7", - "activityBar.activeBackground": "#ce7350", - "activityBar.background": "#ce7350", - "activityBar.foreground": "#15202b", - "activityBar.inactiveForeground": "#15202b99", - "activityBarBadge.background": "#82dc9b", - "activityBarBadge.foreground": "#15202b", - "commandCenter.border": "#e7e7e799", - "sash.hoverBorder": "#ce7350", - "statusBarItem.remoteBackground": "#b85833", - "statusBarItem.remoteForeground": "#e7e7e7", - "titleBar.activeBackground": "#b85833", - "titleBar.activeForeground": "#e7e7e7", - "titleBar.inactiveBackground": "#b8583399", - "titleBar.inactiveForeground": "#e7e7e799" + "statusBar.foreground": "#e7e7e7" }, "search.exclude": { "**/dist": true, diff --git a/packages/vite/src/getMergedConfig.ts b/packages/vite/src/getMergedConfig.ts index d8e1fc25bfcc..00cf6f574621 100644 --- a/packages/vite/src/getMergedConfig.ts +++ b/packages/vite/src/getMergedConfig.ts @@ -35,14 +35,17 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { const defaultRwViteConfig: UserConfig = { root: rwPaths.web.src, - resolve: { - alias: [ - { - find: 'src', - replacement: rwPaths.web.src, - }, - ], - }, + // @MARK: when we have these aliases, the warnings from the FE server go away + // BUT, if you have imports like this: import RandomNumberServerCell from 'src/components/RandomNumberServerCell/RandomNumberServerCell' + // they start failing + // resolve: { + // alias: [ + // { + // find: 'src', + // replacement: rwPaths.web.src, + // }, + // ], + // }, envPrefix: 'REDWOOD_ENV_', publicDir: path.join(rwPaths.web.base, 'public'), define: getEnvVarDefinitions(), From f6f0b4daabbf24bbc86aa8208a304fcf88ae3f11 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 4 Mar 2024 11:10:19 +0100 Subject: [PATCH 71/75] runFeServer: Always look for client-build-manifest in distClient --- packages/vite/src/runFeServer.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/vite/src/runFeServer.ts b/packages/vite/src/runFeServer.ts index 39d363568f80..486565eada90 100644 --- a/packages/vite/src/runFeServer.ts +++ b/packages/vite/src/runFeServer.ts @@ -67,10 +67,7 @@ export async function runFeServer() { ).default const clientBuildManifestUrl = url.pathToFileURL( - path.join( - rscEnabled ? rwPaths.web.distClient : rwPaths.web.dist, - 'client-build-manifest.json' - ) + path.join(rwPaths.web.distClient, 'client-build-manifest.json') ).href const clientBuildManifest: ViteBuildManifest = ( await import(clientBuildManifestUrl, { with: { type: 'json' } }) From ae5c9987ad3b6e71333d572dc0adc84de1b55ee3 Mon Sep 17 00:00:00 2001 From: Daniel Choudhury Date: Mon, 4 Mar 2024 17:34:16 +0700 Subject: [PATCH 72/75] Update comments --- packages/cli/src/index.js | 3 ++- packages/vite/src/index.ts | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/index.js b/packages/cli/src/index.js index 1d284d733f26..bd76df4c9af3 100644 --- a/packages/cli/src/index.js +++ b/packages/cli/src/index.js @@ -128,7 +128,8 @@ async function main() { recordTelemetryAttributes({ command: '--help' }) } - // @TODO: BIG RED BOX > FIX MEEEEEEE + // @FIXME: BIG RED BOX on exiting feServer + // I think yargs OR the RW cli is not passing SigInt to the child process try { // Run the command via yargs await runYargs() diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 026766d6fb31..010e09563b2d 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -148,10 +148,6 @@ export default function redwoodPluginVite(): PluginOption[] { babel: { ...getWebSideDefaultBabelConfig({ forVite: true, - // @MARK: Potential issue in the future. We don't want to set react plugins in each build file - // because we should be able to trigger the builds from the vite CLI directly. - // Right now, we can compile to SPA+SSR OR RSCClient+SSR+RSC - once this experimental flag is removed - // we will need to make sure SPA+SSR is still possible forRscClient: rwConfig.experimental.rsc?.enabled, }), }, From f8ec6732d9d11b83fcc0be307036e67111c17156 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 4 Mar 2024 16:24:12 +0100 Subject: [PATCH 73/75] node:path --- packages/vite/src/lib/getMergedConfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite/src/lib/getMergedConfig.ts b/packages/vite/src/lib/getMergedConfig.ts index 00cf6f574621..6753decd2480 100644 --- a/packages/vite/src/lib/getMergedConfig.ts +++ b/packages/vite/src/lib/getMergedConfig.ts @@ -1,4 +1,4 @@ -import path from 'path' +import path from 'node:path' import type { InputOption } from 'rollup' import type { ConfigEnv, UserConfig } from 'vite' From 7935477ce8bd12d9a6b84818752ccb97a274e234 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 4 Mar 2024 16:26:47 +0100 Subject: [PATCH 74/75] streamingSsrEnabled rscEnabled --- packages/vite/src/lib/getMergedConfig.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vite/src/lib/getMergedConfig.ts b/packages/vite/src/lib/getMergedConfig.ts index 6753decd2480..710d8a61faaf 100644 --- a/packages/vite/src/lib/getMergedConfig.ts +++ b/packages/vite/src/lib/getMergedConfig.ts @@ -22,9 +22,9 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { apiHost ??= rwConfig.api.host apiHost ??= process.env.NODE_ENV === 'production' ? '0.0.0.0' : '[::]' - const streamingBuild = rwConfig.experimental.streamingSsr?.enabled + const streamingSsrEnabled = rwConfig.experimental.streamingSsr?.enabled // @MARK: note that most RSC settings sit in their individual build functions - const rscBuild = rwConfig.experimental.rsc?.enabled + const rscEnabled = rwConfig.experimental.rsc?.enabled let apiPort if (process.env.REDWOOD_API_PORT) { @@ -108,7 +108,7 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { // NOTE this gets overridden when build gets called anyway! outDir: // @MARK: For RSC and Streaming, we build to dist/client directory - streamingBuild || rscBuild + streamingSsrEnabled || rscEnabled ? rwPaths.web.distClient : rwPaths.web.dist, emptyOutDir: true, From 1be4ef156a856d3f2f43d055e104e8344731701b Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 4 Mar 2024 16:36:41 +0100 Subject: [PATCH 75/75] chore(style): getDefaultViteConfig source format --- packages/vite/src/lib/getDefaultViteConfig.ts | 54 ++++++++++++------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/packages/vite/src/lib/getDefaultViteConfig.ts b/packages/vite/src/lib/getDefaultViteConfig.ts index 7f7fe77cb4b4..828055c901d6 100644 --- a/packages/vite/src/lib/getDefaultViteConfig.ts +++ b/packages/vite/src/lib/getDefaultViteConfig.ts @@ -14,6 +14,8 @@ export function getDefaultViteConfig(rwConfig: Config, rwPaths: Paths) { apiHost ??= rwConfig.api.host apiHost ??= process.env.NODE_ENV === 'production' ? '0.0.0.0' : '[::]' + const rscEnabled = rwConfig.experimental.rsc?.enabled + let apiPort if (process.env.REDWOOD_API_PORT) { apiPort = parseInt(process.env.REDWOOD_API_PORT) @@ -21,14 +23,22 @@ export function getDefaultViteConfig(rwConfig: Config, rwPaths: Paths) { apiPort = rwConfig.api.port } - return { + const defaultRwViteConfig: UserConfig = { root: rwPaths.web.src, - // Disabling for now, let babel handle this for consistency + // @MARK: when we have these aliases, the warnings from the FE server go + // away BUT, if you have imports like this: + // ``` + // import RandomNumberServerCell from + // 'src/components/RandomNumberServerCell/RandomNumberServerCell' + // ``` + // they start failing (can't have the double + // `/RandomNumberServerCell/RandomNumberServerCell` at the end) + // // resolve: { // alias: [ // { // find: 'src', - // replacement: redwoodPaths.web.src, + // replacement: rwPaths.web.src, // }, // ], // }, @@ -36,8 +46,8 @@ export function getDefaultViteConfig(rwConfig: Config, rwPaths: Paths) { publicDir: path.join(rwPaths.web.base, 'public'), define: getEnvVarDefinitions(), css: { - // @NOTE config path is relative to where vite.config.js is if you use relative path - // postcss: './config/', + // @NOTE config path is relative to where vite.config.js is if you use + // relative path postcss: rwPaths.web.config, }, server: { @@ -51,8 +61,9 @@ export function getDefaultViteConfig(rwConfig: Config, rwPaths: Paths) { // Remove the `.redwood/functions` part, but leave the `/graphql` rewrite: (path) => path.replace(rwConfig.web.apiUrl, ''), configure: (proxy) => { - // @MARK: this is a hack to prevent showing confusing proxy errors on startup - // because Vite launches so much faster than the API server. + // @MARK: this is a hack to prevent showing confusing proxy + // errors on startup because Vite launches so much faster than + // the API server. let waitingForApiServer = true // Wait for 2.5s, then restore regular proxy error logging @@ -72,7 +83,8 @@ export function getDefaultViteConfig(rwConfig: Config, rwPaths: Paths) { errors: [ { message: - 'The RedwoodJS API server is not available or is currently reloading. Please refresh.', + 'The RedwoodJS API server is not available or is ' + + 'currently reloading. Please refresh.', }, ], } @@ -92,19 +104,19 @@ export function getDefaultViteConfig(rwConfig: Config, rwPaths: Paths) { outDir: options.build?.outDir || rwPaths.web.dist, emptyOutDir: true, manifest: !env.ssrBuild ? 'client-build-manifest.json' : undefined, - sourcemap: !env.ssrBuild && rwConfig.web.sourceMap, // Note that this can be boolean or 'inline' + // Note that sourcemap can be boolean or 'inline' + sourcemap: !env.ssrBuild && rwConfig.web.sourceMap, rollupOptions: { input: getRollupInput(!!env.ssrBuild), }, }, legacy: { - buildSsrCjsExternalHeuristics: rwConfig.experimental?.rsc?.enabled - ? false - : env.ssrBuild, + buildSsrCjsExternalHeuristics: rscEnabled ? false : env.ssrBuild, }, optimizeDeps: { esbuildOptions: { - // @MARK this is because JS projects in Redwood don't have .jsx extensions + // @MARK this is because JS projects in Redwood don't have .jsx + // extensions loader: { '.js': 'jsx', }, @@ -117,16 +129,20 @@ export function getDefaultViteConfig(rwConfig: Config, rwPaths: Paths) { }, }, } + + return defaultRwViteConfig } } /** * This function configures how vite (actually Rollup) will bundle. * - * By default, the entry point is the index.html file - even if you don't specify it in RollupOptions + * By default, the entry point is the index.html file - even if you don't + * specify it in RollupOptions * - * With streaming SSR, out entrypoint is different - either entry.client.tsx or entry.server.tsx - * and the html file is not used at all, because it is defined in Document.tsx + * With streaming SSR, out entrypoint is different - either entry.client.tsx or + * entry.server.tsx and the html file is not used at all, because it is defined + * in Document.tsx * * @param ssr {boolean} Whether to return the SSR inputs or not * @returns Rollup input Options @@ -135,12 +151,14 @@ function getRollupInput(ssr: boolean): InputOption | undefined { const rwConfig = getConfig() const rwPaths = getPaths() - // @NOTE once streaming ssr is out of experimental, this will become the default + // @NOTE once streaming ssr is out of experimental, this will become the + // default if (rwConfig.experimental.streamingSsr.enabled) { return ssr ? { 'entry.server': rwPaths.web.entryServer as string, - Document: rwPaths.web.document, // We need the document for React's fallback + // We need the document for React's fallback + Document: rwPaths.web.document, } : (rwPaths.web.entryClient as string) }