|
1 | 1 | /// <reference types="vite/client" /> |
2 | 2 | import crypto from 'node:crypto' |
| 3 | +import assert from 'node:assert' |
3 | 4 | import { TanStackDirectiveFunctionsPluginEnv } from '@tanstack/directive-functions-plugin' |
4 | | -import type { DevEnvironment, Plugin } from 'vite' |
| 5 | +import type { Plugin } from 'vite' |
5 | 6 | import type { |
6 | 7 | DirectiveFn, |
7 | 8 | GenerateFunctionIdFn, |
@@ -39,16 +40,26 @@ const debug = |
39 | 40 | process.env.TSR_VITE_DEBUG && |
40 | 41 | ['true', 'server-functions-plugin'].includes(process.env.TSR_VITE_DEBUG) |
41 | 42 |
|
| 43 | +const validateServerFnIdVirtualModule = `virtual:tanstack-start-validate-server-fn-id` |
| 44 | + |
| 45 | +function parseIdQuery(id: string): { |
| 46 | + filename: string |
| 47 | + query: { |
| 48 | + [k: string]: string |
| 49 | + } |
| 50 | +} { |
| 51 | + if (!id.includes('?')) return { filename: id, query: {} } |
| 52 | + const [filename, rawQuery] = id.split(`?`, 2) as [string, string] |
| 53 | + const query = Object.fromEntries(new URLSearchParams(rawQuery)) |
| 54 | + return { filename, query } |
| 55 | +} |
| 56 | + |
42 | 57 | export function TanStackServerFnPlugin( |
43 | 58 | opts: TanStackServerFnPluginOpts, |
44 | 59 | ): Array<Plugin> { |
45 | 60 | const directiveFnsById: Record<string, DirectiveFn> = {} |
46 | | - let serverDevEnv: DevEnvironment | undefined |
47 | 61 |
|
48 | 62 | const onDirectiveFnsById = (d: Record<string, DirectiveFn>) => { |
49 | | - if (serverDevEnv) { |
50 | | - return |
51 | | - } |
52 | 63 | if (debug) { |
53 | 64 | console.info(`onDirectiveFnsById received: `, d) |
54 | 65 | } |
@@ -152,6 +163,26 @@ export function TanStackServerFnPlugin( |
152 | 163 | provider: opts.provider, |
153 | 164 | callers: opts.callers, |
154 | 165 | }), |
| 166 | + { |
| 167 | + name: 'tanstack-start-server-fn-vite-plugin-validate-serverfn-id', |
| 168 | + apply: 'serve', |
| 169 | + load: { |
| 170 | + filter: { |
| 171 | + id: new RegExp( |
| 172 | + `^${resolveViteId(validateServerFnIdVirtualModule)}?.+`, |
| 173 | + ), |
| 174 | + }, |
| 175 | + handler(id) { |
| 176 | + const parsed = parseIdQuery(id) |
| 177 | + assert(parsed) |
| 178 | + assert(parsed.query.id) |
| 179 | + if (directiveFnsById[parsed.query.id]) { |
| 180 | + return `export {}` |
| 181 | + } |
| 182 | + this.error(`Invalid server function ID: ${parsed.query.id}`) |
| 183 | + }, |
| 184 | + }, |
| 185 | + }, |
155 | 186 | { |
156 | 187 | // On the server, we need to be able to read the server-function manifest from the client build. |
157 | 188 | // This is likely used in the handler for server functions, so we can find the server function |
@@ -191,6 +222,8 @@ export function TanStackServerFnPlugin( |
191 | 222 | if (this.environment.mode !== 'build') { |
192 | 223 | const mod = ` |
193 | 224 | export async function getServerFnById(id) { |
| 225 | + const validateIdImport = ${JSON.stringify(validateServerFnIdVirtualModule)} + '?id=' + encodeURIComponent(id) |
| 226 | + await import(/* @vite-ignore */ '/@id/__x00__' + validateIdImport) |
194 | 227 | const decoded = Buffer.from(id, 'base64url').toString('utf8') |
195 | 228 | const devServerFn = JSON.parse(decoded) |
196 | 229 | const mod = await import(/* @vite-ignore */ devServerFn.file) |
|
0 commit comments