@@ -1065,93 +1065,117 @@ export function createRpcClient(params) {
10651065 } ,
10661066 } ,
10671067 // client build
1068- generateBundle ( _options , bundle ) {
1069- // copy assets from rsc build to client build
1070- manager . bundles [ this . environment . name ] = bundle
1071-
1072- if ( this . environment . name === 'client' ) {
1073- const rscBundle = manager . bundles [ 'rsc' ] !
1074- const assets = new Set (
1075- Object . values ( rscBundle ) . flatMap ( ( output ) =>
1076- output . type === 'chunk'
1077- ? [
1078- ...( output . viteMetadata ?. importedCss ?? [ ] ) ,
1079- ...( output . viteMetadata ?. importedAssets ?? [ ] ) ,
1080- ]
1081- : [ ] ,
1082- ) ,
1083- )
1084- for ( const fileName of assets ) {
1085- const asset = rscBundle [ fileName ]
1086- assert ( asset ?. type === 'asset' )
1087- this . emitFile ( {
1088- type : 'asset' ,
1089- fileName : asset . fileName ,
1090- source : asset . source ,
1091- } )
1092- }
1068+ generateBundle : {
1069+ // run after vite's css plugin emits the consolidated stylesheet
1070+ // (relevant when `build.cssCodeSplit: false`).
1071+ order : 'post' ,
1072+ handler ( _options , bundle ) {
1073+ // copy assets from rsc build to client build
1074+ manager . bundles [ this . environment . name ] = bundle
10931075
1094- const serverResources : Record < string , AssetDeps > = { }
1095- const rscAssetDeps = collectAssetDeps ( manager . bundles [ 'rsc' ] ! )
1096- for ( const [ id , meta ] of Object . entries (
1097- manager . serverResourcesMetaMap ,
1098- ) ) {
1099- serverResources [ meta . key ] = assetsURLOfDeps (
1100- {
1101- js : [ ] ,
1102- css : rscAssetDeps [ id ] ?. deps . css ?? [ ] ,
1103- } ,
1104- manager ,
1076+ if ( this . environment . name === 'client' ) {
1077+ const rscBundle = manager . bundles [ 'rsc' ] !
1078+ const assets = new Set (
1079+ Object . values ( rscBundle ) . flatMap ( ( output ) =>
1080+ output . type === 'chunk'
1081+ ? [
1082+ ... ( output . viteMetadata ?. importedCss ?? [ ] ) ,
1083+ ... ( output . viteMetadata ?. importedAssets ?? [ ] ) ,
1084+ ]
1085+ : [ ] ,
1086+ ) ,
11051087 )
1106- }
1107-
1108- const assetDeps = collectAssetDeps ( bundle )
1109- let bootstrapScriptContent : string | RuntimeAsset = ''
1110-
1111- const clientReferenceDeps : Record < string , AssetDeps > = { }
1112- for ( const meta of Object . values ( manager . clientReferenceMetaMap ) ) {
1113- const deps : AssetDeps = assetDeps [ meta . groupChunkId ! ] ?. deps ?? {
1114- js : [ ] ,
1115- css : [ ] ,
1088+ // when the rsc environment has `cssCodeSplit: false`, Vite emits a
1089+ // single bundled CSS asset and chunks carry no `importedCss` metadata.
1090+ // Pick the bundled CSS asset(s) directly so they get copied and
1091+ // referenced by server resources.
1092+ const rscCssCodeSplit =
1093+ manager . config . environments . rsc ?. build . cssCodeSplit
1094+ const rscBundledCssFileNames =
1095+ rscCssCodeSplit === false
1096+ ? collectBundledCssAssetFileNames ( rscBundle )
1097+ : [ ]
1098+ for ( const fileName of rscBundledCssFileNames ) {
1099+ assets . add ( fileName )
1100+ }
1101+ for ( const fileName of assets ) {
1102+ const asset = rscBundle [ fileName ]
1103+ assert ( asset ?. type === 'asset' )
1104+ this . emitFile ( {
1105+ type : 'asset' ,
1106+ fileName : asset . fileName ,
1107+ source : asset . source ,
1108+ } )
11161109 }
1117- clientReferenceDeps [ meta . referenceKey ] = assetsURLOfDeps (
1118- deps ,
1119- manager ,
1120- )
1121- }
11221110
1123- // When customClientEntry is enabled, don't require "index" entry
1124- // and don't merge entry deps into client references
1125- if ( ! rscPluginOptions . customClientEntry ) {
1126- const entry = Object . values ( assetDeps ) . find (
1127- ( v ) => v . chunk . name === 'index' && v . chunk . isEntry ,
1128- )
1129- if ( ! entry ) {
1130- throw new Error (
1131- `[vite-rsc] Client build must have an entry chunk named "index". Use 'customClientEntry' option to disable this requirement.` ,
1111+ const serverResources : Record < string , AssetDeps > = { }
1112+ const rscAssetDeps = collectAssetDeps ( manager . bundles [ 'rsc' ] ! )
1113+ for ( const [ id , meta ] of Object . entries (
1114+ manager . serverResourcesMetaMap ,
1115+ ) ) {
1116+ const cssDeps = new Set ( rscAssetDeps [ id ] ?. deps . css ?? [ ] )
1117+ if ( rscCssCodeSplit === false ) {
1118+ for ( const fileName of rscBundledCssFileNames ) {
1119+ cssDeps . add ( fileName )
1120+ }
1121+ }
1122+ serverResources [ meta . key ] = assetsURLOfDeps (
1123+ {
1124+ js : [ ] ,
1125+ css : [ ...cssDeps ] ,
1126+ } ,
1127+ manager ,
11321128 )
11331129 }
1134- const entryDeps = assetsURLOfDeps ( entry . deps , manager )
1135- for ( const [ key , deps ] of Object . entries ( clientReferenceDeps ) ) {
1136- clientReferenceDeps [ key ] = mergeAssetDeps ( deps , entryDeps )
1130+
1131+ const assetDeps = collectAssetDeps ( bundle )
1132+ let bootstrapScriptContent : string | RuntimeAsset = ''
1133+
1134+ const clientReferenceDeps : Record < string , AssetDeps > = { }
1135+ for ( const meta of Object . values ( manager . clientReferenceMetaMap ) ) {
1136+ const deps : AssetDeps = assetDeps [ meta . groupChunkId ! ] ?. deps ?? {
1137+ js : [ ] ,
1138+ css : [ ] ,
1139+ }
1140+ clientReferenceDeps [ meta . referenceKey ] = assetsURLOfDeps (
1141+ deps ,
1142+ manager ,
1143+ )
11371144 }
1138- const entryUrl = assetsURL ( entry . chunk . fileName , manager )
1139- if ( typeof entryUrl === 'string' ) {
1140- bootstrapScriptContent = `import( ${ JSON . stringify ( entryUrl ) } )`
1141- } else {
1142- bootstrapScriptContent = new RuntimeAsset (
1143- `"import(" + JSON.stringify( ${ entryUrl . runtime } ) + ")"` ,
1145+
1146+ // When customClientEntry is enabled, don't require "index" entry
1147+ // and don't merge entry deps into client references
1148+ if ( ! rscPluginOptions . customClientEntry ) {
1149+ const entry = Object . values ( assetDeps ) . find (
1150+ ( v ) => v . chunk . name === 'index' && v . chunk . isEntry ,
11441151 )
1152+ if ( ! entry ) {
1153+ throw new Error (
1154+ `[vite-rsc] Client build must have an entry chunk named "index". Use 'customClientEntry' option to disable this requirement.` ,
1155+ )
1156+ }
1157+ const entryDeps = assetsURLOfDeps ( entry . deps , manager )
1158+ for ( const [ key , deps ] of Object . entries ( clientReferenceDeps ) ) {
1159+ clientReferenceDeps [ key ] = mergeAssetDeps ( deps , entryDeps )
1160+ }
1161+ const entryUrl = assetsURL ( entry . chunk . fileName , manager )
1162+ if ( typeof entryUrl === 'string' ) {
1163+ bootstrapScriptContent = `import(${ JSON . stringify ( entryUrl ) } )`
1164+ } else {
1165+ bootstrapScriptContent = new RuntimeAsset (
1166+ `"import(" + JSON.stringify(${ entryUrl . runtime } ) + ")"` ,
1167+ )
1168+ }
11451169 }
1146- }
11471170
1148- manager . buildAssetsManifest = {
1149- bootstrapScriptContent,
1150- clientReferenceDeps,
1151- serverResources,
1152- cssLinkPrecedence : rscPluginOptions . cssLinkPrecedence ,
1171+ manager . buildAssetsManifest = {
1172+ bootstrapScriptContent,
1173+ clientReferenceDeps,
1174+ serverResources,
1175+ cssLinkPrecedence : rscPluginOptions . cssLinkPrecedence ,
1176+ }
11531177 }
1154- }
1178+ } ,
11551179 } ,
11561180 // non-client builds can load assets manifest as external
11571181 renderChunk ( code , chunk ) {
@@ -2172,6 +2196,18 @@ function collectAssetDepsInner(
21722196 }
21732197}
21742198
2199+ function collectBundledCssAssetFileNames (
2200+ bundle : Rollup . OutputBundle ,
2201+ ) : string [ ] {
2202+ return Object . values ( bundle )
2203+ . filter (
2204+ ( output ) : output is Rollup . OutputAsset =>
2205+ output . type === 'asset' &&
2206+ output . originalFileNames ?. includes ( 'style.css' ) ,
2207+ )
2208+ . map ( ( output ) => output . fileName )
2209+ }
2210+
21752211//
21762212// css support
21772213//
0 commit comments