@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.
- package/dist/esm/output-directory.d.ts +2 -2
- package/dist/esm/output-directory.js.map +1 -1
- package/dist/esm/plugin.js +9 -2
- package/dist/esm/plugin.js.map +1 -1
- package/dist/esm/post-server-build.d.ts +2 -3
- package/dist/esm/post-server-build.js +2 -4
- package/dist/esm/post-server-build.js.map +1 -1
- package/dist/esm/prerender.d.ts +2 -7
- package/dist/esm/prerender.js +31 -52
- package/dist/esm/prerender.js.map +1 -1
- package/dist/esm/preview-server-plugin/plugin.d.ts +2 -0
- package/dist/esm/preview-server-plugin/plugin.js +46 -0
- package/dist/esm/preview-server-plugin/plugin.js.map +1 -0
- package/package.json +2 -2
- package/src/output-directory.ts +7 -3
- package/src/plugin.ts +9 -2
- package/src/post-server-build.ts +1 -4
- package/src/prerender.ts +35 -62
- package/src/preview-server-plugin/plugin.ts +64 -0
|
@@ -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,
|
|
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;"}
|
package/dist/esm/plugin.js
CHANGED
|
@@ -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) {
|
package/dist/esm/plugin.js.map
CHANGED
|
@@ -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 {
|
|
3
|
-
export declare function postServerBuild({ builder, startConfig,
|
|
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 {
|
|
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;"}
|
package/dist/esm/prerender.d.ts
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ViteBuilder } from 'vite';
|
|
2
2
|
import { TanStackStartOutputConfig } from './schema.js';
|
|
3
|
-
export declare function prerender({ startConfig, builder,
|
|
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>;
|
package/dist/esm/prerender.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
47
|
-
|
|
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(
|
|
63
|
-
const
|
|
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
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
|
|
210
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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,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.
|
|
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"
|
package/src/output-directory.ts
CHANGED
|
@@ -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(
|
|
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(
|
|
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) {
|
package/src/post-server-build.ts
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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(
|
|
94
|
-
const
|
|
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
|
|
282
|
-
|
|
265
|
+
async function startPreviewServer(
|
|
266
|
+
viteConfig: ResolvedConfig,
|
|
267
|
+
): Promise<PreviewServer> {
|
|
268
|
+
const vite = await import('vite')
|
|
283
269
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
|
|
303
|
-
|
|
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
|
-
|
|
312
|
-
|
|
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
|
+
}
|