Skip to content

Commit

Permalink
RSC: Generate a route manifest (redwoodjs#9592)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tobbe authored Nov 30, 2023
1 parent 195b17a commit 99685ee
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 80 deletions.
8 changes: 6 additions & 2 deletions packages/vite/src/buildFeServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
60 changes: 3 additions & 57 deletions packages/vite/src/buildRscFeServer.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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<RWRouteManifest>((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)
}
52 changes: 52 additions & 0 deletions packages/vite/src/rsc/rscBuildRouteManifest.ts
Original file line number Diff line number Diff line change
@@ -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<RWRouteManifest>((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))
}
42 changes: 21 additions & 21 deletions packages/vite/src/runFeServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 99685ee

Please sign in to comment.