@tanstack/start-plugin-core 1.120.4-alpha.9 → 1.121.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/cjs/constants.cjs +10 -0
  2. package/dist/cjs/constants.cjs.map +1 -0
  3. package/dist/cjs/constants.d.cts +4 -0
  4. package/dist/cjs/index.cjs +0 -6
  5. package/dist/cjs/index.cjs.map +1 -1
  6. package/dist/cjs/index.d.cts +0 -5
  7. package/dist/cjs/nitro/dev-server-plugin.cjs +11 -3
  8. package/dist/cjs/nitro/dev-server-plugin.cjs.map +1 -1
  9. package/dist/cjs/nitro/nitro-plugin.cjs +35 -5
  10. package/dist/cjs/nitro/nitro-plugin.cjs.map +1 -1
  11. package/dist/cjs/plugin.cjs +82 -15
  12. package/dist/cjs/plugin.cjs.map +1 -1
  13. package/dist/cjs/plugin.d.cts +766 -147
  14. package/dist/cjs/prerender.cjs +19 -15
  15. package/dist/cjs/prerender.cjs.map +1 -1
  16. package/dist/cjs/schema.cjs +21 -2
  17. package/dist/cjs/schema.cjs.map +1 -1
  18. package/dist/cjs/schema.d.cts +2362 -601
  19. package/dist/esm/constants.d.ts +4 -0
  20. package/dist/esm/constants.js +10 -0
  21. package/dist/esm/constants.js.map +1 -0
  22. package/dist/esm/index.d.ts +0 -5
  23. package/dist/esm/index.js +0 -6
  24. package/dist/esm/index.js.map +1 -1
  25. package/dist/esm/nitro/dev-server-plugin.js +11 -3
  26. package/dist/esm/nitro/dev-server-plugin.js.map +1 -1
  27. package/dist/esm/nitro/nitro-plugin.js +35 -5
  28. package/dist/esm/nitro/nitro-plugin.js.map +1 -1
  29. package/dist/esm/plugin.d.ts +766 -147
  30. package/dist/esm/plugin.js +65 -15
  31. package/dist/esm/plugin.js.map +1 -1
  32. package/dist/esm/prerender.js +19 -15
  33. package/dist/esm/prerender.js.map +1 -1
  34. package/dist/esm/schema.d.ts +2362 -601
  35. package/dist/esm/schema.js +22 -3
  36. package/dist/esm/schema.js.map +1 -1
  37. package/package.json +6 -4
  38. package/src/constants.ts +6 -0
  39. package/src/index.ts +0 -8
  40. package/src/nitro/dev-server-plugin.ts +14 -4
  41. package/src/nitro/nitro-plugin.ts +42 -4
  42. package/src/plugin.ts +81 -15
  43. package/src/prerender.ts +26 -19
  44. package/src/schema.ts +26 -1
  45. package/dist/cjs/nitro/build-sitemap.cjs +0 -54
  46. package/dist/cjs/nitro/build-sitemap.cjs.map +0 -1
  47. package/dist/esm/nitro/build-sitemap.js +0 -54
  48. package/dist/esm/nitro/build-sitemap.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { existsSync } from "node:fs";
3
3
  import { z } from "zod";
4
- import { getConfig, configSchema } from "@tanstack/router-generator";
4
+ import { configSchema, getConfig } from "@tanstack/router-generator";
5
5
  const tsrConfig = configSchema.partial().extend({
6
6
  srcDirectory: z.string().optional().default("src")
7
7
  });
@@ -30,6 +30,12 @@ function createTanStackConfig(frameworkPlugin) {
30
30
  if (existsSync(path.join(srcDirectory, "server.tsx"))) {
31
31
  return path.join(srcDirectory, "server.tsx");
32
32
  }
33
+ if (existsSync(path.join(srcDirectory, "server.ts"))) {
34
+ return path.join(srcDirectory, "server.ts");
35
+ }
36
+ if (existsSync(path.join(srcDirectory, "server.js"))) {
37
+ return path.join(srcDirectory, "server.js");
38
+ }
33
39
  return "/~start/default-server-entry";
34
40
  })();
35
41
  return {
@@ -62,7 +68,7 @@ function createTanStackStartOptionsSchema(frameworkPlugin = {}) {
62
68
  entry: z.string().optional()
63
69
  }).optional().default({}),
64
70
  serverFns: z.object({
65
- base: z.string().optional().default("/_server")
71
+ base: z.string().optional().default("/_serverFn")
66
72
  }).optional().default({}),
67
73
  public: z.object({
68
74
  dir: z.string().optional().default("public"),
@@ -77,7 +83,8 @@ function createTanStackStartOptionsSchema(frameworkPlugin = {}) {
77
83
  concurrency: z.number().optional(),
78
84
  filter: z.function().args(pageSchema).returns(z.any()).optional(),
79
85
  failOnError: z.boolean().optional()
80
- }).and(pagePrerenderOptionsSchema.optional()).optional()
86
+ }).and(pagePrerenderOptionsSchema.optional()).optional(),
87
+ spa: spaSchema.optional()
81
88
  }).optional().default({});
82
89
  }
83
90
  const pageSitemapOptionsSchema = z.object({
@@ -114,6 +121,7 @@ const pageBaseSchema = z.object({
114
121
  });
115
122
  const pagePrerenderOptionsSchema = z.object({
116
123
  enabled: z.boolean().optional(),
124
+ outputPath: z.string().optional(),
117
125
  autoSubfolderIndex: z.boolean().optional(),
118
126
  crawlLinks: z.boolean().optional(),
119
127
  retryCount: z.number().optional(),
@@ -125,6 +133,17 @@ const pagePrerenderOptionsSchema = z.object({
125
133
  })
126
134
  ).returns(z.any()).optional()
127
135
  });
136
+ const spaSchema = z.object({
137
+ enabled: z.boolean().optional().default(true),
138
+ maskPath: z.string().optional().default("/"),
139
+ prerender: pagePrerenderOptionsSchema.optional().default({}).transform((opts) => ({
140
+ outputPath: opts.outputPath ?? "/_shell",
141
+ crawlLinks: false,
142
+ retryCount: 0,
143
+ ...opts,
144
+ enabled: true
145
+ }))
146
+ });
128
147
  const pageSchema = pageBaseSchema.extend({
129
148
  prerender: pagePrerenderOptionsSchema.optional()
130
149
  });
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sources":["../../src/schema.ts"],"sourcesContent":["import path from 'node:path'\nimport { existsSync } from 'node:fs'\nimport { z } from 'zod'\nimport { configSchema, getConfig } from '@tanstack/router-generator'\nimport type { NitroConfig } from 'nitropack'\n\nconst tsrConfig = configSchema.partial().extend({\n srcDirectory: z.string().optional().default('src'),\n})\n\nexport function createTanStackConfig<\n TFrameworkPlugin extends Record<string, unknown>,\n>(frameworkPlugin?: TFrameworkPlugin) {\n const schema = createTanStackStartOptionsSchema(frameworkPlugin)\n\n return {\n schema,\n parse: (opts?: z.input<typeof schema>) => {\n const options = schema.parse(opts)\n\n const srcDirectory = options.tsr.srcDirectory\n\n const routesDirectory =\n options.tsr.routesDirectory ?? path.join(srcDirectory, 'routes')\n\n const generatedRouteTree =\n options.tsr.generatedRouteTree ??\n path.join(srcDirectory, 'routeTree.gen.ts')\n\n const clientEntryPath = (() => {\n if (options.client.entry) {\n return path.join(srcDirectory, options.client.entry)\n }\n\n if (existsSync(path.join(srcDirectory, 'client.tsx'))) {\n return path.join(srcDirectory, 'client.tsx')\n }\n\n return '/~start/default-client-entry'\n })()\n\n const serverEntryPath = (() => {\n if (options.server.entry) {\n return path.join(srcDirectory, options.server.entry)\n }\n\n if (existsSync(path.join(srcDirectory, 'server.tsx'))) {\n return path.join(srcDirectory, 'server.tsx')\n }\n\n return '/~start/default-server-entry'\n })()\n\n return {\n ...options,\n tsr: {\n ...options.tsr,\n ...getConfig({\n ...options.tsr,\n routesDirectory,\n generatedRouteTree,\n }),\n },\n clientEntryPath,\n serverEntryPath,\n }\n },\n }\n}\n\nexport function createTanStackStartOptionsSchema(\n frameworkPlugin: Record<string, unknown> = {},\n) {\n return z\n .object({\n root: z.string().optional().default(process.cwd()),\n target: z.custom<NitroConfig['preset']>().optional(),\n ...frameworkPlugin,\n tsr: tsrConfig.optional().default({}),\n client: z\n .object({\n entry: z.string().optional(),\n base: z.string().optional().default('/_build'),\n })\n .optional()\n .default({}),\n server: z\n .object({\n entry: z.string().optional(),\n })\n .optional()\n .default({}),\n serverFns: z\n .object({\n base: z.string().optional().default('/_server'),\n })\n .optional()\n .default({}),\n public: z\n .object({\n dir: z.string().optional().default('public'),\n base: z.string().optional().default('/'),\n })\n .optional()\n .default({}),\n pages: z\n .array(z.union([z.string(), pageSchema]))\n .optional()\n .default([]),\n sitemap: pagePrerenderOptionsSchema\n .extend({\n host: z.string().optional(),\n })\n .optional(),\n prerender: z\n .object({\n enabled: z.boolean().optional(),\n concurrency: z.number().optional(),\n filter: z.function().args(pageSchema).returns(z.any()).optional(),\n failOnError: z.boolean().optional(),\n })\n .and(pagePrerenderOptionsSchema.optional())\n .optional(),\n })\n .optional()\n .default({})\n}\n\nconst pageSitemapOptionsSchema = z.object({\n exclude: z.boolean().optional(),\n priority: z.number().min(0).max(1).optional(),\n changefreq: z\n .enum(['always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never'])\n .optional(),\n lastmod: z.union([z.string(), z.date()]).optional(),\n alternateRefs: z\n .array(\n z.object({\n href: z.string(),\n hreflang: z.string(),\n }),\n )\n .optional(),\n images: z\n .array(\n z.object({\n loc: z.string(),\n caption: z.string().optional(),\n title: z.string().optional(),\n }),\n )\n .optional(),\n news: z\n .object({\n publication: z.object({\n name: z.string(),\n language: z.string(),\n }),\n publicationDate: z.union([z.string(), z.date()]),\n title: z.string(),\n })\n .optional(),\n})\n\nconst pageBaseSchema = z.object({\n path: z.string(),\n sitemap: pageSitemapOptionsSchema.optional(),\n fromCrawl: z.boolean().optional(),\n})\n\nconst pagePrerenderOptionsSchema = z.object({\n enabled: z.boolean().optional(),\n autoSubfolderIndex: z.boolean().optional(),\n crawlLinks: z.boolean().optional(),\n retryCount: z.number().optional(),\n retryDelay: z.number().optional(),\n onSuccess: z\n .function()\n .args(\n z.object({\n page: pageBaseSchema,\n html: z.string(),\n }),\n )\n .returns(z.any())\n .optional(),\n})\n\nexport const pageSchema = pageBaseSchema.extend({\n prerender: pagePrerenderOptionsSchema.optional(),\n})\n\nexport type Page = z.infer<typeof pageSchema>\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.js","sources":["../../src/schema.ts"],"sourcesContent":["import path from 'node:path'\nimport { existsSync } from 'node:fs'\nimport { z } from 'zod'\nimport { configSchema, getConfig } from '@tanstack/router-generator'\nimport type { NitroConfig } from 'nitropack'\n\nconst tsrConfig = configSchema.partial().extend({\n srcDirectory: z.string().optional().default('src'),\n})\n\nexport function createTanStackConfig<\n TFrameworkPlugin extends Record<string, unknown>,\n>(frameworkPlugin?: TFrameworkPlugin) {\n const schema = createTanStackStartOptionsSchema(frameworkPlugin)\n\n return {\n schema,\n parse: (opts?: z.input<typeof schema>) => {\n const options = schema.parse(opts)\n\n const srcDirectory = options.tsr.srcDirectory\n\n const routesDirectory =\n options.tsr.routesDirectory ?? path.join(srcDirectory, 'routes')\n\n const generatedRouteTree =\n options.tsr.generatedRouteTree ??\n path.join(srcDirectory, 'routeTree.gen.ts')\n\n const clientEntryPath = (() => {\n if (options.client.entry) {\n return path.join(srcDirectory, options.client.entry)\n }\n\n if (existsSync(path.join(srcDirectory, 'client.tsx'))) {\n return path.join(srcDirectory, 'client.tsx')\n }\n\n return '/~start/default-client-entry'\n })()\n\n const serverEntryPath = (() => {\n if (options.server.entry) {\n return path.join(srcDirectory, options.server.entry)\n }\n\n if (existsSync(path.join(srcDirectory, 'server.tsx'))) {\n return path.join(srcDirectory, 'server.tsx')\n }\n\n if (existsSync(path.join(srcDirectory, 'server.ts'))) {\n return path.join(srcDirectory, 'server.ts')\n }\n\n if (existsSync(path.join(srcDirectory, 'server.js'))) {\n return path.join(srcDirectory, 'server.js')\n }\n\n return '/~start/default-server-entry'\n })()\n\n return {\n ...options,\n tsr: {\n ...options.tsr,\n ...getConfig({\n ...options.tsr,\n routesDirectory,\n generatedRouteTree,\n }),\n },\n clientEntryPath,\n serverEntryPath,\n }\n },\n }\n}\n\nexport function createTanStackStartOptionsSchema(\n frameworkPlugin: Record<string, unknown> = {},\n) {\n return z\n .object({\n root: z.string().optional().default(process.cwd()),\n target: z.custom<NitroConfig['preset']>().optional(),\n ...frameworkPlugin,\n tsr: tsrConfig.optional().default({}),\n client: z\n .object({\n entry: z.string().optional(),\n base: z.string().optional().default('/_build'),\n })\n .optional()\n .default({}),\n server: z\n .object({\n entry: z.string().optional(),\n })\n .optional()\n .default({}),\n serverFns: z\n .object({\n base: z.string().optional().default('/_serverFn'),\n })\n .optional()\n .default({}),\n public: z\n .object({\n dir: z.string().optional().default('public'),\n base: z.string().optional().default('/'),\n })\n .optional()\n .default({}),\n pages: z\n .array(z.union([z.string(), pageSchema]))\n .optional()\n .default([]),\n sitemap: pagePrerenderOptionsSchema\n .extend({\n host: z.string().optional(),\n })\n .optional(),\n prerender: z\n .object({\n enabled: z.boolean().optional(),\n concurrency: z.number().optional(),\n filter: z.function().args(pageSchema).returns(z.any()).optional(),\n failOnError: z.boolean().optional(),\n })\n .and(pagePrerenderOptionsSchema.optional())\n .optional(),\n spa: spaSchema.optional(),\n })\n .optional()\n .default({})\n}\n\nconst pageSitemapOptionsSchema = z.object({\n exclude: z.boolean().optional(),\n priority: z.number().min(0).max(1).optional(),\n changefreq: z\n .enum(['always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never'])\n .optional(),\n lastmod: z.union([z.string(), z.date()]).optional(),\n alternateRefs: z\n .array(\n z.object({\n href: z.string(),\n hreflang: z.string(),\n }),\n )\n .optional(),\n images: z\n .array(\n z.object({\n loc: z.string(),\n caption: z.string().optional(),\n title: z.string().optional(),\n }),\n )\n .optional(),\n news: z\n .object({\n publication: z.object({\n name: z.string(),\n language: z.string(),\n }),\n publicationDate: z.union([z.string(), z.date()]),\n title: z.string(),\n })\n .optional(),\n})\n\nconst pageBaseSchema = z.object({\n path: z.string(),\n sitemap: pageSitemapOptionsSchema.optional(),\n fromCrawl: z.boolean().optional(),\n})\n\nconst pagePrerenderOptionsSchema = z.object({\n enabled: z.boolean().optional(),\n outputPath: z.string().optional(),\n autoSubfolderIndex: z.boolean().optional(),\n crawlLinks: z.boolean().optional(),\n retryCount: z.number().optional(),\n retryDelay: z.number().optional(),\n onSuccess: z\n .function()\n .args(\n z.object({\n page: pageBaseSchema,\n html: z.string(),\n }),\n )\n .returns(z.any())\n .optional(),\n})\n\nconst spaSchema = z.object({\n enabled: z.boolean().optional().default(true),\n maskPath: z.string().optional().default('/'),\n prerender: pagePrerenderOptionsSchema\n .optional()\n .default({})\n .transform((opts) => ({\n outputPath: opts.outputPath ?? '/_shell',\n crawlLinks: false,\n retryCount: 0,\n ...opts,\n enabled: true,\n })),\n})\n\nexport const pageSchema = pageBaseSchema.extend({\n prerender: pagePrerenderOptionsSchema.optional(),\n})\n\nexport type Page = z.infer<typeof pageSchema>\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/start-plugin-core",
3
- "version": "1.120.4-alpha.9",
3
+ "version": "1.121.0-alpha.2",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
@@ -63,9 +63,11 @@
63
63
  "ufo": "^1.5.4",
64
64
  "xmlbuilder2": "^3.1.1",
65
65
  "zod": "^3.24.2",
66
- "@tanstack/router-core": "^1.120.4-alpha.1",
67
- "@tanstack/router-utils": "^1.120.4-alpha.1",
68
- "@tanstack/router-generator": "^1.120.4-alpha.4"
66
+ "@tanstack/router-core": "^1.121.0-alpha.1",
67
+ "@tanstack/router-generator": "^1.121.0-alpha.1",
68
+ "@tanstack/router-plugin": "^1.121.0-alpha.2",
69
+ "@tanstack/router-utils": "^1.121.0-alpha.2",
70
+ "@tanstack/server-functions-plugin": "^1.121.0-alpha.2"
69
71
  },
70
72
  "devDependencies": {
71
73
  "vite": "^6.0.0"
@@ -0,0 +1,6 @@
1
+ export const VITE_ENVIRONMENT_NAMES = {
2
+ // 'ssr' is chosen as the name for the server environment to ensure backwards compatibility
3
+ // with vite plugins that are not compatible with the new vite environment API (e.g. tailwindcss)
4
+ server: 'ssr',
5
+ client: 'client',
6
+ } as const
package/src/index.ts CHANGED
@@ -1,10 +1,3 @@
1
- export type { CompileOptions, IdentifierConfig } from './compilers'
2
-
3
- export { compileStartOutputFactory } from './compilers'
4
-
5
- export type { PagesJson } from './nitro/build-sitemap'
6
- export { buildSitemap } from './nitro/build-sitemap'
7
-
8
1
  export {
9
2
  createTanStackConfig,
10
3
  createTanStackStartOptionsSchema,
@@ -12,4 +5,3 @@ export {
12
5
  } from './schema'
13
6
 
14
7
  export { TanStackStartVitePluginCore } from './plugin'
15
- export { TanStackStartServerRoutesVite } from './start-server-routes-plugin/plugin'
@@ -1,7 +1,8 @@
1
1
  import { createEvent, getHeader, sendWebResponse } from 'h3'
2
2
  import { isRunnableDevEnvironment } from 'vite'
3
3
  import { extractHtmlScripts } from '../extractHtmlScripts'
4
- import type { Connect, Plugin, ViteDevServer } from 'vite'
4
+ import { VITE_ENVIRONMENT_NAMES } from '../constants'
5
+ import type { Connect, DevEnvironment, Plugin, ViteDevServer } from 'vite'
5
6
 
6
7
  declare global {
7
8
  // eslint-disable-next-line no-var
@@ -30,11 +31,20 @@ export function devServerPlugin(): Plugin {
30
31
  let cachedScripts: string | undefined
31
32
  viteDevServer.middlewares.use(async (req, res) => {
32
33
  const event = createEvent(req, res)
33
- const serverEnv = viteDevServer.environments['server']
34
+ const serverEnv = viteDevServer.environments[
35
+ VITE_ENVIRONMENT_NAMES.server
36
+ ] as DevEnvironment | undefined
34
37
 
35
38
  try {
36
- if (!serverEnv || !isRunnableDevEnvironment(serverEnv)) {
37
- throw new Error('Server environment not found')
39
+ if (!serverEnv) {
40
+ throw new Error(
41
+ `Server environment ${VITE_ENVIRONMENT_NAMES.server} not found`,
42
+ )
43
+ }
44
+ if (!isRunnableDevEnvironment(serverEnv)) {
45
+ throw new Error(
46
+ `Expected server environment ${VITE_ENVIRONMENT_NAMES.server} to be a RunnableDevEnvironment. This can be caused by multiple vite versions being installed in the project.`,
47
+ )
38
48
  }
39
49
  if (cachedScripts === undefined) {
40
50
  const templateHtml = `<html><head></head><body></body></html>`
@@ -4,6 +4,7 @@ import { build, createNitro } from 'nitropack'
4
4
  import { dirname, resolve } from 'pathe'
5
5
  import { clientDistDir, ssrEntryFile } from '../plugin'
6
6
  import { prerender } from '../prerender'
7
+ import { VITE_ENVIRONMENT_NAMES } from '../constants'
7
8
  import { devServerPlugin } from './dev-server-plugin'
8
9
  import { buildNitroEnvironment } from './build-nitro'
9
10
  import type { EnvironmentOptions, PluginOption, Rollup } from 'vite'
@@ -22,7 +23,7 @@ export function nitroPlugin(
22
23
  {
23
24
  name: 'tanstack-vite-plugin-nitro',
24
25
  configEnvironment(name) {
25
- if (name === 'server') {
26
+ if (name === VITE_ENVIRONMENT_NAMES.server) {
26
27
  return {
27
28
  build: {
28
29
  commonjsOptions: {
@@ -44,8 +45,10 @@ export function nitroPlugin(
44
45
  builder: {
45
46
  sharedPlugins: true,
46
47
  async buildApp(builder) {
47
- const clientEnv = builder.environments['client']
48
- const serverEnv = builder.environments['server']
48
+ const clientEnv =
49
+ builder.environments[VITE_ENVIRONMENT_NAMES.client]
50
+ const serverEnv =
51
+ builder.environments[VITE_ENVIRONMENT_NAMES.server]
49
52
 
50
53
  if (!clientEnv) {
51
54
  throw new Error('Client environment not found')
@@ -86,7 +89,42 @@ export function nitroPlugin(
86
89
 
87
90
  await buildNitroEnvironment(nitro, () => build(nitro))
88
91
 
89
- if (options.prerender?.enabled) {
92
+ // If the user has not set a prerender option, we need to set it to true
93
+ // if the pages array is not empty and has sub options requiring for prerendering
94
+ if (options.prerender?.enabled !== false) {
95
+ options.prerender = {
96
+ ...options.prerender,
97
+ enabled: options.pages.some((d) =>
98
+ typeof d === 'string' ? false : !!d.prerender?.enabled,
99
+ ),
100
+ }
101
+ }
102
+
103
+ // Setup the options for prerendering the SPA shell (i.e `src/routes/__root.tsx`)
104
+ if (options.spa?.enabled) {
105
+ options.prerender = {
106
+ ...options.prerender,
107
+ enabled: true,
108
+ }
109
+
110
+ const maskUrl = new URL(
111
+ options.spa.maskPath,
112
+ 'http://localhost',
113
+ )
114
+
115
+ maskUrl.searchParams.set('__TSS_SHELL', 'true')
116
+
117
+ options.pages.push({
118
+ path: maskUrl.toString().replace('http://localhost', ''),
119
+ prerender: options.spa.prerender,
120
+ sitemap: {
121
+ exclude: true,
122
+ },
123
+ })
124
+ }
125
+
126
+ // Start prerendering!!!
127
+ if (options.prerender.enabled) {
90
128
  await prerender({
91
129
  options,
92
130
  nitro,
package/src/plugin.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  import path from 'node:path'
2
2
  import { createNitro } from 'nitropack'
3
+ import { tanstackRouter } from '@tanstack/router-plugin/vite'
4
+ import { TanStackServerFnPluginEnv } from '@tanstack/server-functions-plugin'
5
+ import * as vite from 'vite'
3
6
  import {
4
7
  createTanStackConfig,
5
8
  createTanStackStartOptionsSchema,
@@ -7,6 +10,8 @@ import {
7
10
  import { nitroPlugin } from './nitro/nitro-plugin'
8
11
  import { startManifestPlugin } from './routesManifestPlugin'
9
12
  import { TanStackStartCompilerPlugin } from './start-compiler-plugin'
13
+ import { VITE_ENVIRONMENT_NAMES } from './constants'
14
+ import { TanStackStartServerRoutesVite } from './start-server-routes-plugin/plugin'
10
15
  import type { PluginOption, Rollup } from 'vite'
11
16
  import type { z } from 'zod'
12
17
  import type { CompileStartFrameworkOptions } from './compilers'
@@ -28,21 +33,31 @@ export type TanStackStartOutputConfig = ReturnType<
28
33
  export const clientDistDir = '.tanstack-start/build/client-dist'
29
34
  export const ssrEntryFile = 'ssr.mjs'
30
35
 
36
+ export interface TanStackStartVitePluginCoreOptions {
37
+ framework: CompileStartFrameworkOptions
38
+ }
31
39
  // this needs to live outside of the TanStackStartVitePluginCore since it will be invoked multiple times by vite
32
40
  let ssrBundle: Rollup.OutputBundle
33
41
 
34
42
  export function TanStackStartVitePluginCore(
35
- framework: CompileStartFrameworkOptions,
36
- opts: TanStackStartOutputConfig,
43
+ opts: TanStackStartVitePluginCoreOptions,
44
+ startConfig: TanStackStartOutputConfig,
37
45
  ): Array<PluginOption> {
38
46
  return [
47
+ tanstackRouter({
48
+ verboseFileRoutes: false,
49
+ ...startConfig.tsr,
50
+ target: opts.framework,
51
+ enableRouteGeneration: true,
52
+ autoCodeSplitting: true,
53
+ }),
39
54
  {
40
55
  name: 'tanstack-start-core:config-client',
41
56
  async config() {
42
57
  const nitroOutputPublicDir = await (async () => {
43
58
  // Create a dummy nitro app to get the resolved public output path
44
59
  const dummyNitroApp = await createNitro({
45
- preset: opts.target,
60
+ preset: startConfig.target,
46
61
  compatibilityDate: '2024-12-01',
47
62
  })
48
63
 
@@ -52,25 +67,41 @@ export function TanStackStartVitePluginCore(
52
67
  return nitroOutputPublicDir
53
68
  })()
54
69
 
70
+ const getClientEntryPath = (startConfig: TanStackStartOutputConfig) => {
71
+ // when the user specifies a custom client entry path, we need to resolve it
72
+ // relative to the root of the project, keeping in mind that if not specified
73
+ // it will be /~start/default-client-entry which is a virtual path
74
+ // that is resolved by vite to the actual client entry path
75
+ const entry = startConfig.clientEntryPath.startsWith(
76
+ '/~start/default-client-entry',
77
+ )
78
+ ? startConfig.clientEntryPath
79
+ : vite.normalizePath(
80
+ path.resolve(startConfig.root, startConfig.clientEntryPath),
81
+ )
82
+
83
+ return entry
84
+ }
85
+
55
86
  return {
56
87
  environments: {
57
- client: {
88
+ [VITE_ENVIRONMENT_NAMES.client]: {
58
89
  consumer: 'client',
59
90
  build: {
60
91
  manifest: true,
61
92
  rollupOptions: {
62
93
  input: {
63
- main: opts.clientEntryPath,
94
+ main: getClientEntryPath(startConfig),
64
95
  },
65
96
  output: {
66
- dir: path.resolve(opts.root, clientDistDir),
97
+ dir: path.resolve(startConfig.root, clientDistDir),
67
98
  },
68
99
  // TODO this should be removed
69
100
  external: ['node:fs', 'node:path', 'node:os', 'node:crypto'],
70
101
  },
71
102
  },
72
103
  },
73
- server: {
104
+ [VITE_ENVIRONMENT_NAMES.server]: {
74
105
  consumer: 'server',
75
106
  build: {
76
107
  ssr: true,
@@ -115,22 +146,57 @@ export function TanStackStartVitePluginCore(
115
146
  'nitropack',
116
147
  '@tanstack/**',
117
148
  ],
118
- external: ['undici'],
119
149
  },
120
150
  /* prettier-ignore */
121
151
  define: {
122
- ...injectDefineEnv('TSS_PUBLIC_BASE', opts.public.base),
123
- ...injectDefineEnv('TSS_CLIENT_BASE', opts.client.base),
124
- ...injectDefineEnv('TSS_CLIENT_ENTRY', opts.clientEntryPath),
125
- ...injectDefineEnv('TSS_SERVER_FN_BASE', opts.serverFns.base),
152
+ ...injectDefineEnv('TSS_PUBLIC_BASE', startConfig.public.base),
153
+ ...injectDefineEnv('TSS_CLIENT_BASE', startConfig.client.base),
154
+ ...injectDefineEnv('TSS_CLIENT_ENTRY', getClientEntryPath(startConfig)), // This is consumed by the router-manifest, where the entry point is imported after the dev refresh runtime is resolved
155
+ ...injectDefineEnv('TSS_SERVER_FN_BASE', startConfig.serverFns.base),
126
156
  ...injectDefineEnv('TSS_OUTPUT_PUBLIC_DIR', nitroOutputPublicDir),
127
157
  },
128
158
  }
129
159
  },
130
160
  },
131
- TanStackStartCompilerPlugin(framework),
132
- startManifestPlugin(opts),
133
- nitroPlugin(opts, () => ssrBundle),
161
+ // N.B. TanStackStartCompilerPlugin must be before the TanStackServerFnPluginEnv
162
+ TanStackStartCompilerPlugin(opts.framework, {
163
+ client: { envName: VITE_ENVIRONMENT_NAMES.client },
164
+ server: { envName: VITE_ENVIRONMENT_NAMES.server },
165
+ }),
166
+ TanStackServerFnPluginEnv({
167
+ // This is the ID that will be available to look up and import
168
+ // our server function manifest and resolve its module
169
+ manifestVirtualImportId: 'tanstack:server-fn-manifest',
170
+ client: {
171
+ getRuntimeCode: () =>
172
+ `import { createClientRpc } from '@tanstack/${opts.framework}-start/server-functions-client'`,
173
+ replacer: (d) =>
174
+ `createClientRpc('${d.functionId}', '${startConfig.serverFns.base}')`,
175
+ envName: VITE_ENVIRONMENT_NAMES.client,
176
+ },
177
+ server: {
178
+ getRuntimeCode: () =>
179
+ `import { createServerRpc } from '@tanstack/${opts.framework}-start/server-functions-server'`,
180
+ replacer: (d) =>
181
+ `createServerRpc('${d.functionId}', '${startConfig.serverFns.base}', ${d.fn})`,
182
+ envName: VITE_ENVIRONMENT_NAMES.server,
183
+ },
184
+ importer: (fn) => {
185
+ const serverEnv = (globalThis as any).viteDevServer.environments[
186
+ VITE_ENVIRONMENT_NAMES.server
187
+ ]
188
+ if (!serverEnv) {
189
+ throw new Error(`'ssr' vite dev environment not found`)
190
+ }
191
+ return serverEnv.runner.import(fn.extractedFilename)
192
+ },
193
+ }),
194
+ startManifestPlugin(startConfig),
195
+ nitroPlugin(startConfig, () => ssrBundle),
196
+ TanStackStartServerRoutesVite({
197
+ ...startConfig.tsr,
198
+ target: opts.framework,
199
+ }),
134
200
  ]
135
201
  }
136
202
 
package/src/prerender.ts CHANGED
@@ -6,6 +6,7 @@ import { build as buildNitro, createNitro } from 'nitropack'
6
6
  import { joinURL, withBase, withoutBase } from 'ufo'
7
7
  import { Queue } from './queue'
8
8
  import { buildNitroEnvironment } from './nitro/build-nitro'
9
+ import { VITE_ENVIRONMENT_NAMES } from './constants'
9
10
  import type { ViteBuilder } from 'vite'
10
11
  import type { $Fetch, Nitro } from 'nitropack'
11
12
  import type { TanStackStartOutputConfig } from './plugin'
@@ -20,7 +21,7 @@ export async function prerender({
20
21
  nitro: Nitro
21
22
  builder: ViteBuilder
22
23
  }) {
23
- nitro.logger.info('Prendering pages...')
24
+ console.info('Prendering pages...')
24
25
 
25
26
  // If prerender is enabled but no pages are provided, default to prerendering the root page
26
27
  if (options.prerender?.enabled && !options.pages.length) {
@@ -31,10 +32,12 @@ export async function prerender({
31
32
  ]
32
33
  }
33
34
 
34
- const serverEnv = builder.environments['server']
35
+ const serverEnv = builder.environments[VITE_ENVIRONMENT_NAMES.server]
35
36
 
36
37
  if (!serverEnv) {
37
- throw new Error(`Vite's "server" environment not found`)
38
+ throw new Error(
39
+ `Vite's "${VITE_ENVIRONMENT_NAMES.server}" environment not found`,
40
+ )
38
41
  }
39
42
 
40
43
  const prerenderOutputDir = path.resolve(
@@ -90,14 +93,14 @@ export async function prerender({
90
93
  // Crawl all pages
91
94
  const pages = await prerenderPages()
92
95
 
93
- nitro.logger.info(`Prerendered ${pages.length} pages:`)
96
+ console.info(`Prerendered ${pages.length} pages:`)
94
97
  pages.forEach((page) => {
95
- nitro.logger.info(`- ${page}`)
98
+ console.info(`- ${page}`)
96
99
  })
97
100
 
98
101
  // TODO: Write the prerendered pages to the output directory
99
102
  } catch (error) {
100
- nitro.logger.error(error)
103
+ console.error(error)
101
104
  } finally {
102
105
  // Ensure server is always closed
103
106
  // server.process.kill()
@@ -123,7 +126,7 @@ export async function prerender({
123
126
  const seen = new Set<string>()
124
127
  const retriesByPath = new Map<string, number>()
125
128
  const concurrency = options.prerender?.concurrency ?? os.cpus().length
126
- nitro.logger.info(`Concurrency: ${concurrency}`)
129
+ console.info(`Concurrency: ${concurrency}`)
127
130
  const queue = new Queue({ concurrency })
128
131
 
129
132
  options.pages.forEach((_page) => {
@@ -165,7 +168,7 @@ export async function prerender({
165
168
 
166
169
  // Add the task
167
170
  queue.add(async () => {
168
- nitro.logger.info(`Crawling: ${page.path}`)
171
+ console.info(`Crawling: ${page.path}`)
169
172
  const retries = retriesByPath.get(page.path) || 0
170
173
  try {
171
174
  // Fetch the route
@@ -179,23 +182,29 @@ export async function prerender({
179
182
  )
180
183
 
181
184
  if (!res.ok) {
182
- throw new Error(`Failed to fetch ${page.path}: ${res.statusText}`)
185
+ throw new Error(`Failed to fetch ${page.path}: ${res.statusText}`, {
186
+ cause: res,
187
+ })
183
188
  }
184
189
 
190
+ const cleanPagePath = (
191
+ prerenderOptions.outputPath || page.path
192
+ ).split(/[?#]/)[0]!
193
+
185
194
  // Guess route type and populate fileName
186
195
  const contentType = res.headers.get('content-type') || ''
187
196
  const isImplicitHTML =
188
- !page.path.endsWith('.html') && contentType.includes('html')
197
+ !cleanPagePath.endsWith('.html') && contentType.includes('html')
189
198
  // &&
190
199
  // !JsonSigRx.test(dataBuff.subarray(0, 32).toString('utf8'))
191
- const routeWithIndex = page.path.endsWith('/')
192
- ? page.path + 'index'
193
- : page.path
200
+ const routeWithIndex = cleanPagePath.endsWith('/')
201
+ ? cleanPagePath + 'index'
202
+ : cleanPagePath
194
203
 
195
204
  const htmlPath =
196
- page.path.endsWith('/') || prerenderOptions.autoSubfolderIndex
197
- ? joinURL(page.path, 'index.html')
198
- : page.path + '.html'
205
+ cleanPagePath.endsWith('/') || prerenderOptions.autoSubfolderIndex
206
+ ? joinURL(cleanPagePath, 'index.html')
207
+ : cleanPagePath + '.html'
199
208
 
200
209
  const filename = withoutBase(
201
210
  isImplicitHTML ? htmlPath : routeWithIndex,
@@ -227,9 +236,7 @@ export async function prerender({
227
236
  }
228
237
  } catch (error) {
229
238
  if (retries < (prerenderOptions.retryCount ?? 0)) {
230
- nitro.logger.warn(
231
- `Encountered error, retrying: ${page.path} in 500ms`,
232
- )
239
+ console.warn(`Encountered error, retrying: ${page.path} in 500ms`)
233
240
  await new Promise((resolve) =>
234
241
  setTimeout(resolve, prerenderOptions.retryDelay),
235
242
  )
package/src/schema.ts CHANGED
@@ -48,6 +48,14 @@ export function createTanStackConfig<
48
48
  return path.join(srcDirectory, 'server.tsx')
49
49
  }
50
50
 
51
+ if (existsSync(path.join(srcDirectory, 'server.ts'))) {
52
+ return path.join(srcDirectory, 'server.ts')
53
+ }
54
+
55
+ if (existsSync(path.join(srcDirectory, 'server.js'))) {
56
+ return path.join(srcDirectory, 'server.js')
57
+ }
58
+
51
59
  return '/~start/default-server-entry'
52
60
  })()
53
61
 
@@ -92,7 +100,7 @@ export function createTanStackStartOptionsSchema(
92
100
  .default({}),
93
101
  serverFns: z
94
102
  .object({
95
- base: z.string().optional().default('/_server'),
103
+ base: z.string().optional().default('/_serverFn'),
96
104
  })
97
105
  .optional()
98
106
  .default({}),
@@ -121,6 +129,7 @@ export function createTanStackStartOptionsSchema(
121
129
  })
122
130
  .and(pagePrerenderOptionsSchema.optional())
123
131
  .optional(),
132
+ spa: spaSchema.optional(),
124
133
  })
125
134
  .optional()
126
135
  .default({})
@@ -170,6 +179,7 @@ const pageBaseSchema = z.object({
170
179
 
171
180
  const pagePrerenderOptionsSchema = z.object({
172
181
  enabled: z.boolean().optional(),
182
+ outputPath: z.string().optional(),
173
183
  autoSubfolderIndex: z.boolean().optional(),
174
184
  crawlLinks: z.boolean().optional(),
175
185
  retryCount: z.number().optional(),
@@ -186,6 +196,21 @@ const pagePrerenderOptionsSchema = z.object({
186
196
  .optional(),
187
197
  })
188
198
 
199
+ const spaSchema = z.object({
200
+ enabled: z.boolean().optional().default(true),
201
+ maskPath: z.string().optional().default('/'),
202
+ prerender: pagePrerenderOptionsSchema
203
+ .optional()
204
+ .default({})
205
+ .transform((opts) => ({
206
+ outputPath: opts.outputPath ?? '/_shell',
207
+ crawlLinks: false,
208
+ retryCount: 0,
209
+ ...opts,
210
+ enabled: true,
211
+ })),
212
+ })
213
+
189
214
  export const pageSchema = pageBaseSchema.extend({
190
215
  prerender: pagePrerenderOptionsSchema.optional(),
191
216
  })
@@ -1,54 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const fs = require("node:fs");
4
- const path = require("node:path");
5
- const xmlbuilder2 = require("xmlbuilder2");
6
- async function buildSitemap({
7
- host,
8
- routes,
9
- outputDir
10
- }) {
11
- const routeList = await optionHasRoutes(routes);
12
- if (routeList.length) {
13
- const slash = checkSlash(host);
14
- const sitemapData = routeList.map((page) => ({
15
- page: `${host}${slash}${page.replace(/^\/+/g, "")}`,
16
- lastMod: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
17
- }));
18
- const sitemap = createXml("urlset");
19
- for (const item of sitemapData) {
20
- const page = sitemap.ele("url");
21
- page.ele("loc").txt(item.page);
22
- page.ele("lastmod").txt(item.lastMod);
23
- }
24
- const mapPath = `${path.resolve(outputDir)}/sitemap.xml`;
25
- try {
26
- console.log(`Writing sitemap at ${mapPath}`);
27
- fs.writeFileSync(mapPath, sitemap.end({ prettyPrint: true }));
28
- } catch (e) {
29
- console.error(`Unable to write file at ${mapPath}`, e);
30
- }
31
- }
32
- }
33
- function createXml(elementName) {
34
- return xmlbuilder2.create({ version: "1.0", encoding: "UTF-8" }).ele(elementName, {
35
- xmlns: "https://www.sitemaps.org/schemas/sitemap/0.9"
36
- }).com(`This file was automatically generated by Analog.`);
37
- }
38
- function checkSlash(host) {
39
- const finalChar = host.slice(-1);
40
- return finalChar === "/" ? "" : "/";
41
- }
42
- async function optionHasRoutes(routes) {
43
- let routeList;
44
- if (typeof routes === "function") {
45
- routeList = await routes();
46
- } else if (Array.isArray(routes)) {
47
- routeList = routes;
48
- } else {
49
- routeList = [];
50
- }
51
- return routeList.filter(Boolean);
52
- }
53
- exports.buildSitemap = buildSitemap;
54
- //# sourceMappingURL=build-sitemap.cjs.map