@tanstack/start-plugin-core 1.167.35 → 1.169.0

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.
Files changed (187) hide show
  1. package/dist/esm/import-protection/adapterUtils.d.ts +27 -0
  2. package/dist/esm/import-protection/adapterUtils.js +31 -0
  3. package/dist/esm/import-protection/adapterUtils.js.map +1 -0
  4. package/dist/esm/import-protection/analysis.d.ts +36 -0
  5. package/dist/esm/import-protection/analysis.js +407 -0
  6. package/dist/esm/import-protection/analysis.js.map +1 -0
  7. package/dist/esm/{import-protection-plugin → import-protection}/ast.js +1 -1
  8. package/dist/esm/import-protection/ast.js.map +1 -0
  9. package/dist/esm/import-protection/constants.d.ts +11 -0
  10. package/dist/esm/{import-protection-plugin → import-protection}/constants.js +7 -2
  11. package/dist/esm/import-protection/constants.js.map +1 -0
  12. package/dist/esm/{import-protection-plugin → import-protection}/defaults.js +1 -1
  13. package/dist/esm/import-protection/defaults.js.map +1 -0
  14. package/dist/esm/{import-protection-plugin → import-protection}/extensionlessAbsoluteIdResolver.js +2 -2
  15. package/dist/esm/import-protection/extensionlessAbsoluteIdResolver.js.map +1 -0
  16. package/dist/esm/{import-protection-plugin → import-protection}/matchers.js +1 -1
  17. package/dist/esm/import-protection/matchers.js.map +1 -0
  18. package/dist/esm/{import-protection-plugin/rewriteDeniedImports.d.ts → import-protection/rewrite.d.ts} +0 -4
  19. package/dist/esm/import-protection/rewrite.js +121 -0
  20. package/dist/esm/import-protection/rewrite.js.map +1 -0
  21. package/dist/esm/{import-protection-plugin → import-protection}/sourceLocation.d.ts +32 -3
  22. package/dist/esm/{import-protection-plugin → import-protection}/sourceLocation.js +65 -10
  23. package/dist/esm/import-protection/sourceLocation.js.map +1 -0
  24. package/dist/esm/{import-protection-plugin → import-protection}/trace.d.ts +0 -1
  25. package/dist/esm/{import-protection-plugin → import-protection}/trace.js +1 -1
  26. package/dist/esm/import-protection/trace.js.map +1 -0
  27. package/dist/esm/{import-protection-plugin → import-protection}/utils.d.ts +18 -1
  28. package/dist/esm/{import-protection-plugin → import-protection}/utils.js +13 -20
  29. package/dist/esm/import-protection/utils.js.map +1 -0
  30. package/dist/esm/import-protection/virtualModules.d.ts +25 -0
  31. package/dist/esm/{import-protection-plugin → import-protection}/virtualModules.js +5 -117
  32. package/dist/esm/import-protection/virtualModules.js.map +1 -0
  33. package/dist/esm/index.d.ts +1 -5
  34. package/dist/esm/index.js +2 -4
  35. package/dist/esm/post-build.d.ts +9 -0
  36. package/dist/esm/post-build.js +37 -0
  37. package/dist/esm/post-build.js.map +1 -0
  38. package/dist/esm/prerender.d.ts +11 -0
  39. package/dist/esm/prerender.js +159 -0
  40. package/dist/esm/prerender.js.map +1 -0
  41. package/dist/esm/rsbuild/dev-server.d.ts +21 -0
  42. package/dist/esm/rsbuild/dev-server.js +76 -0
  43. package/dist/esm/rsbuild/dev-server.js.map +1 -0
  44. package/dist/esm/rsbuild/import-protection.d.ts +10 -0
  45. package/dist/esm/rsbuild/import-protection.js +775 -0
  46. package/dist/esm/rsbuild/import-protection.js.map +1 -0
  47. package/dist/esm/rsbuild/index.d.ts +4 -0
  48. package/dist/esm/rsbuild/index.js +3 -0
  49. package/dist/esm/rsbuild/normalized-client-build.d.ts +18 -0
  50. package/dist/esm/rsbuild/normalized-client-build.js +207 -0
  51. package/dist/esm/rsbuild/normalized-client-build.js.map +1 -0
  52. package/dist/esm/rsbuild/planning.d.ts +52 -0
  53. package/dist/esm/rsbuild/planning.js +108 -0
  54. package/dist/esm/rsbuild/planning.js.map +1 -0
  55. package/dist/esm/rsbuild/plugin.d.ts +4 -0
  56. package/dist/esm/rsbuild/plugin.js +344 -0
  57. package/dist/esm/rsbuild/plugin.js.map +1 -0
  58. package/dist/esm/rsbuild/post-build.d.ts +6 -0
  59. package/dist/esm/rsbuild/post-build.js +57 -0
  60. package/dist/esm/rsbuild/post-build.js.map +1 -0
  61. package/dist/esm/rsbuild/schema.d.ts +3372 -0
  62. package/dist/esm/rsbuild/schema.js +12 -0
  63. package/dist/esm/rsbuild/schema.js.map +1 -0
  64. package/dist/esm/rsbuild/start-compiler-host.d.ts +20 -0
  65. package/dist/esm/rsbuild/start-compiler-host.js +150 -0
  66. package/dist/esm/rsbuild/start-compiler-host.js.map +1 -0
  67. package/dist/esm/rsbuild/start-router-plugin.d.ts +18 -0
  68. package/dist/esm/rsbuild/start-router-plugin.js +63 -0
  69. package/dist/esm/rsbuild/start-router-plugin.js.map +1 -0
  70. package/dist/esm/rsbuild/swc-rsc.d.ts +14 -0
  71. package/dist/esm/rsbuild/swc-rsc.js +93 -0
  72. package/dist/esm/rsbuild/swc-rsc.js.map +1 -0
  73. package/dist/esm/rsbuild/types.d.ts +17 -0
  74. package/dist/esm/rsbuild/types.js +0 -0
  75. package/dist/esm/rsbuild/virtual-modules.d.ts +53 -0
  76. package/dist/esm/rsbuild/virtual-modules.js +287 -0
  77. package/dist/esm/rsbuild/virtual-modules.js.map +1 -0
  78. package/dist/esm/schema.d.ts +43 -43
  79. package/dist/esm/start-compiler/compiler.d.ts +1 -1
  80. package/dist/esm/start-compiler/compiler.js +80 -9
  81. package/dist/esm/start-compiler/compiler.js.map +1 -1
  82. package/dist/esm/start-compiler/handleCreateServerFn.js +9 -0
  83. package/dist/esm/start-compiler/handleCreateServerFn.js.map +1 -1
  84. package/dist/esm/start-compiler/host.js +5 -1
  85. package/dist/esm/start-compiler/host.js.map +1 -1
  86. package/dist/esm/start-compiler/types.d.ts +1 -0
  87. package/dist/esm/utils.d.ts +1 -0
  88. package/dist/esm/utils.js +10 -1
  89. package/dist/esm/utils.js.map +1 -1
  90. package/dist/esm/{import-protection-plugin → vite/import-protection-plugin}/plugin.js +41 -92
  91. package/dist/esm/vite/import-protection-plugin/plugin.js.map +1 -0
  92. package/dist/esm/{import-protection-plugin → vite/import-protection-plugin}/types.d.ts +5 -5
  93. package/dist/esm/vite/import-protection-plugin/virtualModules.d.ts +8 -0
  94. package/dist/esm/vite/import-protection-plugin/virtualModules.js +49 -0
  95. package/dist/esm/vite/import-protection-plugin/virtualModules.js.map +1 -0
  96. package/dist/esm/vite/index.d.ts +5 -0
  97. package/dist/esm/vite/index.js +4 -0
  98. package/dist/esm/vite/plugin.js +1 -1
  99. package/dist/esm/vite/plugin.js.map +1 -1
  100. package/dist/esm/vite/post-server-build.js +14 -32
  101. package/dist/esm/vite/post-server-build.js.map +1 -1
  102. package/dist/esm/vite/prerender.d.ts +2 -2
  103. package/dist/esm/vite/prerender.js +17 -147
  104. package/dist/esm/vite/prerender.js.map +1 -1
  105. package/dist/esm/vite/schema.d.ts +23 -23
  106. package/dist/esm/vite/start-compiler-plugin/hot-update.d.ts +2 -0
  107. package/dist/esm/vite/start-compiler-plugin/hot-update.js +16 -0
  108. package/dist/esm/vite/start-compiler-plugin/hot-update.js.map +1 -0
  109. package/dist/esm/vite/start-compiler-plugin/module-specifier.js +9 -4
  110. package/dist/esm/vite/start-compiler-plugin/module-specifier.js.map +1 -1
  111. package/dist/esm/vite/start-compiler-plugin/plugin.js +86 -13
  112. package/dist/esm/vite/start-compiler-plugin/plugin.js.map +1 -1
  113. package/package.json +32 -4
  114. package/src/import-protection/INTERNALS.md +266 -0
  115. package/src/import-protection/adapterUtils.ts +94 -0
  116. package/src/import-protection/analysis.ts +853 -0
  117. package/src/{import-protection-plugin → import-protection}/constants.ts +7 -0
  118. package/src/import-protection/rewrite.ts +229 -0
  119. package/src/{import-protection-plugin → import-protection}/sourceLocation.ts +125 -9
  120. package/src/{import-protection-plugin → import-protection}/trace.ts +0 -1
  121. package/src/{import-protection-plugin → import-protection}/utils.ts +36 -21
  122. package/src/{import-protection-plugin → import-protection}/virtualModules.ts +30 -177
  123. package/src/index.ts +1 -8
  124. package/src/post-build.ts +64 -0
  125. package/src/prerender.ts +292 -0
  126. package/src/rsbuild/INTERNALS-import-protection.md +169 -0
  127. package/src/rsbuild/dev-server.ts +129 -0
  128. package/src/rsbuild/import-protection.ts +1599 -0
  129. package/src/rsbuild/index.ts +4 -0
  130. package/src/rsbuild/normalized-client-build.ts +346 -0
  131. package/src/rsbuild/planning.ts +234 -0
  132. package/src/rsbuild/plugin.ts +754 -0
  133. package/src/rsbuild/post-build.ts +96 -0
  134. package/src/rsbuild/schema.ts +31 -0
  135. package/src/rsbuild/start-compiler-host.ts +250 -0
  136. package/src/rsbuild/start-router-plugin.ts +86 -0
  137. package/src/rsbuild/swc-rsc.ts +166 -0
  138. package/src/rsbuild/types.ts +20 -0
  139. package/src/rsbuild/virtual-modules.ts +565 -0
  140. package/src/start-compiler/compiler.ts +153 -19
  141. package/src/start-compiler/handleCreateServerFn.ts +18 -0
  142. package/src/start-compiler/types.ts +1 -0
  143. package/src/utils.ts +14 -0
  144. package/src/vite/import-protection-plugin/INTERNALS.md +187 -0
  145. package/src/{import-protection-plugin → vite/import-protection-plugin}/plugin.ts +73 -158
  146. package/src/{import-protection-plugin → vite/import-protection-plugin}/types.ts +5 -5
  147. package/src/vite/import-protection-plugin/virtualModules.ts +122 -0
  148. package/src/vite/index.ts +8 -0
  149. package/src/vite/plugin.ts +1 -1
  150. package/src/vite/post-server-build.ts +14 -57
  151. package/src/vite/prerender.ts +19 -260
  152. package/src/vite/start-compiler-plugin/hot-update.ts +24 -0
  153. package/src/vite/start-compiler-plugin/module-specifier.ts +15 -5
  154. package/src/vite/start-compiler-plugin/plugin.ts +193 -18
  155. package/dist/esm/import-protection-plugin/ast.js.map +0 -1
  156. package/dist/esm/import-protection-plugin/constants.d.ts +0 -6
  157. package/dist/esm/import-protection-plugin/constants.js.map +0 -1
  158. package/dist/esm/import-protection-plugin/defaults.js.map +0 -1
  159. package/dist/esm/import-protection-plugin/extensionlessAbsoluteIdResolver.js.map +0 -1
  160. package/dist/esm/import-protection-plugin/matchers.js.map +0 -1
  161. package/dist/esm/import-protection-plugin/plugin.js.map +0 -1
  162. package/dist/esm/import-protection-plugin/postCompileUsage.d.ts +0 -13
  163. package/dist/esm/import-protection-plugin/postCompileUsage.js +0 -63
  164. package/dist/esm/import-protection-plugin/postCompileUsage.js.map +0 -1
  165. package/dist/esm/import-protection-plugin/rewriteDeniedImports.js +0 -205
  166. package/dist/esm/import-protection-plugin/rewriteDeniedImports.js.map +0 -1
  167. package/dist/esm/import-protection-plugin/sourceLocation.js.map +0 -1
  168. package/dist/esm/import-protection-plugin/trace.js.map +0 -1
  169. package/dist/esm/import-protection-plugin/utils.js.map +0 -1
  170. package/dist/esm/import-protection-plugin/virtualModules.d.ts +0 -78
  171. package/dist/esm/import-protection-plugin/virtualModules.js.map +0 -1
  172. package/dist/esm/start-compiler/load-module.d.ts +0 -14
  173. package/dist/esm/start-compiler/load-module.js +0 -18
  174. package/dist/esm/start-compiler/load-module.js.map +0 -1
  175. package/src/import-protection-plugin/INTERNALS.md +0 -700
  176. package/src/import-protection-plugin/postCompileUsage.ts +0 -100
  177. package/src/import-protection-plugin/rewriteDeniedImports.ts +0 -379
  178. package/src/start-compiler/load-module.ts +0 -31
  179. /package/dist/esm/{import-protection-plugin → import-protection}/ast.d.ts +0 -0
  180. /package/dist/esm/{import-protection-plugin → import-protection}/defaults.d.ts +0 -0
  181. /package/dist/esm/{import-protection-plugin → import-protection}/extensionlessAbsoluteIdResolver.d.ts +0 -0
  182. /package/dist/esm/{import-protection-plugin → import-protection}/matchers.d.ts +0 -0
  183. /package/dist/esm/{import-protection-plugin → vite/import-protection-plugin}/plugin.d.ts +0 -0
  184. /package/src/{import-protection-plugin → import-protection}/ast.ts +0 -0
  185. /package/src/{import-protection-plugin → import-protection}/defaults.ts +0 -0
  186. /package/src/{import-protection-plugin → import-protection}/extensionlessAbsoluteIdResolver.ts +0 -0
  187. /package/src/{import-protection-plugin → import-protection}/matchers.ts +0 -0
@@ -0,0 +1,169 @@
1
+ # Rsbuild Import Protection - Adapter Internals
2
+
3
+ ## Scope
4
+
5
+ This document covers the Rsbuild-specific orchestration around the shared
6
+ import-protection core in `src/import-protection/INTERNALS.md`.
7
+
8
+ Rsbuild owns:
9
+
10
+ - post-transform enforcement through `api.transform({ order: 'post' })`
11
+ - virtual-module transport through `VirtualModulesPlugin`
12
+ - compilation-truth reporting in `processAssets`
13
+ - final graph reconstruction from Rspack compilation data
14
+ - the small build-only deferred queue for file violations that can disappear
15
+ from the compiled graph
16
+
17
+ Shared AST analysis, rewrite logic, source extraction, usage lookup, source
18
+ locations, trace formatting, and mock code generation are described in the
19
+ shared internals doc.
20
+
21
+ ## Mental Model
22
+
23
+ Vite is primarily `resolveId`-driven. Rsbuild is primarily `transform` +
24
+ `processAssets`-driven.
25
+
26
+ That difference explains most of the adapter divergence.
27
+
28
+ Rsbuild does not emulate the Vite pending-queue state machine. It relies more
29
+ directly on post-transform code and final compilation truth.
30
+
31
+ ## Native Hook Shape
32
+
33
+ Import protection is attached through Rsbuild hooks, not a dedicated mini-plugin
34
+ object:
35
+
36
+ 1. `onBeforeBuild`
37
+ 2. `onBeforeDevCompile`
38
+ 3. `modifyRspackConfig`
39
+ 4. `transform(..., { order: 'post' })`
40
+ 5. `processAssets(..., { stage: 'report' })`
41
+
42
+ ## State Model
43
+
44
+ Per environment, Rsbuild keeps a smaller runtime state than Vite:
45
+
46
+ - `resolveCache`
47
+ - `seenViolations`
48
+ - `buildTransformResults`
49
+ - `deferredFileViolations`
50
+ - `deferredFileViolationKeys`
51
+
52
+ Shared state is for virtual module transport and compiler fs access:
53
+
54
+ - `virtualModules`
55
+ - `vmPlugins`
56
+ - `readyVmPlugins`
57
+ - `inputFileSystems`
58
+ - `pendingWrites`
59
+
60
+ Notably absent compared to Vite:
61
+
62
+ - no `pendingViolations`
63
+ - no `postTransformImports`
64
+ - no broad `deferredBuildViolations`
65
+ - no resolve-time dev reachability pipeline
66
+
67
+ ## Transform Phase
68
+
69
+ Rsbuild enforcement runs after the Start compiler in a `post` transform.
70
+
71
+ That matters because many compiler-safe imports are already stripped by the time
72
+ import protection runs. This naturally suppresses a large class of false
73
+ positives without a Vite-style pending verification pass.
74
+
75
+ The transform phase is responsible for:
76
+
77
+ - self-denial for forbidden files
78
+ - self-denial for marker-protected files in the wrong environment
79
+ - direct specifier rewrites to mock-edge modules
80
+ - build-time transformed/original source preloading for later diagnostics
81
+ - recording build-only deferred file violations when original unsafe usage may
82
+ outlive a direct compiled graph edge
83
+
84
+ ## Virtual Module Transport
85
+
86
+ Rsbuild reuses the shared mock generators, but not the Vite id transport.
87
+
88
+ Instead it writes environment-scoped virtual files under:
89
+
90
+ ```text
91
+ <root>/node_modules/.virtual/import-protection/<env>/
92
+ ```
93
+
94
+ The important forms are:
95
+
96
+ - `mock-silent.mjs`
97
+ - `mock-runtime-<base64>.mjs`
98
+ - `mock-edge-<base64>.mjs`
99
+
100
+ Writes may happen before the `VirtualModulesPlugin` instance is ready, so the
101
+ adapter queues them and flushes during compilation setup.
102
+
103
+ ## Reporting Phase
104
+
105
+ `processAssets({ stage: 'report' })` is the authoritative reporting step.
106
+
107
+ It reconstructs the final view of the compilation from Rspack data by:
108
+
109
+ 1. building a `TransformResultProvider` from `compilation.modules`
110
+ 2. rebuilding the active compilation graph from outgoing connections
111
+ 3. reconstructing surviving specifier violations from compiled mock-edge files
112
+ 4. reporting live file violations from active edges
113
+ 5. reporting live marker violations from active edges plus original source
114
+ 6. reporting deferred file violations only when both importer and target truly
115
+ survived compilation
116
+
117
+ This is the core Rsbuild-native replacement for Vite's `generateBundle`
118
+ verification plus dev pending-violation flow.
119
+
120
+ ## Why The Deferred Queue Is Narrow
121
+
122
+ Rsbuild only needs explicit build deferral for file violations whose direct edge
123
+ may disappear after compilation.
124
+
125
+ Specifier violations are rediscovered from surviving mock-edge virtual files.
126
+ Marker violations are rediscovered from live compiled edges.
127
+
128
+ Only file violations need extra bookkeeping when the final compiled graph can no
129
+ longer show the original denied edge directly.
130
+
131
+ ## Source And Compilation APIs
132
+
133
+ The Rsbuild adapter intentionally prefers native Rspack APIs where possible.
134
+
135
+ Transform-time:
136
+
137
+ - `ctx.resource`
138
+ - `ctx.context`
139
+ - `ctx.resolve(...)`
140
+ - captured `compiler.inputFileSystem.readFile(...)`
141
+
142
+ Compilation-time:
143
+
144
+ - `module.nameForCondition?.()`
145
+ - `module.resourceResolveData?.resource`
146
+ - `module.originalSource().sourceAndMap()`
147
+ - sourcemap `sourcesContent`
148
+ - `compilation.inputFileSystem.readFile(...)`
149
+
150
+ This keeps the adapter closer to Rsbuild/Rspack truth and avoids falling back to
151
+ Node fs when the compilation already has the needed data.
152
+
153
+ ## Marker Handling
154
+
155
+ Unlike Vite, Rsbuild does not introduce plugin-owned virtual marker modules for
156
+ normal operation.
157
+
158
+ The real package marker files are used as source-level markers, and the adapter
159
+ later infers marker kind from original source while reporting compiled edges.
160
+
161
+ ## Practical Maintainer Rule
162
+
163
+ When changing Rsbuild import protection, ask:
164
+
165
+ 1. Can the change be expressed in shared analysis/rewrite/mock codegen instead?
166
+ 2. If it is adapter-specific, is it really about post-transform enforcement,
167
+ virtual-file transport, or compilation-time reporting?
168
+ 3. Can final compilation truth answer the question more simply than adding a new
169
+ pending state machine?
@@ -0,0 +1,129 @@
1
+ import { NodeRequest, sendNodeResponse } from 'srvx/node'
2
+ import { RSBUILD_ENVIRONMENT_NAMES } from './planning'
3
+ import type { IncomingMessage, ServerResponse } from 'node:http'
4
+ import type { RsbuildConfig } from '@rsbuild/core'
5
+
6
+ type ServerSetupFn = Extract<
7
+ NonNullable<NonNullable<RsbuildConfig['server']>['setup']>,
8
+ (...args: Array<any>) => any
9
+ >
10
+ type SSRMiddleware = (
11
+ req: IncomingMessage & { originalUrl?: string },
12
+ res: ServerResponse,
13
+ next: () => void,
14
+ ) => Promise<void>
15
+
16
+ /**
17
+ * Returns a `server.setup` function for rsbuild v2.
18
+ *
19
+ * Two middleware positions are used:
20
+ *
21
+ * 1. **Setup body** (BEFORE built-ins): Intercepts `/_serverFn/` URLs so
22
+ * they never reach rsbuild's htmlFallback/htmlCompletion middleware,
23
+ * which can swallow long base64 function IDs.
24
+ *
25
+ * 2. **Returned callback** (AFTER built-ins, BEFORE fallback): Handles
26
+ * all remaining SSR requests (page navigations). This position lets
27
+ * rsbuild's asset middleware serve compiled JS/CSS first.
28
+ *
29
+ * See rsbuild source: devMiddlewares.ts `applyDefaultMiddlewares()`.
30
+ */
31
+ export function createServerSetup(opts: {
32
+ serverFnBasePath: string
33
+ }): ServerSetupFn {
34
+ return (context) => {
35
+ // Only install SSR middleware in dev mode
36
+ if (context.action !== 'dev') {
37
+ return () => {}
38
+ }
39
+
40
+ const serverFnBase = opts.serverFnBasePath
41
+
42
+ const handleSSR: SSRMiddleware = async (req, res, next) => {
43
+ const ssrEnv =
44
+ context.server.environments[RSBUILD_ENVIRONMENT_NAMES.server]
45
+
46
+ if (!ssrEnv) {
47
+ console.error(
48
+ `[tanstack-start] SSR environment "${RSBUILD_ENVIRONMENT_NAMES.server}" not found`,
49
+ )
50
+ return next()
51
+ }
52
+
53
+ try {
54
+ const serverEntry = await ssrEnv.loadBundle<{
55
+ default: { fetch: (req: Request) => Promise<Response> }
56
+ }>('index')
57
+
58
+ // Restore the original URL (rsbuild may rewrite to /index.html)
59
+ if (req.originalUrl) {
60
+ req.url = req.originalUrl
61
+ }
62
+
63
+ const webReq = new NodeRequest({ req, res })
64
+ const webRes = await serverEntry.default.fetch(webReq)
65
+ return sendNodeResponse(res, webRes)
66
+ } catch (e) {
67
+ console.error('[tanstack-start] SSR error:', e)
68
+
69
+ const webReq = new NodeRequest({ req, res })
70
+ if (webReq.headers.get('content-type')?.includes('application/json')) {
71
+ return sendNodeResponse(
72
+ res,
73
+ new Response(
74
+ JSON.stringify(
75
+ {
76
+ status: 500,
77
+ error: 'Internal Server Error',
78
+ message:
79
+ 'An unexpected error occurred. Please try again later.',
80
+ timestamp: new Date().toISOString(),
81
+ },
82
+ null,
83
+ 2,
84
+ ),
85
+ {
86
+ status: 500,
87
+ headers: { 'Content-Type': 'application/json' },
88
+ },
89
+ ),
90
+ )
91
+ }
92
+
93
+ return sendNodeResponse(
94
+ res,
95
+ new Response(
96
+ `<!DOCTYPE html>
97
+ <html lang="en">
98
+ <head><meta charset="UTF-8" /><title>Error</title></head>
99
+ <body>
100
+ <h1>Internal Server Error</h1>
101
+ <pre>${e instanceof Error ? e.message : String(e)}</pre>
102
+ </body>
103
+ </html>`,
104
+ {
105
+ status: 500,
106
+ headers: { 'Content-Type': 'text/html' },
107
+ },
108
+ ),
109
+ )
110
+ }
111
+ }
112
+
113
+ // Position 1: BEFORE built-ins — intercept server function calls
114
+ // early so they are not swallowed by htmlFallback or assetsMiddleware.
115
+ context.server.middlewares.use(async (req, res, next) => {
116
+ const url = req.url || '/'
117
+ if (url.startsWith(serverFnBase)) {
118
+ return handleSSR(req, res, next)
119
+ }
120
+ return next()
121
+ })
122
+
123
+ // Position 2: AFTER built-ins, before fallback — SSR catch-all for
124
+ // page navigations. Assets are already handled by rsbuild middleware.
125
+ return () => {
126
+ context.server.middlewares.use(handleSSR)
127
+ }
128
+ }
129
+ }