@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,96 @@
1
+ import { join } from 'pathe'
2
+ import { postBuild } from '../post-build'
3
+ import { prerender } from '../prerender'
4
+ import type { PrerenderHandler } from '../prerender'
5
+ import type { TanStackStartOutputConfig } from '../schema'
6
+
7
+ export async function postBuildWithRsbuild({
8
+ startConfig,
9
+ clientOutputDirectory,
10
+ serverOutputDirectory,
11
+ }: {
12
+ startConfig: TanStackStartOutputConfig
13
+ clientOutputDirectory: string
14
+ serverOutputDirectory: string
15
+ }) {
16
+ await postBuild({
17
+ startConfig,
18
+ adapter: {
19
+ getClientOutputDirectory() {
20
+ return clientOutputDirectory
21
+ },
22
+ prerender(startConfig) {
23
+ return prerender({
24
+ startConfig,
25
+ handler: createRsbuildPrerenderHandler({
26
+ clientOutputDirectory,
27
+ serverOutputDirectory,
28
+ }),
29
+ })
30
+ },
31
+ },
32
+ })
33
+ }
34
+
35
+ function createRsbuildPrerenderHandler({
36
+ clientOutputDirectory,
37
+ serverOutputDirectory,
38
+ }: {
39
+ clientOutputDirectory: string
40
+ serverOutputDirectory: string
41
+ }): PrerenderHandler {
42
+ process.env.TSS_PRERENDERING = 'true'
43
+ process.env.TSS_CLIENT_OUTPUT_DIR = clientOutputDirectory
44
+
45
+ let requestHandlerPromise:
46
+ | Promise<
47
+ (request: Request, opts?: unknown) => Promise<Response> | Response
48
+ >
49
+ | undefined
50
+
51
+ return {
52
+ getClientOutputDirectory() {
53
+ return clientOutputDirectory
54
+ },
55
+ async request(path, options) {
56
+ const requestHandler = await getRequestHandler()
57
+ const url = new URL(path, 'http://localhost')
58
+
59
+ return requestHandler(
60
+ new Request(url, {
61
+ ...options,
62
+ redirect: 'manual',
63
+ }),
64
+ )
65
+ },
66
+ }
67
+
68
+ function getRequestHandler() {
69
+ if (!requestHandlerPromise) {
70
+ requestHandlerPromise = loadRequestHandler(serverOutputDirectory)
71
+ }
72
+
73
+ return requestHandlerPromise
74
+ }
75
+ }
76
+
77
+ async function loadRequestHandler(serverOutputDirectory: string) {
78
+ const { pathToFileURL } = await import('node:url')
79
+ const serverEntryUrl = pathToFileURL(
80
+ join(serverOutputDirectory, 'index.js'),
81
+ ).toString()
82
+ const serverModule = await import(serverEntryUrl)
83
+ const handler = serverModule.default
84
+
85
+ if (typeof handler === 'function') {
86
+ return handler as (request: Request) => Promise<Response> | Response
87
+ }
88
+
89
+ if (handler && typeof handler.fetch === 'function') {
90
+ return (request: Request) => handler.fetch(request)
91
+ }
92
+
93
+ throw new Error(
94
+ `Unable to resolve a request handler from Rsbuild server bundle at ${serverEntryUrl}`,
95
+ )
96
+ }
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod'
2
+ import {
3
+ parseStartConfig as parseCoreStartConfig,
4
+ tanstackStartOptionsObjectSchema,
5
+ } from '../schema'
6
+ import type { CompileStartFrameworkOptions } from '../types'
7
+
8
+ export const tanstackStartRsbuildOptionsSchema =
9
+ tanstackStartOptionsObjectSchema
10
+ .extend({
11
+ rsbuild: z
12
+ .object({ installDevServerMiddleware: z.boolean().optional() })
13
+ .optional(),
14
+ })
15
+ .optional()
16
+ .default({})
17
+
18
+ export function parseStartConfig(
19
+ opts: z.input<typeof tanstackStartRsbuildOptionsSchema>,
20
+ corePluginOpts: { framework: CompileStartFrameworkOptions },
21
+ root: string,
22
+ ) {
23
+ const { rsbuild: _rsbuild, ...coreOptions } =
24
+ tanstackStartRsbuildOptionsSchema.parse(opts)
25
+
26
+ return parseCoreStartConfig(coreOptions, corePluginOpts, root)
27
+ }
28
+
29
+ export type TanStackStartRsbuildInputConfig = z.input<
30
+ typeof tanstackStartRsbuildOptionsSchema
31
+ >
@@ -0,0 +1,250 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks'
2
+ import { pathToFileURL } from 'node:url'
3
+ import { TRANSFORM_ID_REGEX } from '../constants'
4
+ import { detectKindsInCode } from '../start-compiler/compiler'
5
+ import { getTransformCodeFilterForEnv } from '../start-compiler/config'
6
+ import {
7
+ createStartCompiler,
8
+ matchesCodeFilters,
9
+ mergeServerFnsById,
10
+ } from '../start-compiler/host'
11
+ import { cleanId } from '../start-compiler/utils'
12
+ import { RSBUILD_ENVIRONMENT_NAMES } from './planning'
13
+ import type { RsbuildPluginAPI, Rspack } from '@rsbuild/core'
14
+ import type { CompileStartFrameworkOptions } from '../types'
15
+ import type {
16
+ DevServerFnModuleSpecifierEncoder,
17
+ GenerateFunctionIdFnOptional,
18
+ ServerFn,
19
+ } from '../start-compiler/types'
20
+
21
+ type RsbuildTransformContext = Parameters<
22
+ Parameters<RsbuildPluginAPI['transform']>[1]
23
+ >[0]
24
+ type RsbuildInputFileSystem = NonNullable<Rspack.Compiler['inputFileSystem']>
25
+
26
+ /**
27
+ * Rsbuild dev server fn ref strategy: uses file:// URLs for absolute paths.
28
+ * These are directly importable by Node's ESM VM runner without any bundler
29
+ * path conventions (unlike Vite's /@id/ prefix).
30
+ */
31
+ const rsbuildDevServerFnModuleSpecifierEncoder: DevServerFnModuleSpecifierEncoder =
32
+ ({ extractedFilename }) => pathToFileURL(extractedFilename).href
33
+
34
+ export interface StartCompilerHostOptions {
35
+ framework: CompileStartFrameworkOptions
36
+ root: string | (() => string)
37
+ providerEnvName: string
38
+ generateFunctionId?: GenerateFunctionIdFnOptional
39
+ serverFnsById?: Record<string, ServerFn>
40
+ onServerFnsByIdChange?: () => void
41
+ }
42
+
43
+ /**
44
+ * Registers the shared StartCompiler as rsbuild transforms for client + ssr environments.
45
+ *
46
+ * Uses `api.transform()` to hook into the rsbuild loader pipeline, and the
47
+ * transform context's native `resolve()` for module resolution.
48
+ */
49
+ export function registerStartCompilerTransforms(
50
+ api: RsbuildPluginAPI,
51
+ opts: StartCompilerHostOptions,
52
+ ): {
53
+ serverFnsById: Record<string, ServerFn>
54
+ } {
55
+ const compilers = new Map<string, ReturnType<typeof createStartCompiler>>()
56
+ const inputFileSystems = new Map<string, RsbuildInputFileSystem>()
57
+ const transformContextStorage =
58
+ new AsyncLocalStorage<RsbuildTransformContext>()
59
+ const serverFnsById = opts.serverFnsById ?? {}
60
+ const getRoot = () =>
61
+ typeof opts.root === 'function' ? opts.root() : opts.root
62
+
63
+ const onServerFnsById = (d: Record<string, ServerFn>) => {
64
+ mergeServerFnsById(serverFnsById, d)
65
+ opts.onServerFnsByIdChange?.()
66
+ }
67
+
68
+ const isDev = api.context.action === 'dev'
69
+ const mode = isDev ? 'dev' : 'build'
70
+
71
+ const environments: Array<{
72
+ name: string
73
+ type: 'client' | 'server'
74
+ }> = [
75
+ { name: RSBUILD_ENVIRONMENT_NAMES.client, type: 'client' },
76
+ { name: RSBUILD_ENVIRONMENT_NAMES.server, type: 'server' },
77
+ ]
78
+
79
+ // Pre-compute code filter patterns per environment type
80
+ const codeFilters: Record<'client' | 'server', Array<RegExp>> = {
81
+ client: getTransformCodeFilterForEnv('client'),
82
+ server: getTransformCodeFilterForEnv('server'),
83
+ }
84
+
85
+ for (const env of environments) {
86
+ const envCodeFilters = codeFilters[env.type]
87
+
88
+ api.transform(
89
+ {
90
+ test: TRANSFORM_ID_REGEX[0],
91
+ environments: [env.name],
92
+ order: 'pre',
93
+ },
94
+ async (ctx: RsbuildTransformContext) => {
95
+ return transformContextStorage.run(ctx, async () => {
96
+ const code = ctx.code
97
+ const id = ctx.resourcePath + (ctx.resourceQuery || '')
98
+
99
+ // Quick string-level check: does this file contain any patterns for this env?
100
+ if (!matchesCodeFilters(code, envCodeFilters)) {
101
+ return code
102
+ }
103
+
104
+ let compiler = compilers.get(env.name)
105
+ if (!compiler) {
106
+ const root = getRoot()
107
+
108
+ compiler = createStartCompiler({
109
+ env: env.type,
110
+ envName: env.name,
111
+ root,
112
+ mode,
113
+ framework: opts.framework,
114
+ providerEnvName: opts.providerEnvName,
115
+ generateFunctionId: opts.generateFunctionId,
116
+ onServerFnsById,
117
+ getKnownServerFns: () => serverFnsById,
118
+ encodeModuleSpecifierInDev: isDev
119
+ ? rsbuildDevServerFnModuleSpecifierEncoder
120
+ : undefined,
121
+ loadModule: async (moduleId: string) => {
122
+ const activeCtx = transformContextStorage.getStore()
123
+ if (!activeCtx) {
124
+ throw new Error(
125
+ `could not load module ${moduleId}: missing active rsbuild transform context for ${env.name}`,
126
+ )
127
+ }
128
+
129
+ const inputFileSystem = inputFileSystems.get(env.name)
130
+ if (!inputFileSystem) {
131
+ throw new Error(
132
+ `could not load module ${moduleId}: missing rspack input filesystem for ${env.name}`,
133
+ )
134
+ }
135
+
136
+ const cleanedId = cleanId(moduleId)
137
+ activeCtx.addDependency(cleanedId)
138
+ const loaded = await readFileFromInputFileSystem(
139
+ inputFileSystem,
140
+ cleanedId,
141
+ )
142
+ const moduleCode = Buffer.isBuffer(loaded)
143
+ ? loaded.toString('utf8')
144
+ : loaded
145
+
146
+ compiler!.ingestModule({ code: moduleCode, id: cleanedId })
147
+ },
148
+ resolveId: async (
149
+ source: string,
150
+ importer?: string,
151
+ ): Promise<string | null> => {
152
+ const activeCtx = transformContextStorage.getStore()
153
+ if (!activeCtx) {
154
+ throw new Error(
155
+ `could not resolve ${source}: missing active rsbuild transform context for ${env.name}`,
156
+ )
157
+ }
158
+
159
+ const context = importer
160
+ ? importer.replace(/[/\\][^/\\]*$/, '')
161
+ : getRoot()
162
+
163
+ return await new Promise((resolve, reject) => {
164
+ activeCtx.resolve(context, source, (error, resolved) => {
165
+ if (error) {
166
+ reject(error)
167
+ return
168
+ }
169
+
170
+ if (!resolved) {
171
+ resolve(null)
172
+ return
173
+ }
174
+
175
+ resolve(cleanId(resolved))
176
+ })
177
+ })
178
+ },
179
+ })
180
+ compilers.set(env.name, compiler)
181
+ }
182
+
183
+ const detectedKinds = detectKindsInCode(code, env.type)
184
+ const result = await compiler.compile({ id, code, detectedKinds })
185
+
186
+ if (!result) {
187
+ return code
188
+ }
189
+
190
+ return {
191
+ code: result.code,
192
+ map: result.map ?? null,
193
+ }
194
+ })
195
+ },
196
+ )
197
+ }
198
+
199
+ api.modifyRspackConfig((config, utils) => {
200
+ config.plugins.push({
201
+ apply(compiler: Rspack.Compiler) {
202
+ if (compiler.inputFileSystem) {
203
+ inputFileSystems.set(utils.environment.name, compiler.inputFileSystem)
204
+ }
205
+
206
+ compiler.hooks.watchRun.tap(
207
+ 'TanStackStartCompilerModuleInvalidation',
208
+ (watchCompiler) => {
209
+ const startCompiler = compilers.get(utils.environment.name)
210
+
211
+ if (!startCompiler) {
212
+ return
213
+ }
214
+
215
+ for (const file of watchCompiler.modifiedFiles ?? []) {
216
+ startCompiler.invalidateModule(file)
217
+ }
218
+
219
+ for (const file of watchCompiler.removedFiles ?? []) {
220
+ startCompiler.invalidateModule(file)
221
+ }
222
+ },
223
+ )
224
+ },
225
+ })
226
+ })
227
+
228
+ return { serverFnsById }
229
+ }
230
+
231
+ function readFileFromInputFileSystem(
232
+ inputFileSystem: RsbuildInputFileSystem,
233
+ file: string,
234
+ ): Promise<string | Buffer> {
235
+ return new Promise((resolve, reject) => {
236
+ inputFileSystem.readFile(file, (error, data) => {
237
+ if (error) {
238
+ reject(error)
239
+ return
240
+ }
241
+
242
+ if (data == null) {
243
+ reject(new Error(`could not read module source for ${file}`))
244
+ return
245
+ }
246
+
247
+ resolve(data)
248
+ })
249
+ })
250
+ }
@@ -0,0 +1,86 @@
1
+ import path from 'pathe'
2
+ import { routesManifestPlugin } from '../start-router-plugin/generator-plugins/routes-manifest-plugin'
3
+ import { prerenderRoutesPlugin } from '../start-router-plugin/generator-plugins/prerender-routes-plugin'
4
+ import { buildRouteTreeFileFooterFromConfig } from '../start-router-plugin/route-tree-footer'
5
+ import { RSBUILD_ENVIRONMENT_NAMES } from './planning'
6
+ import type { RsbuildPluginAPI } from '@rsbuild/core'
7
+ import type { GetConfigFn, TanStackStartCoreOptions } from '../types'
8
+ import type { TanStackStartRsbuildInputConfig } from './schema'
9
+
10
+ /**
11
+ * Registers the TanStack Router generator and code-splitter plugins
12
+ * as rspack plugins via `modifyRspackConfig`.
13
+ *
14
+ * The router-plugin package exports rspack-compatible unplugin wrappers:
15
+ * - TanStackRouterGeneratorRspack: file-based route generation
16
+ * - TanStackRouterCodeSplitterRspack: route code splitting
17
+ *
18
+ * These are dynamically imported to avoid hard dependency on router-plugin/rspack.
19
+ */
20
+ export function registerRouterPlugins(
21
+ api: RsbuildPluginAPI,
22
+ opts: {
23
+ getConfig: GetConfigFn
24
+ corePluginOpts: TanStackStartCoreOptions
25
+ startPluginOpts: TanStackStartRsbuildInputConfig
26
+ },
27
+ ): void {
28
+ api.modifyRspackConfig(async (config, utils) => {
29
+ const envName = utils.environment.name
30
+ const { startConfig } = opts.getConfig()
31
+ const routerConfig = startConfig.router
32
+
33
+ // Generator only runs once — register for the client environment
34
+ if (envName === RSBUILD_ENVIRONMENT_NAMES.client) {
35
+ try {
36
+ const { TanStackRouterGeneratorRspack } =
37
+ await import('@tanstack/router-plugin/rspack')
38
+ const generatorPlugin = TanStackRouterGeneratorRspack({
39
+ ...routerConfig,
40
+ target: opts.corePluginOpts.framework,
41
+ routeTreeFileFooter: () => {
42
+ return buildRouteTreeFileFooterFromConfig({
43
+ generatedRouteTreePath: path.resolve(
44
+ routerConfig.generatedRouteTree,
45
+ ),
46
+ getConfig: opts.getConfig,
47
+ corePluginOpts: opts.corePluginOpts,
48
+ })
49
+ },
50
+ plugins: [
51
+ routesManifestPlugin(),
52
+ ...(opts.startPluginOpts?.prerender?.enabled === true
53
+ ? [prerenderRoutesPlugin()]
54
+ : []),
55
+ ],
56
+ })
57
+ utils.appendPlugins(generatorPlugin as any)
58
+ } catch {
59
+ // router-plugin/rspack not available — skip
60
+ }
61
+ }
62
+
63
+ if (
64
+ envName === RSBUILD_ENVIRONMENT_NAMES.client ||
65
+ envName === RSBUILD_ENVIRONMENT_NAMES.server
66
+ ) {
67
+ const isClient = envName === RSBUILD_ENVIRONMENT_NAMES.client
68
+ try {
69
+ const { TanStackRouterCodeSplitterRspack } =
70
+ await import('@tanstack/router-plugin/rspack')
71
+ const splitterPlugin = TanStackRouterCodeSplitterRspack({
72
+ ...routerConfig,
73
+ target: opts.corePluginOpts.framework,
74
+ codeSplittingOptions: {
75
+ ...routerConfig.codeSplittingOptions,
76
+ deleteNodes: isClient ? ['ssr', 'server', 'headers'] : undefined,
77
+ addHmr: isClient,
78
+ },
79
+ })
80
+ utils.appendPlugins(splitterPlugin as any)
81
+ } catch {
82
+ // router-plugin/rspack not available — skip
83
+ }
84
+ }
85
+ })
86
+ }
@@ -0,0 +1,166 @@
1
+ import { RSBUILD_RSC_LAYERS } from './planning'
2
+ import type { ModifyRspackConfigFn, Rspack } from '@rsbuild/core'
3
+
4
+ type RspackConfig = Parameters<ModifyRspackConfigFn>[0]
5
+ type RspackRule = Rspack.RuleSetRule
6
+
7
+ /**
8
+ * Walk the rspack config's module.rules and inject
9
+ * `rspackExperiments.reactServerComponents: true` into SWC loaders.
10
+ *
11
+ * Recurses into `oneOf` arrays because rsbuild nests the main SWC loader
12
+ * inside a `oneOf` rule (e.g. separate branches for asset/source vs
13
+ * javascript/auto). Without recursion, only the mimetype-based fallback
14
+ * SWC rule gets the flag, leaving most .js/.ts files without RSC
15
+ * directive detection.
16
+ */
17
+ export function enableSwcReactServerComponents(
18
+ config: RspackConfig,
19
+ scope: 'all' | 'rsc-subtree',
20
+ ): void {
21
+ const isRspackRule = (
22
+ rule: NonNullable<RspackConfig['module']['rules']>[number],
23
+ ): rule is RspackRule => !!rule && rule !== '...'
24
+ const getRuleLoaders = (rule: RspackRule) => {
25
+ const { use } = rule
26
+ if (!use) {
27
+ return []
28
+ }
29
+
30
+ return typeof use === 'function' ? [] : Array.isArray(use) ? use : [use]
31
+ }
32
+ const getLoaderPath = (
33
+ loader: ReturnType<typeof getRuleLoaders>[number],
34
+ ): string | undefined => (typeof loader === 'string' ? loader : loader.loader)
35
+ const cloneLoader = (
36
+ loader: ReturnType<typeof getRuleLoaders>[number],
37
+ ): ReturnType<typeof getRuleLoaders>[number] => {
38
+ if (typeof loader === 'string') {
39
+ return loader
40
+ }
41
+
42
+ const options = loader.options
43
+ return {
44
+ ...loader,
45
+ ...(options && typeof options === 'object' && !Array.isArray(options)
46
+ ? {
47
+ options: {
48
+ ...options,
49
+ ...(options.rspackExperiments &&
50
+ typeof options.rspackExperiments === 'object' &&
51
+ !Array.isArray(options.rspackExperiments)
52
+ ? {
53
+ rspackExperiments: {
54
+ ...options.rspackExperiments,
55
+ },
56
+ }
57
+ : {}),
58
+ },
59
+ }
60
+ : {}),
61
+ }
62
+ }
63
+ const cloneRuleUse = (use: RspackRule['use']): RspackRule['use'] => {
64
+ if (!use || typeof use === 'function') {
65
+ return use
66
+ }
67
+
68
+ if (Array.isArray(use)) {
69
+ return use.map((loader) => cloneLoader(loader)) as RspackRule['use']
70
+ }
71
+
72
+ return cloneLoader(use) as RspackRule['use']
73
+ }
74
+ const cloneRspackRule = (rule: RspackRule): RspackRule => {
75
+ return {
76
+ ...rule,
77
+ use: cloneRuleUse(rule.use),
78
+ resolve: rule.resolve ? { ...rule.resolve } : rule.resolve,
79
+ oneOf: Array.isArray(rule.oneOf)
80
+ ? rule.oneOf.map((childRule) =>
81
+ isRspackRule(childRule) ? cloneRspackRule(childRule) : childRule,
82
+ )
83
+ : rule.oneOf,
84
+ } as unknown as RspackRule
85
+ }
86
+ const rootRules = (config.module.rules ??= []).filter(isRspackRule)
87
+
88
+ function processRules(rules = rootRules): void {
89
+ for (const rule of rules) {
90
+ processRules(
91
+ Array.isArray(rule.oneOf) ? rule.oneOf.filter(isRspackRule) : [],
92
+ )
93
+
94
+ const hasSwcLoader = getRuleLoaders(rule).some((loader) =>
95
+ Boolean(getLoaderPath(loader)?.includes('swc-loader')),
96
+ )
97
+ if (!hasSwcLoader) continue
98
+
99
+ const enableReactServerComponentsOnRule = (nextRule: RspackRule) => {
100
+ for (const loader of getRuleLoaders(nextRule)) {
101
+ if (typeof loader === 'string') {
102
+ continue
103
+ }
104
+
105
+ const loaderPath = getLoaderPath(loader)
106
+ if (!loaderPath || !loaderPath.includes('swc-loader')) {
107
+ continue
108
+ }
109
+
110
+ const options =
111
+ loader.options && typeof loader.options === 'object'
112
+ ? loader.options
113
+ : (loader.options = {})
114
+ const experiments =
115
+ options.rspackExperiments &&
116
+ typeof options.rspackExperiments === 'object'
117
+ ? options.rspackExperiments
118
+ : (options.rspackExperiments = {})
119
+ const current = experiments.reactServerComponents
120
+
121
+ experiments.reactServerComponents =
122
+ current === true || current == null
123
+ ? {}
124
+ : typeof current === 'object' &&
125
+ current !== null &&
126
+ !Array.isArray(current)
127
+ ? { ...current }
128
+ : {}
129
+ }
130
+ }
131
+
132
+ if (scope === 'all') {
133
+ enableReactServerComponentsOnRule(rule)
134
+ continue
135
+ }
136
+
137
+ const originalRule = cloneRspackRule(rule)
138
+ const providerRule = cloneRspackRule(originalRule)
139
+ providerRule.resourceQuery = /(?:^|[?&])tss-serverfn-split(?:&|$)/
140
+ enableReactServerComponentsOnRule(providerRule)
141
+
142
+ const routeSplitRule = cloneRspackRule(originalRule)
143
+ routeSplitRule.resourceQuery = /(?:^|[?&])tsr-split(?:=|&|$)/
144
+ const routeSplitConditionNames = originalRule.resolve?.conditionNames
145
+ routeSplitRule.resolve = {
146
+ ...originalRule.resolve,
147
+ conditionNames: Array.isArray(routeSplitConditionNames)
148
+ ? routeSplitConditionNames.includes('...')
149
+ ? [...routeSplitConditionNames]
150
+ : ['...', ...routeSplitConditionNames]
151
+ : ['...'],
152
+ }
153
+
154
+ const subtreeRule = cloneRspackRule(originalRule)
155
+ subtreeRule.issuerLayer = RSBUILD_RSC_LAYERS.rsc
156
+ enableReactServerComponentsOnRule(subtreeRule)
157
+
158
+ for (const key of Object.keys(rule) as Array<keyof RspackRule>) {
159
+ delete rule[key]
160
+ }
161
+ rule.oneOf = [providerRule, routeSplitRule, subtreeRule, originalRule]
162
+ }
163
+ }
164
+
165
+ processRules()
166
+ }
@@ -0,0 +1,20 @@
1
+ import type { EnvironmentConfig } from '@rsbuild/core'
2
+ import type { TanStackStartCoreOptions } from '../types'
3
+
4
+ export interface RsbuildEnvironmentOverrides {
5
+ all?: EnvironmentConfig | undefined
6
+ client?: EnvironmentConfig | undefined
7
+ server?: EnvironmentConfig | undefined
8
+ provider?: EnvironmentConfig | undefined
9
+ }
10
+
11
+ export interface RsbuildCoreOptions {
12
+ environments?: RsbuildEnvironmentOverrides | undefined
13
+ }
14
+
15
+ export type TanStackStartRsbuildPluginCoreOptions = TanStackStartCoreOptions & {
16
+ providerEnvironmentName: string
17
+ ssrIsProvider: boolean
18
+ rsbuild?: RsbuildCoreOptions | undefined
19
+ rsc?: boolean | undefined
20
+ }