11import fs from 'fs/promises'
22import path from 'path'
33
4- import react from '@vitejs/plugin-react'
5- import { build as viteBuild } from 'vite'
64import type { Manifest as ViteBuildManifest } from 'vite'
75
86import type { RouteSpec } from '@redwoodjs/internal/dist/routes'
97
10- import { onWarn } from './lib/onWarn'
11- import { rscBuild } from './rscBuild'
8+ import { rscBuildAnalyze } from './rsc/rscBuildAnalyze'
9+ import { rscBuildClient } from './rsc/rscBuildClient'
10+ import { rscBuildClientEntriesMappings } from './rsc/rscBuildClientEntriesFile'
11+ import { rscBuildCopyCssAssets } from './rsc/rscBuildCopyCssAssets'
12+ import { rscBuildServer } from './rsc/rscBuildServer'
1213import type { RWRouteManifest } from './types'
13- import { serverBuild } from './waku-lib/build-server'
14- import { rscIndexPlugin } from './waku-lib/vite-plugin-rsc'
1514
1615interface Args {
1716 viteConfigPath : string
@@ -20,7 +19,7 @@ interface Args {
2019 entries : string
2120 webDist : string
2221 webDistServer : string
23- webDistEntries : string
22+ webDistServerEntries : string
2423 webRouteManifest : string
2524}
2625
@@ -31,189 +30,41 @@ export const buildRscFeServer = async ({
3130 entries,
3231 webDist,
3332 webDistServer,
34- webDistEntries ,
33+ webDistServerEntries ,
3534 webRouteManifest,
3635} : Args ) => {
37- // Step 1: Analyze all files and generate a list of RSCs and RSFs
38- const { clientEntryFiles, serverEntryFiles } = await rscBuild ( viteConfigPath )
39-
40- // Step 2: Generate the client bundle
41- const clientBuildOutput = await viteBuild ( {
42- // configFile: viteConfigPath,
43- root : webSrc ,
44- plugins : [ react ( ) , rscIndexPlugin ( ) ] ,
45- build : {
46- outDir : webDist ,
47- emptyOutDir : true , // Needed because `outDir` is not inside `root`
48- // TODO (RSC) Enable this when we switch to a server-first approach
49- // emptyOutDir: false, // Already done when building server
50- rollupOptions : {
51- onwarn : onWarn ,
52- input : {
53- main : webHtml ,
54- ...clientEntryFiles ,
55- } ,
56- preserveEntrySignatures : 'exports-only' ,
57- output : {
58- // This is not ideal. See
59- // https://rollupjs.org/faqs/#why-do-additional-imports-turn-up-in-my-entry-chunks-when-code-splitting
60- // But we need it to prevent `import 'client-only'` from being
61- // hoisted into App.tsx
62- // TODO (RSC): Fix when https://github.com/rollup/rollup/issues/5235
63- // is resolved
64- hoistTransitiveImports : false ,
65- } ,
66- } ,
67- manifest : 'client-build-manifest.json' ,
68- } ,
69- esbuild : {
70- logLevel : 'debug' ,
71- } ,
72- } )
36+ // Analyze all files and generate a list of RSCs and RSFs
37+ const { clientEntryFiles, serverEntryFiles } = await rscBuildAnalyze (
38+ viteConfigPath
39+ )
7340
74- if ( ! ( 'output' in clientBuildOutput ) ) {
75- throw new Error ( 'Unexpected vite client build output' )
76- }
41+ // Generate the client bundle
42+ const clientBuildOutput = await rscBuildClient (
43+ webSrc ,
44+ webHtml ,
45+ webDist ,
46+ clientEntryFiles
47+ )
7748
78- // Step 3: Generate the server output
79- const serverBuildOutput = await serverBuild (
49+ // Generate the server output
50+ const serverBuildOutput = await rscBuildServer (
8051 entries ,
8152 clientEntryFiles ,
8253 serverEntryFiles ,
8354 { }
8455 )
8556
86- // TODO (RSC) Some css is now duplicated in two files (i.e. for client
87- // components). Probably don't want that.
88- // Also not sure if this works on "soft" rerenders (i.e. not a full page
89- // load)
90- await Promise . all (
91- serverBuildOutput . output
92- . filter ( ( item ) => {
93- return item . type === 'asset' && item . fileName . endsWith ( '.css' )
94- } )
95- . map ( ( cssAsset ) => {
96- return fs . copyFile (
97- path . join ( webDistServer , cssAsset . fileName ) ,
98- path . join ( webDist , cssAsset . fileName )
99- )
100- } )
101- )
102-
103- const clientEntries : Record < string , string > = { }
104- for ( const item of clientBuildOutput . output ) {
105- const { name, fileName } = item
106- const entryFile =
107- name &&
108- // TODO (RSC) Can't we just compare the names? `item.name === name`
109- serverBuildOutput . output . find (
110- ( item ) =>
111- 'moduleIds' in item &&
112- item . moduleIds . includes ( clientEntryFiles [ name ] as string )
113- ) ?. fileName
57+ // Copy CSS assets from server to client
58+ await rscBuildCopyCssAssets ( serverBuildOutput , webDist , webDistServer )
11459
115- if ( entryFile ) {
116- console . log ( 'entryFile' , entryFile )
117- if ( process . platform === 'win32' ) {
118- const entryFileSlash = entryFile . replaceAll ( '\\' , '/' )
119- console . log ( 'entryFileSlash' , entryFileSlash )
120- // Prevent errors on Windows like
121- // Error: No client entry found for D:/a/redwood/rsc-project/web/dist/server/assets/rsc0.js
122- clientEntries [ entryFileSlash ] = fileName
123- } else {
124- clientEntries [ entryFile ] = fileName
125- }
126- }
127- }
128-
129- console . log ( 'clientEntries' , clientEntries )
130-
131- await fs . appendFile (
132- webDistEntries ,
133- `export const clientEntries=${ JSON . stringify ( clientEntries ) } ;`
60+ // Mappings from server to client asset file names
61+ await rscBuildClientEntriesMappings (
62+ clientBuildOutput ,
63+ serverBuildOutput ,
64+ clientEntryFiles ,
65+ webDistServerEntries
13466 )
13567
136- // // Step 1A: Generate the client bundle
137- // await buildWeb({ verbose })
138-
139- // const rollupInput = {
140- // entries: rwPaths.web.entryServer,
141- // ...clientEntryFiles,
142- // ...serverEntryFiles,
143- // }
144-
145- // Step 1B: Generate the server output
146- // await build({
147- // // TODO (RSC) I had this marked as 'FIXME'. I guess I just need to make
148- // // sure we still include it, or at least make it possible for users to pass
149- // // in their own config
150- // // configFile: viteConfig,
151- // ssr: {
152- // noExternal: Array.from(clientEntryFileSet).map(
153- // // TODO (RSC) I think the comment below is from waku. We don't care
154- // // about pnpm, do we? Does it also affect yarn?
155- // // FIXME this might not work with pnpm
156- // // TODO (RSC) No idea what's going on here
157- // (filename) => {
158- // const nodeModulesPath = path.join(rwPaths.base, 'node_modules')
159- // console.log('nodeModulesPath', nodeModulesPath)
160- // const relativePath = path.relative(nodeModulesPath, filename)
161- // console.log('relativePath', relativePath)
162- // console.log('first split', relativePath.split('/')[0])
163-
164- // return relativePath.split('/')[0]
165- // }
166- // ),
167- // },
168- // build: {
169- // // Because we configure the root to be web/src, we need to go up one level
170- // outDir: rwPaths.web.distServer,
171- // // TODO (RSC) Maybe we should re-enable this. I can't remember anymore)
172- // // What does 'ssr' even mean?
173- // // ssr: rwPaths.web.entryServer,
174- // rollupOptions: {
175- // input: {
176- // // TODO (RSC) entries: rwPaths.web.entryServer,
177- // ...clientEntryFiles,
178- // ...serverEntryFiles,
179- // },
180- // output: {
181- // banner: (chunk) => {
182- // console.log('chunk', chunk)
183-
184- // // HACK to bring directives to the front
185- // let code = ''
186-
187- // if (chunk.moduleIds.some((id) => clientEntryFileSet.has(id))) {
188- // code += '"use client";'
189- // }
190-
191- // if (chunk.moduleIds.some((id) => serverEntryFileSet.has(id))) {
192- // code += '"use server";'
193- // }
194-
195- // console.log('code', code)
196- // return code
197- // },
198- // entryFileNames: (chunkInfo) => {
199- // console.log('chunkInfo', chunkInfo)
200-
201- // // TODO (RSC) Don't hardcode 'entry.server'
202- // if (chunkInfo.name === 'entry.server') {
203- // return '[name].js'
204- // }
205-
206- // return 'assets/[name].js'
207- // },
208- // },
209- // },
210- // },
211- // envFile: false,
212- // logLevel: verbose ? 'info' : 'warn',
213- // })
214-
215- // Step 3: Generate route-manifest.json
216-
21768 // TODO When https://github.com/tc39/proposal-import-attributes and
21869 // https://github.com/microsoft/TypeScript/issues/53656 have both landed we
21970 // should try to do this instead:
0 commit comments