diff --git a/packages/vite/src/buildFeServer.ts b/packages/vite/src/buildFeServer.ts index 80c04f906234..bdff87e4812f 100644 --- a/packages/vite/src/buildFeServer.ts +++ b/packages/vite/src/buildFeServer.ts @@ -149,7 +149,7 @@ export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { acc[route.pathDefinition] = { name: route.name, bundle: route.relativeFilePath - ? clientBuildManifest[route.relativeFilePath]?.file + ? clientBuildManifest[route.relativeFilePath]?.file ?? null : null, matchRegexString: route.matchRegexString, // @NOTE this is the path definition, not the actual path @@ -165,10 +165,14 @@ export const buildFeServer = async ({ verbose, webDir }: BuildOptions = {}) => { : null, renderMode: route.renderMode, } + return acc }, {}) - await fs.writeFile(rwPaths.web.routeManifest, JSON.stringify(routeManifest)) + await fs.writeFile( + rwPaths.web.routeManifest, + JSON.stringify(routeManifest, null, 2) + ) } // TODO (STREAMING) Hacky work around because when you don't have a App.routeHook, esbuild doesn't create diff --git a/packages/vite/src/buildRscFeServer.ts b/packages/vite/src/buildRscFeServer.ts index 2cd3db4c8dfc..1fdd6e191ecc 100644 --- a/packages/vite/src/buildRscFeServer.ts +++ b/packages/vite/src/buildRscFeServer.ts @@ -1,16 +1,9 @@ -import fs from 'fs/promises' -import path from 'path' - -import type { Manifest as ViteBuildManifest } from 'vite' - -import type { RouteSpec } from '@redwoodjs/internal/dist/routes' - import { rscBuildAnalyze } from './rsc/rscBuildAnalyze' import { rscBuildClient } from './rsc/rscBuildClient' import { rscBuildClientEntriesMappings } from './rsc/rscBuildClientEntriesFile' import { rscBuildCopyCssAssets } from './rsc/rscBuildCopyCssAssets' +import { rscBuildRouteManifest } from './rsc/rscBuildRouteManifest' import { rscBuildServer } from './rsc/rscBuildServer' -import type { RWRouteManifest } from './types' interface Args { viteConfigPath: string @@ -65,53 +58,6 @@ export const buildRscFeServer = async ({ webDistServerEntries ) - // TODO When https://github.com/tc39/proposal-import-attributes and - // https://github.com/microsoft/TypeScript/issues/53656 have both landed we - // should try to do this instead: - // const clientBuildManifest: ViteBuildManifest = await import( - // path.join(getPaths().web.dist, 'client-build-manifest.json'), - // { with: { type: 'json' } } - // ) - // NOTES: - // * There's a related babel plugin here - // https://babeljs.io/docs/babel-plugin-syntax-import-attributes - // * Included in `preset-env` if you set `shippedProposals: true` - // * We had this before, but with `assert` instead of `with`. We really - // should be using `with`. See motivation in issues linked above. - // * With `assert` and `@babel/plugin-syntax-import-assertions` the - // code compiled and ran properly, but Jest tests failed, complaining - // about the syntax. - const manifestPath = path.join(webDist, 'client-build-manifest.json') - const manifestStr = await fs.readFile(manifestPath, 'utf-8') - const clientBuildManifest: ViteBuildManifest = JSON.parse(manifestStr) - - // TODO (RSC) We don't have support for a router yet, so skip all routes - const routesList = [] as RouteSpec[] // getProjectRoutes() - - // This is all a no-op for now - const routeManifest = routesList.reduce((acc, route) => { - acc[route.pathDefinition] = { - name: route.name, - bundle: route.relativeFilePath - ? clientBuildManifest[route.relativeFilePath].file - : null, - matchRegexString: route.matchRegexString, - // NOTE this is the path definition, not the actual path - // E.g. /blog/post/{id:Int} - pathDefinition: route.pathDefinition, - hasParams: route.hasParams, - routeHooks: null, - redirect: route.redirect - ? { - to: route.redirect?.to, - permanent: false, - } - : null, - renderMode: route.renderMode, - } - - return acc - }, {}) - - await fs.writeFile(webRouteManifest, JSON.stringify(routeManifest)) + // Write a route manifest + await rscBuildRouteManifest(webRouteManifest) } diff --git a/packages/vite/src/rsc/rscBuildRouteManifest.ts b/packages/vite/src/rsc/rscBuildRouteManifest.ts new file mode 100644 index 000000000000..db2e82f57a0e --- /dev/null +++ b/packages/vite/src/rsc/rscBuildRouteManifest.ts @@ -0,0 +1,52 @@ +import fs from 'fs/promises' +import path from 'path' + +import type { Manifest as ViteBuildManifest } from 'vite' + +import { getProjectRoutes } from '@redwoodjs/internal/dist/routes' +import { getPaths } from '@redwoodjs/project-config' + +import type { RWRouteManifest } from '../types' + +/** + * RSC build. Step 6. + * Generate a route manifest file for the web server side. + */ +export async function rscBuildRouteManifest(webRouteManifest: string) { + const manifestPath = path.join( + getPaths().web.dist, + 'client-build-manifest.json' + ) + const buildManifestStr = await fs.readFile(manifestPath, 'utf-8') + const clientBuildManifest: ViteBuildManifest = JSON.parse(buildManifestStr) + + const routesList = getProjectRoutes() + + const routeManifest = routesList.reduce((acc, route) => { + acc[route.pathDefinition] = { + name: route.name, + bundle: route.relativeFilePath + ? clientBuildManifest[route.relativeFilePath]?.file ?? null + : null, + matchRegexString: route.matchRegexString, + // NOTE this is the path definition, not the actual path + // E.g. /blog/post/{id:Int} + pathDefinition: route.pathDefinition, + hasParams: route.hasParams, + routeHooks: null, + redirect: route.redirect + ? { + to: route.redirect?.to, + permanent: false, + } + : null, + renderMode: route.renderMode, + } + + return acc + }, {}) + + console.log('routeManifest', JSON.stringify(routeManifest, null, 2)) + + return fs.writeFile(webRouteManifest, JSON.stringify(routeManifest, null, 2)) +} diff --git a/packages/vite/src/runFeServer.ts b/packages/vite/src/runFeServer.ts index 37655cf1da47..6810fa9212e5 100644 --- a/packages/vite/src/runFeServer.ts +++ b/packages/vite/src/runFeServer.ts @@ -127,28 +127,28 @@ export async function runFeServer() { const getStylesheetLinks = () => indexEntry.css || [] const clientEntry = '/' + indexEntry.file - // `routeManifest` is empty for RSC builds for now, so we're not doing SSR - // when we have RSC experimental support enabled - for (const route of Object.values(routeManifest)) { - const routeHandler = await createReactStreamingHandler({ - route, - clientEntryPath: clientEntry, - getStylesheetLinks, - }) - - // if it is a 404, register it at the end somehow. - if (!route.matchRegexString) { - continue + if (!rwConfig.experimental?.rsc?.enabled) { + for (const route of Object.values(routeManifest)) { + const routeHandler = await createReactStreamingHandler({ + route, + clientEntryPath: clientEntry, + getStylesheetLinks, + }) + + // if it is a 404, register it at the end somehow. + if (!route.matchRegexString) { + continue + } + + // @TODO: we don't need regexes here + // Param matching, etc. all handled within the route handler now + const expressPathDef = route.hasParams + ? route.matchRegexString + : route.pathDefinition + + // Wrap with whatg/server adapter. Express handler -> Fetch API handler + app.get(expressPathDef, createServerAdapter(routeHandler)) } - - // @TODO: we don't need regexes here - // Param matching, etc. all handled within the route handler now - const expressPathDef = route.hasParams - ? route.matchRegexString - : 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