Skip to content

Commit 8dc7e5f

Browse files
hi-ogawaclaude
andauthored
feat(plugin-rsc): add customClientEntry option to opt out of "index" entry convention (#1068)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent c2549ce commit 8dc7e5f

File tree

1 file changed

+46
-12
lines changed

1 file changed

+46
-12
lines changed

packages/plugin-rsc/src/plugin.ts

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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

270285
export 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

Comments
 (0)