@@ -1032,6 +1032,21 @@ export function createRpcClient(params) {
10321032 } ,
10331033 } ,
10341034 } ,
1035+ {
1036+ // keep track of build output for each environment build to generate
1037+ // assets manaifest and environment imports manifest.
1038+ // use `order: "post"` to include all chunks/assets
1039+ // generated by other plugins
1040+ // (e.g. `styles.css` generated by `vite:css-post` when `cssCodeSplit: false`).
1041+ // https://github.com/vitejs/vite/blob/a19003516951a3710aab0f2646d78c48b2e5d2ad/packages/vite/src/node/plugins/css.ts#L1040
1042+ name : 'rsc:virtual:vite-rsc/track-bundles' ,
1043+ generateBundle : {
1044+ order : 'post' ,
1045+ handler ( _options , bundle ) {
1046+ manager . bundles [ this . environment . name ] = bundle
1047+ } ,
1048+ } ,
1049+ } ,
10351050 {
10361051 name : 'rsc:virtual:vite-rsc/assets-manifest' ,
10371052 resolveId : {
@@ -1065,117 +1080,114 @@ export function createRpcClient(params) {
10651080 } ,
10661081 } ,
10671082 // client build
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
1075-
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- ) ,
1087- )
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- } )
1083+ generateBundle ( _options , bundle ) {
1084+ // copy assets from rsc build to client build
1085+ if ( this . environment . name === 'client' ) {
1086+ const rscBundle = manager . bundles [ 'rsc' ] !
1087+
1088+ // when css code split is disabled, treat vite's single css bundle `style.css`
1089+ // as dependency of all server chunks
1090+ if ( ! manager . config . environments . rsc ! . build . cssCodeSplit ) {
1091+ let cssBundleName : string | undefined
1092+ for ( const output of Object . values ( rscBundle ) ) {
1093+ if (
1094+ output . type === 'asset' &&
1095+ output . names . includes ( 'style.css' )
1096+ ) {
1097+ cssBundleName = output . fileName
1098+ break
1099+ }
11091100 }
1110-
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 )
1101+ if ( cssBundleName ) {
1102+ for ( const output of Object . values ( rscBundle ) ) {
1103+ if ( output . type === 'chunk' ) {
1104+ output . viteMetadata ! . importedCss . add ( cssBundleName )
11201105 }
11211106 }
1122- serverResources [ meta . key ] = assetsURLOfDeps (
1123- {
1124- js : [ ] ,
1125- css : [ ...cssDeps ] ,
1126- } ,
1127- manager ,
1128- )
11291107 }
1108+ }
11301109
1131- const assetDeps = collectAssetDeps ( bundle )
1132- let bootstrapScriptContent : string | RuntimeAsset = ''
1110+ const assets = new Set (
1111+ Object . values ( rscBundle ) . flatMap ( ( output ) =>
1112+ output . type === 'chunk'
1113+ ? [
1114+ ...( output . viteMetadata ?. importedCss ?? [ ] ) ,
1115+ ...( output . viteMetadata ?. importedAssets ?? [ ] ) ,
1116+ ]
1117+ : [ ] ,
1118+ ) ,
1119+ )
1120+ for ( const fileName of assets ) {
1121+ const asset = rscBundle [ fileName ]
1122+ assert ( asset ?. type === 'asset' )
1123+ this . emitFile ( {
1124+ type : 'asset' ,
1125+ fileName : asset . fileName ,
1126+ source : asset . source ,
1127+ } )
1128+ }
11331129
1134- const clientReferenceDeps : Record < string , AssetDeps > = { }
1135- for ( const meta of Object . values ( manager . clientReferenceMetaMap ) ) {
1136- const deps : AssetDeps = assetDeps [ meta . groupChunkId ! ] ?. deps ?? {
1130+ const serverResources : Record < string , AssetDeps > = { }
1131+ const rscAssetDeps = collectAssetDeps ( rscBundle )
1132+ for ( const [ id , meta ] of Object . entries (
1133+ manager . serverResourcesMetaMap ,
1134+ ) ) {
1135+ serverResources [ meta . key ] = assetsURLOfDeps (
1136+ {
11371137 js : [ ] ,
1138- css : [ ] ,
1139- }
1140- clientReferenceDeps [ meta . referenceKey ] = assetsURLOfDeps (
1141- deps ,
1142- manager ,
1143- )
1138+ css : rscAssetDeps [ id ] ?. deps . css ?? [ ] ,
1139+ } ,
1140+ manager ,
1141+ )
1142+ }
1143+
1144+ const assetDeps = collectAssetDeps ( bundle )
1145+ let bootstrapScriptContent : string | RuntimeAsset = ''
1146+
1147+ const clientReferenceDeps : Record < string , AssetDeps > = { }
1148+ for ( const meta of Object . values ( manager . clientReferenceMetaMap ) ) {
1149+ const deps : AssetDeps = assetDeps [ meta . groupChunkId ! ] ?. deps ?? {
1150+ js : [ ] ,
1151+ css : [ ] ,
11441152 }
1153+ clientReferenceDeps [ meta . referenceKey ] = assetsURLOfDeps (
1154+ deps ,
1155+ manager ,
1156+ )
1157+ }
11451158
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 ,
1159+ // When customClientEntry is enabled, don't require "index" entry
1160+ // and don't merge entry deps into client references
1161+ if ( ! rscPluginOptions . customClientEntry ) {
1162+ const entry = Object . values ( assetDeps ) . find (
1163+ ( v ) => v . chunk . name === 'index' && v . chunk . isEntry ,
1164+ )
1165+ if ( ! entry ) {
1166+ throw new Error (
1167+ `[vite-rsc] Client build must have an entry chunk named "index". Use 'customClientEntry' option to disable this requirement.` ,
11511168 )
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- }
11691169 }
1170-
1171- manager . buildAssetsManifest = {
1172- bootstrapScriptContent,
1173- clientReferenceDeps,
1174- serverResources,
1175- cssLinkPrecedence : rscPluginOptions . cssLinkPrecedence ,
1170+ const entryDeps = assetsURLOfDeps ( entry . deps , manager )
1171+ for ( const [ key , deps ] of Object . entries ( clientReferenceDeps ) ) {
1172+ clientReferenceDeps [ key ] = mergeAssetDeps ( deps , entryDeps )
1173+ }
1174+ const entryUrl = assetsURL ( entry . chunk . fileName , manager )
1175+ if ( typeof entryUrl === 'string' ) {
1176+ bootstrapScriptContent = `import(${ JSON . stringify ( entryUrl ) } )`
1177+ } else {
1178+ bootstrapScriptContent = new RuntimeAsset (
1179+ `"import(" + JSON.stringify(${ entryUrl . runtime } ) + ")"` ,
1180+ )
11761181 }
11771182 }
1178- } ,
1183+
1184+ manager . buildAssetsManifest = {
1185+ bootstrapScriptContent,
1186+ clientReferenceDeps,
1187+ serverResources,
1188+ cssLinkPrecedence : rscPluginOptions . cssLinkPrecedence ,
1189+ }
1190+ }
11791191 } ,
11801192 // non-client builds can load assets manifest as external
11811193 renderChunk ( code , chunk ) {
@@ -2196,18 +2208,6 @@ function collectAssetDepsInner(
21962208 }
21972209}
21982210
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-
22112211//
22122212// css support
22132213//
0 commit comments