@tanstack/start-plugin-core 1.167.31 → 1.167.33

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.
@@ -1,6 +1,7 @@
1
1
  export type { TanStackStartInputConfig } from './schema.js';
2
2
  export type { TanStackStartCoreOptions } from './types.js';
3
- export type { TanStackStartVitePluginCoreOptions } from './vite/types.js';
3
+ export type { TanStackStartVitePluginCoreOptions, ViteRscForwardSsrResolverStrategy, } from './vite/types.js';
4
4
  export type { TanStackStartViteInputConfig } from './vite/schema.js';
5
5
  export { START_ENVIRONMENT_NAMES, VITE_ENVIRONMENT_NAMES } from './constants.js';
6
+ export { createVirtualModule } from './vite/createVirtualModule.js';
6
7
  export { tanStackStartVite } from './vite/plugin.js';
package/dist/esm/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  import { START_ENVIRONMENT_NAMES, VITE_ENVIRONMENT_NAMES } from "./constants.js";
2
+ import { createVirtualModule } from "./vite/createVirtualModule.js";
2
3
  import { tanStackStartVite } from "./vite/plugin.js";
3
- export { START_ENVIRONMENT_NAMES, VITE_ENVIRONMENT_NAMES, tanStackStartVite };
4
+ export { START_ENVIRONMENT_NAMES, VITE_ENVIRONMENT_NAMES, createVirtualModule, tanStackStartVite };
@@ -0,0 +1,12 @@
1
+ import { Plugin } from 'vite';
2
+ type VirtualModuleLoadHandler = (this: any, id: string) => string | null | undefined | Promise<string | null | undefined>;
3
+ export declare function createVirtualModule(opts: {
4
+ name: string;
5
+ moduleId: string;
6
+ load: VirtualModuleLoadHandler;
7
+ apply?: Plugin['apply'];
8
+ applyToEnvironment?: Plugin['applyToEnvironment'];
9
+ enforce?: Plugin['enforce'];
10
+ sharedDuringBuild?: boolean;
11
+ }): Plugin;
12
+ export {};
@@ -0,0 +1,32 @@
1
+ import { resolveViteId } from "../utils.js";
2
+ //#region src/vite/createVirtualModule.ts
3
+ function escapeRegExp(value) {
4
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5
+ }
6
+ function createVirtualModule(opts) {
7
+ const resolvedId = resolveViteId(opts.moduleId.replaceAll("#", "%23"));
8
+ return {
9
+ name: opts.name,
10
+ apply: opts.apply,
11
+ applyToEnvironment: opts.applyToEnvironment,
12
+ enforce: opts.enforce,
13
+ sharedDuringBuild: opts.sharedDuringBuild,
14
+ resolveId: {
15
+ filter: { id: new RegExp(escapeRegExp(opts.moduleId)) },
16
+ handler(id) {
17
+ if (id === opts.moduleId) return resolvedId;
18
+ }
19
+ },
20
+ load: {
21
+ filter: { id: new RegExp(escapeRegExp(resolvedId)) },
22
+ handler(id) {
23
+ if (id !== resolvedId) return;
24
+ return opts.load.call(this, id);
25
+ }
26
+ }
27
+ };
28
+ }
29
+ //#endregion
30
+ export { createVirtualModule };
31
+
32
+ //# sourceMappingURL=createVirtualModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createVirtualModule.js","names":[],"sources":["../../../src/vite/createVirtualModule.ts"],"sourcesContent":["import { resolveViteId } from '../utils'\nimport type { Plugin } from 'vite'\n\ntype VirtualModuleLoadHandler = (\n this: any,\n id: string,\n) => string | null | undefined | Promise<string | null | undefined>\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\nexport function createVirtualModule(opts: {\n name: string\n moduleId: string\n load: VirtualModuleLoadHandler\n apply?: Plugin['apply']\n applyToEnvironment?: Plugin['applyToEnvironment']\n enforce?: Plugin['enforce']\n sharedDuringBuild?: boolean\n}): Plugin {\n // Encode '#' as '%23' in the resolved ID to avoid browser treating it as URL fragment.\n // The browser requests /@id/__x00__%23tanstack-start-plugin-adapters instead of\n // /@id/__x00__#tanstack-start-plugin-adapters (which would truncate at #).\n const resolvedId = resolveViteId(opts.moduleId.replaceAll('#', '%23'))\n\n return {\n name: opts.name,\n apply: opts.apply,\n applyToEnvironment: opts.applyToEnvironment,\n enforce: opts.enforce,\n sharedDuringBuild: opts.sharedDuringBuild,\n resolveId: {\n filter: { id: new RegExp(escapeRegExp(opts.moduleId)) },\n handler(id) {\n if (id === opts.moduleId) {\n return resolvedId\n }\n\n return undefined\n },\n },\n load: {\n filter: { id: new RegExp(escapeRegExp(resolvedId)) },\n handler(id) {\n if (id !== resolvedId) {\n return undefined\n }\n\n return opts.load.call(this, id)\n },\n },\n }\n}\n"],"mappings":";;AAQA,SAAS,aAAa,OAAuB;AAC3C,QAAO,MAAM,QAAQ,uBAAuB,OAAO;;AAGrD,SAAgB,oBAAoB,MAQzB;CAIT,MAAM,aAAa,cAAc,KAAK,SAAS,WAAW,KAAK,MAAM,CAAC;AAEtE,QAAO;EACL,MAAM,KAAK;EACX,OAAO,KAAK;EACZ,oBAAoB,KAAK;EACzB,SAAS,KAAK;EACd,mBAAmB,KAAK;EACxB,WAAW;GACT,QAAQ,EAAE,IAAI,IAAI,OAAO,aAAa,KAAK,SAAS,CAAC,EAAE;GACvD,QAAQ,IAAI;AACV,QAAI,OAAO,KAAK,SACd,QAAO;;GAKZ;EACD,MAAM;GACJ,QAAQ,EAAE,IAAI,IAAI,OAAO,aAAa,WAAW,CAAC,EAAE;GACpD,QAAQ,IAAI;AACV,QAAI,OAAO,WACT;AAGF,WAAO,KAAK,KAAK,KAAK,MAAM,GAAG;;GAElC;EACF"}
@@ -1,5 +1,5 @@
1
1
  import { ENTRY_POINTS, VITE_ENVIRONMENT_NAMES } from "../../constants.js";
2
- import { resolveViteId } from "../../utils.js";
2
+ import { createVirtualModule } from "../createVirtualModule.js";
3
3
  import { extractHtmlScripts } from "./extract-html-scripts.js";
4
4
  import { CSS_MODULES_REGEX, collectDevStyles, normalizeCssModuleCacheKey } from "./dev-styles.js";
5
5
  import { isRunnableDevEnvironment } from "vite";
@@ -105,24 +105,16 @@ function devServerPlugin({ getConfig: _getConfig, devSsrStylesEnabled, installDe
105
105
  });
106
106
  };
107
107
  }
108
- }, {
108
+ }, createVirtualModule({
109
109
  name: "tanstack-start-core:dev-server:injected-head-scripts",
110
110
  sharedDuringBuild: true,
111
111
  applyToEnvironment: (env) => env.config.consumer === "server",
112
- resolveId: {
113
- filter: { id: new RegExp(VIRTUAL_MODULES.injectedHeadScripts) },
114
- handler(_id) {
115
- return resolveViteId(VIRTUAL_MODULES.injectedHeadScripts);
116
- }
117
- },
118
- load: {
119
- filter: { id: new RegExp(resolveViteId(VIRTUAL_MODULES.injectedHeadScripts)) },
120
- handler() {
121
- return `
112
+ moduleId: VIRTUAL_MODULES.injectedHeadScripts,
113
+ load() {
114
+ return `
122
115
  export const injectedHeadScripts = ${JSON.stringify(injectedHeadScripts) || "undefined"}`;
123
- }
124
116
  }
125
- }];
117
+ })];
126
118
  }
127
119
  /**
128
120
  * Formats error for SSR message in error overlay
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":[],"sources":["../../../../src/vite/dev-server-plugin/plugin.ts"],"sourcesContent":["import { isRunnableDevEnvironment } from 'vite'\nimport { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport { NodeRequest, sendNodeResponse } from 'srvx/node'\nimport { ENTRY_POINTS, VITE_ENVIRONMENT_NAMES } from '../../constants'\nimport { resolveViteId } from '../../utils'\nimport { extractHtmlScripts } from './extract-html-scripts'\nimport {\n CSS_MODULES_REGEX,\n collectDevStyles,\n normalizeCssModuleCacheKey,\n} from './dev-styles'\nimport type { Connect, DevEnvironment, PluginOption } from 'vite'\nimport type { GetConfigFn } from '../../types'\n\nexport function devServerPlugin({\n getConfig: _getConfig,\n devSsrStylesEnabled,\n installDevServerMiddleware,\n}: {\n getConfig: GetConfigFn\n devSsrStylesEnabled: boolean\n installDevServerMiddleware: boolean | undefined\n}): PluginOption {\n let isTest = false\n\n let injectedHeadScripts: string | undefined\n\n // Cache CSS modules content during transform hook.\n // For CSS modules, the transform hook receives the raw CSS content before\n // Vite wraps it in JS. We capture this to use during SSR style collection.\n const cssModulesCache: Record<string, string> = {}\n\n return [\n {\n name: 'tanstack-start-core:dev-server',\n config(_userConfig, { mode }) {\n isTest = isTest ? isTest : mode === 'test'\n },\n // Capture CSS modules content during transform\n transform: {\n filter: {\n id: CSS_MODULES_REGEX,\n },\n handler(code, id) {\n cssModulesCache[normalizeCssModuleCacheKey(id)] = code\n },\n },\n async configureServer(viteDevServer) {\n if (isTest) {\n return\n }\n\n // Extract the scripts that Vite plugins would inject into the initial HTML\n const templateHtml = `<html><head></head><body></body></html>`\n const transformedHtml = await viteDevServer.transformIndexHtml(\n '/',\n templateHtml,\n )\n const scripts = extractHtmlScripts(transformedHtml)\n injectedHeadScripts = scripts\n .flatMap((script) => script.content ?? [])\n .join(';')\n\n // CSS middleware registered in PRE-PHASE (before Vite's internal middlewares)\n // This ensures it handles /@tanstack-start/styles.css before any catch-all middleware\n // from other plugins (like nitro) that may be registered in the post-phase.\n // This makes the CSS endpoint work regardless of plugin order in the Vite config.\n // We check pathname.endsWith() to handle basepaths (e.g., /my-app/@tanstack-start/styles.css)\n // since pre-phase runs before Vite's base middleware strips the basepath.\n if (devSsrStylesEnabled) {\n viteDevServer.middlewares.use(async (req, res, next) => {\n const url = req.url ?? ''\n const pathname = url.split('?')[0]\n if (!pathname?.endsWith('/@tanstack-start/styles.css')) {\n return next()\n }\n\n try {\n // Parse route IDs from query param\n const urlObj = new URL(url, 'http://localhost')\n const routesParam = urlObj.searchParams.get('routes')\n const routeIds = routesParam ? routesParam.split(',') : []\n\n // Build entries list from route file paths\n const entries: Array<string> = []\n\n // Look up route file paths from manifest\n // Only routes registered in the manifest are used - this prevents path injection\n const routesManifest = (globalThis as any).TSS_ROUTES_MANIFEST as\n | Record<string, { filePath: string; children?: Array<string> }>\n | undefined\n\n if (routesManifest && routeIds.length > 0) {\n for (const routeId of routeIds) {\n const route = routesManifest[routeId]\n if (route?.filePath) {\n entries.push(route.filePath)\n }\n }\n }\n\n const css =\n entries.length > 0\n ? await collectDevStyles({\n viteDevServer,\n entries,\n cssModulesCache,\n })\n : undefined\n\n res.setHeader('Content-Type', 'text/css')\n res.setHeader('Cache-Control', 'no-store')\n res.end(css ?? '')\n } catch (e) {\n // Log error but still return valid CSS response to avoid MIME type issues\n console.error('[tanstack-start] Error collecting dev styles:', e)\n res.setHeader('Content-Type', 'text/css')\n res.setHeader('Cache-Control', 'no-store')\n res.end(\n `/* Error collecting styles: ${e instanceof Error ? e.message : String(e)} */`,\n )\n }\n })\n }\n\n return () => {\n const serverEnv = viteDevServer.environments[\n VITE_ENVIRONMENT_NAMES.server\n ] as DevEnvironment | undefined\n\n if (!serverEnv) {\n throw new Error(\n `Server environment ${VITE_ENVIRONMENT_NAMES.server} not found`,\n )\n }\n\n const installMiddleware = installDevServerMiddleware\n if (installMiddleware === false) {\n return\n }\n if (installMiddleware == undefined) {\n // do not install middleware in middlewareMode by default\n if (viteDevServer.config.server.middlewareMode) {\n return\n }\n\n // do not install middleware if SSR env in case another plugin already did\n if (\n !isRunnableDevEnvironment(serverEnv) ||\n // do not check via `isFetchableDevEnvironment` since nitro does implement the `FetchableDevEnvironment` interface but not via inheritance (which this helper checks)\n 'dispatchFetch' in serverEnv\n ) {\n return\n }\n }\n\n if (!isRunnableDevEnvironment(serverEnv)) {\n throw new Error(\n 'cannot install vite dev server middleware for TanStack Start since the SSR environment is not a RunnableDevEnvironment',\n )\n }\n\n viteDevServer.middlewares.use(async (req, res) => {\n // fix the request URL to match the original URL\n // otherwise, the request URL will '/index.html'\n if (req.originalUrl) {\n req.url = req.originalUrl\n }\n const webReq = new NodeRequest({ req, res })\n\n try {\n // Import and resolve the request by running the server request entry point\n // this request entry point must implement the `fetch` API as follows:\n /**\n * export default {\n * fetch(req: Request): Promise<Response>\n * }\n */\n const serverEntry = await serverEnv.runner.import(\n ENTRY_POINTS.server,\n )\n const webRes = await serverEntry['default'].fetch(webReq)\n\n return sendNodeResponse(res, webRes)\n } catch (e) {\n console.error(e)\n try {\n viteDevServer.ssrFixStacktrace(e as Error)\n } catch {}\n\n if (\n webReq.headers.get('content-type')?.includes('application/json')\n ) {\n return sendNodeResponse(\n res,\n new Response(\n JSON.stringify(\n {\n status: 500,\n error: 'Internal Server Error',\n message:\n 'An unexpected error occurred. Please try again later.',\n timestamp: new Date().toISOString(),\n },\n null,\n 2,\n ),\n {\n status: 500,\n headers: {\n 'Content-Type': 'application/json',\n },\n },\n ),\n )\n }\n\n return sendNodeResponse(\n res,\n new Response(\n `\n <!DOCTYPE html>\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <title>Error</title>\n <script type=\"module\">\n import { ErrorOverlay } from '/@vite/client'\n document.body.appendChild(new ErrorOverlay(${JSON.stringify(\n prepareError(req, e),\n ).replace(/</g, '\\\\u003c')}))\n </script>\n </head>\n <body>\n </body>\n </html>\n `,\n {\n status: 500,\n headers: {\n 'Content-Type': 'text/html',\n },\n },\n ),\n )\n }\n })\n }\n },\n },\n {\n name: 'tanstack-start-core:dev-server:injected-head-scripts',\n sharedDuringBuild: true,\n applyToEnvironment: (env) => env.config.consumer === 'server',\n resolveId: {\n filter: { id: new RegExp(VIRTUAL_MODULES.injectedHeadScripts) },\n handler(_id) {\n return resolveViteId(VIRTUAL_MODULES.injectedHeadScripts)\n },\n },\n load: {\n filter: {\n id: new RegExp(resolveViteId(VIRTUAL_MODULES.injectedHeadScripts)),\n },\n handler() {\n const mod = `\n export const injectedHeadScripts = ${JSON.stringify(injectedHeadScripts) || 'undefined'}`\n return mod\n },\n },\n },\n ]\n}\n\n/**\n * Formats error for SSR message in error overlay\n * @param req\n * @param error\n * @returns\n */\nfunction prepareError(req: Connect.IncomingMessage, error: unknown) {\n const e = error as Error\n return {\n message: `An error occurred while server rendering ${req.url}:\\n\\n\\t${\n typeof e === 'string' ? e : e.message\n } `,\n stack: typeof e === 'string' ? '' : e.stack,\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../../../../src/vite/dev-server-plugin/plugin.ts"],"sourcesContent":["import { isRunnableDevEnvironment } from 'vite'\nimport { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport { NodeRequest, sendNodeResponse } from 'srvx/node'\nimport { ENTRY_POINTS, VITE_ENVIRONMENT_NAMES } from '../../constants'\nimport { createVirtualModule } from '../createVirtualModule'\nimport { extractHtmlScripts } from './extract-html-scripts'\nimport {\n CSS_MODULES_REGEX,\n collectDevStyles,\n normalizeCssModuleCacheKey,\n} from './dev-styles'\nimport type { Connect, DevEnvironment, PluginOption } from 'vite'\nimport type { GetConfigFn } from '../../types'\n\nexport function devServerPlugin({\n getConfig: _getConfig,\n devSsrStylesEnabled,\n installDevServerMiddleware,\n}: {\n getConfig: GetConfigFn\n devSsrStylesEnabled: boolean\n installDevServerMiddleware: boolean | undefined\n}): PluginOption {\n let isTest = false\n\n let injectedHeadScripts: string | undefined\n\n // Cache CSS modules content during transform hook.\n // For CSS modules, the transform hook receives the raw CSS content before\n // Vite wraps it in JS. We capture this to use during SSR style collection.\n const cssModulesCache: Record<string, string> = {}\n\n return [\n {\n name: 'tanstack-start-core:dev-server',\n config(_userConfig, { mode }) {\n isTest = isTest ? isTest : mode === 'test'\n },\n // Capture CSS modules content during transform\n transform: {\n filter: {\n id: CSS_MODULES_REGEX,\n },\n handler(code, id) {\n cssModulesCache[normalizeCssModuleCacheKey(id)] = code\n },\n },\n async configureServer(viteDevServer) {\n if (isTest) {\n return\n }\n\n // Extract the scripts that Vite plugins would inject into the initial HTML\n const templateHtml = `<html><head></head><body></body></html>`\n const transformedHtml = await viteDevServer.transformIndexHtml(\n '/',\n templateHtml,\n )\n const scripts = extractHtmlScripts(transformedHtml)\n injectedHeadScripts = scripts\n .flatMap((script) => script.content ?? [])\n .join(';')\n\n // CSS middleware registered in PRE-PHASE (before Vite's internal middlewares)\n // This ensures it handles /@tanstack-start/styles.css before any catch-all middleware\n // from other plugins (like nitro) that may be registered in the post-phase.\n // This makes the CSS endpoint work regardless of plugin order in the Vite config.\n // We check pathname.endsWith() to handle basepaths (e.g., /my-app/@tanstack-start/styles.css)\n // since pre-phase runs before Vite's base middleware strips the basepath.\n if (devSsrStylesEnabled) {\n viteDevServer.middlewares.use(async (req, res, next) => {\n const url = req.url ?? ''\n const pathname = url.split('?')[0]\n if (!pathname?.endsWith('/@tanstack-start/styles.css')) {\n return next()\n }\n\n try {\n // Parse route IDs from query param\n const urlObj = new URL(url, 'http://localhost')\n const routesParam = urlObj.searchParams.get('routes')\n const routeIds = routesParam ? routesParam.split(',') : []\n\n // Build entries list from route file paths\n const entries: Array<string> = []\n\n // Look up route file paths from manifest\n // Only routes registered in the manifest are used - this prevents path injection\n const routesManifest = (globalThis as any).TSS_ROUTES_MANIFEST as\n | Record<string, { filePath: string; children?: Array<string> }>\n | undefined\n\n if (routesManifest && routeIds.length > 0) {\n for (const routeId of routeIds) {\n const route = routesManifest[routeId]\n if (route?.filePath) {\n entries.push(route.filePath)\n }\n }\n }\n\n const css =\n entries.length > 0\n ? await collectDevStyles({\n viteDevServer,\n entries,\n cssModulesCache,\n })\n : undefined\n\n res.setHeader('Content-Type', 'text/css')\n res.setHeader('Cache-Control', 'no-store')\n res.end(css ?? '')\n } catch (e) {\n // Log error but still return valid CSS response to avoid MIME type issues\n console.error('[tanstack-start] Error collecting dev styles:', e)\n res.setHeader('Content-Type', 'text/css')\n res.setHeader('Cache-Control', 'no-store')\n res.end(\n `/* Error collecting styles: ${e instanceof Error ? e.message : String(e)} */`,\n )\n }\n })\n }\n\n return () => {\n const serverEnv = viteDevServer.environments[\n VITE_ENVIRONMENT_NAMES.server\n ] as DevEnvironment | undefined\n\n if (!serverEnv) {\n throw new Error(\n `Server environment ${VITE_ENVIRONMENT_NAMES.server} not found`,\n )\n }\n\n const installMiddleware = installDevServerMiddleware\n if (installMiddleware === false) {\n return\n }\n if (installMiddleware == undefined) {\n // do not install middleware in middlewareMode by default\n if (viteDevServer.config.server.middlewareMode) {\n return\n }\n\n // do not install middleware if SSR env in case another plugin already did\n if (\n !isRunnableDevEnvironment(serverEnv) ||\n // do not check via `isFetchableDevEnvironment` since nitro does implement the `FetchableDevEnvironment` interface but not via inheritance (which this helper checks)\n 'dispatchFetch' in serverEnv\n ) {\n return\n }\n }\n\n if (!isRunnableDevEnvironment(serverEnv)) {\n throw new Error(\n 'cannot install vite dev server middleware for TanStack Start since the SSR environment is not a RunnableDevEnvironment',\n )\n }\n\n viteDevServer.middlewares.use(async (req, res) => {\n // fix the request URL to match the original URL\n // otherwise, the request URL will '/index.html'\n if (req.originalUrl) {\n req.url = req.originalUrl\n }\n const webReq = new NodeRequest({ req, res })\n\n try {\n // Import and resolve the request by running the server request entry point\n // this request entry point must implement the `fetch` API as follows:\n /**\n * export default {\n * fetch(req: Request): Promise<Response>\n * }\n */\n const serverEntry = await serverEnv.runner.import(\n ENTRY_POINTS.server,\n )\n const webRes = await serverEntry['default'].fetch(webReq)\n\n return sendNodeResponse(res, webRes)\n } catch (e) {\n console.error(e)\n try {\n viteDevServer.ssrFixStacktrace(e as Error)\n } catch {}\n\n if (\n webReq.headers.get('content-type')?.includes('application/json')\n ) {\n return sendNodeResponse(\n res,\n new Response(\n JSON.stringify(\n {\n status: 500,\n error: 'Internal Server Error',\n message:\n 'An unexpected error occurred. Please try again later.',\n timestamp: new Date().toISOString(),\n },\n null,\n 2,\n ),\n {\n status: 500,\n headers: {\n 'Content-Type': 'application/json',\n },\n },\n ),\n )\n }\n\n return sendNodeResponse(\n res,\n new Response(\n `\n <!DOCTYPE html>\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <title>Error</title>\n <script type=\"module\">\n import { ErrorOverlay } from '/@vite/client'\n document.body.appendChild(new ErrorOverlay(${JSON.stringify(\n prepareError(req, e),\n ).replace(/</g, '\\\\u003c')}))\n </script>\n </head>\n <body>\n </body>\n </html>\n `,\n {\n status: 500,\n headers: {\n 'Content-Type': 'text/html',\n },\n },\n ),\n )\n }\n })\n }\n },\n },\n createVirtualModule({\n name: 'tanstack-start-core:dev-server:injected-head-scripts',\n sharedDuringBuild: true,\n applyToEnvironment: (env) => env.config.consumer === 'server',\n moduleId: VIRTUAL_MODULES.injectedHeadScripts,\n load() {\n const mod = `\n export const injectedHeadScripts = ${JSON.stringify(injectedHeadScripts) || 'undefined'}`\n return mod\n },\n }),\n ]\n}\n\n/**\n * Formats error for SSR message in error overlay\n * @param req\n * @param error\n * @returns\n */\nfunction prepareError(req: Connect.IncomingMessage, error: unknown) {\n const e = error as Error\n return {\n message: `An error occurred while server rendering ${req.url}:\\n\\n\\t${\n typeof e === 'string' ? e : e.message\n } `,\n stack: typeof e === 'string' ? '' : e.stack,\n }\n}\n"],"mappings":";;;;;;;;AAcA,SAAgB,gBAAgB,EAC9B,WAAW,YACX,qBACA,8BAKe;CACf,IAAI,SAAS;CAEb,IAAI;CAKJ,MAAM,kBAA0C,EAAE;AAElD,QAAO,CACL;EACE,MAAM;EACN,OAAO,aAAa,EAAE,QAAQ;AAC5B,YAAS,SAAS,SAAS,SAAS;;EAGtC,WAAW;GACT,QAAQ,EACN,IAAI,mBACL;GACD,QAAQ,MAAM,IAAI;AAChB,oBAAgB,2BAA2B,GAAG,IAAI;;GAErD;EACD,MAAM,gBAAgB,eAAe;AACnC,OAAI,OACF;AAUF,yBADgB,mBAJQ,MAAM,cAAc,mBAC1C,KAFmB,0CAIpB,CACkD,CAEhD,SAAS,WAAW,OAAO,WAAW,EAAE,CAAC,CACzC,KAAK,IAAI;AAQZ,OAAI,oBACF,eAAc,YAAY,IAAI,OAAO,KAAK,KAAK,SAAS;IACtD,MAAM,MAAM,IAAI,OAAO;AAEvB,QAAI,CADa,IAAI,MAAM,IAAI,CAAC,IACjB,SAAS,8BAA8B,CACpD,QAAO,MAAM;AAGf,QAAI;KAGF,MAAM,cADS,IAAI,IAAI,KAAK,mBAAmB,CACpB,aAAa,IAAI,SAAS;KACrD,MAAM,WAAW,cAAc,YAAY,MAAM,IAAI,GAAG,EAAE;KAG1D,MAAM,UAAyB,EAAE;KAIjC,MAAM,iBAAkB,WAAmB;AAI3C,SAAI,kBAAkB,SAAS,SAAS,EACtC,MAAK,MAAM,WAAW,UAAU;MAC9B,MAAM,QAAQ,eAAe;AAC7B,UAAI,OAAO,SACT,SAAQ,KAAK,MAAM,SAAS;;KAKlC,MAAM,MACJ,QAAQ,SAAS,IACb,MAAM,iBAAiB;MACrB;MACA;MACA;MACD,CAAC,GACF,KAAA;AAEN,SAAI,UAAU,gBAAgB,WAAW;AACzC,SAAI,UAAU,iBAAiB,WAAW;AAC1C,SAAI,IAAI,OAAO,GAAG;aACX,GAAG;AAEV,aAAQ,MAAM,iDAAiD,EAAE;AACjE,SAAI,UAAU,gBAAgB,WAAW;AACzC,SAAI,UAAU,iBAAiB,WAAW;AAC1C,SAAI,IACF,+BAA+B,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE,CAAC,KAC3E;;KAEH;AAGJ,gBAAa;IACX,MAAM,YAAY,cAAc,aAC9B,uBAAuB;AAGzB,QAAI,CAAC,UACH,OAAM,IAAI,MACR,sBAAsB,uBAAuB,OAAO,YACrD;IAGH,MAAM,oBAAoB;AAC1B,QAAI,sBAAsB,MACxB;AAEF,QAAI,qBAAqB,KAAA,GAAW;AAElC,SAAI,cAAc,OAAO,OAAO,eAC9B;AAIF,SACE,CAAC,yBAAyB,UAAU,IAEpC,mBAAmB,UAEnB;;AAIJ,QAAI,CAAC,yBAAyB,UAAU,CACtC,OAAM,IAAI,MACR,yHACD;AAGH,kBAAc,YAAY,IAAI,OAAO,KAAK,QAAQ;AAGhD,SAAI,IAAI,YACN,KAAI,MAAM,IAAI;KAEhB,MAAM,SAAS,IAAI,YAAY;MAAE;MAAK;MAAK,CAAC;AAE5C,SAAI;AAaF,aAAO,iBAAiB,KAFT,OAHK,MAAM,UAAU,OAAO,OACzC,aAAa,OACd,EACgC,WAAW,MAAM,OAAO,CAErB;cAC7B,GAAG;AACV,cAAQ,MAAM,EAAE;AAChB,UAAI;AACF,qBAAc,iBAAiB,EAAW;cACpC;AAER,UACE,OAAO,QAAQ,IAAI,eAAe,EAAE,SAAS,mBAAmB,CAEhE,QAAO,iBACL,KACA,IAAI,SACF,KAAK,UACH;OACE,QAAQ;OACR,OAAO;OACP,SACE;OACF,4BAAW,IAAI,MAAM,EAAC,aAAa;OACpC,EACD,MACA,EACD,EACD;OACE,QAAQ;OACR,SAAS,EACP,gBAAgB,oBACjB;OACF,CACF,CACF;AAGH,aAAO,iBACL,KACA,IAAI,SACF;;;;;;;;iEAQ+C,KAAK,UAChD,aAAa,KAAK,EAAE,CACrB,CAAC,QAAQ,MAAM,UAAU,CAAC;;;;;;eAO7B;OACE,QAAQ;OACR,SAAS,EACP,gBAAgB,aACjB;OACF,CACF,CACF;;MAEH;;;EAGP,EACD,oBAAoB;EAClB,MAAM;EACN,mBAAmB;EACnB,qBAAqB,QAAQ,IAAI,OAAO,aAAa;EACrD,UAAU,gBAAgB;EAC1B,OAAO;AAGL,UAFY;6CACyB,KAAK,UAAU,oBAAoB,IAAI;;EAG/E,CAAC,CACH;;;;;;;;AASH,SAAS,aAAa,KAA8B,OAAgB;CAClE,MAAM,IAAI;AACV,QAAO;EACL,SAAS,4CAA4C,IAAI,IAAI,SAC3D,OAAO,MAAM,WAAW,IAAI,EAAE,QAC/B;EACD,OAAO,OAAO,MAAM,WAAW,KAAK,EAAE;EACvC"}
@@ -8,7 +8,7 @@ import { buildStartViteEnvironments, createViteConfigPlan, createViteDefineConfi
8
8
  import { devServerPlugin } from "./dev-server-plugin/plugin.js";
9
9
  import { getClientOutputDirectory, getServerOutputDirectory } from "./output-directory.js";
10
10
  import { previewServerPlugin } from "./preview-server-plugin/plugin.js";
11
- import { createCaptureClientBuildPlugin, createDevBaseRewritePlugin, createPostBuildPlugin } from "./plugins.js";
11
+ import { createCaptureClientBuildPlugin, createDevBaseRewritePlugin, createPostBuildPlugin, createVirtualClientEntryPlugin } from "./plugins.js";
12
12
  import { parseStartConfig } from "./schema.js";
13
13
  import { startManifestPlugin } from "./start-manifest-plugin/plugin.js";
14
14
  import { tanStackStartRouter } from "./start-router-plugin/plugin.js";
@@ -139,6 +139,7 @@ function tanStackStartVite(corePluginOpts, startPluginOpts) {
139
139
  }),
140
140
  tanStackStartRouter(normalizedStartPluginOpts, getConfig, corePluginOpts),
141
141
  loadEnvPlugin(),
142
+ createVirtualClientEntryPlugin({ getClientEntry: () => configContext.resolveEntries().entryPaths.client }),
142
143
  startManifestPlugin({
143
144
  getClientBuild: () => getClientBuild(START_ENVIRONMENT_NAMES.client),
144
145
  getConfig
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":[],"sources":["../../../src/vite/plugin.ts"],"sourcesContent":["import { crawlFrameworkPkgs } from 'vitefu'\nimport {\n applyResolvedBaseAndOutput,\n applyResolvedRouterBasepath,\n createStartConfigContext,\n} from '../config-context'\nimport { START_ENVIRONMENT_NAMES } from '../constants'\nimport { importProtectionPlugin } from '../import-protection-plugin/plugin'\nimport {\n createServerFnBasePath,\n normalizePublicBase,\n shouldRewriteDevBasepath,\n} from '../planning'\nimport { startCompilerPlugin } from './start-compiler-plugin/plugin'\nimport { loadEnvPlugin } from './load-env-plugin/plugin'\nimport {\n buildStartViteEnvironments,\n createViteConfigPlan,\n createViteDefineConfig,\n createViteResolvedEntryAliases,\n} from './planning'\nimport { devServerPlugin } from './dev-server-plugin/plugin'\nimport { previewServerPlugin } from './preview-server-plugin/plugin'\nimport {\n createCaptureClientBuildPlugin,\n createDevBaseRewritePlugin,\n createPostBuildPlugin,\n} from './plugins'\nimport { parseStartConfig } from './schema'\nimport { startManifestPlugin } from './start-manifest-plugin/plugin'\nimport { tanStackStartRouter } from './start-router-plugin/plugin'\nimport {\n getClientOutputDirectory,\n getServerOutputDirectory,\n} from './output-directory'\nimport { postServerBuild } from './post-server-build'\nimport { serializationAdaptersPlugin } from './serialization-adapters-plugin'\nimport type { NormalizedClientBuild } from '../types'\nimport type {\n TanStackStartVitePluginCoreOptions,\n ViteRscForwardSsrResolverStrategy,\n} from './types'\nimport type { StartEnvironmentName } from '../constants'\nimport type { TanStackStartViteInputConfig } from './schema'\nimport type { PluginOption } from 'vite'\n\nexport function tanStackStartVite(\n corePluginOpts: TanStackStartVitePluginCoreOptions,\n startPluginOpts: TanStackStartViteInputConfig | undefined,\n): Array<PluginOption> {\n const normalizedStartPluginOpts = startPluginOpts ?? {}\n\n const configContext = createStartConfigContext({\n corePluginOpts,\n startPluginOpts: normalizedStartPluginOpts,\n parseConfig: parseStartConfig,\n })\n const { getConfig, resolvedStartConfig } = configContext\n const serverFnProviderEnv = corePluginOpts.providerEnvironmentName\n const ssrIsProvider = corePluginOpts.ssrIsProvider\n\n // When the router basepath and vite base are misaligned during dev,\n // we install a URL rewrite middleware instead of erroring.\n let needsDevBaseRewrite = false\n\n const capturedClientBuild: Partial<\n Record<StartEnvironmentName, NormalizedClientBuild>\n > = {}\n\n function getClientBuild(\n envName: StartEnvironmentName,\n ): NormalizedClientBuild | undefined {\n return capturedClientBuild[envName]\n }\n\n const environments: Array<{\n name: string\n type: 'client' | 'server'\n getServerFnById?: string\n }> = [\n { name: START_ENVIRONMENT_NAMES.client, type: 'client' },\n {\n name: START_ENVIRONMENT_NAMES.server,\n type: 'server',\n getServerFnById:\n corePluginOpts.ssrResolverStrategy.type === 'vite-rsc-forward'\n ? createViteRscForwarder(corePluginOpts.ssrResolverStrategy)\n : undefined,\n },\n ]\n if (\n serverFnProviderEnv !== START_ENVIRONMENT_NAMES.server &&\n !environments.find((e) => e.name === serverFnProviderEnv)\n ) {\n environments.push({\n name: serverFnProviderEnv,\n type: 'server',\n })\n }\n return [\n {\n name: 'tanstack-start-core:config',\n enforce: 'pre',\n async config(viteConfig, { command }) {\n const publicBase = normalizePublicBase(viteConfig.base)\n applyResolvedBaseAndOutput({\n resolvedStartConfig,\n root: viteConfig.root || process.cwd(),\n publicBase,\n clientOutputDirectory: getClientOutputDirectory(viteConfig),\n serverOutputDirectory: getServerOutputDirectory(viteConfig),\n })\n const { startConfig } = getConfig()\n const routerBasepath = applyResolvedRouterBasepath({\n resolvedStartConfig,\n startConfig,\n })\n\n if (\n shouldRewriteDevBasepath({\n command,\n middlewareMode: Boolean(viteConfig.server?.middlewareMode),\n routerBasepath,\n publicBase: resolvedStartConfig.basePaths.publicBase,\n })\n ) {\n // The router basepath and vite base are misaligned.\n // Instead of erroring, we install a dev-server middleware that\n // rewrites incoming request URLs to prepend the vite base prefix.\n // This allows users to have e.g. base: '/_ui/' for asset URLs\n // while keeping router basepath at '/' for page navigation.\n needsDevBaseRewrite = true\n }\n\n const TSS_SERVER_FN_BASE = createServerFnBasePath({\n routerBasepath,\n serverFnBase: startConfig.serverFns.base,\n })\n const resolvedEntryPlan = configContext.resolveEntries()\n\n const entryAliases = createViteResolvedEntryAliases({\n entryPaths: resolvedEntryPlan.entryPaths,\n })\n\n const startPackageName =\n `@tanstack/${corePluginOpts.framework}-start` as const\n\n // crawl packages that have start in \"peerDependencies\"\n // see https://github.com/svitejs/vitefu/blob/d8d82fa121e3b2215ba437107093c77bde51b63b/src/index.js#L95-L101\n\n // this is currently uncached; could be implemented similarly as vite handles lock file changes\n // see https://github.com/vitejs/vite/blob/557f797d29422027e8c451ca50dd84bf8c41b5f0/packages/vite/src/node/optimizer/index.ts#L1282\n\n const crawlFrameworkPkgsResult = await crawlFrameworkPkgs({\n root: process.cwd(),\n isBuild: command === 'build',\n isFrameworkPkgByJson(pkgJson) {\n const peerDependencies = pkgJson['peerDependencies']\n\n if (peerDependencies) {\n if (\n startPackageName in peerDependencies ||\n '@tanstack/start-client-core' in peerDependencies\n ) {\n return true\n }\n }\n\n return false\n },\n })\n\n const viteConfigPlan = createViteConfigPlan({\n viteConfig,\n framework: corePluginOpts.framework,\n entryAliases,\n clientOutputDirectory: resolvedStartConfig.outputDirectories.client,\n serverOutputDirectory: resolvedStartConfig.outputDirectories.server,\n serverFnProviderEnv,\n optimizeDepsExclude: crawlFrameworkPkgsResult.optimizeDeps.exclude,\n noExternal: crawlFrameworkPkgsResult.ssr.noExternal.sort(),\n })\n\n return {\n // see https://vite.dev/config/shared-options.html#apptype\n // this will prevent vite from injecting middlewares that we don't want\n appType: viteConfig.appType ?? 'custom',\n environments: viteConfigPlan.environments,\n resolve: viteConfigPlan.resolve,\n define: createViteDefineConfig({\n command,\n mode: viteConfig.mode,\n serverFnBase: TSS_SERVER_FN_BASE,\n routerBasepath,\n spaEnabled: startConfig.spa?.enabled,\n devSsrStylesEnabled: startConfig.dev.ssrStyles.enabled,\n devSsrStylesBasepath:\n startConfig.dev.ssrStyles.basepath ??\n resolvedStartConfig.basePaths.publicBase,\n staticNodeEnv: startConfig.server.build.staticNodeEnv,\n }),\n builder: {\n sharedPlugins: true,\n async buildApp(builder) {\n await buildStartViteEnvironments({\n builder,\n providerEnvironmentName: serverFnProviderEnv,\n ssrIsProvider,\n })\n },\n },\n }\n },\n },\n createPostBuildPlugin({\n getConfig,\n postServerBuild,\n }),\n // Server function plugin handles:\n // 1. Identifying createServerFn().handler() calls\n // 2. Extracting server functions to separate modules\n // 3. Replacing call sites with RPC stubs\n // 4. Generating the server function manifest\n // Also handles createIsomorphicFn, createServerOnlyFn, createClientOnlyFn, createMiddleware\n startCompilerPlugin({\n framework: corePluginOpts.framework,\n environments,\n generateFunctionId:\n normalizedStartPluginOpts.serverFns?.generateFunctionId,\n providerEnvName: serverFnProviderEnv,\n }),\n importProtectionPlugin({\n getConfig,\n framework: corePluginOpts.framework,\n environments,\n providerEnvName: serverFnProviderEnv,\n }),\n tanStackStartRouter(normalizedStartPluginOpts, getConfig, corePluginOpts),\n loadEnvPlugin(),\n startManifestPlugin({\n getClientBuild: () => getClientBuild(START_ENVIRONMENT_NAMES.client),\n getConfig,\n }),\n // When the vite base and router basepath are misaligned (e.g. base: '/_ui/', basepath: '/'),\n // install a middleware that rewrites incoming request URLs to prepend the vite base prefix.\n // This allows Vite's internal base middleware to accept the requests, then strips the prefix\n // before passing to the SSR handler.\n // Registered BEFORE devServerPlugin so this middleware is added to the Connect stack first,\n // ensuring all subsequent middlewares (CSS, SSR, etc.) see the rewritten URL.\n createDevBaseRewritePlugin({\n shouldRewriteDevBase: () => needsDevBaseRewrite,\n resolvedStartConfig,\n }),\n devServerPlugin({\n getConfig,\n devSsrStylesEnabled:\n normalizedStartPluginOpts.dev?.ssrStyles?.enabled ?? true,\n installDevServerMiddleware:\n normalizedStartPluginOpts.vite?.installDevServerMiddleware,\n }),\n previewServerPlugin(),\n serializationAdaptersPlugin({\n adapters: corePluginOpts.serializationAdapters,\n }),\n createCaptureClientBuildPlugin({\n capturedClientBuild,\n }),\n ]\n}\n\nfunction createViteRscForwarder(strategy: ViteRscForwardSsrResolverStrategy) {\n return `export async function getServerFnById(id, access) {\n const m = await import.meta.viteRsc.loadModule(${JSON.stringify(strategy.sourceEnvironmentName)}, ${JSON.stringify(strategy.sourceEntry)})\n return m[${JSON.stringify(strategy.exportName)}](id, access)\n}`\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../../../src/vite/plugin.ts"],"sourcesContent":["import { crawlFrameworkPkgs } from 'vitefu'\nimport {\n applyResolvedBaseAndOutput,\n applyResolvedRouterBasepath,\n createStartConfigContext,\n} from '../config-context'\nimport { START_ENVIRONMENT_NAMES } from '../constants'\nimport { importProtectionPlugin } from '../import-protection-plugin/plugin'\nimport {\n createServerFnBasePath,\n normalizePublicBase,\n shouldRewriteDevBasepath,\n} from '../planning'\nimport { startCompilerPlugin } from './start-compiler-plugin/plugin'\nimport { loadEnvPlugin } from './load-env-plugin/plugin'\nimport {\n buildStartViteEnvironments,\n createViteConfigPlan,\n createViteDefineConfig,\n createViteResolvedEntryAliases,\n} from './planning'\nimport { devServerPlugin } from './dev-server-plugin/plugin'\nimport { previewServerPlugin } from './preview-server-plugin/plugin'\nimport {\n createCaptureClientBuildPlugin,\n createDevBaseRewritePlugin,\n createPostBuildPlugin,\n createVirtualClientEntryPlugin,\n} from './plugins'\nimport { parseStartConfig } from './schema'\nimport { startManifestPlugin } from './start-manifest-plugin/plugin'\nimport { tanStackStartRouter } from './start-router-plugin/plugin'\nimport {\n getClientOutputDirectory,\n getServerOutputDirectory,\n} from './output-directory'\nimport { postServerBuild } from './post-server-build'\nimport { serializationAdaptersPlugin } from './serialization-adapters-plugin'\nimport type { NormalizedClientBuild } from '../types'\nimport type {\n TanStackStartVitePluginCoreOptions,\n ViteRscForwardSsrResolverStrategy,\n} from './types'\nimport type { StartEnvironmentName } from '../constants'\nimport type { TanStackStartViteInputConfig } from './schema'\nimport type { PluginOption } from 'vite'\n\nexport function tanStackStartVite(\n corePluginOpts: TanStackStartVitePluginCoreOptions,\n startPluginOpts: TanStackStartViteInputConfig | undefined,\n): Array<PluginOption> {\n const normalizedStartPluginOpts = startPluginOpts ?? {}\n\n const configContext = createStartConfigContext({\n corePluginOpts,\n startPluginOpts: normalizedStartPluginOpts,\n parseConfig: parseStartConfig,\n })\n const { getConfig, resolvedStartConfig } = configContext\n const serverFnProviderEnv = corePluginOpts.providerEnvironmentName\n const ssrIsProvider = corePluginOpts.ssrIsProvider\n\n // When the router basepath and vite base are misaligned during dev,\n // we install a URL rewrite middleware instead of erroring.\n let needsDevBaseRewrite = false\n\n const capturedClientBuild: Partial<\n Record<StartEnvironmentName, NormalizedClientBuild>\n > = {}\n\n function getClientBuild(\n envName: StartEnvironmentName,\n ): NormalizedClientBuild | undefined {\n return capturedClientBuild[envName]\n }\n\n const environments: Array<{\n name: string\n type: 'client' | 'server'\n getServerFnById?: string\n }> = [\n { name: START_ENVIRONMENT_NAMES.client, type: 'client' },\n {\n name: START_ENVIRONMENT_NAMES.server,\n type: 'server',\n getServerFnById:\n corePluginOpts.ssrResolverStrategy.type === 'vite-rsc-forward'\n ? createViteRscForwarder(corePluginOpts.ssrResolverStrategy)\n : undefined,\n },\n ]\n if (\n serverFnProviderEnv !== START_ENVIRONMENT_NAMES.server &&\n !environments.find((e) => e.name === serverFnProviderEnv)\n ) {\n environments.push({\n name: serverFnProviderEnv,\n type: 'server',\n })\n }\n return [\n {\n name: 'tanstack-start-core:config',\n enforce: 'pre',\n async config(viteConfig, { command }) {\n const publicBase = normalizePublicBase(viteConfig.base)\n applyResolvedBaseAndOutput({\n resolvedStartConfig,\n root: viteConfig.root || process.cwd(),\n publicBase,\n clientOutputDirectory: getClientOutputDirectory(viteConfig),\n serverOutputDirectory: getServerOutputDirectory(viteConfig),\n })\n const { startConfig } = getConfig()\n const routerBasepath = applyResolvedRouterBasepath({\n resolvedStartConfig,\n startConfig,\n })\n\n if (\n shouldRewriteDevBasepath({\n command,\n middlewareMode: Boolean(viteConfig.server?.middlewareMode),\n routerBasepath,\n publicBase: resolvedStartConfig.basePaths.publicBase,\n })\n ) {\n // The router basepath and vite base are misaligned.\n // Instead of erroring, we install a dev-server middleware that\n // rewrites incoming request URLs to prepend the vite base prefix.\n // This allows users to have e.g. base: '/_ui/' for asset URLs\n // while keeping router basepath at '/' for page navigation.\n needsDevBaseRewrite = true\n }\n\n const TSS_SERVER_FN_BASE = createServerFnBasePath({\n routerBasepath,\n serverFnBase: startConfig.serverFns.base,\n })\n const resolvedEntryPlan = configContext.resolveEntries()\n\n const entryAliases = createViteResolvedEntryAliases({\n entryPaths: resolvedEntryPlan.entryPaths,\n })\n\n const startPackageName =\n `@tanstack/${corePluginOpts.framework}-start` as const\n\n // crawl packages that have start in \"peerDependencies\"\n // see https://github.com/svitejs/vitefu/blob/d8d82fa121e3b2215ba437107093c77bde51b63b/src/index.js#L95-L101\n\n // this is currently uncached; could be implemented similarly as vite handles lock file changes\n // see https://github.com/vitejs/vite/blob/557f797d29422027e8c451ca50dd84bf8c41b5f0/packages/vite/src/node/optimizer/index.ts#L1282\n\n const crawlFrameworkPkgsResult = await crawlFrameworkPkgs({\n root: process.cwd(),\n isBuild: command === 'build',\n isFrameworkPkgByJson(pkgJson) {\n const peerDependencies = pkgJson['peerDependencies']\n\n if (peerDependencies) {\n if (\n startPackageName in peerDependencies ||\n '@tanstack/start-client-core' in peerDependencies\n ) {\n return true\n }\n }\n\n return false\n },\n })\n\n const viteConfigPlan = createViteConfigPlan({\n viteConfig,\n framework: corePluginOpts.framework,\n entryAliases,\n clientOutputDirectory: resolvedStartConfig.outputDirectories.client,\n serverOutputDirectory: resolvedStartConfig.outputDirectories.server,\n serverFnProviderEnv,\n optimizeDepsExclude: crawlFrameworkPkgsResult.optimizeDeps.exclude,\n noExternal: crawlFrameworkPkgsResult.ssr.noExternal.sort(),\n })\n\n return {\n // see https://vite.dev/config/shared-options.html#apptype\n // this will prevent vite from injecting middlewares that we don't want\n appType: viteConfig.appType ?? 'custom',\n environments: viteConfigPlan.environments,\n resolve: viteConfigPlan.resolve,\n define: createViteDefineConfig({\n command,\n mode: viteConfig.mode,\n serverFnBase: TSS_SERVER_FN_BASE,\n routerBasepath,\n spaEnabled: startConfig.spa?.enabled,\n devSsrStylesEnabled: startConfig.dev.ssrStyles.enabled,\n devSsrStylesBasepath:\n startConfig.dev.ssrStyles.basepath ??\n resolvedStartConfig.basePaths.publicBase,\n staticNodeEnv: startConfig.server.build.staticNodeEnv,\n }),\n builder: {\n sharedPlugins: true,\n async buildApp(builder) {\n await buildStartViteEnvironments({\n builder,\n providerEnvironmentName: serverFnProviderEnv,\n ssrIsProvider,\n })\n },\n },\n }\n },\n },\n createPostBuildPlugin({\n getConfig,\n postServerBuild,\n }),\n // Server function plugin handles:\n // 1. Identifying createServerFn().handler() calls\n // 2. Extracting server functions to separate modules\n // 3. Replacing call sites with RPC stubs\n // 4. Generating the server function manifest\n // Also handles createIsomorphicFn, createServerOnlyFn, createClientOnlyFn, createMiddleware\n startCompilerPlugin({\n framework: corePluginOpts.framework,\n environments,\n generateFunctionId:\n normalizedStartPluginOpts.serverFns?.generateFunctionId,\n providerEnvName: serverFnProviderEnv,\n }),\n importProtectionPlugin({\n getConfig,\n framework: corePluginOpts.framework,\n environments,\n providerEnvName: serverFnProviderEnv,\n }),\n tanStackStartRouter(normalizedStartPluginOpts, getConfig, corePluginOpts),\n loadEnvPlugin(),\n createVirtualClientEntryPlugin({\n getClientEntry: () => configContext.resolveEntries().entryPaths.client,\n }),\n startManifestPlugin({\n getClientBuild: () => getClientBuild(START_ENVIRONMENT_NAMES.client),\n getConfig,\n }),\n // When the vite base and router basepath are misaligned (e.g. base: '/_ui/', basepath: '/'),\n // install a middleware that rewrites incoming request URLs to prepend the vite base prefix.\n // This allows Vite's internal base middleware to accept the requests, then strips the prefix\n // before passing to the SSR handler.\n // Registered BEFORE devServerPlugin so this middleware is added to the Connect stack first,\n // ensuring all subsequent middlewares (CSS, SSR, etc.) see the rewritten URL.\n createDevBaseRewritePlugin({\n shouldRewriteDevBase: () => needsDevBaseRewrite,\n resolvedStartConfig,\n }),\n devServerPlugin({\n getConfig,\n devSsrStylesEnabled:\n normalizedStartPluginOpts.dev?.ssrStyles?.enabled ?? true,\n installDevServerMiddleware:\n normalizedStartPluginOpts.vite?.installDevServerMiddleware,\n }),\n previewServerPlugin(),\n serializationAdaptersPlugin({\n adapters: corePluginOpts.serializationAdapters,\n }),\n createCaptureClientBuildPlugin({\n capturedClientBuild,\n }),\n ]\n}\n\nfunction createViteRscForwarder(strategy: ViteRscForwardSsrResolverStrategy) {\n return `export async function getServerFnById(id, access) {\n const m = await import.meta.viteRsc.loadModule(${JSON.stringify(strategy.sourceEnvironmentName)}, ${JSON.stringify(strategy.sourceEntry)})\n return m[${JSON.stringify(strategy.exportName)}](id, access)\n}`\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA+CA,SAAgB,kBACd,gBACA,iBACqB;CACrB,MAAM,4BAA4B,mBAAmB,EAAE;CAEvD,MAAM,gBAAgB,yBAAyB;EAC7C;EACA,iBAAiB;EACjB,aAAa;EACd,CAAC;CACF,MAAM,EAAE,WAAW,wBAAwB;CAC3C,MAAM,sBAAsB,eAAe;CAC3C,MAAM,gBAAgB,eAAe;CAIrC,IAAI,sBAAsB;CAE1B,MAAM,sBAEF,EAAE;CAEN,SAAS,eACP,SACmC;AACnC,SAAO,oBAAoB;;CAG7B,MAAM,eAID,CACH;EAAE,MAAM,wBAAwB;EAAQ,MAAM;EAAU,EACxD;EACE,MAAM,wBAAwB;EAC9B,MAAM;EACN,iBACE,eAAe,oBAAoB,SAAS,qBACxC,uBAAuB,eAAe,oBAAoB,GAC1D,KAAA;EACP,CACF;AACD,KACE,wBAAwB,wBAAwB,UAChD,CAAC,aAAa,MAAM,MAAM,EAAE,SAAS,oBAAoB,CAEzD,cAAa,KAAK;EAChB,MAAM;EACN,MAAM;EACP,CAAC;AAEJ,QAAO;EACL;GACE,MAAM;GACN,SAAS;GACT,MAAM,OAAO,YAAY,EAAE,WAAW;IACpC,MAAM,aAAa,oBAAoB,WAAW,KAAK;AACvD,+BAA2B;KACzB;KACA,MAAM,WAAW,QAAQ,QAAQ,KAAK;KACtC;KACA,uBAAuB,yBAAyB,WAAW;KAC3D,uBAAuB,yBAAyB,WAAW;KAC5D,CAAC;IACF,MAAM,EAAE,gBAAgB,WAAW;IACnC,MAAM,iBAAiB,4BAA4B;KACjD;KACA;KACD,CAAC;AAEF,QACE,yBAAyB;KACvB;KACA,gBAAgB,QAAQ,WAAW,QAAQ,eAAe;KAC1D;KACA,YAAY,oBAAoB,UAAU;KAC3C,CAAC,CAOF,uBAAsB;IAGxB,MAAM,qBAAqB,uBAAuB;KAChD;KACA,cAAc,YAAY,UAAU;KACrC,CAAC;IAGF,MAAM,eAAe,+BAA+B,EAClD,YAHwB,cAAc,gBAAgB,CAGxB,YAC/B,CAAC;IAEF,MAAM,mBACJ,aAAa,eAAe,UAAU;IAQxC,MAAM,2BAA2B,MAAM,mBAAmB;KACxD,MAAM,QAAQ,KAAK;KACnB,SAAS,YAAY;KACrB,qBAAqB,SAAS;MAC5B,MAAM,mBAAmB,QAAQ;AAEjC,UAAI;WAEA,oBAAoB,oBACpB,iCAAiC,iBAEjC,QAAO;;AAIX,aAAO;;KAEV,CAAC;IAEF,MAAM,iBAAiB,qBAAqB;KAC1C;KACA,WAAW,eAAe;KAC1B;KACA,uBAAuB,oBAAoB,kBAAkB;KAC7D,uBAAuB,oBAAoB,kBAAkB;KAC7D;KACA,qBAAqB,yBAAyB,aAAa;KAC3D,YAAY,yBAAyB,IAAI,WAAW,MAAM;KAC3D,CAAC;AAEF,WAAO;KAGL,SAAS,WAAW,WAAW;KAC/B,cAAc,eAAe;KAC7B,SAAS,eAAe;KACxB,QAAQ,uBAAuB;MAC7B;MACA,MAAM,WAAW;MACjB,cAAc;MACd;MACA,YAAY,YAAY,KAAK;MAC7B,qBAAqB,YAAY,IAAI,UAAU;MAC/C,sBACE,YAAY,IAAI,UAAU,YAC1B,oBAAoB,UAAU;MAChC,eAAe,YAAY,OAAO,MAAM;MACzC,CAAC;KACF,SAAS;MACP,eAAe;MACf,MAAM,SAAS,SAAS;AACtB,aAAM,2BAA2B;QAC/B;QACA,yBAAyB;QACzB;QACD,CAAC;;MAEL;KACF;;GAEJ;EACD,sBAAsB;GACpB;GACA;GACD,CAAC;EAOF,oBAAoB;GAClB,WAAW,eAAe;GAC1B;GACA,oBACE,0BAA0B,WAAW;GACvC,iBAAiB;GAClB,CAAC;EACF,uBAAuB;GACrB;GACA,WAAW,eAAe;GAC1B;GACA,iBAAiB;GAClB,CAAC;EACF,oBAAoB,2BAA2B,WAAW,eAAe;EACzE,eAAe;EACf,+BAA+B,EAC7B,sBAAsB,cAAc,gBAAgB,CAAC,WAAW,QACjE,CAAC;EACF,oBAAoB;GAClB,sBAAsB,eAAe,wBAAwB,OAAO;GACpE;GACD,CAAC;EAOF,2BAA2B;GACzB,4BAA4B;GAC5B;GACD,CAAC;EACF,gBAAgB;GACd;GACA,qBACE,0BAA0B,KAAK,WAAW,WAAW;GACvD,4BACE,0BAA0B,MAAM;GACnC,CAAC;EACF,qBAAqB;EACrB,4BAA4B,EAC1B,UAAU,eAAe,uBAC1B,CAAC;EACF,+BAA+B,EAC7B,qBACD,CAAC;EACH;;AAGH,SAAS,uBAAuB,UAA6C;AAC3E,QAAO;mDAC0C,KAAK,UAAU,SAAS,sBAAsB,CAAC,IAAI,KAAK,UAAU,SAAS,YAAY,CAAC;aAC9H,KAAK,UAAU,SAAS,WAAW,CAAC"}
@@ -1,6 +1,9 @@
1
1
  import { GetConfigFn, NormalizedClientBuild, ResolvedStartConfig } from '../types.js';
2
2
  import { StartEnvironmentName } from '../constants.js';
3
3
  import { PluginOption, ViteBuilder } from 'vite';
4
+ export declare function createVirtualClientEntryPlugin(opts: {
5
+ getClientEntry: () => string;
6
+ }): PluginOption;
4
7
  export declare function createPostBuildPlugin(opts: {
5
8
  getConfig: GetConfigFn;
6
9
  postServerBuild: (opts: {
@@ -1,6 +1,18 @@
1
- import { START_ENVIRONMENT_NAMES } from "../constants.js";
1
+ import { ENTRY_POINTS, START_ENVIRONMENT_NAMES } from "../constants.js";
2
+ import { createVirtualModule } from "./createVirtualModule.js";
2
3
  import { normalizeViteClientBuild } from "./start-manifest-plugin/normalized-client-build.js";
4
+ import { normalizePath } from "vite";
3
5
  //#region src/vite/plugins.ts
6
+ function createVirtualClientEntryPlugin(opts) {
7
+ return createVirtualModule({
8
+ name: "tanstack-start-core:virtual-client-entry",
9
+ moduleId: ENTRY_POINTS.client,
10
+ enforce: "pre",
11
+ load() {
12
+ return `import ${JSON.stringify(normalizePath(opts.getClientEntry()).replaceAll("\\", "/"))}`;
13
+ }
14
+ });
15
+ }
4
16
  function createPostBuildPlugin(opts) {
5
17
  return {
6
18
  name: "tanstack-start-core:post-build",
@@ -45,6 +57,6 @@ function createCaptureClientBuildPlugin(opts) {
45
57
  };
46
58
  }
47
59
  //#endregion
48
- export { createCaptureClientBuildPlugin, createDevBaseRewritePlugin, createPostBuildPlugin };
60
+ export { createCaptureClientBuildPlugin, createDevBaseRewritePlugin, createPostBuildPlugin, createVirtualClientEntryPlugin };
49
61
 
50
62
  //# sourceMappingURL=plugins.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugins.js","names":[],"sources":["../../../src/vite/plugins.ts"],"sourcesContent":["import { START_ENVIRONMENT_NAMES } from '../constants'\nimport { normalizeViteClientBuild } from './start-manifest-plugin/normalized-client-build'\nimport type {\n GetConfigFn,\n NormalizedClientBuild,\n ResolvedStartConfig,\n} from '../types'\nimport type { StartEnvironmentName } from '../constants'\nimport type { PluginOption, ViteBuilder } from 'vite'\n\nexport function createPostBuildPlugin(opts: {\n getConfig: GetConfigFn\n postServerBuild: (opts: {\n startConfig: ReturnType<GetConfigFn>['startConfig']\n builder: ViteBuilder\n }) => Promise<void>\n}): PluginOption {\n return {\n name: 'tanstack-start-core:post-build',\n enforce: 'post',\n buildApp: {\n order: 'post',\n async handler(builder) {\n const { startConfig } = opts.getConfig()\n await opts.postServerBuild({ builder, startConfig })\n },\n },\n }\n}\n\nexport function createDevBaseRewritePlugin(opts: {\n shouldRewriteDevBase: () => boolean\n resolvedStartConfig: ResolvedStartConfig\n}): PluginOption {\n return {\n name: 'tanstack-start-core:dev-base-rewrite',\n configureServer(server) {\n if (!opts.shouldRewriteDevBase()) {\n return\n }\n\n const basePrefix = opts.resolvedStartConfig.basePaths.publicBase.replace(\n /\\/$/,\n '',\n )\n\n server.middlewares.use((req, _res, next) => {\n if (req.url && !req.url.startsWith(basePrefix)) {\n req.url = basePrefix + req.url\n }\n\n next()\n })\n },\n }\n}\n\nexport function createCaptureClientBuildPlugin(opts: {\n capturedClientBuild: Partial<\n Record<StartEnvironmentName, NormalizedClientBuild>\n >\n}): PluginOption {\n return {\n name: 'tanstack-start:core:capture-bundle',\n applyToEnvironment(environment) {\n return environment.name === START_ENVIRONMENT_NAMES.client\n },\n enforce: 'post',\n generateBundle(_options, bundle) {\n const environment = this.environment.name as StartEnvironmentName\n\n if (environment !== START_ENVIRONMENT_NAMES.client) {\n throw new Error(\n `Unexpected environment for client build capture: ${environment}`,\n )\n }\n\n opts.capturedClientBuild[environment] = normalizeViteClientBuild(bundle)\n },\n }\n}\n"],"mappings":";;;AAUA,SAAgB,sBAAsB,MAMrB;AACf,QAAO;EACL,MAAM;EACN,SAAS;EACT,UAAU;GACR,OAAO;GACP,MAAM,QAAQ,SAAS;IACrB,MAAM,EAAE,gBAAgB,KAAK,WAAW;AACxC,UAAM,KAAK,gBAAgB;KAAE;KAAS;KAAa,CAAC;;GAEvD;EACF;;AAGH,SAAgB,2BAA2B,MAG1B;AACf,QAAO;EACL,MAAM;EACN,gBAAgB,QAAQ;AACtB,OAAI,CAAC,KAAK,sBAAsB,CAC9B;GAGF,MAAM,aAAa,KAAK,oBAAoB,UAAU,WAAW,QAC/D,OACA,GACD;AAED,UAAO,YAAY,KAAK,KAAK,MAAM,SAAS;AAC1C,QAAI,IAAI,OAAO,CAAC,IAAI,IAAI,WAAW,WAAW,CAC5C,KAAI,MAAM,aAAa,IAAI;AAG7B,UAAM;KACN;;EAEL;;AAGH,SAAgB,+BAA+B,MAI9B;AACf,QAAO;EACL,MAAM;EACN,mBAAmB,aAAa;AAC9B,UAAO,YAAY,SAAS,wBAAwB;;EAEtD,SAAS;EACT,eAAe,UAAU,QAAQ;GAC/B,MAAM,cAAc,KAAK,YAAY;AAErC,OAAI,gBAAgB,wBAAwB,OAC1C,OAAM,IAAI,MACR,oDAAoD,cACrD;AAGH,QAAK,oBAAoB,eAAe,yBAAyB,OAAO;;EAE3E"}
1
+ {"version":3,"file":"plugins.js","names":[],"sources":["../../../src/vite/plugins.ts"],"sourcesContent":["import { normalizePath } from 'vite'\nimport { ENTRY_POINTS, START_ENVIRONMENT_NAMES } from '../constants'\nimport { createVirtualModule } from './createVirtualModule'\nimport { normalizeViteClientBuild } from './start-manifest-plugin/normalized-client-build'\nimport type {\n GetConfigFn,\n NormalizedClientBuild,\n ResolvedStartConfig,\n} from '../types'\nimport type { StartEnvironmentName } from '../constants'\nimport type { PluginOption, ViteBuilder } from 'vite'\n\nexport function createVirtualClientEntryPlugin(opts: {\n getClientEntry: () => string\n}): PluginOption {\n return createVirtualModule({\n name: 'tanstack-start-core:virtual-client-entry',\n moduleId: ENTRY_POINTS.client,\n enforce: 'pre',\n load() {\n return `import ${JSON.stringify(normalizePath(opts.getClientEntry()).replaceAll('\\\\', '/'))}`\n },\n })\n}\n\nexport function createPostBuildPlugin(opts: {\n getConfig: GetConfigFn\n postServerBuild: (opts: {\n startConfig: ReturnType<GetConfigFn>['startConfig']\n builder: ViteBuilder\n }) => Promise<void>\n}): PluginOption {\n return {\n name: 'tanstack-start-core:post-build',\n enforce: 'post',\n buildApp: {\n order: 'post',\n async handler(builder) {\n const { startConfig } = opts.getConfig()\n await opts.postServerBuild({ builder, startConfig })\n },\n },\n }\n}\n\nexport function createDevBaseRewritePlugin(opts: {\n shouldRewriteDevBase: () => boolean\n resolvedStartConfig: ResolvedStartConfig\n}): PluginOption {\n return {\n name: 'tanstack-start-core:dev-base-rewrite',\n configureServer(server) {\n if (!opts.shouldRewriteDevBase()) {\n return\n }\n\n const basePrefix = opts.resolvedStartConfig.basePaths.publicBase.replace(\n /\\/$/,\n '',\n )\n\n server.middlewares.use((req, _res, next) => {\n if (req.url && !req.url.startsWith(basePrefix)) {\n req.url = basePrefix + req.url\n }\n\n next()\n })\n },\n }\n}\n\nexport function createCaptureClientBuildPlugin(opts: {\n capturedClientBuild: Partial<\n Record<StartEnvironmentName, NormalizedClientBuild>\n >\n}): PluginOption {\n return {\n name: 'tanstack-start:core:capture-bundle',\n applyToEnvironment(environment) {\n return environment.name === START_ENVIRONMENT_NAMES.client\n },\n enforce: 'post',\n generateBundle(_options, bundle) {\n const environment = this.environment.name as StartEnvironmentName\n\n if (environment !== START_ENVIRONMENT_NAMES.client) {\n throw new Error(\n `Unexpected environment for client build capture: ${environment}`,\n )\n }\n\n opts.capturedClientBuild[environment] = normalizeViteClientBuild(bundle)\n },\n }\n}\n"],"mappings":";;;;;AAYA,SAAgB,+BAA+B,MAE9B;AACf,QAAO,oBAAoB;EACzB,MAAM;EACN,UAAU,aAAa;EACvB,SAAS;EACT,OAAO;AACL,UAAO,UAAU,KAAK,UAAU,cAAc,KAAK,gBAAgB,CAAC,CAAC,WAAW,MAAM,IAAI,CAAC;;EAE9F,CAAC;;AAGJ,SAAgB,sBAAsB,MAMrB;AACf,QAAO;EACL,MAAM;EACN,SAAS;EACT,UAAU;GACR,OAAO;GACP,MAAM,QAAQ,SAAS;IACrB,MAAM,EAAE,gBAAgB,KAAK,WAAW;AACxC,UAAM,KAAK,gBAAgB;KAAE;KAAS;KAAa,CAAC;;GAEvD;EACF;;AAGH,SAAgB,2BAA2B,MAG1B;AACf,QAAO;EACL,MAAM;EACN,gBAAgB,QAAQ;AACtB,OAAI,CAAC,KAAK,sBAAsB,CAC9B;GAGF,MAAM,aAAa,KAAK,oBAAoB,UAAU,WAAW,QAC/D,OACA,GACD;AAED,UAAO,YAAY,KAAK,KAAK,MAAM,SAAS;AAC1C,QAAI,IAAI,OAAO,CAAC,IAAI,IAAI,WAAW,WAAW,CAC5C,KAAI,MAAM,aAAa,IAAI;AAG7B,UAAM;KACN;;EAEL;;AAGH,SAAgB,+BAA+B,MAI9B;AACf,QAAO;EACL,MAAM;EACN,mBAAmB,aAAa;AAC9B,UAAO,YAAY,SAAS,wBAAwB;;EAEtD,SAAS;EACT,eAAe,UAAU,QAAQ;GAC/B,MAAM,cAAc,KAAK,YAAY;AAErC,OAAI,gBAAgB,wBAAwB,OAC1C,OAAM,IAAI,MACR,oDAAoD,cACrD;AAGH,QAAK,oBAAoB,eAAe,yBAAyB,OAAO;;EAE3E"}
@@ -1,40 +1,27 @@
1
1
  import { START_ENVIRONMENT_NAMES } from "../constants.js";
2
- import { resolveViteId } from "../utils.js";
2
+ import { createVirtualModule } from "./createVirtualModule.js";
3
3
  import { EMPTY_SERIALIZATION_ADAPTERS_MODULE, generateSerializationAdaptersModule } from "../serialization-adapters-module.js";
4
4
  import { VIRTUAL_MODULES } from "@tanstack/start-server-core";
5
5
  //#region src/vite/serialization-adapters-plugin.ts
6
- var resolvedModuleId = resolveViteId(VIRTUAL_MODULES.pluginAdapters.replace("#", "%23"));
7
- function escapeRegex(str) {
8
- return str.replace(/[.*+?^${}()|[\]\\#]/g, "\\$&");
9
- }
10
6
  function serializationAdaptersPlugin(opts) {
11
- return {
7
+ return createVirtualModule({
12
8
  name: "tanstack-start:plugin-adapters",
9
+ moduleId: VIRTUAL_MODULES.pluginAdapters,
13
10
  enforce: "pre",
14
- resolveId: {
15
- filter: { id: new RegExp(escapeRegex(VIRTUAL_MODULES.pluginAdapters)) },
16
- handler(id) {
17
- if (id === VIRTUAL_MODULES.pluginAdapters) return resolvedModuleId;
18
- }
19
- },
20
- load: {
21
- filter: { id: new RegExp(escapeRegex(resolvedModuleId)) },
22
- handler(id) {
23
- if (id !== resolvedModuleId) return;
24
- const adapters = opts.adapters;
25
- if (!adapters || adapters.length === 0) return EMPTY_SERIALIZATION_ADAPTERS_MODULE;
26
- if (this.environment.name === START_ENVIRONMENT_NAMES.client) return generateSerializationAdaptersModule({
27
- adapters,
28
- runtime: "client"
29
- });
30
- if (this.environment.name === START_ENVIRONMENT_NAMES.server) return generateSerializationAdaptersModule({
31
- adapters,
32
- runtime: "server"
33
- });
34
- return EMPTY_SERIALIZATION_ADAPTERS_MODULE;
35
- }
11
+ load() {
12
+ const adapters = opts.adapters;
13
+ if (!adapters || adapters.length === 0) return EMPTY_SERIALIZATION_ADAPTERS_MODULE;
14
+ if (this.environment.name === START_ENVIRONMENT_NAMES.client) return generateSerializationAdaptersModule({
15
+ adapters,
16
+ runtime: "client"
17
+ });
18
+ if (this.environment.name === START_ENVIRONMENT_NAMES.server) return generateSerializationAdaptersModule({
19
+ adapters,
20
+ runtime: "server"
21
+ });
22
+ return EMPTY_SERIALIZATION_ADAPTERS_MODULE;
36
23
  }
37
- };
24
+ });
38
25
  }
39
26
  //#endregion
40
27
  export { serializationAdaptersPlugin };
@@ -1 +1 @@
1
- {"version":3,"file":"serialization-adapters-plugin.js","names":[],"sources":["../../../src/vite/serialization-adapters-plugin.ts"],"sourcesContent":["import { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport {\n EMPTY_SERIALIZATION_ADAPTERS_MODULE,\n generateSerializationAdaptersModule,\n} from '../serialization-adapters-module'\nimport { START_ENVIRONMENT_NAMES } from '../constants'\nimport { resolveViteId } from '../utils'\nimport type { SerializationAdapterConfig } from '../types'\nimport type { PluginOption } from 'vite'\n\n// Encode '#' as '%23' in the resolved ID to avoid browser treating it as URL fragment.\n// The browser requests /@id/__x00__%23tanstack-start-plugin-adapters instead of\n// /@id/__x00__#tanstack-start-plugin-adapters (which would truncate at #).\nconst resolvedModuleId = resolveViteId(\n VIRTUAL_MODULES.pluginAdapters.replace('#', '%23'),\n)\n\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\#]/g, '\\\\$&')\n}\n\nexport function serializationAdaptersPlugin(opts: {\n adapters: Array<SerializationAdapterConfig> | undefined\n}): PluginOption {\n return {\n name: 'tanstack-start:plugin-adapters',\n enforce: 'pre',\n resolveId: {\n filter: { id: new RegExp(escapeRegex(VIRTUAL_MODULES.pluginAdapters)) },\n handler(id: string) {\n if (id === VIRTUAL_MODULES.pluginAdapters) {\n return resolvedModuleId\n }\n return undefined\n },\n },\n load: {\n filter: {\n id: new RegExp(escapeRegex(resolvedModuleId)),\n },\n handler(this: { environment: { name: string } }, id: string) {\n if (id !== resolvedModuleId) {\n return undefined\n }\n\n const adapters = opts.adapters\n if (!adapters || adapters.length === 0) {\n return EMPTY_SERIALIZATION_ADAPTERS_MODULE\n }\n\n if (this.environment.name === START_ENVIRONMENT_NAMES.client) {\n return generateSerializationAdaptersModule({\n adapters,\n runtime: 'client',\n })\n }\n\n if (this.environment.name === START_ENVIRONMENT_NAMES.server) {\n return generateSerializationAdaptersModule({\n adapters,\n runtime: 'server',\n })\n }\n\n return EMPTY_SERIALIZATION_ADAPTERS_MODULE\n },\n },\n }\n}\n"],"mappings":";;;;;AAaA,IAAM,mBAAmB,cACvB,gBAAgB,eAAe,QAAQ,KAAK,MAAM,CACnD;AAED,SAAS,YAAY,KAAqB;AACxC,QAAO,IAAI,QAAQ,wBAAwB,OAAO;;AAGpD,SAAgB,4BAA4B,MAE3B;AACf,QAAO;EACL,MAAM;EACN,SAAS;EACT,WAAW;GACT,QAAQ,EAAE,IAAI,IAAI,OAAO,YAAY,gBAAgB,eAAe,CAAC,EAAE;GACvE,QAAQ,IAAY;AAClB,QAAI,OAAO,gBAAgB,eACzB,QAAO;;GAIZ;EACD,MAAM;GACJ,QAAQ,EACN,IAAI,IAAI,OAAO,YAAY,iBAAiB,CAAC,EAC9C;GACD,QAAiD,IAAY;AAC3D,QAAI,OAAO,iBACT;IAGF,MAAM,WAAW,KAAK;AACtB,QAAI,CAAC,YAAY,SAAS,WAAW,EACnC,QAAO;AAGT,QAAI,KAAK,YAAY,SAAS,wBAAwB,OACpD,QAAO,oCAAoC;KACzC;KACA,SAAS;KACV,CAAC;AAGJ,QAAI,KAAK,YAAY,SAAS,wBAAwB,OACpD,QAAO,oCAAoC;KACzC;KACA,SAAS;KACV,CAAC;AAGJ,WAAO;;GAEV;EACF"}
1
+ {"version":3,"file":"serialization-adapters-plugin.js","names":[],"sources":["../../../src/vite/serialization-adapters-plugin.ts"],"sourcesContent":["import { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport {\n EMPTY_SERIALIZATION_ADAPTERS_MODULE,\n generateSerializationAdaptersModule,\n} from '../serialization-adapters-module'\nimport { START_ENVIRONMENT_NAMES } from '../constants'\nimport { createVirtualModule } from './createVirtualModule'\nimport type { SerializationAdapterConfig } from '../types'\nimport type { PluginOption } from 'vite'\n\nexport function serializationAdaptersPlugin(opts: {\n adapters: Array<SerializationAdapterConfig> | undefined\n}): PluginOption {\n return createVirtualModule({\n name: 'tanstack-start:plugin-adapters',\n moduleId: VIRTUAL_MODULES.pluginAdapters,\n enforce: 'pre',\n load() {\n const adapters = opts.adapters\n if (!adapters || adapters.length === 0) {\n return EMPTY_SERIALIZATION_ADAPTERS_MODULE\n }\n\n if (this.environment.name === START_ENVIRONMENT_NAMES.client) {\n return generateSerializationAdaptersModule({\n adapters,\n runtime: 'client',\n })\n }\n\n if (this.environment.name === START_ENVIRONMENT_NAMES.server) {\n return generateSerializationAdaptersModule({\n adapters,\n runtime: 'server',\n })\n }\n\n return EMPTY_SERIALIZATION_ADAPTERS_MODULE\n },\n })\n}\n"],"mappings":";;;;;AAUA,SAAgB,4BAA4B,MAE3B;AACf,QAAO,oBAAoB;EACzB,MAAM;EACN,UAAU,gBAAgB;EAC1B,SAAS;EACT,OAAO;GACL,MAAM,WAAW,KAAK;AACtB,OAAI,CAAC,YAAY,SAAS,WAAW,EACnC,QAAO;AAGT,OAAI,KAAK,YAAY,SAAS,wBAAwB,OACpD,QAAO,oCAAoC;IACzC;IACA,SAAS;IACV,CAAC;AAGJ,OAAI,KAAK,YAAY,SAAS,wBAAwB,OACpD,QAAO,oCAAoC;IACzC;IACA,SAAS;IACV,CAAC;AAGJ,UAAO;;EAEV,CAAC"}
@@ -1,4 +1,6 @@
1
1
  import { SERVER_FN_LOOKUP, TRANSFORM_ID_REGEX, VITE_ENVIRONMENT_NAMES } from "../../constants.js";
2
+ import { resolveViteId } from "../../utils.js";
3
+ import { createVirtualModule } from "../createVirtualModule.js";
2
4
  import { cleanId } from "../../start-compiler/utils.js";
3
5
  import { detectKindsInCode } from "../../start-compiler/compiler.js";
4
6
  import { getTransformCodeFilterForEnv } from "../../start-compiler/config.js";
@@ -10,9 +12,6 @@ import { resolve } from "pathe";
10
12
  import assert from "node:assert";
11
13
  import { VIRTUAL_MODULES } from "@tanstack/start-server-core";
12
14
  //#region src/vite/start-compiler-plugin/plugin.ts
13
- function resolveViteId(id) {
14
- return `\0${id}`;
15
- }
16
15
  var validateServerFnIdVirtualModule = `virtual:tanstack-start-validate-server-fn-id`;
17
16
  function getDevServerFnValidatorModule() {
18
17
  return `
@@ -44,7 +43,6 @@ function startCompilerPlugin(opts) {
44
43
  mergeServerFnsById(serverFnsById, d);
45
44
  };
46
45
  let root = process.cwd();
47
- const resolvedResolverVirtualImportId = resolveViteId(VIRTUAL_MODULES.serverFnResolver);
48
46
  const ssrEnvName = VITE_ENVIRONMENT_NAMES.server;
49
47
  const ssrIsProvider = opts.providerEnvName === ssrEnvName;
50
48
  const appliedResolverEnvironments = new Set(ssrIsProvider ? [opts.providerEnvName] : [ssrEnvName, opts.providerEnvName]);
@@ -163,37 +161,26 @@ function startCompilerPlugin(opts) {
163
161
  }
164
162
  }
165
163
  },
166
- {
164
+ createVirtualModule({
167
165
  name: "tanstack-start-core:server-fn-resolver",
166
+ moduleId: VIRTUAL_MODULES.serverFnResolver,
168
167
  enforce: "pre",
169
168
  applyToEnvironment: (env) => {
170
169
  return appliedResolverEnvironments.has(env.name);
171
170
  },
172
- configResolved(config) {
173
- root = config.root;
174
- },
175
- resolveId: {
176
- filter: { id: new RegExp(VIRTUAL_MODULES.serverFnResolver) },
177
- handler() {
178
- return resolvedResolverVirtualImportId;
179
- }
180
- },
181
- load: {
182
- filter: { id: new RegExp(resolvedResolverVirtualImportId) },
183
- handler() {
184
- if (this.environment.name !== opts.providerEnvName) {
185
- const mod = opts.environments.find((e) => e.name === this.environment.name)?.getServerFnById;
186
- if (mod) return mod;
187
- else this.error(`No getServerFnById implementation found for caller environment: ${this.environment.name}`);
188
- }
189
- if (this.environment.mode !== "build") return getDevServerFnValidatorModule();
190
- return generateServerFnResolverModule({
191
- serverFnsById,
192
- includeClientReferencedCheck: !ssrIsProvider
193
- });
171
+ load() {
172
+ if (this.environment.name !== opts.providerEnvName) {
173
+ const mod = opts.environments.find((e) => e.name === this.environment.name)?.getServerFnById;
174
+ if (mod) return mod;
175
+ this.error(`No getServerFnById implementation found for caller environment: ${this.environment.name}`);
194
176
  }
177
+ if (this.environment.mode !== "build") return getDevServerFnValidatorModule();
178
+ return generateServerFnResolverModule({
179
+ serverFnsById,
180
+ includeClientReferencedCheck: !ssrIsProvider
181
+ });
195
182
  }
196
- }
183
+ })
197
184
  ];
198
185
  }
199
186
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":[],"sources":["../../../../src/vite/start-compiler-plugin/plugin.ts"],"sourcesContent":["import assert from 'node:assert'\nimport { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport { resolve as resolvePath } from 'pathe'\nimport {\n SERVER_FN_LOOKUP,\n TRANSFORM_ID_REGEX,\n VITE_ENVIRONMENT_NAMES,\n} from '../../constants'\nimport { detectKindsInCode } from '../../start-compiler/compiler'\nimport { getTransformCodeFilterForEnv } from '../../start-compiler/config'\nimport {\n createStartCompiler,\n mergeServerFnsById,\n} from '../../start-compiler/host'\nimport { loadModuleForViteCompiler } from '../../start-compiler/load-module'\nimport { generateServerFnResolverModule } from '../../start-compiler/server-fn-resolver-module'\nimport { cleanId } from '../../start-compiler/utils'\nimport {\n createViteDevServerFnModuleSpecifierEncoder,\n decodeViteDevServerModuleSpecifier,\n} from './module-specifier'\nimport type { CompileStartFrameworkOptions } from '../../types'\nimport type {\n GenerateFunctionIdFnOptional,\n ServerFn,\n} from '../../start-compiler/types'\nimport type { PluginOption } from 'vite'\n\n// Re-export from shared constants for backwards compatibility\nexport { SERVER_FN_LOOKUP }\n\nfunction resolveViteId(id: string) {\n return `\\0${id}`\n}\n\nconst validateServerFnIdVirtualModule = `virtual:tanstack-start-validate-server-fn-id`\n\nfunction getDevServerFnValidatorModule(): string {\n return `\nexport async function getServerFnById(id, _access) {\n const validateIdImport = ${JSON.stringify(validateServerFnIdVirtualModule)} + '?id=' + id\n await import(/* @vite-ignore */ '/@id/__x00__' + validateIdImport)\n const decoded = Buffer.from(id, 'base64url').toString('utf8')\n const devServerFn = JSON.parse(decoded)\n const mod = await import(/* @vite-ignore */ devServerFn.file)\n return mod[devServerFn.export]\n}\n`\n}\n\nfunction parseIdQuery(id: string): {\n filename: string\n query: {\n [k: string]: string\n }\n} {\n if (!id.includes('?')) return { filename: id, query: {} }\n const [filename, rawQuery] = id.split(`?`, 2) as [string, string]\n const query = Object.fromEntries(new URLSearchParams(rawQuery))\n return { filename, query }\n}\n\nexport interface StartCompilerPluginOptions {\n framework: CompileStartFrameworkOptions\n environments: Array<{\n name: string\n type: 'client' | 'server'\n getServerFnById?: string\n }>\n /**\n * Custom function ID generator (optional).\n */\n generateFunctionId?: GenerateFunctionIdFnOptional\n /**\n * The Vite environment name for the server function provider.\n */\n providerEnvName: string\n}\n\nexport function startCompilerPlugin(\n opts: StartCompilerPluginOptions,\n): PluginOption {\n const compilers = new Map<string, ReturnType<typeof createStartCompiler>>()\n\n // Shared registry of server functions across all environments\n const serverFnsById: Record<string, ServerFn> = {}\n\n const onServerFnsById = (d: Record<string, ServerFn>) => {\n mergeServerFnsById(serverFnsById, d)\n }\n\n let root = process.cwd()\n const resolvedResolverVirtualImportId = resolveViteId(\n VIRTUAL_MODULES.serverFnResolver,\n )\n\n // Determine which environments need the resolver (getServerFnById)\n // SSR environment always needs the resolver for server-side calls\n // Provider environment needs it for the actual implementation\n const ssrEnvName = VITE_ENVIRONMENT_NAMES.server\n\n // SSR is the provider when the provider environment is the default server environment\n const ssrIsProvider = opts.providerEnvName === ssrEnvName\n\n // Environments that need the resolver: SSR (for server calls) and provider (for implementation)\n const appliedResolverEnvironments = new Set(\n ssrIsProvider ? [opts.providerEnvName] : [ssrEnvName, opts.providerEnvName],\n )\n\n function perEnvServerFnPlugin(environment: {\n name: string\n type: 'client' | 'server'\n }): PluginOption {\n // Derive transform code filter from KindDetectionPatterns (single source of truth)\n const transformCodeFilter = getTransformCodeFilterForEnv(environment.type)\n return {\n name: `tanstack-start-core::server-fn:${environment.name}`,\n enforce: 'pre',\n applyToEnvironment(env) {\n return env.name === environment.name\n },\n configResolved(config) {\n root = config.root\n },\n transform: {\n filter: {\n id: {\n exclude: new RegExp(`${SERVER_FN_LOOKUP}$`),\n include: TRANSFORM_ID_REGEX,\n },\n code: {\n include: transformCodeFilter,\n },\n },\n async handler(code, id) {\n let compiler = compilers.get(this.environment.name)\n\n if (!compiler) {\n // Default to 'dev' mode for unknown environments (conservative: no caching)\n const mode = this.environment.mode === 'build' ? 'build' : 'dev'\n\n compiler = createStartCompiler({\n env: environment.type,\n envName: environment.name,\n root,\n mode,\n framework: opts.framework,\n providerEnvName: opts.providerEnvName,\n generateFunctionId: opts.generateFunctionId,\n onServerFnsById,\n getKnownServerFns: () => serverFnsById,\n encodeModuleSpecifierInDev:\n mode === 'dev'\n ? createViteDevServerFnModuleSpecifierEncoder(root)\n : undefined,\n loadModule: async (id: string) => {\n await loadModuleForViteCompiler({\n compiler: compiler!,\n mode: this.environment.mode,\n fetchModule:\n this.environment.mode === 'dev'\n ? this.environment.fetchModule.bind(this.environment)\n : undefined,\n loadModule: this.load.bind(this),\n id,\n })\n },\n\n resolveId: async (source: string, importer?: string) => {\n const r = await this.resolve(source, importer)\n\n if (r) {\n if (!r.external) {\n return cleanId(r.id)\n }\n }\n\n return null\n },\n })\n\n compilers.set(this.environment.name, compiler)\n }\n\n // Detect which kinds are present in this file before parsing\n const detectedKinds = detectKindsInCode(code, environment.type)\n\n const result = await compiler.compile({\n id,\n code,\n detectedKinds,\n })\n return result\n },\n },\n\n hotUpdate(ctx) {\n const compiler = compilers.get(this.environment.name)\n\n ctx.modules.forEach((m) => {\n if (m.id) {\n const deleted = compiler?.invalidateModule(m.id)\n if (deleted) {\n m.importers.forEach((importer) => {\n if (importer.id) {\n compiler?.invalidateModule(importer.id)\n }\n })\n }\n }\n })\n },\n }\n }\n\n return [\n ...opts.environments.map(perEnvServerFnPlugin),\n {\n name: 'tanstack-start-core:capture-server-fn-module-lookup',\n // we only need this plugin in dev mode\n apply: 'serve',\n applyToEnvironment(env) {\n return !!opts.environments.find((e) => e.name === env.name)\n },\n transform: {\n filter: {\n id: new RegExp(`${SERVER_FN_LOOKUP}$`),\n },\n handler(code, id) {\n const compiler = compilers.get(this.environment.name)\n compiler?.ingestModule({ code, id: cleanId(id) })\n },\n },\n },\n // Validate server function ID in dev mode\n {\n name: 'tanstack-start-core:validate-server-fn-id',\n apply: 'serve',\n load: {\n filter: {\n id: new RegExp(resolveViteId(validateServerFnIdVirtualModule)),\n },\n async handler(id) {\n const parsed = parseIdQuery(id)\n const fnId = parsed.query.id\n if (fnId && serverFnsById[fnId]) {\n return `export {}`\n }\n\n // ID not yet registered — the source file may not have been\n // transformed in this dev session yet (e.g. cold restart with\n // cached client). Try to decode the ID, discover the source\n // file, trigger its compilation, and re-check.\n if (fnId) {\n try {\n const decoded = JSON.parse(\n Buffer.from(fnId, 'base64url').toString('utf8'),\n )\n if (\n typeof decoded.file === 'string' &&\n typeof decoded.export === 'string'\n ) {\n // Use the Vite strategy to decode the module specifier\n // back to the original source file path.\n const sourceFile = decodeViteDevServerModuleSpecifier(\n decoded.file,\n )\n\n if (sourceFile) {\n // Resolve to absolute path\n const absPath = resolvePath(root, sourceFile)\n\n // Trigger transform of the source file in this environment,\n // which will compile createServerFn calls and populate\n // serverFnsById as a side effect.\n // This plugin only runs in dev (apply: 'serve'), so mode\n // must be 'dev' — assert to narrow to DevEnvironment.\n assert(this.environment.mode === 'dev')\n await this.environment.fetchModule(absPath)\n\n // Re-check after lazy compilation\n if (serverFnsById[fnId]) {\n return `export {}`\n }\n }\n }\n } catch {\n // Decoding or fetching failed — fall through to error\n }\n }\n\n this.error(`Invalid server function ID: ${fnId}`)\n },\n },\n },\n // Manifest plugin for server environments\n {\n name: 'tanstack-start-core:server-fn-resolver',\n enforce: 'pre',\n applyToEnvironment: (env) => {\n return appliedResolverEnvironments.has(env.name)\n },\n configResolved(config) {\n root = config.root\n },\n resolveId: {\n filter: { id: new RegExp(VIRTUAL_MODULES.serverFnResolver) },\n handler() {\n return resolvedResolverVirtualImportId\n },\n },\n load: {\n filter: { id: new RegExp(resolvedResolverVirtualImportId) },\n handler() {\n if (this.environment.name !== opts.providerEnvName) {\n const mod = opts.environments.find(\n (e) => e.name === this.environment.name,\n )?.getServerFnById\n if (mod) {\n return mod\n } else {\n this.error(\n `No getServerFnById implementation found for caller environment: ${this.environment.name}`,\n )\n }\n }\n\n if (this.environment.mode !== 'build') {\n return getDevServerFnValidatorModule()\n }\n\n // When SSR is the provider, server-only-referenced functions aren't in the manifest,\n // so no isClientReferenced check is needed.\n // When SSR is NOT the provider (custom provider env), server-only-referenced\n // functions ARE in the manifest and need the isClientReferenced check to\n // block direct client HTTP requests to server-only-referenced functions.\n return generateServerFnResolverModule({\n serverFnsById,\n includeClientReferencedCheck: !ssrIsProvider,\n })\n },\n },\n },\n ]\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../../../../src/vite/start-compiler-plugin/plugin.ts"],"sourcesContent":["import assert from 'node:assert'\nimport { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport { resolve as resolvePath } from 'pathe'\nimport {\n SERVER_FN_LOOKUP,\n TRANSFORM_ID_REGEX,\n VITE_ENVIRONMENT_NAMES,\n} from '../../constants'\nimport { detectKindsInCode } from '../../start-compiler/compiler'\nimport { getTransformCodeFilterForEnv } from '../../start-compiler/config'\nimport {\n createStartCompiler,\n mergeServerFnsById,\n} from '../../start-compiler/host'\nimport { loadModuleForViteCompiler } from '../../start-compiler/load-module'\nimport { generateServerFnResolverModule } from '../../start-compiler/server-fn-resolver-module'\nimport { cleanId } from '../../start-compiler/utils'\nimport { createVirtualModule } from '../createVirtualModule'\nimport { resolveViteId } from '../../utils'\nimport {\n createViteDevServerFnModuleSpecifierEncoder,\n decodeViteDevServerModuleSpecifier,\n} from './module-specifier'\nimport type { CompileStartFrameworkOptions } from '../../types'\nimport type {\n GenerateFunctionIdFnOptional,\n ServerFn,\n} from '../../start-compiler/types'\nimport type { PluginOption } from 'vite'\n\n// Re-export from shared constants for backwards compatibility\nexport { SERVER_FN_LOOKUP }\n\nconst validateServerFnIdVirtualModule = `virtual:tanstack-start-validate-server-fn-id`\n\nfunction getDevServerFnValidatorModule(): string {\n return `\nexport async function getServerFnById(id, _access) {\n const validateIdImport = ${JSON.stringify(validateServerFnIdVirtualModule)} + '?id=' + id\n await import(/* @vite-ignore */ '/@id/__x00__' + validateIdImport)\n const decoded = Buffer.from(id, 'base64url').toString('utf8')\n const devServerFn = JSON.parse(decoded)\n const mod = await import(/* @vite-ignore */ devServerFn.file)\n return mod[devServerFn.export]\n}\n`\n}\n\nfunction parseIdQuery(id: string): {\n filename: string\n query: {\n [k: string]: string\n }\n} {\n if (!id.includes('?')) return { filename: id, query: {} }\n const [filename, rawQuery] = id.split(`?`, 2) as [string, string]\n const query = Object.fromEntries(new URLSearchParams(rawQuery))\n return { filename, query }\n}\n\nexport interface StartCompilerPluginOptions {\n framework: CompileStartFrameworkOptions\n environments: Array<{\n name: string\n type: 'client' | 'server'\n getServerFnById?: string\n }>\n /**\n * Custom function ID generator (optional).\n */\n generateFunctionId?: GenerateFunctionIdFnOptional\n /**\n * The Vite environment name for the server function provider.\n */\n providerEnvName: string\n}\n\nexport function startCompilerPlugin(\n opts: StartCompilerPluginOptions,\n): PluginOption {\n const compilers = new Map<string, ReturnType<typeof createStartCompiler>>()\n\n // Shared registry of server functions across all environments\n const serverFnsById: Record<string, ServerFn> = {}\n\n const onServerFnsById = (d: Record<string, ServerFn>) => {\n mergeServerFnsById(serverFnsById, d)\n }\n\n let root = process.cwd()\n // Determine which environments need the resolver (getServerFnById)\n // SSR environment always needs the resolver for server-side calls\n // Provider environment needs it for the actual implementation\n const ssrEnvName = VITE_ENVIRONMENT_NAMES.server\n\n // SSR is the provider when the provider environment is the default server environment\n const ssrIsProvider = opts.providerEnvName === ssrEnvName\n\n // Environments that need the resolver: SSR (for server calls) and provider (for implementation)\n const appliedResolverEnvironments = new Set(\n ssrIsProvider ? [opts.providerEnvName] : [ssrEnvName, opts.providerEnvName],\n )\n\n function perEnvServerFnPlugin(environment: {\n name: string\n type: 'client' | 'server'\n }): PluginOption {\n // Derive transform code filter from KindDetectionPatterns (single source of truth)\n const transformCodeFilter = getTransformCodeFilterForEnv(environment.type)\n return {\n name: `tanstack-start-core::server-fn:${environment.name}`,\n enforce: 'pre',\n applyToEnvironment(env) {\n return env.name === environment.name\n },\n configResolved(config) {\n root = config.root\n },\n transform: {\n filter: {\n id: {\n exclude: new RegExp(`${SERVER_FN_LOOKUP}$`),\n include: TRANSFORM_ID_REGEX,\n },\n code: {\n include: transformCodeFilter,\n },\n },\n async handler(code, id) {\n let compiler = compilers.get(this.environment.name)\n\n if (!compiler) {\n // Default to 'dev' mode for unknown environments (conservative: no caching)\n const mode = this.environment.mode === 'build' ? 'build' : 'dev'\n\n compiler = createStartCompiler({\n env: environment.type,\n envName: environment.name,\n root,\n mode,\n framework: opts.framework,\n providerEnvName: opts.providerEnvName,\n generateFunctionId: opts.generateFunctionId,\n onServerFnsById,\n getKnownServerFns: () => serverFnsById,\n encodeModuleSpecifierInDev:\n mode === 'dev'\n ? createViteDevServerFnModuleSpecifierEncoder(root)\n : undefined,\n loadModule: async (id: string) => {\n await loadModuleForViteCompiler({\n compiler: compiler!,\n mode: this.environment.mode,\n fetchModule:\n this.environment.mode === 'dev'\n ? this.environment.fetchModule.bind(this.environment)\n : undefined,\n loadModule: this.load.bind(this),\n id,\n })\n },\n\n resolveId: async (source: string, importer?: string) => {\n const r = await this.resolve(source, importer)\n\n if (r) {\n if (!r.external) {\n return cleanId(r.id)\n }\n }\n\n return null\n },\n })\n\n compilers.set(this.environment.name, compiler)\n }\n\n // Detect which kinds are present in this file before parsing\n const detectedKinds = detectKindsInCode(code, environment.type)\n\n const result = await compiler.compile({\n id,\n code,\n detectedKinds,\n })\n return result\n },\n },\n\n hotUpdate(ctx) {\n const compiler = compilers.get(this.environment.name)\n\n ctx.modules.forEach((m) => {\n if (m.id) {\n const deleted = compiler?.invalidateModule(m.id)\n if (deleted) {\n m.importers.forEach((importer) => {\n if (importer.id) {\n compiler?.invalidateModule(importer.id)\n }\n })\n }\n }\n })\n },\n }\n }\n\n return [\n ...opts.environments.map(perEnvServerFnPlugin),\n {\n name: 'tanstack-start-core:capture-server-fn-module-lookup',\n // we only need this plugin in dev mode\n apply: 'serve',\n applyToEnvironment(env) {\n return !!opts.environments.find((e) => e.name === env.name)\n },\n transform: {\n filter: {\n id: new RegExp(`${SERVER_FN_LOOKUP}$`),\n },\n handler(code, id) {\n const compiler = compilers.get(this.environment.name)\n compiler?.ingestModule({ code, id: cleanId(id) })\n },\n },\n },\n // Validate server function ID in dev mode\n {\n name: 'tanstack-start-core:validate-server-fn-id',\n apply: 'serve',\n load: {\n filter: {\n id: new RegExp(resolveViteId(validateServerFnIdVirtualModule)),\n },\n async handler(id) {\n const parsed = parseIdQuery(id)\n const fnId = parsed.query.id\n if (fnId && serverFnsById[fnId]) {\n return `export {}`\n }\n\n // ID not yet registered — the source file may not have been\n // transformed in this dev session yet (e.g. cold restart with\n // cached client). Try to decode the ID, discover the source\n // file, trigger its compilation, and re-check.\n if (fnId) {\n try {\n const decoded = JSON.parse(\n Buffer.from(fnId, 'base64url').toString('utf8'),\n )\n if (\n typeof decoded.file === 'string' &&\n typeof decoded.export === 'string'\n ) {\n // Use the Vite strategy to decode the module specifier\n // back to the original source file path.\n const sourceFile = decodeViteDevServerModuleSpecifier(\n decoded.file,\n )\n\n if (sourceFile) {\n // Resolve to absolute path\n const absPath = resolvePath(root, sourceFile)\n\n // Trigger transform of the source file in this environment,\n // which will compile createServerFn calls and populate\n // serverFnsById as a side effect.\n // This plugin only runs in dev (apply: 'serve'), so mode\n // must be 'dev' — assert to narrow to DevEnvironment.\n assert(this.environment.mode === 'dev')\n await this.environment.fetchModule(absPath)\n\n // Re-check after lazy compilation\n if (serverFnsById[fnId]) {\n return `export {}`\n }\n }\n }\n } catch {\n // Decoding or fetching failed — fall through to error\n }\n }\n\n this.error(`Invalid server function ID: ${fnId}`)\n },\n },\n },\n // Manifest plugin for server environments\n createVirtualModule({\n name: 'tanstack-start-core:server-fn-resolver',\n moduleId: VIRTUAL_MODULES.serverFnResolver,\n enforce: 'pre',\n applyToEnvironment: (env) => {\n return appliedResolverEnvironments.has(env.name)\n },\n load() {\n if (this.environment.name !== opts.providerEnvName) {\n const mod = opts.environments.find(\n (e) => e.name === this.environment.name,\n )?.getServerFnById\n if (mod) {\n return mod\n }\n\n this.error(\n `No getServerFnById implementation found for caller environment: ${this.environment.name}`,\n )\n }\n\n if (this.environment.mode !== 'build') {\n return getDevServerFnValidatorModule()\n }\n\n // When SSR is the provider, server-only-referenced functions aren't in the manifest,\n // so no isClientReferenced check is needed.\n // When SSR is NOT the provider (custom provider env), server-only-referenced\n // functions ARE in the manifest and need the isClientReferenced check to\n // block direct client HTTP requests to server-only-referenced functions.\n return generateServerFnResolverModule({\n serverFnsById,\n includeClientReferencedCheck: !ssrIsProvider,\n })\n },\n }),\n ]\n}\n"],"mappings":"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"}
@@ -1,45 +1,34 @@
1
1
  import { ENTRY_POINTS, START_ENVIRONMENT_NAMES } from "../../constants.js";
2
- import { resolveViteId } from "../../utils.js";
2
+ import { createVirtualModule } from "../createVirtualModule.js";
3
3
  import { buildStartManifest, serializeStartManifest } from "../../start-manifest-plugin/manifestBuilder.js";
4
4
  import { VIRTUAL_MODULES } from "@tanstack/start-server-core";
5
5
  import { joinURL } from "ufo";
6
6
  //#region src/vite/start-manifest-plugin/plugin.ts
7
- var resolvedModuleId = resolveViteId(VIRTUAL_MODULES.startManifest);
8
7
  function startManifestPlugin(opts) {
9
- return {
8
+ return createVirtualModule({
10
9
  name: "tanstack-start:start-manifest-plugin",
10
+ moduleId: VIRTUAL_MODULES.startManifest,
11
11
  enforce: "pre",
12
- resolveId: {
13
- filter: { id: new RegExp(VIRTUAL_MODULES.startManifest) },
14
- handler(id) {
15
- if (id === VIRTUAL_MODULES.startManifest) return resolvedModuleId;
16
- }
17
- },
18
- load: {
19
- filter: { id: new RegExp(resolvedModuleId) },
20
- handler(id) {
21
- const { resolvedStartConfig } = opts.getConfig();
22
- if (id === resolvedModuleId) {
23
- if (this.environment.name !== START_ENVIRONMENT_NAMES.server) return "export default {}";
24
- if (this.environment.config.command === "serve") return `export const tsrStartManifest = () => ({
12
+ load() {
13
+ const { resolvedStartConfig } = opts.getConfig();
14
+ if (this.environment.name !== START_ENVIRONMENT_NAMES.server) return "export default {}";
15
+ if (this.environment.config.command === "serve") return `export const tsrStartManifest = () => ({
25
16
  routes: {},
26
17
  clientEntry: '${joinURL(resolvedStartConfig.basePaths.publicBase, "@id", ENTRY_POINTS.client)}',
27
18
  })`;
28
- const routeTreeRoutes = globalThis.TSS_ROUTES_MANIFEST;
29
- const clientBuild = opts.getClientBuild();
30
- if (!clientBuild) return `export const tsrStartManifest = () => ({
19
+ const routeTreeRoutes = globalThis.TSS_ROUTES_MANIFEST;
20
+ const clientBuild = opts.getClientBuild();
21
+ if (!clientBuild) return `export const tsrStartManifest = () => ({
31
22
  routes: {},
32
23
  clientEntry: '${joinURL(resolvedStartConfig.basePaths.publicBase, "@id", ENTRY_POINTS.client)}',
33
24
  })`;
34
- return `export const tsrStartManifest = () => (${serializeStartManifest(buildStartManifest({
35
- clientBuild,
36
- routeTreeRoutes,
37
- basePath: resolvedStartConfig.basePaths.publicBase
38
- }))})`;
39
- }
40
- }
25
+ return `export const tsrStartManifest = () => (${serializeStartManifest(buildStartManifest({
26
+ clientBuild,
27
+ routeTreeRoutes,
28
+ basePath: resolvedStartConfig.basePaths.publicBase
29
+ }))})`;
41
30
  }
42
- };
31
+ });
43
32
  }
44
33
  //#endregion
45
34
  export { startManifestPlugin };
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":[],"sources":["../../../../src/vite/start-manifest-plugin/plugin.ts"],"sourcesContent":["import { joinURL } from 'ufo'\nimport { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport { resolveViteId } from '../../utils'\nimport { ENTRY_POINTS, START_ENVIRONMENT_NAMES } from '../../constants'\nimport {\n buildStartManifest,\n serializeStartManifest,\n} from '../../start-manifest-plugin/manifestBuilder'\nimport type { GetConfigFn, NormalizedClientBuild } from '../../types'\nimport type { PluginOption } from 'vite'\n\nconst resolvedModuleId = resolveViteId(VIRTUAL_MODULES.startManifest)\n\nexport function startManifestPlugin(opts: {\n getClientBuild: () => NormalizedClientBuild | undefined\n getConfig: GetConfigFn\n}): PluginOption {\n return {\n name: 'tanstack-start:start-manifest-plugin',\n enforce: 'pre',\n resolveId: {\n filter: { id: new RegExp(VIRTUAL_MODULES.startManifest) },\n handler(id) {\n if (id === VIRTUAL_MODULES.startManifest) {\n return resolvedModuleId\n }\n return undefined\n },\n },\n load: {\n filter: {\n id: new RegExp(resolvedModuleId),\n },\n handler(id) {\n const { resolvedStartConfig } = opts.getConfig()\n if (id === resolvedModuleId) {\n if (this.environment.name !== START_ENVIRONMENT_NAMES.server) {\n return 'export default {}'\n }\n\n if (this.environment.config.command === 'serve') {\n return `export const tsrStartManifest = () => ({\n routes: {},\n clientEntry: '${joinURL(resolvedStartConfig.basePaths.publicBase, '@id', ENTRY_POINTS.client)}',\n })`\n }\n\n const routeTreeRoutes = globalThis.TSS_ROUTES_MANIFEST\n const clientBuild = opts.getClientBuild()\n // TODO this needs further discussion with vite-rsc, this is a temporary workaround\n // If the client bundle isn't available yet (e.g., during RSC scan builds),\n // return a dummy manifest. The real manifest will be generated in the actual build.\n if (!clientBuild) {\n return `export const tsrStartManifest = () => ({\n routes: {},\n clientEntry: '${joinURL(resolvedStartConfig.basePaths.publicBase, '@id', ENTRY_POINTS.client)}',\n })`\n }\n const startManifest = buildStartManifest({\n clientBuild,\n routeTreeRoutes,\n basePath: resolvedStartConfig.basePaths.publicBase,\n })\n\n return `export const tsrStartManifest = () => (${serializeStartManifest(startManifest)})`\n }\n\n return undefined\n },\n },\n }\n}\n"],"mappings":";;;;;;AAWA,IAAM,mBAAmB,cAAc,gBAAgB,cAAc;AAErE,SAAgB,oBAAoB,MAGnB;AACf,QAAO;EACL,MAAM;EACN,SAAS;EACT,WAAW;GACT,QAAQ,EAAE,IAAI,IAAI,OAAO,gBAAgB,cAAc,EAAE;GACzD,QAAQ,IAAI;AACV,QAAI,OAAO,gBAAgB,cACzB,QAAO;;GAIZ;EACD,MAAM;GACJ,QAAQ,EACN,IAAI,IAAI,OAAO,iBAAiB,EACjC;GACD,QAAQ,IAAI;IACV,MAAM,EAAE,wBAAwB,KAAK,WAAW;AAChD,QAAI,OAAO,kBAAkB;AAC3B,SAAI,KAAK,YAAY,SAAS,wBAAwB,OACpD,QAAO;AAGT,SAAI,KAAK,YAAY,OAAO,YAAY,QACtC,QAAO;;4BAES,QAAQ,oBAAoB,UAAU,YAAY,OAAO,aAAa,OAAO,CAAC;;KAIhG,MAAM,kBAAkB,WAAW;KACnC,MAAM,cAAc,KAAK,gBAAgB;AAIzC,SAAI,CAAC,YACH,QAAO;;8BAEW,QAAQ,oBAAoB,UAAU,YAAY,OAAO,aAAa,OAAO,CAAC;;AASlG,YAAO,0CAA0C,uBAN3B,mBAAmB;MACvC;MACA;MACA,UAAU,oBAAoB,UAAU;MACzC,CAAC,CAEoF,CAAC;;;GAK5F;EACF"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../../../../src/vite/start-manifest-plugin/plugin.ts"],"sourcesContent":["import { joinURL } from 'ufo'\nimport { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport { ENTRY_POINTS, START_ENVIRONMENT_NAMES } from '../../constants'\nimport {\n buildStartManifest,\n serializeStartManifest,\n} from '../../start-manifest-plugin/manifestBuilder'\nimport { createVirtualModule } from '../createVirtualModule'\nimport type { GetConfigFn, NormalizedClientBuild } from '../../types'\nimport type { PluginOption } from 'vite'\n\nexport function startManifestPlugin(opts: {\n getClientBuild: () => NormalizedClientBuild | undefined\n getConfig: GetConfigFn\n}): PluginOption {\n return createVirtualModule({\n name: 'tanstack-start:start-manifest-plugin',\n moduleId: VIRTUAL_MODULES.startManifest,\n enforce: 'pre',\n load() {\n const { resolvedStartConfig } = opts.getConfig()\n if (this.environment.name !== START_ENVIRONMENT_NAMES.server) {\n return 'export default {}'\n }\n\n if (this.environment.config.command === 'serve') {\n return `export const tsrStartManifest = () => ({\n routes: {},\n clientEntry: '${joinURL(resolvedStartConfig.basePaths.publicBase, '@id', ENTRY_POINTS.client)}',\n })`\n }\n\n const routeTreeRoutes = globalThis.TSS_ROUTES_MANIFEST\n const clientBuild = opts.getClientBuild()\n // TODO this needs further discussion with vite-rsc, this is a temporary workaround\n // If the client bundle isn't available yet (e.g., during RSC scan builds),\n // return a dummy manifest. The real manifest will be generated in the actual build.\n if (!clientBuild) {\n return `export const tsrStartManifest = () => ({\n routes: {},\n clientEntry: '${joinURL(resolvedStartConfig.basePaths.publicBase, '@id', ENTRY_POINTS.client)}',\n })`\n }\n const startManifest = buildStartManifest({\n clientBuild,\n routeTreeRoutes,\n basePath: resolvedStartConfig.basePaths.publicBase,\n })\n\n return `export const tsrStartManifest = () => (${serializeStartManifest(startManifest)})`\n },\n })\n}\n"],"mappings":";;;;;;AAWA,SAAgB,oBAAoB,MAGnB;AACf,QAAO,oBAAoB;EACzB,MAAM;EACN,UAAU,gBAAgB;EAC1B,SAAS;EACT,OAAO;GACL,MAAM,EAAE,wBAAwB,KAAK,WAAW;AAChD,OAAI,KAAK,YAAY,SAAS,wBAAwB,OACpD,QAAO;AAGT,OAAI,KAAK,YAAY,OAAO,YAAY,QACtC,QAAO;;4BAEa,QAAQ,oBAAoB,UAAU,YAAY,OAAO,aAAa,OAAO,CAAC;;GAIpG,MAAM,kBAAkB,WAAW;GACnC,MAAM,cAAc,KAAK,gBAAgB;AAIzC,OAAI,CAAC,YACH,QAAO;;8BAEe,QAAQ,oBAAoB,UAAU,YAAY,OAAO,aAAa,OAAO,CAAC;;AAStG,UAAO,0CAA0C,uBAN3B,mBAAmB;IACvC;IACA;IACA,UAAU,oBAAoB,UAAU;IACzC,CAAC,CAEoF,CAAC;;EAE1F,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-plugin-core",
3
- "version": "1.167.31",
3
+ "version": "1.167.33",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -40,11 +40,6 @@
40
40
  "default": "./dist/esm/utils.js"
41
41
  }
42
42
  },
43
- "./vite/types": {
44
- "import": {
45
- "types": "./dist/esm/vite/types.d.ts"
46
- }
47
- },
48
43
  "./package.json": "./package.json"
49
44
  },
50
45
  "sideEffects": false,
@@ -73,8 +68,8 @@
73
68
  "xmlbuilder2": "^4.0.3",
74
69
  "zod": "^3.24.2",
75
70
  "@tanstack/router-core": "1.168.14",
76
- "@tanstack/router-generator": "1.166.30",
77
- "@tanstack/router-plugin": "1.167.20",
71
+ "@tanstack/router-generator": "1.166.31",
72
+ "@tanstack/router-plugin": "1.167.21",
78
73
  "@tanstack/router-utils": "1.161.6",
79
74
  "@tanstack/start-client-core": "1.167.16",
80
75
  "@tanstack/start-server-core": "1.167.18"
package/src/index.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  export type { TanStackStartInputConfig } from './schema'
2
2
  export type { TanStackStartCoreOptions } from './types'
3
- export type { TanStackStartVitePluginCoreOptions } from './vite/types'
3
+ export type {
4
+ TanStackStartVitePluginCoreOptions,
5
+ ViteRscForwardSsrResolverStrategy,
6
+ } from './vite/types'
4
7
  export type { TanStackStartViteInputConfig } from './vite/schema'
5
8
  export { START_ENVIRONMENT_NAMES, VITE_ENVIRONMENT_NAMES } from './constants'
9
+ export { createVirtualModule } from './vite/createVirtualModule'
6
10
  export { tanStackStartVite } from './vite/plugin'
@@ -0,0 +1,54 @@
1
+ import { resolveViteId } from '../utils'
2
+ import type { Plugin } from 'vite'
3
+
4
+ type VirtualModuleLoadHandler = (
5
+ this: any,
6
+ id: string,
7
+ ) => string | null | undefined | Promise<string | null | undefined>
8
+
9
+ function escapeRegExp(value: string): string {
10
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
11
+ }
12
+
13
+ export function createVirtualModule(opts: {
14
+ name: string
15
+ moduleId: string
16
+ load: VirtualModuleLoadHandler
17
+ apply?: Plugin['apply']
18
+ applyToEnvironment?: Plugin['applyToEnvironment']
19
+ enforce?: Plugin['enforce']
20
+ sharedDuringBuild?: boolean
21
+ }): Plugin {
22
+ // Encode '#' as '%23' in the resolved ID to avoid browser treating it as URL fragment.
23
+ // The browser requests /@id/__x00__%23tanstack-start-plugin-adapters instead of
24
+ // /@id/__x00__#tanstack-start-plugin-adapters (which would truncate at #).
25
+ const resolvedId = resolveViteId(opts.moduleId.replaceAll('#', '%23'))
26
+
27
+ return {
28
+ name: opts.name,
29
+ apply: opts.apply,
30
+ applyToEnvironment: opts.applyToEnvironment,
31
+ enforce: opts.enforce,
32
+ sharedDuringBuild: opts.sharedDuringBuild,
33
+ resolveId: {
34
+ filter: { id: new RegExp(escapeRegExp(opts.moduleId)) },
35
+ handler(id) {
36
+ if (id === opts.moduleId) {
37
+ return resolvedId
38
+ }
39
+
40
+ return undefined
41
+ },
42
+ },
43
+ load: {
44
+ filter: { id: new RegExp(escapeRegExp(resolvedId)) },
45
+ handler(id) {
46
+ if (id !== resolvedId) {
47
+ return undefined
48
+ }
49
+
50
+ return opts.load.call(this, id)
51
+ },
52
+ },
53
+ }
54
+ }
@@ -2,7 +2,7 @@ import { isRunnableDevEnvironment } from 'vite'
2
2
  import { VIRTUAL_MODULES } from '@tanstack/start-server-core'
3
3
  import { NodeRequest, sendNodeResponse } from 'srvx/node'
4
4
  import { ENTRY_POINTS, VITE_ENVIRONMENT_NAMES } from '../../constants'
5
- import { resolveViteId } from '../../utils'
5
+ import { createVirtualModule } from '../createVirtualModule'
6
6
  import { extractHtmlScripts } from './extract-html-scripts'
7
7
  import {
8
8
  CSS_MODULES_REGEX,
@@ -248,27 +248,17 @@ export function devServerPlugin({
248
248
  }
249
249
  },
250
250
  },
251
- {
251
+ createVirtualModule({
252
252
  name: 'tanstack-start-core:dev-server:injected-head-scripts',
253
253
  sharedDuringBuild: true,
254
254
  applyToEnvironment: (env) => env.config.consumer === 'server',
255
- resolveId: {
256
- filter: { id: new RegExp(VIRTUAL_MODULES.injectedHeadScripts) },
257
- handler(_id) {
258
- return resolveViteId(VIRTUAL_MODULES.injectedHeadScripts)
259
- },
260
- },
261
- load: {
262
- filter: {
263
- id: new RegExp(resolveViteId(VIRTUAL_MODULES.injectedHeadScripts)),
264
- },
265
- handler() {
266
- const mod = `
255
+ moduleId: VIRTUAL_MODULES.injectedHeadScripts,
256
+ load() {
257
+ const mod = `
267
258
  export const injectedHeadScripts = ${JSON.stringify(injectedHeadScripts) || 'undefined'}`
268
- return mod
269
- },
259
+ return mod
270
260
  },
271
- },
261
+ }),
272
262
  ]
273
263
  }
274
264
 
@@ -25,6 +25,7 @@ import {
25
25
  createCaptureClientBuildPlugin,
26
26
  createDevBaseRewritePlugin,
27
27
  createPostBuildPlugin,
28
+ createVirtualClientEntryPlugin,
28
29
  } from './plugins'
29
30
  import { parseStartConfig } from './schema'
30
31
  import { startManifestPlugin } from './start-manifest-plugin/plugin'
@@ -237,6 +238,9 @@ export function tanStackStartVite(
237
238
  }),
238
239
  tanStackStartRouter(normalizedStartPluginOpts, getConfig, corePluginOpts),
239
240
  loadEnvPlugin(),
241
+ createVirtualClientEntryPlugin({
242
+ getClientEntry: () => configContext.resolveEntries().entryPaths.client,
243
+ }),
240
244
  startManifestPlugin({
241
245
  getClientBuild: () => getClientBuild(START_ENVIRONMENT_NAMES.client),
242
246
  getConfig,
@@ -1,4 +1,6 @@
1
- import { START_ENVIRONMENT_NAMES } from '../constants'
1
+ import { normalizePath } from 'vite'
2
+ import { ENTRY_POINTS, START_ENVIRONMENT_NAMES } from '../constants'
3
+ import { createVirtualModule } from './createVirtualModule'
2
4
  import { normalizeViteClientBuild } from './start-manifest-plugin/normalized-client-build'
3
5
  import type {
4
6
  GetConfigFn,
@@ -8,6 +10,19 @@ import type {
8
10
  import type { StartEnvironmentName } from '../constants'
9
11
  import type { PluginOption, ViteBuilder } from 'vite'
10
12
 
13
+ export function createVirtualClientEntryPlugin(opts: {
14
+ getClientEntry: () => string
15
+ }): PluginOption {
16
+ return createVirtualModule({
17
+ name: 'tanstack-start-core:virtual-client-entry',
18
+ moduleId: ENTRY_POINTS.client,
19
+ enforce: 'pre',
20
+ load() {
21
+ return `import ${JSON.stringify(normalizePath(opts.getClientEntry()).replaceAll('\\', '/'))}`
22
+ },
23
+ })
24
+ }
25
+
11
26
  export function createPostBuildPlugin(opts: {
12
27
  getConfig: GetConfigFn
13
28
  postServerBuild: (opts: {
@@ -4,66 +4,38 @@ import {
4
4
  generateSerializationAdaptersModule,
5
5
  } from '../serialization-adapters-module'
6
6
  import { START_ENVIRONMENT_NAMES } from '../constants'
7
- import { resolveViteId } from '../utils'
7
+ import { createVirtualModule } from './createVirtualModule'
8
8
  import type { SerializationAdapterConfig } from '../types'
9
9
  import type { PluginOption } from 'vite'
10
10
 
11
- // Encode '#' as '%23' in the resolved ID to avoid browser treating it as URL fragment.
12
- // The browser requests /@id/__x00__%23tanstack-start-plugin-adapters instead of
13
- // /@id/__x00__#tanstack-start-plugin-adapters (which would truncate at #).
14
- const resolvedModuleId = resolveViteId(
15
- VIRTUAL_MODULES.pluginAdapters.replace('#', '%23'),
16
- )
17
-
18
- function escapeRegex(str: string): string {
19
- return str.replace(/[.*+?^${}()|[\]\\#]/g, '\\$&')
20
- }
21
-
22
11
  export function serializationAdaptersPlugin(opts: {
23
12
  adapters: Array<SerializationAdapterConfig> | undefined
24
13
  }): PluginOption {
25
- return {
14
+ return createVirtualModule({
26
15
  name: 'tanstack-start:plugin-adapters',
16
+ moduleId: VIRTUAL_MODULES.pluginAdapters,
27
17
  enforce: 'pre',
28
- resolveId: {
29
- filter: { id: new RegExp(escapeRegex(VIRTUAL_MODULES.pluginAdapters)) },
30
- handler(id: string) {
31
- if (id === VIRTUAL_MODULES.pluginAdapters) {
32
- return resolvedModuleId
33
- }
34
- return undefined
35
- },
36
- },
37
- load: {
38
- filter: {
39
- id: new RegExp(escapeRegex(resolvedModuleId)),
40
- },
41
- handler(this: { environment: { name: string } }, id: string) {
42
- if (id !== resolvedModuleId) {
43
- return undefined
44
- }
45
-
46
- const adapters = opts.adapters
47
- if (!adapters || adapters.length === 0) {
48
- return EMPTY_SERIALIZATION_ADAPTERS_MODULE
49
- }
18
+ load() {
19
+ const adapters = opts.adapters
20
+ if (!adapters || adapters.length === 0) {
21
+ return EMPTY_SERIALIZATION_ADAPTERS_MODULE
22
+ }
50
23
 
51
- if (this.environment.name === START_ENVIRONMENT_NAMES.client) {
52
- return generateSerializationAdaptersModule({
53
- adapters,
54
- runtime: 'client',
55
- })
56
- }
24
+ if (this.environment.name === START_ENVIRONMENT_NAMES.client) {
25
+ return generateSerializationAdaptersModule({
26
+ adapters,
27
+ runtime: 'client',
28
+ })
29
+ }
57
30
 
58
- if (this.environment.name === START_ENVIRONMENT_NAMES.server) {
59
- return generateSerializationAdaptersModule({
60
- adapters,
61
- runtime: 'server',
62
- })
63
- }
31
+ if (this.environment.name === START_ENVIRONMENT_NAMES.server) {
32
+ return generateSerializationAdaptersModule({
33
+ adapters,
34
+ runtime: 'server',
35
+ })
36
+ }
64
37
 
65
- return EMPTY_SERIALIZATION_ADAPTERS_MODULE
66
- },
38
+ return EMPTY_SERIALIZATION_ADAPTERS_MODULE
67
39
  },
68
- }
40
+ })
69
41
  }
@@ -15,6 +15,8 @@ import {
15
15
  import { loadModuleForViteCompiler } from '../../start-compiler/load-module'
16
16
  import { generateServerFnResolverModule } from '../../start-compiler/server-fn-resolver-module'
17
17
  import { cleanId } from '../../start-compiler/utils'
18
+ import { createVirtualModule } from '../createVirtualModule'
19
+ import { resolveViteId } from '../../utils'
18
20
  import {
19
21
  createViteDevServerFnModuleSpecifierEncoder,
20
22
  decodeViteDevServerModuleSpecifier,
@@ -29,10 +31,6 @@ import type { PluginOption } from 'vite'
29
31
  // Re-export from shared constants for backwards compatibility
30
32
  export { SERVER_FN_LOOKUP }
31
33
 
32
- function resolveViteId(id: string) {
33
- return `\0${id}`
34
- }
35
-
36
34
  const validateServerFnIdVirtualModule = `virtual:tanstack-start-validate-server-fn-id`
37
35
 
38
36
  function getDevServerFnValidatorModule(): string {
@@ -90,10 +88,6 @@ export function startCompilerPlugin(
90
88
  }
91
89
 
92
90
  let root = process.cwd()
93
- const resolvedResolverVirtualImportId = resolveViteId(
94
- VIRTUAL_MODULES.serverFnResolver,
95
- )
96
-
97
91
  // Determine which environments need the resolver (getServerFnById)
98
92
  // SSR environment always needs the resolver for server-side calls
99
93
  // Provider environment needs it for the actual implementation
@@ -294,52 +288,41 @@ export function startCompilerPlugin(
294
288
  },
295
289
  },
296
290
  // Manifest plugin for server environments
297
- {
291
+ createVirtualModule({
298
292
  name: 'tanstack-start-core:server-fn-resolver',
293
+ moduleId: VIRTUAL_MODULES.serverFnResolver,
299
294
  enforce: 'pre',
300
295
  applyToEnvironment: (env) => {
301
296
  return appliedResolverEnvironments.has(env.name)
302
297
  },
303
- configResolved(config) {
304
- root = config.root
305
- },
306
- resolveId: {
307
- filter: { id: new RegExp(VIRTUAL_MODULES.serverFnResolver) },
308
- handler() {
309
- return resolvedResolverVirtualImportId
310
- },
311
- },
312
- load: {
313
- filter: { id: new RegExp(resolvedResolverVirtualImportId) },
314
- handler() {
315
- if (this.environment.name !== opts.providerEnvName) {
316
- const mod = opts.environments.find(
317
- (e) => e.name === this.environment.name,
318
- )?.getServerFnById
319
- if (mod) {
320
- return mod
321
- } else {
322
- this.error(
323
- `No getServerFnById implementation found for caller environment: ${this.environment.name}`,
324
- )
325
- }
326
- }
327
-
328
- if (this.environment.mode !== 'build') {
329
- return getDevServerFnValidatorModule()
298
+ load() {
299
+ if (this.environment.name !== opts.providerEnvName) {
300
+ const mod = opts.environments.find(
301
+ (e) => e.name === this.environment.name,
302
+ )?.getServerFnById
303
+ if (mod) {
304
+ return mod
330
305
  }
331
306
 
332
- // When SSR is the provider, server-only-referenced functions aren't in the manifest,
333
- // so no isClientReferenced check is needed.
334
- // When SSR is NOT the provider (custom provider env), server-only-referenced
335
- // functions ARE in the manifest and need the isClientReferenced check to
336
- // block direct client HTTP requests to server-only-referenced functions.
337
- return generateServerFnResolverModule({
338
- serverFnsById,
339
- includeClientReferencedCheck: !ssrIsProvider,
340
- })
341
- },
307
+ this.error(
308
+ `No getServerFnById implementation found for caller environment: ${this.environment.name}`,
309
+ )
310
+ }
311
+
312
+ if (this.environment.mode !== 'build') {
313
+ return getDevServerFnValidatorModule()
314
+ }
315
+
316
+ // When SSR is the provider, server-only-referenced functions aren't in the manifest,
317
+ // so no isClientReferenced check is needed.
318
+ // When SSR is NOT the provider (custom provider env), server-only-referenced
319
+ // functions ARE in the manifest and need the isClientReferenced check to
320
+ // block direct client HTTP requests to server-only-referenced functions.
321
+ return generateServerFnResolverModule({
322
+ serverFnsById,
323
+ includeClientReferencedCheck: !ssrIsProvider,
324
+ })
342
325
  },
343
- },
326
+ }),
344
327
  ]
345
328
  }
@@ -1,72 +1,53 @@
1
1
  import { joinURL } from 'ufo'
2
2
  import { VIRTUAL_MODULES } from '@tanstack/start-server-core'
3
- import { resolveViteId } from '../../utils'
4
3
  import { ENTRY_POINTS, START_ENVIRONMENT_NAMES } from '../../constants'
5
4
  import {
6
5
  buildStartManifest,
7
6
  serializeStartManifest,
8
7
  } from '../../start-manifest-plugin/manifestBuilder'
8
+ import { createVirtualModule } from '../createVirtualModule'
9
9
  import type { GetConfigFn, NormalizedClientBuild } from '../../types'
10
10
  import type { PluginOption } from 'vite'
11
11
 
12
- const resolvedModuleId = resolveViteId(VIRTUAL_MODULES.startManifest)
13
-
14
12
  export function startManifestPlugin(opts: {
15
13
  getClientBuild: () => NormalizedClientBuild | undefined
16
14
  getConfig: GetConfigFn
17
15
  }): PluginOption {
18
- return {
16
+ return createVirtualModule({
19
17
  name: 'tanstack-start:start-manifest-plugin',
18
+ moduleId: VIRTUAL_MODULES.startManifest,
20
19
  enforce: 'pre',
21
- resolveId: {
22
- filter: { id: new RegExp(VIRTUAL_MODULES.startManifest) },
23
- handler(id) {
24
- if (id === VIRTUAL_MODULES.startManifest) {
25
- return resolvedModuleId
26
- }
27
- return undefined
28
- },
29
- },
30
- load: {
31
- filter: {
32
- id: new RegExp(resolvedModuleId),
33
- },
34
- handler(id) {
35
- const { resolvedStartConfig } = opts.getConfig()
36
- if (id === resolvedModuleId) {
37
- if (this.environment.name !== START_ENVIRONMENT_NAMES.server) {
38
- return 'export default {}'
39
- }
20
+ load() {
21
+ const { resolvedStartConfig } = opts.getConfig()
22
+ if (this.environment.name !== START_ENVIRONMENT_NAMES.server) {
23
+ return 'export default {}'
24
+ }
40
25
 
41
- if (this.environment.config.command === 'serve') {
42
- return `export const tsrStartManifest = () => ({
26
+ if (this.environment.config.command === 'serve') {
27
+ return `export const tsrStartManifest = () => ({
43
28
  routes: {},
44
29
  clientEntry: '${joinURL(resolvedStartConfig.basePaths.publicBase, '@id', ENTRY_POINTS.client)}',
45
30
  })`
46
- }
31
+ }
47
32
 
48
- const routeTreeRoutes = globalThis.TSS_ROUTES_MANIFEST
49
- const clientBuild = opts.getClientBuild()
50
- // TODO this needs further discussion with vite-rsc, this is a temporary workaround
51
- // If the client bundle isn't available yet (e.g., during RSC scan builds),
52
- // return a dummy manifest. The real manifest will be generated in the actual build.
53
- if (!clientBuild) {
54
- return `export const tsrStartManifest = () => ({
33
+ const routeTreeRoutes = globalThis.TSS_ROUTES_MANIFEST
34
+ const clientBuild = opts.getClientBuild()
35
+ // TODO this needs further discussion with vite-rsc, this is a temporary workaround
36
+ // If the client bundle isn't available yet (e.g., during RSC scan builds),
37
+ // return a dummy manifest. The real manifest will be generated in the actual build.
38
+ if (!clientBuild) {
39
+ return `export const tsrStartManifest = () => ({
55
40
  routes: {},
56
41
  clientEntry: '${joinURL(resolvedStartConfig.basePaths.publicBase, '@id', ENTRY_POINTS.client)}',
57
42
  })`
58
- }
59
- const startManifest = buildStartManifest({
60
- clientBuild,
61
- routeTreeRoutes,
62
- basePath: resolvedStartConfig.basePaths.publicBase,
63
- })
64
-
65
- return `export const tsrStartManifest = () => (${serializeStartManifest(startManifest)})`
66
- }
43
+ }
44
+ const startManifest = buildStartManifest({
45
+ clientBuild,
46
+ routeTreeRoutes,
47
+ basePath: resolvedStartConfig.basePaths.publicBase,
48
+ })
67
49
 
68
- return undefined
69
- },
50
+ return `export const tsrStartManifest = () => (${serializeStartManifest(startManifest)})`
70
51
  },
71
- }
52
+ })
72
53
  }