@rsc-kit/core 0.20.11 → 0.20.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vite.js CHANGED
@@ -1464,6 +1464,13 @@ async function prerenderAfterBundles(bundle, staticDir, assetsDir, knownActions
1464
1464
  results = await prerender({
1465
1465
  engine,
1466
1466
  write: writeTo(staticDir),
1467
+ // Stamp every shell with the build that froze it. The host reads it
1468
+ // back and refuses to resume one from another build: a postponed
1469
+ // state names components by whatever that build's minifier called
1470
+ // them, and replayed against a different bundle React finds the
1471
+ // wrong name in every slot. Unstamped - which is what every build
1472
+ // before this one wrote - the host cannot tell and resumes anyway.
1473
+ version: await engine.buildId?.(),
1467
1474
  serviceWorker: offline,
1468
1475
  stylesheet: inlineStylesheets === false
1469
1476
  ? undefined
@@ -3896,9 +3903,20 @@ export async function handleRscResume(
3896
3903
  )
3897
3904
 
3898
3905
  const ssr = await (import.meta as any).viteRsc.loadModule('ssr', 'index')
3899
- const htmlStream = await ssr.handleSsrResume(flight, postponed, nonce)
3906
+ // Whether React could replay the recorded tree at all. A bundler that
3907
+ // merges scopes renames components, and a name is how React matches a
3908
+ // slot: the same page, built once and bundled again - which is what
3909
+ // bun build --compile does - has different names at runtime than the
3910
+ // shell recorded. Reported so the host can stop resuming this shell and
3911
+ // render the page whole, rather than serving one whose holes are all
3912
+ // client-rendered. Set before the stream ends, because that is when
3913
+ // React reports it.
3914
+ let replayFailed = false
3915
+ const htmlStream = await ssr.handleSsrResume(flight, postponed, nonce, () => {
3916
+ replayFailed = true
3917
+ })
3900
3918
 
3901
- return { htmlStream }
3919
+ return { htmlStream, replayed: () => !replayFailed }
3902
3920
  }
3903
3921
 
3904
3922
  // Server action (worker: rsc-action).
@@ -4970,6 +4988,11 @@ export async function handleSsrResume(
4970
4988
  rscStream: ReadableStream,
4971
4989
  postponed: unknown,
4972
4990
  nonce?: string,
4991
+ // Told when React refuses to replay: the tree it recorded and the tree
4992
+ // rendering now put different components in a slot, so every hole below
4993
+ // is client-rendered instead of filled here. The caller decides what to
4994
+ // do about it; see the host.
4995
+ onReplayMismatch?: () => void,
4973
4996
  ): Promise<ReadableStream> {
4974
4997
  const root = await createFromReadableStream(rscStream)
4975
4998
 
@@ -4980,7 +5003,13 @@ export async function handleSsrResume(
4980
5003
  // the designed path, on every resume of a shell whose hole holds one -
4981
5004
  // printed as "[rsc-kit:resume] Error: useSearchParams() was read..." on
4982
5005
  // every request, with advice the app had already followed.
4983
- onError: reportRenderError('resume'),
5006
+ onError: (error: unknown, info: unknown) => {
5007
+ const message = String((error as { message?: string } | null)?.message ?? error)
5008
+
5009
+ if (message.includes('instead it rendered') || message.includes('resumable slots')) onReplayMismatch?.()
5010
+
5011
+ return reportRenderError('resume')(error, info)
5012
+ },
4984
5013
  })
4985
5014
 
4986
5015
  return DEV_ORIGIN ? rewriteViteDevUrlStream(html, DEV_ORIGIN) : html
@@ -6024,6 +6053,32 @@ export function rscKit(options = {}) {
6024
6053
  external: serverExternals,
6025
6054
  // Same table, same reason.
6026
6055
  checks: { ineffectiveDynamicImport: false },
6056
+ output: {
6057
+ // One copy of a dependency, however many client components
6058
+ // reach for it.
6059
+ //
6060
+ // Every client component is its own entry here - the SSR
6061
+ // side of a client reference, imported by id - and a
6062
+ // dependency two of them share is copied into each one's
6063
+ // chunk group. Two copies of one component are two
6064
+ // different functions, and that is fine until something
6065
+ // merges the module scopes: `bun build --compile` does,
6066
+ // renames the second copy, and a partially prerendered
6067
+ // page records its tree BY COMPONENT NAME. The shell then
6068
+ // says <J>, the binary renders <J2>, React refuses the
6069
+ // replay and every hole falls to the browser - a page
6070
+ // that arrives looking finished with nothing wired up. A
6071
+ // port found it on one page, in production, on cold
6072
+ // loads only.
6073
+ //
6074
+ // Grouping node_modules into one chunk means a shared
6075
+ // dependency exists once, so there is nothing to collide.
6076
+ // The server does not pay for the bigger chunk the way a
6077
+ // browser would: it reads from disk, once, at startup.
6078
+ advancedChunks: {
6079
+ groups: [{ name: "vendor", test: /[\\/]node_modules[\\/]/ }],
6080
+ },
6081
+ },
6027
6082
  },
6028
6083
  },
6029
6084
  resolve: { noExternal: bundledClientPackages },