Skip to content

Commit

Permalink
Remove index.html requirement
Browse files Browse the repository at this point in the history
Move webpack shims into streaming server (and remove vite plugin)
  • Loading branch information
dac09 committed Feb 19, 2024
1 parent fcd596f commit 5e767b2
Show file tree
Hide file tree
Showing 11 changed files with 44 additions and 56 deletions.
2 changes: 1 addition & 1 deletion packages/vite/src/buildFeServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 4 additions & 1 deletion packages/vite/src/buildRouteManifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 4 additions & 3 deletions packages/vite/src/buildRscFeServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { rscBuildRwEnvVars } from './rsc/rscBuildRwEnvVars'

interface Args {
viteConfigPath: string
webHtml: string
entryClient: string
entries: string
webDist: string
webDistServer: string
Expand All @@ -16,7 +16,7 @@ interface Args {

export const buildRscClientAndWorker = async ({
viteConfigPath,
webHtml,
entryClient,
entries,
webDist,
webDistServerEntries,
Expand All @@ -28,7 +28,7 @@ export const buildRscClientAndWorker = async ({

// Generate the client bundle
const clientBuildOutput = await rscBuildClient(
webHtml,
entryClient,
webDist,
clientEntryFiles
)
Expand All @@ -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,
Expand Down
8 changes: 2 additions & 6 deletions packages/vite/src/rsc/rscBuildAnalyze.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>()
Expand All @@ -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)
Expand All @@ -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,
},
},
Expand Down
14 changes: 6 additions & 8 deletions packages/vite/src/rsc/rscBuildClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>
) {
Expand All @@ -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'),
Expand All @@ -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',
Expand All @@ -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',
Expand Down
8 changes: 6 additions & 2 deletions packages/vite/src/rsc/rscBuildClientEntriesFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<ReturnType<typeof rscBuildClient>>,
serverBuildOutput: Awaited<ReturnType<typeof rscBuildForWorker>>,
Expand All @@ -18,6 +19,9 @@ export function rscBuildClientEntriesMappings(
const clientEntries: Record<string, string> = {}
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`
Expand Down
28 changes: 1 addition & 27 deletions packages/vite/src/rsc/rscVitePlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down
9 changes: 9 additions & 0 deletions packages/vite/src/rsc/rscWebpackShims.ts
Original file line number Diff line number Diff line change
@@ -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`
13 changes: 6 additions & 7 deletions packages/vite/src/runFeServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions packages/vite/src/streaming/createReactStreamingHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion packages/vite/src/streaming/streamHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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,
}
Expand Down

0 comments on commit 5e767b2

Please sign in to comment.