@tanstack/start-plugin-core 1.136.18 → 1.138.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
1
  import type * as vite from 'vite';
2
- export declare function getClientOutputDirectory(userConfig: vite.UserConfig): string;
3
- export declare function getServerOutputDirectory(userConfig: vite.UserConfig): string;
2
+ export declare function getClientOutputDirectory(userConfig: vite.UserConfig | vite.ResolvedConfig): string;
3
+ export declare function getServerOutputDirectory(userConfig: vite.UserConfig | vite.ResolvedConfig): string;
@@ -1 +1 @@
1
- {"version":3,"file":"output-directory.js","sources":["../../src/output-directory.ts"],"sourcesContent":["import { join } from 'pathe'\nimport { VITE_ENVIRONMENT_NAMES } from './constants'\nimport type { ViteEnvironmentNames } from './constants'\nimport type * as vite from 'vite'\n\nexport function getClientOutputDirectory(userConfig: vite.UserConfig) {\n return getOutputDirectory(userConfig, VITE_ENVIRONMENT_NAMES.client, 'client')\n}\n\nexport function getServerOutputDirectory(userConfig: vite.UserConfig) {\n return getOutputDirectory(userConfig, VITE_ENVIRONMENT_NAMES.server, 'server')\n}\n\nfunction getOutputDirectory(\n userConfig: vite.UserConfig,\n environmentName: ViteEnvironmentNames,\n directoryName: string,\n) {\n const rootOutputDirectory = userConfig.build?.outDir ?? 'dist'\n\n return (\n userConfig.environments?.[environmentName]?.build?.outDir ??\n join(rootOutputDirectory, directoryName)\n )\n}\n"],"names":[],"mappings":";;AAKO,SAAS,yBAAyB,YAA6B;AACpE,SAAO,mBAAmB,YAAY,uBAAuB,QAAQ,QAAQ;AAC/E;AAEO,SAAS,yBAAyB,YAA6B;AACpE,SAAO,mBAAmB,YAAY,uBAAuB,QAAQ,QAAQ;AAC/E;AAEA,SAAS,mBACP,YACA,iBACA,eACA;AACA,QAAM,sBAAsB,WAAW,OAAO,UAAU;AAExD,SACE,WAAW,eAAe,eAAe,GAAG,OAAO,UACnD,KAAK,qBAAqB,aAAa;AAE3C;"}
1
+ {"version":3,"file":"output-directory.js","sources":["../../src/output-directory.ts"],"sourcesContent":["import { join } from 'pathe'\nimport { VITE_ENVIRONMENT_NAMES } from './constants'\nimport type { ViteEnvironmentNames } from './constants'\nimport type * as vite from 'vite'\n\nexport function getClientOutputDirectory(\n userConfig: vite.UserConfig | vite.ResolvedConfig,\n) {\n return getOutputDirectory(userConfig, VITE_ENVIRONMENT_NAMES.client, 'client')\n}\n\nexport function getServerOutputDirectory(\n userConfig: vite.UserConfig | vite.ResolvedConfig,\n) {\n return getOutputDirectory(userConfig, VITE_ENVIRONMENT_NAMES.server, 'server')\n}\n\nfunction getOutputDirectory(\n userConfig: vite.UserConfig | vite.ResolvedConfig,\n environmentName: ViteEnvironmentNames,\n directoryName: string,\n) {\n const rootOutputDirectory = userConfig.build?.outDir ?? 'dist'\n\n return (\n userConfig.environments?.[environmentName]?.build?.outDir ??\n join(rootOutputDirectory, directoryName)\n )\n}\n"],"names":[],"mappings":";;AAKO,SAAS,yBACd,YACA;AACA,SAAO,mBAAmB,YAAY,uBAAuB,QAAQ,QAAQ;AAC/E;AAEO,SAAS,yBACd,YACA;AACA,SAAO,mBAAmB,YAAY,uBAAuB,QAAQ,QAAQ;AAC/E;AAEA,SAAS,mBACP,YACA,iBACA,eACA;AACA,QAAM,sBAAsB,WAAW,OAAO,UAAU;AAExD,SACE,WAAW,eAAe,eAAe,GAAG,OAAO,UACnD,KAAK,qBAAqB,aAAa;AAE3C;"}
@@ -11,6 +11,7 @@ import { VITE_ENVIRONMENT_NAMES, ENTRY_POINTS } from "./constants.js";
11
11
  import { tanStackStartRouter } from "./start-router-plugin/plugin.js";
12
12
  import { loadEnvPlugin } from "./load-env-plugin/plugin.js";
13
13
  import { devServerPlugin } from "./dev-server-plugin/plugin.js";
14
+ import { previewServerPlugin } from "./preview-server-plugin/plugin.js";
14
15
  import { parseStartConfig } from "./schema.js";
15
16
  import { resolveEntry } from "./resolve-entries.js";
16
17
  import { getServerOutputDirectory, getClientOutputDirectory } from "./output-directory.js";
@@ -255,11 +256,16 @@ function TanStackStartVitePluginCore(corePluginOpts, startPluginOpts) {
255
256
  if (!server.isBuilt) {
256
257
  await builder.build(server);
257
258
  }
258
- const serverBundle = getBundle(VITE_ENVIRONMENT_NAMES.server);
259
- await postServerBuild({ builder, startConfig: startConfig2, serverBundle });
260
259
  }
261
260
  }
262
261
  };
262
+ },
263
+ buildApp: {
264
+ order: "post",
265
+ async handler(builder) {
266
+ const { startConfig: startConfig2 } = getConfig();
267
+ await postServerBuild({ builder, startConfig: startConfig2 });
268
+ }
263
269
  }
264
270
  },
265
271
  tanStackStartRouter(startPluginOpts, getConfig, corePluginOpts),
@@ -303,6 +309,7 @@ function TanStackStartVitePluginCore(corePluginOpts, startPluginOpts) {
303
309
  getConfig
304
310
  }),
305
311
  devServerPlugin({ getConfig }),
312
+ previewServerPlugin(),
306
313
  {
307
314
  name: "tanstack-start:core:capture-bundle",
308
315
  applyToEnvironment(e) {
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sources":["../../src/plugin.ts"],"sourcesContent":["import { joinPaths } from '@tanstack/router-core'\nimport { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport { TanStackServerFnPlugin } from '@tanstack/server-functions-plugin'\nimport * as vite from 'vite'\nimport { crawlFrameworkPkgs } from 'vitefu'\nimport { join } from 'pathe'\nimport { escapePath } from 'tinyglobby'\nimport { startManifestPlugin } from './start-manifest-plugin/plugin'\nimport { startCompilerPlugin } from './start-compiler-plugin/plugin'\nimport { ENTRY_POINTS, VITE_ENVIRONMENT_NAMES } from './constants'\nimport { tanStackStartRouter } from './start-router-plugin/plugin'\nimport { loadEnvPlugin } from './load-env-plugin/plugin'\nimport { devServerPlugin } from './dev-server-plugin/plugin'\nimport { parseStartConfig } from './schema'\nimport { resolveEntry } from './resolve-entries'\nimport {\n getClientOutputDirectory,\n getServerOutputDirectory,\n} from './output-directory'\nimport { postServerBuild } from './post-server-build'\nimport { createServerFnPlugin } from './create-server-fn-plugin/plugin'\nimport type { ViteEnvironmentNames } from './constants'\nimport type {\n TanStackStartInputConfig,\n TanStackStartOutputConfig,\n} from './schema'\nimport type { PluginOption } from 'vite'\nimport type { CompileStartFrameworkOptions } from './start-compiler-plugin/compilers'\n\nexport interface TanStackStartVitePluginCoreOptions {\n framework: CompileStartFrameworkOptions\n defaultEntryPaths: {\n client: string\n server: string\n start: string\n }\n serverFn?: {\n directive?: string\n ssr?: {\n getServerFnById?: string\n }\n providerEnv?: string\n }\n}\n\nexport interface ResolvedStartConfig {\n root: string\n startFilePath: string | undefined\n routerFilePath: string\n srcDirectory: string\n viteAppBase: string\n}\n\nexport type GetConfigFn = () => {\n startConfig: TanStackStartOutputConfig\n resolvedStartConfig: ResolvedStartConfig\n}\n\nfunction isFullUrl(str: string): boolean {\n try {\n new URL(str)\n return true\n } catch {\n return false\n }\n}\n\nexport function TanStackStartVitePluginCore(\n corePluginOpts: TanStackStartVitePluginCoreOptions,\n startPluginOpts: TanStackStartInputConfig,\n): Array<PluginOption> {\n const resolvedStartConfig: ResolvedStartConfig = {\n root: '',\n startFilePath: undefined,\n routerFilePath: '',\n srcDirectory: '',\n viteAppBase: '',\n }\n\n const directive = corePluginOpts.serverFn?.directive ?? 'use server'\n\n let startConfig: TanStackStartOutputConfig | null\n const getConfig: GetConfigFn = () => {\n if (!resolvedStartConfig.root) {\n throw new Error(`Cannot get config before root is resolved`)\n }\n if (!startConfig) {\n startConfig = parseStartConfig(\n startPluginOpts,\n corePluginOpts,\n resolvedStartConfig.root,\n )\n }\n return { startConfig, resolvedStartConfig }\n }\n\n const capturedBundle: Partial<\n Record<ViteEnvironmentNames, vite.Rollup.OutputBundle>\n > = {}\n\n function getBundle(envName: ViteEnvironmentNames): vite.Rollup.OutputBundle {\n const bundle = capturedBundle[envName]\n if (!bundle) {\n throw new Error(`No bundle captured for environment: ${envName}`)\n }\n return bundle\n }\n\n const environments: Array<{ name: string; type: 'client' | 'server' }> = [\n { name: VITE_ENVIRONMENT_NAMES.client, type: 'client' },\n { name: VITE_ENVIRONMENT_NAMES.server, type: 'server' },\n ]\n if (\n corePluginOpts.serverFn?.providerEnv &&\n !environments.find((e) => e.name === corePluginOpts.serverFn?.providerEnv)\n ) {\n environments.push({\n name: corePluginOpts.serverFn.providerEnv,\n type: 'server',\n })\n }\n return [\n {\n name: 'tanstack-start-core:config',\n enforce: 'pre',\n async config(viteConfig, { command }) {\n resolvedStartConfig.viteAppBase = viteConfig.base ?? '/'\n if (!isFullUrl(resolvedStartConfig.viteAppBase)) {\n resolvedStartConfig.viteAppBase = joinPaths([\n '/',\n viteConfig.base,\n '/',\n ])\n }\n const root = viteConfig.root || process.cwd()\n resolvedStartConfig.root = root\n\n const { startConfig } = getConfig()\n if (startConfig.router.basepath === undefined) {\n if (!isFullUrl(resolvedStartConfig.viteAppBase)) {\n startConfig.router.basepath =\n resolvedStartConfig.viteAppBase.replace(/^\\/|\\/$/g, '')\n } else {\n startConfig.router.basepath = '/'\n }\n } else {\n if (command === 'serve' && !viteConfig.server?.middlewareMode) {\n // when serving, we must ensure that router basepath and viteAppBase are aligned\n if (\n !joinPaths(['/', startConfig.router.basepath, '/']).startsWith(\n joinPaths(['/', resolvedStartConfig.viteAppBase, '/']),\n )\n ) {\n this.error(\n '[tanstack-start]: During `vite dev`, `router.basepath` must start with the vite `base` config value',\n )\n }\n }\n }\n\n const TSS_SERVER_FN_BASE = joinPaths([\n '/',\n startConfig.router.basepath,\n startConfig.serverFns.base,\n '/',\n ])\n const resolvedSrcDirectory = join(root, startConfig.srcDirectory)\n resolvedStartConfig.srcDirectory = resolvedSrcDirectory\n\n const startFilePath = resolveEntry({\n type: 'start entry',\n configuredEntry: startConfig.start.entry,\n defaultEntry: 'start',\n resolvedSrcDirectory,\n required: false,\n })\n resolvedStartConfig.startFilePath = startFilePath\n\n const routerFilePath = resolveEntry({\n type: 'router entry',\n configuredEntry: startConfig.router.entry,\n defaultEntry: 'router',\n resolvedSrcDirectory,\n required: true,\n })\n resolvedStartConfig.routerFilePath = routerFilePath\n\n const clientEntryPath = resolveEntry({\n type: 'client entry',\n configuredEntry: startConfig.client.entry,\n defaultEntry: 'client',\n resolvedSrcDirectory,\n required: false,\n })\n\n const serverEntryPath = resolveEntry({\n type: 'server entry',\n configuredEntry: startConfig.server.entry,\n defaultEntry: 'server',\n resolvedSrcDirectory,\n required: false,\n })\n\n const clientAlias = vite.normalizePath(\n clientEntryPath ?? corePluginOpts.defaultEntryPaths.client,\n )\n const serverAlias = vite.normalizePath(\n serverEntryPath ?? corePluginOpts.defaultEntryPaths.server,\n )\n const startAlias = vite.normalizePath(\n startFilePath ?? corePluginOpts.defaultEntryPaths.start,\n )\n const routerAlias = vite.normalizePath(routerFilePath)\n\n const entryAliasConfiguration: Record<\n (typeof ENTRY_POINTS)[keyof typeof ENTRY_POINTS],\n string\n > = {\n [ENTRY_POINTS.client]: clientAlias,\n [ENTRY_POINTS.server]: serverAlias,\n [ENTRY_POINTS.start]: startAlias,\n [ENTRY_POINTS.router]: routerAlias,\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 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: {\n [VITE_ENVIRONMENT_NAMES.client]: {\n consumer: 'client',\n build: {\n rollupOptions: {\n input: {\n main: ENTRY_POINTS.client,\n },\n },\n outDir: getClientOutputDirectory(viteConfig),\n },\n optimizeDeps: {\n exclude: crawlFrameworkPkgsResult.optimizeDeps.exclude,\n // Ensure user code can be crawled for dependencies\n entries: [clientAlias, routerAlias].map((entry) =>\n // Entries are treated as `tinyglobby` patterns so need to be escaped\n escapePath(entry),\n ),\n },\n },\n [VITE_ENVIRONMENT_NAMES.server]: {\n consumer: 'server',\n build: {\n ssr: true,\n rollupOptions: {\n input:\n viteConfig.environments?.[VITE_ENVIRONMENT_NAMES.server]\n ?.build?.rollupOptions?.input ?? serverAlias,\n },\n outDir: getServerOutputDirectory(viteConfig),\n commonjsOptions: {\n include: [/node_modules/],\n },\n copyPublicDir:\n viteConfig.environments?.[VITE_ENVIRONMENT_NAMES.server]\n ?.build?.copyPublicDir ?? false,\n },\n optimizeDeps: {\n // Ensure user code can be crawled for dependencies\n entries: [serverAlias, startAlias, routerAlias].map((entry) =>\n // Entries are treated as `tinyglobby` patterns so need to be escaped\n escapePath(entry),\n ),\n },\n },\n },\n\n resolve: {\n noExternal: [\n // ENTRY_POINTS.start,\n '@tanstack/start**',\n `@tanstack/${corePluginOpts.framework}-start**`,\n ...crawlFrameworkPkgsResult.ssr.noExternal.sort(),\n ],\n alias: {\n ...entryAliasConfiguration,\n },\n },\n /* prettier-ignore */\n define: {\n // define is an esbuild function that replaces the any instances of given keys with the given values\n // i.e: __FRAMEWORK_NAME__ can be replaced with JSON.stringify(\"TanStack Start\")\n // This is not the same as injecting environment variables.\n\n ...defineReplaceEnv('TSS_SERVER_FN_BASE', TSS_SERVER_FN_BASE),\n ...defineReplaceEnv('TSS_CLIENT_OUTPUT_DIR', getClientOutputDirectory(viteConfig)),\n ...defineReplaceEnv('TSS_ROUTER_BASEPATH', startConfig.router.basepath),\n ...(command === 'serve' ? defineReplaceEnv('TSS_SHELL', startConfig.spa?.enabled ? 'true' : 'false') : {}),\n ...defineReplaceEnv('TSS_DEV_SERVER', command === 'serve' ? 'true' : 'false'),\n },\n builder: {\n sharedPlugins: true,\n async buildApp(builder) {\n const client = builder.environments[VITE_ENVIRONMENT_NAMES.client]\n const server = builder.environments[VITE_ENVIRONMENT_NAMES.server]\n\n if (!client) {\n throw new Error('Client environment not found')\n }\n\n if (!server) {\n throw new Error('SSR environment not found')\n }\n\n if (!client.isBuilt) {\n // Build the client bundle first\n await builder.build(client)\n }\n if (!server.isBuilt) {\n // Build the SSR bundle\n await builder.build(server)\n }\n const serverBundle = getBundle(VITE_ENVIRONMENT_NAMES.server)\n await postServerBuild({ builder, startConfig, serverBundle })\n },\n },\n }\n },\n },\n tanStackStartRouter(startPluginOpts, getConfig, corePluginOpts),\n // N.B. TanStackStartCompilerPlugin must be before the TanStackServerFnPlugin\n startCompilerPlugin({ framework: corePluginOpts.framework, environments }),\n createServerFnPlugin({\n framework: corePluginOpts.framework,\n directive,\n environments,\n }),\n\n TanStackServerFnPlugin({\n // This is the ID that will be available to look up and import\n // our server function manifest and resolve its module\n manifestVirtualImportId: VIRTUAL_MODULES.serverFnManifest,\n directive,\n generateFunctionId: startPluginOpts?.serverFns?.generateFunctionId,\n callers: [\n {\n envConsumer: 'client',\n getRuntimeCode: () =>\n `import { createClientRpc } from '@tanstack/${corePluginOpts.framework}-start/client-rpc'`,\n replacer: (d) => `createClientRpc('${d.functionId}')`,\n envName: VITE_ENVIRONMENT_NAMES.client,\n },\n {\n envConsumer: 'server',\n getRuntimeCode: () =>\n `import { createSsrRpc } from '@tanstack/${corePluginOpts.framework}-start/ssr-rpc'`,\n envName: VITE_ENVIRONMENT_NAMES.server,\n replacer: (d) => `createSsrRpc('${d.functionId}')`,\n getServerFnById: corePluginOpts.serverFn?.ssr?.getServerFnById,\n },\n ],\n provider: {\n getRuntimeCode: () =>\n `import { createServerRpc } from '@tanstack/${corePluginOpts.framework}-start/server-rpc'`,\n replacer: (d) => `createServerRpc('${d.functionId}', ${d.fn})`,\n envName:\n corePluginOpts.serverFn?.providerEnv || VITE_ENVIRONMENT_NAMES.server,\n },\n }),\n loadEnvPlugin(),\n startManifestPlugin({\n getClientBundle: () => getBundle(VITE_ENVIRONMENT_NAMES.client),\n getConfig,\n }),\n devServerPlugin({ getConfig }),\n {\n name: 'tanstack-start:core:capture-bundle',\n applyToEnvironment(e) {\n return (\n e.name === VITE_ENVIRONMENT_NAMES.client ||\n e.name === VITE_ENVIRONMENT_NAMES.server\n )\n },\n enforce: 'post',\n generateBundle(_options, bundle) {\n const environment = this.environment.name as ViteEnvironmentNames\n if (!Object.values(VITE_ENVIRONMENT_NAMES).includes(environment)) {\n throw new Error(`Unknown environment: ${environment}`)\n }\n capturedBundle[environment] = bundle\n },\n },\n ]\n}\n\nfunction defineReplaceEnv<TKey extends string, TValue extends string>(\n key: TKey,\n value: TValue,\n): { [P in `process.env.${TKey}` | `import.meta.env.${TKey}`]: TValue } {\n return {\n [`process.env.${key}`]: JSON.stringify(value),\n [`import.meta.env.${key}`]: JSON.stringify(value),\n } as { [P in `process.env.${TKey}` | `import.meta.env.${TKey}`]: TValue }\n}\n"],"names":["startConfig"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","sources":["../../src/plugin.ts"],"sourcesContent":["import { joinPaths } from '@tanstack/router-core'\nimport { VIRTUAL_MODULES } from '@tanstack/start-server-core'\nimport { TanStackServerFnPlugin } from '@tanstack/server-functions-plugin'\nimport * as vite from 'vite'\nimport { crawlFrameworkPkgs } from 'vitefu'\nimport { join } from 'pathe'\nimport { escapePath } from 'tinyglobby'\nimport { startManifestPlugin } from './start-manifest-plugin/plugin'\nimport { startCompilerPlugin } from './start-compiler-plugin/plugin'\nimport { ENTRY_POINTS, VITE_ENVIRONMENT_NAMES } from './constants'\nimport { tanStackStartRouter } from './start-router-plugin/plugin'\nimport { loadEnvPlugin } from './load-env-plugin/plugin'\nimport { devServerPlugin } from './dev-server-plugin/plugin'\nimport { previewServerPlugin } from './preview-server-plugin/plugin'\nimport { parseStartConfig } from './schema'\nimport { resolveEntry } from './resolve-entries'\nimport {\n getClientOutputDirectory,\n getServerOutputDirectory,\n} from './output-directory'\nimport { postServerBuild } from './post-server-build'\nimport { createServerFnPlugin } from './create-server-fn-plugin/plugin'\nimport type { ViteEnvironmentNames } from './constants'\nimport type {\n TanStackStartInputConfig,\n TanStackStartOutputConfig,\n} from './schema'\nimport type { PluginOption } from 'vite'\nimport type { CompileStartFrameworkOptions } from './start-compiler-plugin/compilers'\n\nexport interface TanStackStartVitePluginCoreOptions {\n framework: CompileStartFrameworkOptions\n defaultEntryPaths: {\n client: string\n server: string\n start: string\n }\n serverFn?: {\n directive?: string\n ssr?: {\n getServerFnById?: string\n }\n providerEnv?: string\n }\n}\n\nexport interface ResolvedStartConfig {\n root: string\n startFilePath: string | undefined\n routerFilePath: string\n srcDirectory: string\n viteAppBase: string\n}\n\nexport type GetConfigFn = () => {\n startConfig: TanStackStartOutputConfig\n resolvedStartConfig: ResolvedStartConfig\n}\n\nfunction isFullUrl(str: string): boolean {\n try {\n new URL(str)\n return true\n } catch {\n return false\n }\n}\n\nexport function TanStackStartVitePluginCore(\n corePluginOpts: TanStackStartVitePluginCoreOptions,\n startPluginOpts: TanStackStartInputConfig,\n): Array<PluginOption> {\n const resolvedStartConfig: ResolvedStartConfig = {\n root: '',\n startFilePath: undefined,\n routerFilePath: '',\n srcDirectory: '',\n viteAppBase: '',\n }\n\n const directive = corePluginOpts.serverFn?.directive ?? 'use server'\n\n let startConfig: TanStackStartOutputConfig | null\n const getConfig: GetConfigFn = () => {\n if (!resolvedStartConfig.root) {\n throw new Error(`Cannot get config before root is resolved`)\n }\n if (!startConfig) {\n startConfig = parseStartConfig(\n startPluginOpts,\n corePluginOpts,\n resolvedStartConfig.root,\n )\n }\n return { startConfig, resolvedStartConfig }\n }\n\n const capturedBundle: Partial<\n Record<ViteEnvironmentNames, vite.Rollup.OutputBundle>\n > = {}\n\n function getBundle(envName: ViteEnvironmentNames): vite.Rollup.OutputBundle {\n const bundle = capturedBundle[envName]\n if (!bundle) {\n throw new Error(`No bundle captured for environment: ${envName}`)\n }\n return bundle\n }\n\n const environments: Array<{ name: string; type: 'client' | 'server' }> = [\n { name: VITE_ENVIRONMENT_NAMES.client, type: 'client' },\n { name: VITE_ENVIRONMENT_NAMES.server, type: 'server' },\n ]\n if (\n corePluginOpts.serverFn?.providerEnv &&\n !environments.find((e) => e.name === corePluginOpts.serverFn?.providerEnv)\n ) {\n environments.push({\n name: corePluginOpts.serverFn.providerEnv,\n type: 'server',\n })\n }\n return [\n {\n name: 'tanstack-start-core:config',\n enforce: 'pre',\n async config(viteConfig, { command }) {\n resolvedStartConfig.viteAppBase = viteConfig.base ?? '/'\n if (!isFullUrl(resolvedStartConfig.viteAppBase)) {\n resolvedStartConfig.viteAppBase = joinPaths([\n '/',\n viteConfig.base,\n '/',\n ])\n }\n const root = viteConfig.root || process.cwd()\n resolvedStartConfig.root = root\n\n const { startConfig } = getConfig()\n if (startConfig.router.basepath === undefined) {\n if (!isFullUrl(resolvedStartConfig.viteAppBase)) {\n startConfig.router.basepath =\n resolvedStartConfig.viteAppBase.replace(/^\\/|\\/$/g, '')\n } else {\n startConfig.router.basepath = '/'\n }\n } else {\n if (command === 'serve' && !viteConfig.server?.middlewareMode) {\n // when serving, we must ensure that router basepath and viteAppBase are aligned\n if (\n !joinPaths(['/', startConfig.router.basepath, '/']).startsWith(\n joinPaths(['/', resolvedStartConfig.viteAppBase, '/']),\n )\n ) {\n this.error(\n '[tanstack-start]: During `vite dev`, `router.basepath` must start with the vite `base` config value',\n )\n }\n }\n }\n\n const TSS_SERVER_FN_BASE = joinPaths([\n '/',\n startConfig.router.basepath,\n startConfig.serverFns.base,\n '/',\n ])\n const resolvedSrcDirectory = join(root, startConfig.srcDirectory)\n resolvedStartConfig.srcDirectory = resolvedSrcDirectory\n\n const startFilePath = resolveEntry({\n type: 'start entry',\n configuredEntry: startConfig.start.entry,\n defaultEntry: 'start',\n resolvedSrcDirectory,\n required: false,\n })\n resolvedStartConfig.startFilePath = startFilePath\n\n const routerFilePath = resolveEntry({\n type: 'router entry',\n configuredEntry: startConfig.router.entry,\n defaultEntry: 'router',\n resolvedSrcDirectory,\n required: true,\n })\n resolvedStartConfig.routerFilePath = routerFilePath\n\n const clientEntryPath = resolveEntry({\n type: 'client entry',\n configuredEntry: startConfig.client.entry,\n defaultEntry: 'client',\n resolvedSrcDirectory,\n required: false,\n })\n\n const serverEntryPath = resolveEntry({\n type: 'server entry',\n configuredEntry: startConfig.server.entry,\n defaultEntry: 'server',\n resolvedSrcDirectory,\n required: false,\n })\n\n const clientAlias = vite.normalizePath(\n clientEntryPath ?? corePluginOpts.defaultEntryPaths.client,\n )\n const serverAlias = vite.normalizePath(\n serverEntryPath ?? corePluginOpts.defaultEntryPaths.server,\n )\n const startAlias = vite.normalizePath(\n startFilePath ?? corePluginOpts.defaultEntryPaths.start,\n )\n const routerAlias = vite.normalizePath(routerFilePath)\n\n const entryAliasConfiguration: Record<\n (typeof ENTRY_POINTS)[keyof typeof ENTRY_POINTS],\n string\n > = {\n [ENTRY_POINTS.client]: clientAlias,\n [ENTRY_POINTS.server]: serverAlias,\n [ENTRY_POINTS.start]: startAlias,\n [ENTRY_POINTS.router]: routerAlias,\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 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: {\n [VITE_ENVIRONMENT_NAMES.client]: {\n consumer: 'client',\n build: {\n rollupOptions: {\n input: {\n main: ENTRY_POINTS.client,\n },\n },\n outDir: getClientOutputDirectory(viteConfig),\n },\n optimizeDeps: {\n exclude: crawlFrameworkPkgsResult.optimizeDeps.exclude,\n // Ensure user code can be crawled for dependencies\n entries: [clientAlias, routerAlias].map((entry) =>\n // Entries are treated as `tinyglobby` patterns so need to be escaped\n escapePath(entry),\n ),\n },\n },\n [VITE_ENVIRONMENT_NAMES.server]: {\n consumer: 'server',\n build: {\n ssr: true,\n rollupOptions: {\n input:\n viteConfig.environments?.[VITE_ENVIRONMENT_NAMES.server]\n ?.build?.rollupOptions?.input ?? serverAlias,\n },\n outDir: getServerOutputDirectory(viteConfig),\n commonjsOptions: {\n include: [/node_modules/],\n },\n copyPublicDir:\n viteConfig.environments?.[VITE_ENVIRONMENT_NAMES.server]\n ?.build?.copyPublicDir ?? false,\n },\n optimizeDeps: {\n // Ensure user code can be crawled for dependencies\n entries: [serverAlias, startAlias, routerAlias].map((entry) =>\n // Entries are treated as `tinyglobby` patterns so need to be escaped\n escapePath(entry),\n ),\n },\n },\n },\n\n resolve: {\n noExternal: [\n // ENTRY_POINTS.start,\n '@tanstack/start**',\n `@tanstack/${corePluginOpts.framework}-start**`,\n ...crawlFrameworkPkgsResult.ssr.noExternal.sort(),\n ],\n alias: {\n ...entryAliasConfiguration,\n },\n },\n /* prettier-ignore */\n define: {\n // define is an esbuild function that replaces the any instances of given keys with the given values\n // i.e: __FRAMEWORK_NAME__ can be replaced with JSON.stringify(\"TanStack Start\")\n // This is not the same as injecting environment variables.\n\n ...defineReplaceEnv('TSS_SERVER_FN_BASE', TSS_SERVER_FN_BASE),\n ...defineReplaceEnv('TSS_CLIENT_OUTPUT_DIR', getClientOutputDirectory(viteConfig)),\n ...defineReplaceEnv('TSS_ROUTER_BASEPATH', startConfig.router.basepath),\n ...(command === 'serve' ? defineReplaceEnv('TSS_SHELL', startConfig.spa?.enabled ? 'true' : 'false') : {}),\n ...defineReplaceEnv('TSS_DEV_SERVER', command === 'serve' ? 'true' : 'false'),\n },\n builder: {\n sharedPlugins: true,\n async buildApp(builder) {\n const client = builder.environments[VITE_ENVIRONMENT_NAMES.client]\n const server = builder.environments[VITE_ENVIRONMENT_NAMES.server]\n\n if (!client) {\n throw new Error('Client environment not found')\n }\n\n if (!server) {\n throw new Error('SSR environment not found')\n }\n\n if (!client.isBuilt) {\n // Build the client bundle first\n await builder.build(client)\n }\n if (!server.isBuilt) {\n // Build the SSR bundle\n await builder.build(server)\n }\n },\n },\n }\n },\n buildApp: {\n order: 'post',\n async handler(builder) {\n const { startConfig } = getConfig()\n await postServerBuild({ builder, startConfig })\n },\n },\n },\n tanStackStartRouter(startPluginOpts, getConfig, corePluginOpts),\n // N.B. TanStackStartCompilerPlugin must be before the TanStackServerFnPlugin\n startCompilerPlugin({ framework: corePluginOpts.framework, environments }),\n createServerFnPlugin({\n framework: corePluginOpts.framework,\n directive,\n environments,\n }),\n\n TanStackServerFnPlugin({\n // This is the ID that will be available to look up and import\n // our server function manifest and resolve its module\n manifestVirtualImportId: VIRTUAL_MODULES.serverFnManifest,\n directive,\n generateFunctionId: startPluginOpts?.serverFns?.generateFunctionId,\n callers: [\n {\n envConsumer: 'client',\n getRuntimeCode: () =>\n `import { createClientRpc } from '@tanstack/${corePluginOpts.framework}-start/client-rpc'`,\n replacer: (d) => `createClientRpc('${d.functionId}')`,\n envName: VITE_ENVIRONMENT_NAMES.client,\n },\n {\n envConsumer: 'server',\n getRuntimeCode: () =>\n `import { createSsrRpc } from '@tanstack/${corePluginOpts.framework}-start/ssr-rpc'`,\n envName: VITE_ENVIRONMENT_NAMES.server,\n replacer: (d) => `createSsrRpc('${d.functionId}')`,\n getServerFnById: corePluginOpts.serverFn?.ssr?.getServerFnById,\n },\n ],\n provider: {\n getRuntimeCode: () =>\n `import { createServerRpc } from '@tanstack/${corePluginOpts.framework}-start/server-rpc'`,\n replacer: (d) => `createServerRpc('${d.functionId}', ${d.fn})`,\n envName:\n corePluginOpts.serverFn?.providerEnv || VITE_ENVIRONMENT_NAMES.server,\n },\n }),\n loadEnvPlugin(),\n startManifestPlugin({\n getClientBundle: () => getBundle(VITE_ENVIRONMENT_NAMES.client),\n getConfig,\n }),\n devServerPlugin({ getConfig }),\n previewServerPlugin(),\n {\n name: 'tanstack-start:core:capture-bundle',\n applyToEnvironment(e) {\n return (\n e.name === VITE_ENVIRONMENT_NAMES.client ||\n e.name === VITE_ENVIRONMENT_NAMES.server\n )\n },\n enforce: 'post',\n generateBundle(_options, bundle) {\n const environment = this.environment.name as ViteEnvironmentNames\n if (!Object.values(VITE_ENVIRONMENT_NAMES).includes(environment)) {\n throw new Error(`Unknown environment: ${environment}`)\n }\n capturedBundle[environment] = bundle\n },\n },\n ]\n}\n\nfunction defineReplaceEnv<TKey extends string, TValue extends string>(\n key: TKey,\n value: TValue,\n): { [P in `process.env.${TKey}` | `import.meta.env.${TKey}`]: TValue } {\n return {\n [`process.env.${key}`]: JSON.stringify(value),\n [`import.meta.env.${key}`]: JSON.stringify(value),\n } as { [P in `process.env.${TKey}` | `import.meta.env.${TKey}`]: TValue }\n}\n"],"names":["startConfig"],"mappings":"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"}
@@ -1,7 +1,6 @@
1
1
  import { TanStackStartOutputConfig } from './schema.js';
2
- import { Rollup, ViteBuilder } from 'vite';
3
- export declare function postServerBuild({ builder, startConfig, serverBundle, }: {
2
+ import { ViteBuilder } from 'vite';
3
+ export declare function postServerBuild({ builder, startConfig, }: {
4
4
  builder: ViteBuilder;
5
5
  startConfig: TanStackStartOutputConfig;
6
- serverBundle: Rollup.OutputBundle;
7
6
  }): Promise<void>;
@@ -4,8 +4,7 @@ import { VITE_ENVIRONMENT_NAMES } from "./constants.js";
4
4
  import { prerender } from "./prerender.js";
5
5
  async function postServerBuild({
6
6
  builder,
7
- startConfig,
8
- serverBundle
7
+ startConfig
9
8
  }) {
10
9
  if (startConfig.prerender?.enabled !== false) {
11
10
  startConfig.prerender = {
@@ -38,8 +37,7 @@ async function postServerBuild({
38
37
  if (startConfig.prerender.enabled) {
39
38
  await prerender({
40
39
  startConfig,
41
- builder,
42
- serverBundle
40
+ builder
43
41
  });
44
42
  }
45
43
  if (startConfig.pages.length) {
@@ -1 +1 @@
1
- {"version":3,"file":"post-server-build.js","sources":["../../src/post-server-build.ts"],"sourcesContent":["import { HEADERS } from '@tanstack/start-server-core'\nimport { buildSitemap } from './build-sitemap'\nimport { VITE_ENVIRONMENT_NAMES } from './constants'\nimport { prerender } from './prerender'\nimport type { TanStackStartOutputConfig } from './schema'\nimport type { Rollup, ViteBuilder } from 'vite'\n\nexport async function postServerBuild({\n builder,\n startConfig,\n serverBundle,\n}: {\n builder: ViteBuilder\n startConfig: TanStackStartOutputConfig\n serverBundle: Rollup.OutputBundle\n}) {\n // If the user has not set a prerender option, we need to set it to true\n // if the pages array is not empty and has sub options requiring for prerendering\n // If the user has explicitly set prerender.enabled, this should be respected\n if (startConfig.prerender?.enabled !== false) {\n startConfig.prerender = {\n ...startConfig.prerender,\n enabled:\n startConfig.prerender?.enabled ??\n startConfig.pages.some((d) =>\n typeof d === 'string' ? false : !!d.prerender?.enabled,\n ),\n }\n }\n\n // Setup the options for prerendering the SPA shell (i.e `src/routes/__root.tsx`)\n if (startConfig.spa?.enabled) {\n startConfig.prerender = {\n ...startConfig.prerender,\n enabled: true,\n }\n\n const maskUrl = new URL(startConfig.spa.maskPath, 'http://localhost')\n\n startConfig.pages.push({\n path: maskUrl.toString().replace('http://localhost', ''),\n prerender: {\n ...startConfig.spa.prerender,\n headers: {\n ...startConfig.spa.prerender.headers,\n [HEADERS.TSS_SHELL]: 'true',\n },\n },\n sitemap: {\n exclude: true,\n },\n })\n }\n\n // Run the prerendering process\n if (startConfig.prerender.enabled) {\n await prerender({\n startConfig,\n builder,\n serverBundle,\n })\n }\n\n // Run the sitemap build process\n if (startConfig.pages.length) {\n buildSitemap({\n startConfig,\n publicDir:\n builder.environments[VITE_ENVIRONMENT_NAMES.client]?.config.build\n .outDir ?? builder.config.build.outDir,\n })\n }\n}\n"],"names":[],"mappings":";;;;AAOA,eAAsB,gBAAgB;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AACF,GAIG;AAID,MAAI,YAAY,WAAW,YAAY,OAAO;AAC5C,gBAAY,YAAY;AAAA,MACtB,GAAG,YAAY;AAAA,MACf,SACE,YAAY,WAAW,WACvB,YAAY,MAAM;AAAA,QAAK,CAAC,MACtB,OAAO,MAAM,WAAW,QAAQ,CAAC,CAAC,EAAE,WAAW;AAAA,MAAA;AAAA,IACjD;AAAA,EAEN;AAGA,MAAI,YAAY,KAAK,SAAS;AAC5B,gBAAY,YAAY;AAAA,MACtB,GAAG,YAAY;AAAA,MACf,SAAS;AAAA,IAAA;AAGX,UAAM,UAAU,IAAI,IAAI,YAAY,IAAI,UAAU,kBAAkB;AAEpE,gBAAY,MAAM,KAAK;AAAA,MACrB,MAAM,QAAQ,SAAA,EAAW,QAAQ,oBAAoB,EAAE;AAAA,MACvD,WAAW;AAAA,QACT,GAAG,YAAY,IAAI;AAAA,QACnB,SAAS;AAAA,UACP,GAAG,YAAY,IAAI,UAAU;AAAA,UAC7B,CAAC,QAAQ,SAAS,GAAG;AAAA,QAAA;AAAA,MACvB;AAAA,MAEF,SAAS;AAAA,QACP,SAAS;AAAA,MAAA;AAAA,IACX,CACD;AAAA,EACH;AAGA,MAAI,YAAY,UAAU,SAAS;AACjC,UAAM,UAAU;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,IAAA,CACD;AAAA,EACH;AAGA,MAAI,YAAY,MAAM,QAAQ;AAC5B,iBAAa;AAAA,MACX;AAAA,MACA,WACE,QAAQ,aAAa,uBAAuB,MAAM,GAAG,OAAO,MACzD,UAAU,QAAQ,OAAO,MAAM;AAAA,IAAA,CACrC;AAAA,EACH;AACF;"}
1
+ {"version":3,"file":"post-server-build.js","sources":["../../src/post-server-build.ts"],"sourcesContent":["import { HEADERS } from '@tanstack/start-server-core'\nimport { buildSitemap } from './build-sitemap'\nimport { VITE_ENVIRONMENT_NAMES } from './constants'\nimport { prerender } from './prerender'\nimport type { TanStackStartOutputConfig } from './schema'\nimport type { ViteBuilder } from 'vite'\n\nexport async function postServerBuild({\n builder,\n startConfig,\n}: {\n builder: ViteBuilder\n startConfig: TanStackStartOutputConfig\n}) {\n // If the user has not set a prerender option, we need to set it to true\n // if the pages array is not empty and has sub options requiring for prerendering\n // If the user has explicitly set prerender.enabled, this should be respected\n if (startConfig.prerender?.enabled !== false) {\n startConfig.prerender = {\n ...startConfig.prerender,\n enabled:\n startConfig.prerender?.enabled ??\n startConfig.pages.some((d) =>\n typeof d === 'string' ? false : !!d.prerender?.enabled,\n ),\n }\n }\n\n // Setup the options for prerendering the SPA shell (i.e `src/routes/__root.tsx`)\n if (startConfig.spa?.enabled) {\n startConfig.prerender = {\n ...startConfig.prerender,\n enabled: true,\n }\n\n const maskUrl = new URL(startConfig.spa.maskPath, 'http://localhost')\n\n startConfig.pages.push({\n path: maskUrl.toString().replace('http://localhost', ''),\n prerender: {\n ...startConfig.spa.prerender,\n headers: {\n ...startConfig.spa.prerender.headers,\n [HEADERS.TSS_SHELL]: 'true',\n },\n },\n sitemap: {\n exclude: true,\n },\n })\n }\n\n // Run the prerendering process\n if (startConfig.prerender.enabled) {\n await prerender({\n startConfig,\n builder,\n })\n }\n\n // Run the sitemap build process\n if (startConfig.pages.length) {\n buildSitemap({\n startConfig,\n publicDir:\n builder.environments[VITE_ENVIRONMENT_NAMES.client]?.config.build\n .outDir ?? builder.config.build.outDir,\n })\n }\n}\n"],"names":[],"mappings":";;;;AAOA,eAAsB,gBAAgB;AAAA,EACpC;AAAA,EACA;AACF,GAGG;AAID,MAAI,YAAY,WAAW,YAAY,OAAO;AAC5C,gBAAY,YAAY;AAAA,MACtB,GAAG,YAAY;AAAA,MACf,SACE,YAAY,WAAW,WACvB,YAAY,MAAM;AAAA,QAAK,CAAC,MACtB,OAAO,MAAM,WAAW,QAAQ,CAAC,CAAC,EAAE,WAAW;AAAA,MAAA;AAAA,IACjD;AAAA,EAEN;AAGA,MAAI,YAAY,KAAK,SAAS;AAC5B,gBAAY,YAAY;AAAA,MACtB,GAAG,YAAY;AAAA,MACf,SAAS;AAAA,IAAA;AAGX,UAAM,UAAU,IAAI,IAAI,YAAY,IAAI,UAAU,kBAAkB;AAEpE,gBAAY,MAAM,KAAK;AAAA,MACrB,MAAM,QAAQ,SAAA,EAAW,QAAQ,oBAAoB,EAAE;AAAA,MACvD,WAAW;AAAA,QACT,GAAG,YAAY,IAAI;AAAA,QACnB,SAAS;AAAA,UACP,GAAG,YAAY,IAAI,UAAU;AAAA,UAC7B,CAAC,QAAQ,SAAS,GAAG;AAAA,QAAA;AAAA,MACvB;AAAA,MAEF,SAAS;AAAA,QACP,SAAS;AAAA,MAAA;AAAA,IACX,CACD;AAAA,EACH;AAGA,MAAI,YAAY,UAAU,SAAS;AACjC,UAAM,UAAU;AAAA,MACd;AAAA,MACA;AAAA,IAAA,CACD;AAAA,EACH;AAGA,MAAI,YAAY,MAAM,QAAQ;AAC5B,iBAAa;AAAA,MACX;AAAA,MACA,WACE,QAAQ,aAAa,uBAAuB,MAAM,GAAG,OAAO,MACzD,UAAU,QAAQ,OAAO,MAAM;AAAA,IAAA,CACrC;AAAA,EACH;AACF;"}
@@ -1,11 +1,6 @@
1
- import { Rollup, ViteBuilder } from 'vite';
1
+ import { ViteBuilder } from 'vite';
2
2
  import { TanStackStartOutputConfig } from './schema.js';
3
- export declare function prerender({ startConfig, builder, serverBundle, }: {
3
+ export declare function prerender({ startConfig, builder, }: {
4
4
  startConfig: TanStackStartOutputConfig;
5
5
  builder: ViteBuilder;
6
- serverBundle: Rollup.OutputBundle;
7
- }): Promise<void>;
8
- export declare function writeBundleToDisk({ bundle, outDir, }: {
9
- bundle: Rollup.OutputBundle;
10
- outDir: string;
11
6
  }): Promise<void>;
@@ -1,5 +1,4 @@
1
- import { existsSync, rmSync, promises } from "node:fs";
2
- import { pathToFileURL } from "node:url";
1
+ import { promises } from "node:fs";
3
2
  import os from "node:os";
4
3
  import path from "pathe";
5
4
  import { joinURL, withBase, withoutBase } from "ufo";
@@ -8,8 +7,7 @@ import { createLogger } from "./utils.js";
8
7
  import { Queue } from "./queue.js";
9
8
  async function prerender({
10
9
  startConfig,
11
- builder,
12
- serverBundle
10
+ builder
13
11
  }) {
14
12
  const logger = createLogger("prerender");
15
13
  logger.info("Prerendering pages...");
@@ -40,27 +38,16 @@ async function prerender({
40
38
  );
41
39
  }
42
40
  const outputDir = clientEnv.config.build.outDir;
43
- const entryFile = findEntryFileInBundle(serverBundle);
44
- let fullEntryFilePath = path.join(serverEnv.config.build.outDir, entryFile);
45
41
  process.env.TSS_PRERENDERING = "true";
46
- if (!existsSync(fullEntryFilePath)) {
47
- const bundleOutputDir = path.resolve(
48
- serverEnv.config.root,
49
- ".tanstack",
50
- "start",
51
- "prerender"
52
- );
53
- rmSync(bundleOutputDir, { recursive: true, force: true });
54
- await writeBundleToDisk({ bundle: serverBundle, outDir: bundleOutputDir });
55
- fullEntryFilePath = path.join(bundleOutputDir, entryFile);
56
- }
57
- const { default: serverEntrypoint } = await import(pathToFileURL(fullEntryFilePath).toString());
42
+ const previewServer = await startPreviewServer(serverEnv.config);
43
+ const baseUrl = getResolvedUrl(previewServer);
58
44
  const isRedirectResponse = (res) => {
59
45
  return res.status >= 300 && res.status < 400 && res.headers.get("location");
60
46
  };
61
47
  async function localFetch(path2, options, maxRedirects = 5) {
62
- const url = new URL(`http://localhost${path2}`);
63
- const response = await serverEntrypoint.fetch(new Request(url, options));
48
+ const url = new URL(path2, baseUrl);
49
+ const request = new Request(url, options);
50
+ const response = await fetch(request);
64
51
  if (isRedirectResponse(response) && maxRedirects > 0) {
65
52
  const location = response.headers.get("location");
66
53
  if (location.startsWith("http://localhost") || location.startsWith("/")) {
@@ -80,6 +67,8 @@ async function prerender({
80
67
  });
81
68
  } catch (error) {
82
69
  logger.error(error);
70
+ } finally {
71
+ await previewServer.close();
83
72
  }
84
73
  function extractLinks(html) {
85
74
  const linkRegex = /<a[^>]+href=["']([^"']+)["'][^>]*>/g;
@@ -192,43 +181,33 @@ async function prerender({
192
181
  }
193
182
  }
194
183
  }
195
- function findEntryFileInBundle(bundle) {
196
- let entryFile;
197
- for (const [_name, file] of Object.entries(bundle)) {
198
- if (file.type === "chunk") {
199
- if (file.isEntry) {
200
- if (entryFile !== void 0) {
201
- throw new Error(
202
- `Multiple entry points found. Only one entry point is allowed.`
203
- );
204
- }
205
- entryFile = file.fileName;
184
+ async function startPreviewServer(viteConfig) {
185
+ const vite = await import("vite");
186
+ try {
187
+ return await vite.preview({
188
+ configFile: viteConfig.configFile,
189
+ preview: {
190
+ port: 0,
191
+ open: false
206
192
  }
207
- }
208
- }
209
- if (entryFile === void 0) {
210
- throw new Error(`No entry point found in the bundle.`);
193
+ });
194
+ } catch (error) {
195
+ throw new Error(
196
+ "Failed to start the Vite preview server for prerendering",
197
+ {
198
+ cause: error
199
+ }
200
+ );
211
201
  }
212
- return entryFile;
213
202
  }
214
- async function writeBundleToDisk({
215
- bundle,
216
- outDir
217
- }) {
218
- const createdDirs = /* @__PURE__ */ new Set();
219
- for (const [fileName, asset] of Object.entries(bundle)) {
220
- const fullPath = path.join(outDir, fileName);
221
- const dir = path.dirname(fullPath);
222
- const content = asset.type === "asset" ? asset.source : asset.code;
223
- if (!createdDirs.has(dir)) {
224
- await promises.mkdir(dir, { recursive: true });
225
- createdDirs.add(dir);
226
- }
227
- await promises.writeFile(fullPath, content);
203
+ function getResolvedUrl(previewServer) {
204
+ const baseUrl = previewServer.resolvedUrls?.local[0];
205
+ if (!baseUrl) {
206
+ throw new Error("No resolved URL is available from the Vite preview server");
228
207
  }
208
+ return new URL(baseUrl);
229
209
  }
230
210
  export {
231
- prerender,
232
- writeBundleToDisk
211
+ prerender
233
212
  };
234
213
  //# sourceMappingURL=prerender.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prerender.js","sources":["../../src/prerender.ts"],"sourcesContent":["import { existsSync, promises as fsp, rmSync } from 'node:fs'\nimport { pathToFileURL } from 'node:url'\nimport os from 'node:os'\nimport path from 'pathe'\nimport { joinURL, withBase, withoutBase } from 'ufo'\nimport { VITE_ENVIRONMENT_NAMES } from './constants'\nimport { createLogger } from './utils'\nimport { Queue } from './queue'\nimport type { Rollup, ViteBuilder } from 'vite'\nimport type { Page, TanStackStartOutputConfig } from './schema'\n\nexport async function prerender({\n startConfig,\n builder,\n serverBundle,\n}: {\n startConfig: TanStackStartOutputConfig\n builder: ViteBuilder\n serverBundle: Rollup.OutputBundle\n}) {\n const logger = createLogger('prerender')\n logger.info('Prerendering pages...')\n\n // If prerender is enabled\n if (startConfig.prerender?.enabled) {\n // default to root page if no pages are defined\n let pages = startConfig.pages.length ? startConfig.pages : [{ path: '/' }]\n\n if (startConfig.prerender.autoStaticPathsDiscovery ?? true) {\n // merge discovered static pages with user-defined pages\n const pagesMap = new Map(pages.map((item) => [item.path, item]))\n const discoveredPages = globalThis.TSS_PRERENDABLE_PATHS || []\n\n for (const page of discoveredPages) {\n if (!pagesMap.has(page.path)) {\n pagesMap.set(page.path, page)\n }\n }\n\n pages = Array.from(pagesMap.values())\n }\n\n startConfig.pages = pages\n }\n\n const serverEnv = builder.environments[VITE_ENVIRONMENT_NAMES.server]\n\n if (!serverEnv) {\n throw new Error(\n `Vite's \"${VITE_ENVIRONMENT_NAMES.server}\" environment not found`,\n )\n }\n\n const clientEnv = builder.environments[VITE_ENVIRONMENT_NAMES.client]\n if (!clientEnv) {\n throw new Error(\n `Vite's \"${VITE_ENVIRONMENT_NAMES.client}\" environment not found`,\n )\n }\n\n const outputDir = clientEnv.config.build.outDir\n\n const entryFile = findEntryFileInBundle(serverBundle)\n let fullEntryFilePath = path.join(serverEnv.config.build.outDir, entryFile)\n process.env.TSS_PRERENDERING = 'true'\n\n if (!existsSync(fullEntryFilePath)) {\n // if the file does not exist, we need to write the bundle to a temporary directory\n // this can happen e.g. with nitro that postprocesses the bundle and thus does not write SSR build to disk\n const bundleOutputDir = path.resolve(\n serverEnv.config.root,\n '.tanstack',\n 'start',\n 'prerender',\n )\n rmSync(bundleOutputDir, { recursive: true, force: true })\n await writeBundleToDisk({ bundle: serverBundle, outDir: bundleOutputDir })\n fullEntryFilePath = path.join(bundleOutputDir, entryFile)\n }\n\n const { default: serverEntrypoint } = await import(\n pathToFileURL(fullEntryFilePath).toString()\n )\n\n const isRedirectResponse = (res: Response) => {\n return res.status >= 300 && res.status < 400 && res.headers.get('location')\n }\n async function localFetch(\n path: string,\n options?: RequestInit,\n maxRedirects: number = 5,\n ): Promise<Response> {\n const url = new URL(`http://localhost${path}`)\n const response = await serverEntrypoint.fetch(new Request(url, options))\n\n if (isRedirectResponse(response) && maxRedirects > 0) {\n const location = response.headers.get('location')!\n if (location.startsWith('http://localhost') || location.startsWith('/')) {\n const newUrl = location.replace('http://localhost', '')\n return localFetch(newUrl, options, maxRedirects - 1)\n } else {\n logger.warn(`Skipping redirect to external location: ${location}`)\n }\n }\n\n return response\n }\n\n try {\n // Crawl all pages\n const pages = await prerenderPages({ outputDir })\n\n logger.info(`Prerendered ${pages.length} pages:`)\n pages.forEach((page) => {\n logger.info(`- ${page}`)\n })\n } catch (error) {\n logger.error(error)\n }\n\n function extractLinks(html: string): Array<string> {\n const linkRegex = /<a[^>]+href=[\"']([^\"']+)[\"'][^>]*>/g\n const links: Array<string> = []\n let match\n\n while ((match = linkRegex.exec(html)) !== null) {\n const href = match[1]\n if (href && (href.startsWith('/') || href.startsWith('./'))) {\n links.push(href)\n }\n }\n\n return links\n }\n\n async function prerenderPages({ outputDir }: { outputDir: string }) {\n const seen = new Set<string>()\n const prerendered = new Set<string>()\n const retriesByPath = new Map<string, number>()\n const concurrency = startConfig.prerender?.concurrency ?? os.cpus().length\n logger.info(`Concurrency: ${concurrency}`)\n const queue = new Queue({ concurrency })\n const routerBasePath = joinURL('/', startConfig.router.basepath ?? '')\n\n startConfig.pages.forEach((page) => addCrawlPageTask(page))\n\n await queue.start()\n\n return Array.from(prerendered)\n\n function addCrawlPageTask(page: Page) {\n // Was the page already seen?\n if (seen.has(page.path)) return\n\n // Add the page to the seen set\n seen.add(page.path)\n\n if (page.fromCrawl) {\n startConfig.pages.push(page)\n }\n\n // If not enabled, skip\n if (!(page.prerender?.enabled ?? true)) return\n\n // If there is a filter link, check if the page should be prerendered\n if (startConfig.prerender?.filter && !startConfig.prerender.filter(page))\n return\n\n // Resolve the merged default and page-specific prerender options\n const prerenderOptions = {\n ...startConfig.prerender,\n ...page.prerender,\n }\n\n // Add the task\n queue.add(async () => {\n logger.info(`Crawling: ${page.path}`)\n const retries = retriesByPath.get(page.path) || 0\n try {\n // Fetch the route\n\n const res = await localFetch(\n withBase(page.path, routerBasePath),\n {\n headers: {\n ...(prerenderOptions.headers ?? {}),\n },\n },\n prerenderOptions.maxRedirects,\n )\n\n if (!res.ok) {\n if (isRedirectResponse(res)) {\n logger.warn(`Max redirects reached for ${page.path}`)\n }\n throw new Error(`Failed to fetch ${page.path}: ${res.statusText}`, {\n cause: res,\n })\n }\n\n const cleanPagePath = (\n prerenderOptions.outputPath || page.path\n ).split(/[?#]/)[0]!\n\n // Guess route type and populate fileName\n const contentType = res.headers.get('content-type') || ''\n const isImplicitHTML =\n !cleanPagePath.endsWith('.html') && contentType.includes('html')\n\n const routeWithIndex = cleanPagePath.endsWith('/')\n ? cleanPagePath + 'index'\n : cleanPagePath\n\n const isSpaShell =\n startConfig.spa?.prerender.outputPath === cleanPagePath\n\n let htmlPath: string\n if (isSpaShell) {\n // For SPA shell, ignore autoSubfolderIndex option\n htmlPath = cleanPagePath + '.html'\n } else {\n if (\n cleanPagePath.endsWith('/') ||\n (prerenderOptions.autoSubfolderIndex ?? true)\n ) {\n htmlPath = joinURL(cleanPagePath, 'index.html')\n } else {\n htmlPath = cleanPagePath + '.html'\n }\n }\n\n const filename = withoutBase(\n isImplicitHTML ? htmlPath : routeWithIndex,\n routerBasePath,\n )\n\n const html = await res.text()\n\n const filepath = path.join(outputDir, filename)\n\n await fsp.mkdir(path.dirname(filepath), {\n recursive: true,\n })\n\n await fsp.writeFile(filepath, html)\n\n prerendered.add(page.path)\n\n const newPage = await prerenderOptions.onSuccess?.({ page, html })\n\n if (newPage) {\n Object.assign(page, newPage)\n }\n\n // Find new links\n if (prerenderOptions.crawlLinks ?? true) {\n const links = extractLinks(html)\n for (const link of links) {\n addCrawlPageTask({ path: link, fromCrawl: true })\n }\n }\n } catch (error) {\n if (retries < (prerenderOptions.retryCount ?? 0)) {\n logger.warn(`Encountered error, retrying: ${page.path} in 500ms`)\n await new Promise((resolve) =>\n setTimeout(resolve, prerenderOptions.retryDelay),\n )\n retriesByPath.set(page.path, retries + 1)\n addCrawlPageTask(page)\n } else {\n if (prerenderOptions.failOnError ?? true) {\n throw error\n }\n }\n }\n })\n }\n }\n}\n\nfunction findEntryFileInBundle(bundle: Rollup.OutputBundle): string {\n let entryFile: string | undefined\n\n for (const [_name, file] of Object.entries(bundle)) {\n if (file.type === 'chunk') {\n if (file.isEntry) {\n if (entryFile !== undefined) {\n throw new Error(\n `Multiple entry points found. Only one entry point is allowed.`,\n )\n }\n entryFile = file.fileName\n }\n }\n }\n if (entryFile === undefined) {\n throw new Error(`No entry point found in the bundle.`)\n }\n return entryFile\n}\n\nexport async function writeBundleToDisk({\n bundle,\n outDir,\n}: {\n bundle: Rollup.OutputBundle\n outDir: string\n}) {\n const createdDirs = new Set<string>()\n\n for (const [fileName, asset] of Object.entries(bundle)) {\n const fullPath = path.join(outDir, fileName)\n const dir = path.dirname(fullPath)\n const content = asset.type === 'asset' ? asset.source : asset.code\n\n if (!createdDirs.has(dir)) {\n await fsp.mkdir(dir, { recursive: true })\n createdDirs.add(dir)\n }\n\n await fsp.writeFile(fullPath, content)\n }\n}\n"],"names":["path","outputDir","fsp"],"mappings":"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"}
1
+ {"version":3,"file":"prerender.js","sources":["../../src/prerender.ts"],"sourcesContent":["import { promises as fsp } from 'node:fs'\nimport os from 'node:os'\nimport path from 'pathe'\nimport { joinURL, withBase, withoutBase } from 'ufo'\nimport { VITE_ENVIRONMENT_NAMES } from './constants'\nimport { createLogger } from './utils'\nimport { Queue } from './queue'\nimport type { PreviewServer, ResolvedConfig, ViteBuilder } from 'vite'\nimport type { Page, TanStackStartOutputConfig } from './schema'\n\nexport async function prerender({\n startConfig,\n builder,\n}: {\n startConfig: TanStackStartOutputConfig\n builder: ViteBuilder\n}) {\n const logger = createLogger('prerender')\n logger.info('Prerendering pages...')\n\n // If prerender is enabled\n if (startConfig.prerender?.enabled) {\n // default to root page if no pages are defined\n let pages = startConfig.pages.length ? startConfig.pages : [{ path: '/' }]\n\n if (startConfig.prerender.autoStaticPathsDiscovery ?? true) {\n // merge discovered static pages with user-defined pages\n const pagesMap = new Map(pages.map((item) => [item.path, item]))\n const discoveredPages = globalThis.TSS_PRERENDABLE_PATHS || []\n\n for (const page of discoveredPages) {\n if (!pagesMap.has(page.path)) {\n pagesMap.set(page.path, page)\n }\n }\n\n pages = Array.from(pagesMap.values())\n }\n\n startConfig.pages = pages\n }\n\n const serverEnv = builder.environments[VITE_ENVIRONMENT_NAMES.server]\n\n if (!serverEnv) {\n throw new Error(\n `Vite's \"${VITE_ENVIRONMENT_NAMES.server}\" environment not found`,\n )\n }\n\n const clientEnv = builder.environments[VITE_ENVIRONMENT_NAMES.client]\n if (!clientEnv) {\n throw new Error(\n `Vite's \"${VITE_ENVIRONMENT_NAMES.client}\" environment not found`,\n )\n }\n\n const outputDir = clientEnv.config.build.outDir\n\n process.env.TSS_PRERENDERING = 'true'\n\n // Start Vite preview server instead of importing module\n const previewServer = await startPreviewServer(serverEnv.config)\n const baseUrl = getResolvedUrl(previewServer)\n\n const isRedirectResponse = (res: Response) => {\n return res.status >= 300 && res.status < 400 && res.headers.get('location')\n }\n async function localFetch(\n path: string,\n options?: RequestInit,\n maxRedirects: number = 5,\n ): Promise<Response> {\n const url = new URL(path, baseUrl)\n const request = new Request(url, options)\n const response = await fetch(request)\n\n if (isRedirectResponse(response) && maxRedirects > 0) {\n const location = response.headers.get('location')!\n if (location.startsWith('http://localhost') || location.startsWith('/')) {\n const newUrl = location.replace('http://localhost', '')\n return localFetch(newUrl, options, maxRedirects - 1)\n } else {\n logger.warn(`Skipping redirect to external location: ${location}`)\n }\n }\n\n return response\n }\n\n try {\n // Crawl all pages\n const pages = await prerenderPages({ outputDir })\n\n logger.info(`Prerendered ${pages.length} pages:`)\n pages.forEach((page) => {\n logger.info(`- ${page}`)\n })\n } catch (error) {\n logger.error(error)\n } finally {\n await previewServer.close()\n }\n\n function extractLinks(html: string): Array<string> {\n const linkRegex = /<a[^>]+href=[\"']([^\"']+)[\"'][^>]*>/g\n const links: Array<string> = []\n let match\n\n while ((match = linkRegex.exec(html)) !== null) {\n const href = match[1]\n if (href && (href.startsWith('/') || href.startsWith('./'))) {\n links.push(href)\n }\n }\n\n return links\n }\n\n async function prerenderPages({ outputDir }: { outputDir: string }) {\n const seen = new Set<string>()\n const prerendered = new Set<string>()\n const retriesByPath = new Map<string, number>()\n const concurrency = startConfig.prerender?.concurrency ?? os.cpus().length\n logger.info(`Concurrency: ${concurrency}`)\n const queue = new Queue({ concurrency })\n const routerBasePath = joinURL('/', startConfig.router.basepath ?? '')\n\n startConfig.pages.forEach((page) => addCrawlPageTask(page))\n\n await queue.start()\n\n return Array.from(prerendered)\n\n function addCrawlPageTask(page: Page) {\n // Was the page already seen?\n if (seen.has(page.path)) return\n\n // Add the page to the seen set\n seen.add(page.path)\n\n if (page.fromCrawl) {\n startConfig.pages.push(page)\n }\n\n // If not enabled, skip\n if (!(page.prerender?.enabled ?? true)) return\n\n // If there is a filter link, check if the page should be prerendered\n if (startConfig.prerender?.filter && !startConfig.prerender.filter(page))\n return\n\n // Resolve the merged default and page-specific prerender options\n const prerenderOptions = {\n ...startConfig.prerender,\n ...page.prerender,\n }\n\n // Add the task\n queue.add(async () => {\n logger.info(`Crawling: ${page.path}`)\n const retries = retriesByPath.get(page.path) || 0\n try {\n // Fetch the route\n\n const res = await localFetch(\n withBase(page.path, routerBasePath),\n {\n headers: {\n ...(prerenderOptions.headers ?? {}),\n },\n },\n prerenderOptions.maxRedirects,\n )\n\n if (!res.ok) {\n if (isRedirectResponse(res)) {\n logger.warn(`Max redirects reached for ${page.path}`)\n }\n throw new Error(`Failed to fetch ${page.path}: ${res.statusText}`, {\n cause: res,\n })\n }\n\n const cleanPagePath = (\n prerenderOptions.outputPath || page.path\n ).split(/[?#]/)[0]!\n\n // Guess route type and populate fileName\n const contentType = res.headers.get('content-type') || ''\n const isImplicitHTML =\n !cleanPagePath.endsWith('.html') && contentType.includes('html')\n\n const routeWithIndex = cleanPagePath.endsWith('/')\n ? cleanPagePath + 'index'\n : cleanPagePath\n\n const isSpaShell =\n startConfig.spa?.prerender.outputPath === cleanPagePath\n\n let htmlPath: string\n if (isSpaShell) {\n // For SPA shell, ignore autoSubfolderIndex option\n htmlPath = cleanPagePath + '.html'\n } else {\n if (\n cleanPagePath.endsWith('/') ||\n (prerenderOptions.autoSubfolderIndex ?? true)\n ) {\n htmlPath = joinURL(cleanPagePath, 'index.html')\n } else {\n htmlPath = cleanPagePath + '.html'\n }\n }\n\n const filename = withoutBase(\n isImplicitHTML ? htmlPath : routeWithIndex,\n routerBasePath,\n )\n\n const html = await res.text()\n\n const filepath = path.join(outputDir, filename)\n\n await fsp.mkdir(path.dirname(filepath), {\n recursive: true,\n })\n\n await fsp.writeFile(filepath, html)\n\n prerendered.add(page.path)\n\n const newPage = await prerenderOptions.onSuccess?.({ page, html })\n\n if (newPage) {\n Object.assign(page, newPage)\n }\n\n // Find new links\n if (prerenderOptions.crawlLinks ?? true) {\n const links = extractLinks(html)\n for (const link of links) {\n addCrawlPageTask({ path: link, fromCrawl: true })\n }\n }\n } catch (error) {\n if (retries < (prerenderOptions.retryCount ?? 0)) {\n logger.warn(`Encountered error, retrying: ${page.path} in 500ms`)\n await new Promise((resolve) =>\n setTimeout(resolve, prerenderOptions.retryDelay),\n )\n retriesByPath.set(page.path, retries + 1)\n addCrawlPageTask(page)\n } else {\n if (prerenderOptions.failOnError ?? true) {\n throw error\n }\n }\n }\n })\n }\n }\n}\n\nasync function startPreviewServer(\n viteConfig: ResolvedConfig,\n): Promise<PreviewServer> {\n const vite = await import('vite')\n\n try {\n return await vite.preview({\n configFile: viteConfig.configFile,\n preview: {\n port: 0,\n open: false,\n },\n })\n } catch (error) {\n throw new Error(\n 'Failed to start the Vite preview server for prerendering',\n {\n cause: error,\n },\n )\n }\n}\n\nfunction getResolvedUrl(previewServer: PreviewServer): URL {\n const baseUrl = previewServer.resolvedUrls?.local[0]\n\n if (!baseUrl) {\n throw new Error('No resolved URL is available from the Vite preview server')\n }\n\n return new URL(baseUrl)\n}\n"],"names":["path","outputDir","fsp"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { Plugin } from 'vite';
2
+ export declare function previewServerPlugin(): Plugin;
@@ -0,0 +1,46 @@
1
+ import { pathToFileURL } from "node:url";
2
+ import { basename, extname, join } from "pathe";
3
+ import { NodeRequest, sendNodeResponse } from "srvx/node";
4
+ import { VITE_ENVIRONMENT_NAMES } from "../constants.js";
5
+ import { getServerOutputDirectory } from "../output-directory.js";
6
+ function previewServerPlugin() {
7
+ return {
8
+ name: "tanstack-start-core:preview-server",
9
+ configurePreviewServer: {
10
+ // Run last so platform plugins (Cloudflare, Vercel, etc.) can register their handlers first
11
+ order: "post",
12
+ handler(server) {
13
+ return () => {
14
+ let serverBuild = null;
15
+ server.middlewares.use(async (req, res, next) => {
16
+ try {
17
+ if (!serverBuild) {
18
+ const serverEnv = server.config.environments[VITE_ENVIRONMENT_NAMES.server];
19
+ const serverInput = serverEnv?.build.rollupOptions.input ?? "server";
20
+ if (typeof serverInput !== "string") {
21
+ throw new Error("Invalid server input. Expected a string.");
22
+ }
23
+ const outputFilename = `${basename(serverInput, extname(serverInput))}.js`;
24
+ const serverOutputDir = getServerOutputDirectory(server.config);
25
+ const serverEntryPath = join(serverOutputDir, outputFilename);
26
+ const imported = await import(pathToFileURL(serverEntryPath).toString());
27
+ serverBuild = imported.default;
28
+ }
29
+ const webReq = new NodeRequest({ req, res });
30
+ const webRes = await serverBuild.fetch(webReq);
31
+ res.setHeaders(webRes.headers);
32
+ res.writeHead(webRes.status, webRes.statusText);
33
+ return sendNodeResponse(res, webRes);
34
+ } catch (error) {
35
+ next(error);
36
+ }
37
+ });
38
+ };
39
+ }
40
+ }
41
+ };
42
+ }
43
+ export {
44
+ previewServerPlugin
45
+ };
46
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["../../../src/preview-server-plugin/plugin.ts"],"sourcesContent":["import { pathToFileURL } from 'node:url'\nimport { basename, extname, join } from 'pathe'\nimport { NodeRequest, sendNodeResponse } from 'srvx/node'\nimport { VITE_ENVIRONMENT_NAMES } from '../constants'\nimport { getServerOutputDirectory } from '../output-directory'\nimport type { Plugin } from 'vite'\n\nexport function previewServerPlugin(): Plugin {\n return {\n name: 'tanstack-start-core:preview-server',\n configurePreviewServer: {\n // Run last so platform plugins (Cloudflare, Vercel, etc.) can register their handlers first\n order: 'post',\n handler(server) {\n // Return a function so Vite's internal middlewares (static files, etc.) handle requests first.\n // Our SSR handler only processes requests that nothing else handled.\n return () => {\n // Cache the server build to avoid re-importing on every request\n let serverBuild: any = null\n\n server.middlewares.use(async (req, res, next) => {\n try {\n // Lazy load server build on first request\n if (!serverBuild) {\n // Derive output filename from input\n const serverEnv =\n server.config.environments[VITE_ENVIRONMENT_NAMES.server]\n const serverInput =\n serverEnv?.build.rollupOptions.input ?? 'server'\n\n if (typeof serverInput !== 'string') {\n throw new Error('Invalid server input. Expected a string.')\n }\n\n // Get basename without extension and add .js\n const outputFilename = `${basename(serverInput, extname(serverInput))}.js`\n const serverOutputDir = getServerOutputDirectory(server.config)\n const serverEntryPath = join(serverOutputDir, outputFilename)\n const imported = await import(\n pathToFileURL(serverEntryPath).toString()\n )\n\n serverBuild = imported.default\n }\n\n const webReq = new NodeRequest({ req, res })\n const webRes: Response = await serverBuild.fetch(webReq)\n\n // Temporary workaround\n // Vite preview's compression middleware doesn't support flattened array headers that srvx sets\n // Call writeHead() before srvx to avoid corruption\n res.setHeaders(webRes.headers)\n res.writeHead(webRes.status, webRes.statusText)\n\n return sendNodeResponse(res, webRes)\n } catch (error) {\n next(error)\n }\n })\n }\n },\n },\n }\n}\n"],"names":[],"mappings":";;;;;AAOO,SAAS,sBAA8B;AAC5C,SAAO;AAAA,IACL,MAAM;AAAA,IACN,wBAAwB;AAAA;AAAA,MAEtB,OAAO;AAAA,MACP,QAAQ,QAAQ;AAGd,eAAO,MAAM;AAEX,cAAI,cAAmB;AAEvB,iBAAO,YAAY,IAAI,OAAO,KAAK,KAAK,SAAS;AAC/C,gBAAI;AAEF,kBAAI,CAAC,aAAa;AAEhB,sBAAM,YACJ,OAAO,OAAO,aAAa,uBAAuB,MAAM;AAC1D,sBAAM,cACJ,WAAW,MAAM,cAAc,SAAS;AAE1C,oBAAI,OAAO,gBAAgB,UAAU;AACnC,wBAAM,IAAI,MAAM,0CAA0C;AAAA,gBAC5D;AAGA,sBAAM,iBAAiB,GAAG,SAAS,aAAa,QAAQ,WAAW,CAAC,CAAC;AACrE,sBAAM,kBAAkB,yBAAyB,OAAO,MAAM;AAC9D,sBAAM,kBAAkB,KAAK,iBAAiB,cAAc;AAC5D,sBAAM,WAAW,MAAM,OACrB,cAAc,eAAe,EAAE,SAAA;AAGjC,8BAAc,SAAS;AAAA,cACzB;AAEA,oBAAM,SAAS,IAAI,YAAY,EAAE,KAAK,KAAK;AAC3C,oBAAM,SAAmB,MAAM,YAAY,MAAM,MAAM;AAKvD,kBAAI,WAAW,OAAO,OAAO;AAC7B,kBAAI,UAAU,OAAO,QAAQ,OAAO,UAAU;AAE9C,qBAAO,iBAAiB,KAAK,MAAM;AAAA,YACrC,SAAS,OAAO;AACd,mBAAK,KAAK;AAAA,YACZ;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IAAA;AAAA,EACF;AAEJ;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-plugin-core",
3
- "version": "1.136.18",
3
+ "version": "1.138.0",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -60,9 +60,9 @@
60
60
  "xmlbuilder2": "^4.0.0",
61
61
  "zod": "^3.24.2",
62
62
  "@tanstack/router-core": "1.136.17",
63
+ "@tanstack/router-plugin": "1.136.18",
63
64
  "@tanstack/router-generator": "1.136.17",
64
65
  "@tanstack/router-utils": "1.133.19",
65
- "@tanstack/router-plugin": "1.136.18",
66
66
  "@tanstack/server-functions-plugin": "1.134.5",
67
67
  "@tanstack/start-client-core": "1.136.17",
68
68
  "@tanstack/start-server-core": "1.136.17"
@@ -3,16 +3,20 @@ import { VITE_ENVIRONMENT_NAMES } from './constants'
3
3
  import type { ViteEnvironmentNames } from './constants'
4
4
  import type * as vite from 'vite'
5
5
 
6
- export function getClientOutputDirectory(userConfig: vite.UserConfig) {
6
+ export function getClientOutputDirectory(
7
+ userConfig: vite.UserConfig | vite.ResolvedConfig,
8
+ ) {
7
9
  return getOutputDirectory(userConfig, VITE_ENVIRONMENT_NAMES.client, 'client')
8
10
  }
9
11
 
10
- export function getServerOutputDirectory(userConfig: vite.UserConfig) {
12
+ export function getServerOutputDirectory(
13
+ userConfig: vite.UserConfig | vite.ResolvedConfig,
14
+ ) {
11
15
  return getOutputDirectory(userConfig, VITE_ENVIRONMENT_NAMES.server, 'server')
12
16
  }
13
17
 
14
18
  function getOutputDirectory(
15
- userConfig: vite.UserConfig,
19
+ userConfig: vite.UserConfig | vite.ResolvedConfig,
16
20
  environmentName: ViteEnvironmentNames,
17
21
  directoryName: string,
18
22
  ) {
package/src/plugin.ts CHANGED
@@ -11,6 +11,7 @@ import { ENTRY_POINTS, VITE_ENVIRONMENT_NAMES } from './constants'
11
11
  import { tanStackStartRouter } from './start-router-plugin/plugin'
12
12
  import { loadEnvPlugin } from './load-env-plugin/plugin'
13
13
  import { devServerPlugin } from './dev-server-plugin/plugin'
14
+ import { previewServerPlugin } from './preview-server-plugin/plugin'
14
15
  import { parseStartConfig } from './schema'
15
16
  import { resolveEntry } from './resolve-entries'
16
17
  import {
@@ -346,12 +347,17 @@ export function TanStackStartVitePluginCore(
346
347
  // Build the SSR bundle
347
348
  await builder.build(server)
348
349
  }
349
- const serverBundle = getBundle(VITE_ENVIRONMENT_NAMES.server)
350
- await postServerBuild({ builder, startConfig, serverBundle })
351
350
  },
352
351
  },
353
352
  }
354
353
  },
354
+ buildApp: {
355
+ order: 'post',
356
+ async handler(builder) {
357
+ const { startConfig } = getConfig()
358
+ await postServerBuild({ builder, startConfig })
359
+ },
360
+ },
355
361
  },
356
362
  tanStackStartRouter(startPluginOpts, getConfig, corePluginOpts),
357
363
  // N.B. TanStackStartCompilerPlugin must be before the TanStackServerFnPlugin
@@ -399,6 +405,7 @@ export function TanStackStartVitePluginCore(
399
405
  getConfig,
400
406
  }),
401
407
  devServerPlugin({ getConfig }),
408
+ previewServerPlugin(),
402
409
  {
403
410
  name: 'tanstack-start:core:capture-bundle',
404
411
  applyToEnvironment(e) {
@@ -3,16 +3,14 @@ import { buildSitemap } from './build-sitemap'
3
3
  import { VITE_ENVIRONMENT_NAMES } from './constants'
4
4
  import { prerender } from './prerender'
5
5
  import type { TanStackStartOutputConfig } from './schema'
6
- import type { Rollup, ViteBuilder } from 'vite'
6
+ import type { ViteBuilder } from 'vite'
7
7
 
8
8
  export async function postServerBuild({
9
9
  builder,
10
10
  startConfig,
11
- serverBundle,
12
11
  }: {
13
12
  builder: ViteBuilder
14
13
  startConfig: TanStackStartOutputConfig
15
- serverBundle: Rollup.OutputBundle
16
14
  }) {
17
15
  // If the user has not set a prerender option, we need to set it to true
18
16
  // if the pages array is not empty and has sub options requiring for prerendering
@@ -57,7 +55,6 @@ export async function postServerBuild({
57
55
  await prerender({
58
56
  startConfig,
59
57
  builder,
60
- serverBundle,
61
58
  })
62
59
  }
63
60
 
package/src/prerender.ts CHANGED
@@ -1,22 +1,19 @@
1
- import { existsSync, promises as fsp, rmSync } from 'node:fs'
2
- import { pathToFileURL } from 'node:url'
1
+ import { promises as fsp } from 'node:fs'
3
2
  import os from 'node:os'
4
3
  import path from 'pathe'
5
4
  import { joinURL, withBase, withoutBase } from 'ufo'
6
5
  import { VITE_ENVIRONMENT_NAMES } from './constants'
7
6
  import { createLogger } from './utils'
8
7
  import { Queue } from './queue'
9
- import type { Rollup, ViteBuilder } from 'vite'
8
+ import type { PreviewServer, ResolvedConfig, ViteBuilder } from 'vite'
10
9
  import type { Page, TanStackStartOutputConfig } from './schema'
11
10
 
12
11
  export async function prerender({
13
12
  startConfig,
14
13
  builder,
15
- serverBundle,
16
14
  }: {
17
15
  startConfig: TanStackStartOutputConfig
18
16
  builder: ViteBuilder
19
- serverBundle: Rollup.OutputBundle
20
17
  }) {
21
18
  const logger = createLogger('prerender')
22
19
  logger.info('Prerendering pages...')
@@ -60,27 +57,11 @@ export async function prerender({
60
57
 
61
58
  const outputDir = clientEnv.config.build.outDir
62
59
 
63
- const entryFile = findEntryFileInBundle(serverBundle)
64
- let fullEntryFilePath = path.join(serverEnv.config.build.outDir, entryFile)
65
60
  process.env.TSS_PRERENDERING = 'true'
66
61
 
67
- if (!existsSync(fullEntryFilePath)) {
68
- // if the file does not exist, we need to write the bundle to a temporary directory
69
- // this can happen e.g. with nitro that postprocesses the bundle and thus does not write SSR build to disk
70
- const bundleOutputDir = path.resolve(
71
- serverEnv.config.root,
72
- '.tanstack',
73
- 'start',
74
- 'prerender',
75
- )
76
- rmSync(bundleOutputDir, { recursive: true, force: true })
77
- await writeBundleToDisk({ bundle: serverBundle, outDir: bundleOutputDir })
78
- fullEntryFilePath = path.join(bundleOutputDir, entryFile)
79
- }
80
-
81
- const { default: serverEntrypoint } = await import(
82
- pathToFileURL(fullEntryFilePath).toString()
83
- )
62
+ // Start Vite preview server instead of importing module
63
+ const previewServer = await startPreviewServer(serverEnv.config)
64
+ const baseUrl = getResolvedUrl(previewServer)
84
65
 
85
66
  const isRedirectResponse = (res: Response) => {
86
67
  return res.status >= 300 && res.status < 400 && res.headers.get('location')
@@ -90,8 +71,9 @@ export async function prerender({
90
71
  options?: RequestInit,
91
72
  maxRedirects: number = 5,
92
73
  ): Promise<Response> {
93
- const url = new URL(`http://localhost${path}`)
94
- const response = await serverEntrypoint.fetch(new Request(url, options))
74
+ const url = new URL(path, baseUrl)
75
+ const request = new Request(url, options)
76
+ const response = await fetch(request)
95
77
 
96
78
  if (isRedirectResponse(response) && maxRedirects > 0) {
97
79
  const location = response.headers.get('location')!
@@ -116,6 +98,8 @@ export async function prerender({
116
98
  })
117
99
  } catch (error) {
118
100
  logger.error(error)
101
+ } finally {
102
+ await previewServer.close()
119
103
  }
120
104
 
121
105
  function extractLinks(html: string): Array<string> {
@@ -278,46 +262,35 @@ export async function prerender({
278
262
  }
279
263
  }
280
264
 
281
- function findEntryFileInBundle(bundle: Rollup.OutputBundle): string {
282
- let entryFile: string | undefined
265
+ async function startPreviewServer(
266
+ viteConfig: ResolvedConfig,
267
+ ): Promise<PreviewServer> {
268
+ const vite = await import('vite')
283
269
 
284
- for (const [_name, file] of Object.entries(bundle)) {
285
- if (file.type === 'chunk') {
286
- if (file.isEntry) {
287
- if (entryFile !== undefined) {
288
- throw new Error(
289
- `Multiple entry points found. Only one entry point is allowed.`,
290
- )
291
- }
292
- entryFile = file.fileName
293
- }
294
- }
295
- }
296
- if (entryFile === undefined) {
297
- throw new Error(`No entry point found in the bundle.`)
270
+ try {
271
+ return await vite.preview({
272
+ configFile: viteConfig.configFile,
273
+ preview: {
274
+ port: 0,
275
+ open: false,
276
+ },
277
+ })
278
+ } catch (error) {
279
+ throw new Error(
280
+ 'Failed to start the Vite preview server for prerendering',
281
+ {
282
+ cause: error,
283
+ },
284
+ )
298
285
  }
299
- return entryFile
300
286
  }
301
287
 
302
- export async function writeBundleToDisk({
303
- bundle,
304
- outDir,
305
- }: {
306
- bundle: Rollup.OutputBundle
307
- outDir: string
308
- }) {
309
- const createdDirs = new Set<string>()
288
+ function getResolvedUrl(previewServer: PreviewServer): URL {
289
+ const baseUrl = previewServer.resolvedUrls?.local[0]
310
290
 
311
- for (const [fileName, asset] of Object.entries(bundle)) {
312
- const fullPath = path.join(outDir, fileName)
313
- const dir = path.dirname(fullPath)
314
- const content = asset.type === 'asset' ? asset.source : asset.code
315
-
316
- if (!createdDirs.has(dir)) {
317
- await fsp.mkdir(dir, { recursive: true })
318
- createdDirs.add(dir)
319
- }
320
-
321
- await fsp.writeFile(fullPath, content)
291
+ if (!baseUrl) {
292
+ throw new Error('No resolved URL is available from the Vite preview server')
322
293
  }
294
+
295
+ return new URL(baseUrl)
323
296
  }
@@ -0,0 +1,64 @@
1
+ import { pathToFileURL } from 'node:url'
2
+ import { basename, extname, join } from 'pathe'
3
+ import { NodeRequest, sendNodeResponse } from 'srvx/node'
4
+ import { VITE_ENVIRONMENT_NAMES } from '../constants'
5
+ import { getServerOutputDirectory } from '../output-directory'
6
+ import type { Plugin } from 'vite'
7
+
8
+ export function previewServerPlugin(): Plugin {
9
+ return {
10
+ name: 'tanstack-start-core:preview-server',
11
+ configurePreviewServer: {
12
+ // Run last so platform plugins (Cloudflare, Vercel, etc.) can register their handlers first
13
+ order: 'post',
14
+ handler(server) {
15
+ // Return a function so Vite's internal middlewares (static files, etc.) handle requests first.
16
+ // Our SSR handler only processes requests that nothing else handled.
17
+ return () => {
18
+ // Cache the server build to avoid re-importing on every request
19
+ let serverBuild: any = null
20
+
21
+ server.middlewares.use(async (req, res, next) => {
22
+ try {
23
+ // Lazy load server build on first request
24
+ if (!serverBuild) {
25
+ // Derive output filename from input
26
+ const serverEnv =
27
+ server.config.environments[VITE_ENVIRONMENT_NAMES.server]
28
+ const serverInput =
29
+ serverEnv?.build.rollupOptions.input ?? 'server'
30
+
31
+ if (typeof serverInput !== 'string') {
32
+ throw new Error('Invalid server input. Expected a string.')
33
+ }
34
+
35
+ // Get basename without extension and add .js
36
+ const outputFilename = `${basename(serverInput, extname(serverInput))}.js`
37
+ const serverOutputDir = getServerOutputDirectory(server.config)
38
+ const serverEntryPath = join(serverOutputDir, outputFilename)
39
+ const imported = await import(
40
+ pathToFileURL(serverEntryPath).toString()
41
+ )
42
+
43
+ serverBuild = imported.default
44
+ }
45
+
46
+ const webReq = new NodeRequest({ req, res })
47
+ const webRes: Response = await serverBuild.fetch(webReq)
48
+
49
+ // Temporary workaround
50
+ // Vite preview's compression middleware doesn't support flattened array headers that srvx sets
51
+ // Call writeHead() before srvx to avoid corruption
52
+ res.setHeaders(webRes.headers)
53
+ res.writeHead(webRes.status, webRes.statusText)
54
+
55
+ return sendNodeResponse(res, webRes)
56
+ } catch (error) {
57
+ next(error)
58
+ }
59
+ })
60
+ }
61
+ },
62
+ },
63
+ }
64
+ }