1 | import { createTanStackServerFnPlugin } from "@tanstack/server-functions-plugin"; |
2 | import defu from "defu"; |
3 | import { existsSync } from "node:fs"; |
4 | import { join } from "node:path"; |
5 | import { fileURLToPath } from "node:url"; |
6 | import { createApp, resolve } from "vinxi"; |
7 | import { normalize } from "vinxi/lib/path"; |
8 | import { config } from "vinxi/plugins/config"; |
9 | import solid from "vite-plugin-solid"; |
10 | import { SolidStartClientFileRouter, SolidStartServerFileRouter } from "./fs-router.js"; |
11 | import { serverComponents } from "./server-components.js"; |
12 |
|
13 | const DEFAULT_EXTENSIONS = ["js", "jsx", "ts", "tsx"]; |
14 |
|
15 | function solidStartClientFsRouter(config) { |
16 | return (router, app) => |
17 | new SolidStartClientFileRouter( |
18 | { |
19 | dir: resolve.absolute(config.dir, router.root), |
20 | extensions: config.extensions ?? DEFAULT_EXTENSIONS |
21 | }, |
22 | router, |
23 | app |
24 | ); |
25 | } |
26 |
|
27 | function solidStartServerFsRouter(config) { |
28 | return (router, app) => |
29 | new SolidStartServerFileRouter( |
30 | { |
31 | dir: resolve.absolute(config.dir, router.root), |
32 | extensions: config.extensions ?? DEFAULT_EXTENSIONS, |
33 | dataOnly: config.dataOnly |
34 | }, |
35 | router, |
36 | app |
37 | ); |
38 | } |
39 |
|
40 | const SolidStartServerFnsPlugin = createTanStackServerFnPlugin({ |
41 | // This is the ID that will be available to look up and import |
42 | // our server function manifest and resolve its module |
43 | manifestVirtualImportId: "solidstart:server-fn-manifest", |
44 | client: { |
45 | getRuntimeCode: () => |
46 | `import { createServerReference } from "${normalize( |
47 | fileURLToPath(new URL("../dist/runtime/server-runtime.js", import.meta.url)) |
48 | )}"`, |
49 | replacer: opts => |
50 | `createServerReference(${() => {}}, '${opts.functionId}', '${opts.extractedFilename}')` |
51 | }, |
52 | ssr: { |
53 | getRuntimeCode: () => |
54 | `import { createServerReference } from '${normalize( |
55 | fileURLToPath(new URL("../dist/runtime/server-fns-runtime.js", import.meta.url)) |
56 | )}'`, |
57 | replacer: opts => |
58 | `createServerReference(${opts.fn}, '${opts.functionId}', '${opts.extractedFilename}')` |
59 | }, |
60 | server: { |
61 | getRuntimeCode: () => |
62 | `import { createServerReference } from '${normalize( |
63 | fileURLToPath(new URL("../dist/runtime/server-fns-runtime.js", import.meta.url)) |
64 | )}'`, |
65 | replacer: opts => |
66 | `createServerReference(${opts.fn}, '${opts.functionId}', '${opts.extractedFilename}')` |
67 | } |
68 | }); |
69 |
|
70 | export function defineConfig(baseConfig = {}) { |
71 | let { vite = {}, ...start } = baseConfig; |
72 | const extensions = [...DEFAULT_EXTENSIONS, ...(start.extensions || [])]; |
73 | start = defu(start, { |
74 | publicDir: "./public", |
75 | appRoot: "./src", |
76 | routeDir: "./routes", |
77 | ssr: true, |
78 | devOverlay: true, |
79 | experimental: { |
80 | islands: false |
81 | }, |
82 | solid: {}, |
83 | server: { |
84 | routeRules: { |
85 | "/_build/assets/**": { headers: { "cache-control": "public, immutable, max-age=31536000" } } |
86 | }, |
87 | experimental: { |
88 | asyncContext: true |
89 | } |
90 | } |
91 | }); |
92 | const routeDir = join(start.appRoot, start.routeDir); |
93 | let server = start.server; |
94 | if (!start.ssr) { |
95 | server = { ...server, prerender: { routes: ["/"] } }; |
96 | } |
97 | let entryExtension = ".tsx"; |
98 | if (existsSync(join(process.cwd(), start.appRoot, "app.jsx"))) { |
99 | entryExtension = ".jsx"; |
100 | } |
101 |
|
102 | return createApp({ |
103 | server: { |
104 | compressPublicAssets: { |
105 | brotli: process.versions.bun ? false : true |
106 | }, |
107 | ...server |
108 | }, |
109 | routers: [ |
110 | { |
111 | name: "public", |
112 | type: "static", |
113 | base: "/", |
114 | dir: start.publicDir |
115 | }, |
116 | { |
117 | name: "ssr", |
118 | type: "http", |
119 | link: { |
120 | client: start.experimental.islands ? undefined : "client" |
121 | }, |
122 | handler: `${start.appRoot}/entry-server${entryExtension}`, |
123 | middleware: start.middleware, |
124 | routes: solidStartServerFsRouter({ dir: routeDir, extensions, dataOnly: !start.ssr }), |
125 | extensions, |
126 | target: "server", |
127 | plugins: async () => { |
128 | const userConfig = |
129 | typeof vite === "function" ? await vite({ router: "server" }) : { ...vite }; |
130 | const plugins = userConfig.plugins || []; |
131 | delete userConfig.plugins; |
132 | return [ |
133 | config("user", { |
134 | ...userConfig, |
135 | optimizeDeps: { |
136 | ...(userConfig.optimizeDeps || {}), |
137 | include: [ |
138 | ...(userConfig.optimizeDeps?.include || []), |
139 | "@solidjs/start > source-map-js", |
140 | "@solidjs/start > error-stack-parser" |
141 | ] |
142 | } |
143 | }), |
144 | ...plugins, |
145 | SolidStartServerFnsPlugin.ssr, |
146 | start.experimental.islands ? serverComponents.server() : null, |
147 | solid({ ...start.solid, ssr: true, extensions: extensions.map(ext => `.${ext}`) }), |
148 | config("app-server", { |
149 | resolve: { |
150 | alias: { |
151 | "#start/app": join(process.cwd(), start.appRoot, `app${entryExtension}`), |
152 | "~": join(process.cwd(), start.appRoot), |
153 | ...(!start.ssr |
154 | ? { |
155 | "@solidjs/start/server": "@solidjs/start/server/spa" |
156 | } |
157 | : {}), |
158 | ...userConfig.resolve?.alias |
159 | } |
160 | }, |
161 | cacheDir: "node_modules/.vinxi/server", |
162 | define: { |
163 | "import.meta.env.START_APP": JSON.stringify( |
164 | `${start.appRoot}/app${entryExtension}` |
165 | ), |
166 | "import.meta.env.START_ISLANDS": JSON.stringify(start.experimental.islands), |
167 | "import.meta.env.SSR": JSON.stringify(true), |
168 | "import.meta.env.START_SSR": JSON.stringify(start.ssr), |
169 | "import.meta.env.START_DEV_OVERLAY": JSON.stringify(start.devOverlay), |
170 | ...userConfig.define |
171 | } |
172 | }) |
173 | ]; |
174 | } |
175 | }, |
176 | { |
177 | name: "client", |
178 | type: "client", |
179 | base: "/_build", |
180 | handler: `${start.appRoot}/entry-client${entryExtension}`, |
181 | ...(start.experimental.islands |
182 | ? {} |
183 | : { |
184 | routes: solidStartClientFsRouter({ dir: routeDir, extensions }) |
185 | }), |
186 | extensions, |
187 | target: "browser", |
188 | plugins: async () => { |
189 | const userConfig = |
190 | typeof vite === "function" ? await vite({ router: "client" }) : { ...vite }; |
191 | const plugins = userConfig.plugins || []; |
192 | delete userConfig.plugins; |
193 | return [ |
194 | config("user", { |
195 | ...userConfig, |
196 | optimizeDeps: { |
197 | ...(userConfig.optimizeDeps || {}), |
198 | include: [ |
199 | ...(userConfig.optimizeDeps?.include || []), |
200 | "@solidjs/start > source-map-js", |
201 | "@solidjs/start > error-stack-parser" |
202 | ] |
203 | } |
204 | }), |
205 | ...plugins, |
206 | SolidStartServerFnsPlugin.client, |
207 | start.experimental.islands ? serverComponents.client() : null, |
208 | solid({ ...start.solid, ssr: start.ssr, extensions: extensions.map(ext => `.${ext}`) }), |
209 | config("app-client", { |
210 | resolve: { |
211 | alias: { |
212 | "#start/app": join(process.cwd(), start.appRoot, `app${entryExtension}`), |
213 | "~": join(process.cwd(), start.appRoot), |
214 | ...(start.experimental.islands |
215 | ? { |
216 | "@solidjs/start/client": "@solidjs/start/client/islands" |
217 | } |
218 | : {}), |
219 | ...(!start.ssr |
220 | ? { |
221 | "@solidjs/start/client": "@solidjs/start/client/spa" |
222 | } |
223 | : {}), |
224 | ...userConfig.resolve?.alias |
225 | } |
226 | }, |
227 | cacheDir: "node_modules/.vinxi/client", |
228 | define: { |
229 | "import.meta.env.START_APP": JSON.stringify( |
230 | `${start.appRoot}/app${entryExtension}` |
231 | ), |
232 | "import.meta.env.START_ISLANDS": JSON.stringify(start.experimental.islands), |
233 | "import.meta.env.SSR": JSON.stringify(false), |
234 | "import.meta.env.START_SSR": JSON.stringify(start.ssr), |
235 | "import.meta.env.START_DEV_OVERLAY": JSON.stringify(start.devOverlay), |
236 | "import.meta.env.SERVER_BASE_URL": JSON.stringify(server?.baseURL ?? ""), |
237 | ...userConfig.define |
238 | } |
239 | }) |
240 | ]; |
241 | } |
242 | }, |
243 | { |
244 | name: "server-fns", |
245 | type: "http", |
246 | base: "/_server", |
247 | handler: normalize( |
248 | fileURLToPath(new URL("../dist/runtime/server-handler.js", import.meta.url)) |
249 | ), |
250 | middleware: start.middleware, |
251 | target: "server", |
252 | routes: solidStartServerFsRouter({ dir: routeDir, extensions, dataOnly: !start.ssr }), |
253 | plugins: async () => { |
254 | const userConfig = |
255 | typeof vite === "function" ? await vite({ router: "server-function" }) : { ...vite }; |
256 | const plugins = userConfig.plugins || []; |
257 | delete userConfig.plugins; |
258 | return [ |
259 | config("user", { |
260 | ...userConfig, |
261 | optimizeDeps: { |
262 | ...(userConfig.optimizeDeps || {}), |
263 | include: [ |
264 | ...(userConfig.optimizeDeps?.include || []), |
265 | "@solidjs/start > source-map-js", |
266 | "@solidjs/start > error-stack-parser" |
267 | ] |
268 | }, |
269 | cacheDir: "node_modules/.vinxi/server-fns" |
270 | }), |
271 | ...plugins, |
272 | SolidStartServerFnsPlugin.server, |
273 | start.experimental.islands ? serverComponents.server() : null, |
274 | solid({ ...start.solid, ssr: true, extensions: extensions.map(ext => `.${ext}`) }), |
275 | config("app-server", { |
276 | resolve: { |
277 | alias: { |
278 | "#start/app": join(process.cwd(), start.appRoot, `app${entryExtension}`), |
279 | "~": join(process.cwd(), start.appRoot), |
280 | ...(!start.ssr |
281 | ? { |
282 | "@solidjs/start/server": "@solidjs/start/server/spa" |
283 | } |
284 | : {}), |
285 | ...userConfig.resolve?.alias |
286 | } |
287 | }, |
288 | define: { |
289 | "import.meta.env.START_APP": JSON.stringify( |
290 | `${start.appRoot}/app${entryExtension}` |
291 | ), |
292 | "import.meta.env.START_ISLANDS": JSON.stringify(start.experimental.islands), |
293 | "import.meta.env.SSR": JSON.stringify(true), |
294 | "import.meta.env.START_SSR": JSON.stringify(start.ssr), |
295 | "import.meta.env.START_DEV_OVERLAY": JSON.stringify(start.devOverlay), |
296 | ...userConfig.define |
297 | } |
298 | }) |
299 | ]; |
300 | } |
301 | } |
302 | ] |
303 | }); |
304 | } |