@@ -265,6 +265,21 @@ export type RscPluginOptions = {
265265 * @default true
266266 */
267267 cssLinkPrecedence ?: boolean
268+
269+ /**
270+ * Opt out of the default "index" client entry convention.
271+ * When enabled, the plugin will not:
272+ * - Require an entry chunk named "index"
273+ * - Automatically include client entry deps in each client reference's dependencies
274+ *
275+ * Note: `import.meta.viteRsc.loadBootstrapScriptContent` cannot be used with this option.
276+ *
277+ * Use this when you manually handle client entry setup and preloading.
278+ *
279+ * @experimental
280+ * @default false
281+ */
282+ customClientEntry ?: boolean
268283}
269284
270285export type PluginApi = {
@@ -1065,30 +1080,45 @@ export function createRpcClient(params) {
10651080 }
10661081
10671082 const assetDeps = collectAssetDeps ( bundle )
1068- const entry = Object . values ( assetDeps ) . find (
1069- ( v ) => v . chunk . name === 'index' && v . chunk . isEntry ,
1070- )
1071- assert ( entry )
1072- const entryUrl = assetsURL ( entry . chunk . fileName , manager )
1083+ let bootstrapScriptContent : string | RuntimeAsset = ''
1084+
10731085 const clientReferenceDeps : Record < string , AssetDeps > = { }
10741086 for ( const meta of Object . values ( manager . clientReferenceMetaMap ) ) {
10751087 const deps : AssetDeps = assetDeps [ meta . groupChunkId ! ] ?. deps ?? {
10761088 js : [ ] ,
10771089 css : [ ] ,
10781090 }
10791091 clientReferenceDeps [ meta . referenceKey ] = assetsURLOfDeps (
1080- mergeAssetDeps ( deps , entry . deps ) ,
1092+ deps ,
10811093 manager ,
10821094 )
10831095 }
1084- let bootstrapScriptContent : string | RuntimeAsset
1085- if ( typeof entryUrl === 'string' ) {
1086- bootstrapScriptContent = `import( ${ JSON . stringify ( entryUrl ) } )`
1087- } else {
1088- bootstrapScriptContent = new RuntimeAsset (
1089- `"import(" + JSON.stringify( ${ entryUrl . runtime } ) + ")"` ,
1096+
1097+ // When customClientEntry is enabled, don't require "index" entry
1098+ // and don't merge entry deps into client references
1099+ if ( ! rscPluginOptions . customClientEntry ) {
1100+ const entry = Object . values ( assetDeps ) . find (
1101+ ( v ) => v . chunk . name === 'index' && v . chunk . isEntry ,
10901102 )
1103+ if ( ! entry ) {
1104+ throw new Error (
1105+ `[vite-rsc] Client build must have an entry chunk named "index". Use 'customClientEntry' option to disable this requirement.` ,
1106+ )
1107+ }
1108+ const entryDeps = assetsURLOfDeps ( entry . deps , manager )
1109+ for ( const [ key , deps ] of Object . entries ( clientReferenceDeps ) ) {
1110+ clientReferenceDeps [ key ] = mergeAssetDeps ( deps , entryDeps )
1111+ }
1112+ const entryUrl = assetsURL ( entry . chunk . fileName , manager )
1113+ if ( typeof entryUrl === 'string' ) {
1114+ bootstrapScriptContent = `import(${ JSON . stringify ( entryUrl ) } )`
1115+ } else {
1116+ bootstrapScriptContent = new RuntimeAsset (
1117+ `"import(" + JSON.stringify(${ entryUrl . runtime } ) + ")"` ,
1118+ )
1119+ }
10911120 }
1121+
10921122 manager . buildAssetsManifest = {
10931123 bootstrapScriptContent,
10941124 clientReferenceDeps,
@@ -1135,6 +1165,10 @@ export default assetsManifest.bootstrapScriptContent;
11351165 return
11361166 }
11371167
1168+ assert (
1169+ ! rscPluginOptions . customClientEntry ,
1170+ `[vite-rsc] 'import.meta.viteRsc.loadBootstrapScriptContent' cannot be used with 'customClientEntry' option` ,
1171+ )
11381172 assert ( this . environment . name !== 'client' )
11391173 const output = new MagicString ( code )
11401174
0 commit comments