@tanstack/server-functions-plugin
Version:
Modern and scalable routing for React applications
1 lines • 13.2 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../../src/index.ts"],"sourcesContent":["import {\n TanStackDirectiveFunctionsPlugin,\n TanStackDirectiveFunctionsPluginEnv,\n} from '@tanstack/directive-functions-plugin'\nimport type { DevEnvironment, Plugin, ViteDevServer } from 'vite'\nimport type {\n DirectiveFn,\n ReplacerFn,\n} from '@tanstack/directive-functions-plugin'\n\nexport type CreateRpcFn = (\n functionId: string,\n serverBase: string,\n splitImportFn?: string,\n) => any\n\nexport type ServerFnPluginOpts = {\n /**\n * The virtual import ID that will be used to import the server function manifest.\n * This virtual import ID will be used in the server build to import the manifest\n * and its modules.\n */\n manifestVirtualImportId: string\n client: ServerFnPluginEnvOpts\n ssr: ServerFnPluginEnvOpts\n server: ServerFnPluginEnvOpts\n}\n\nexport type ServerFnPluginEnvOpts = {\n getRuntimeCode: () => string\n replacer: ReplacerFn\n}\n\nconst debug =\n process.env.TSR_VITE_DEBUG &&\n ['true', 'server-functions-plugin'].includes(process.env.TSR_VITE_DEBUG)\n\nexport function createTanStackServerFnPlugin(opts: ServerFnPluginOpts): {\n client: Array<Plugin>\n ssr: Array<Plugin>\n server: Array<Plugin>\n} {\n const directiveFnsById: Record<string, DirectiveFn> = {}\n let viteDevServer: ViteDevServer | undefined\n\n const onDirectiveFnsById = buildOnDirectiveFnsByIdCallback({\n directiveFnsById,\n manifestVirtualImportId: opts.manifestVirtualImportId,\n invalidateModule: (id) => {\n if (viteDevServer) {\n const mod = viteDevServer.moduleGraph.getModuleById(id)\n if (mod) {\n if (debug) {\n console.info(`invalidating module ${JSON.stringify(mod.id)}`)\n }\n viteDevServer.moduleGraph.invalidateModule(mod)\n }\n }\n },\n })\n\n const directive = 'use server'\n const directiveLabel = 'Server Function'\n\n return {\n client: [\n // The client plugin is used to compile the client directives\n // and save them so we can create a manifest\n TanStackDirectiveFunctionsPlugin({\n envLabel: 'Client',\n directive,\n directiveLabel,\n getRuntimeCode: opts.client.getRuntimeCode,\n replacer: opts.client.replacer,\n onDirectiveFnsById,\n }),\n ],\n ssr: [\n // The SSR plugin is used to compile the server directives\n TanStackDirectiveFunctionsPlugin({\n envLabel: 'SSR',\n directive,\n directiveLabel,\n getRuntimeCode: opts.ssr.getRuntimeCode,\n replacer: opts.ssr.replacer,\n onDirectiveFnsById,\n }),\n ],\n server: [\n {\n // On the server, we need to be able to read the server-function manifest from the client build.\n // This is likely used in the handler for server functions, so we can find the server function\n // by its ID, import it, and call it.\n name: 'tanstack-start-server-fn-vite-plugin-manifest-server',\n enforce: 'pre',\n configureServer(server) {\n viteDevServer = server\n },\n resolveId(id) {\n if (id === opts.manifestVirtualImportId) {\n return resolveViteId(id)\n }\n\n return undefined\n },\n load(id) {\n if (id !== resolveViteId(opts.manifestVirtualImportId)) {\n return undefined\n }\n\n const manifestWithImports = `\n export default {${Object.entries(directiveFnsById)\n .map(\n ([id, fn]: any) =>\n `'${id}': {\n functionName: '${fn.functionName}',\n importer: () => import(${JSON.stringify(fn.extractedFilename)})\n }`,\n )\n .join(',')}}`\n\n return manifestWithImports\n },\n },\n // On the server, we need to compile the server functions\n // so they can be called by other server functions.\n // This is also where we split the server function into a separate file\n // so we can load them on demand in the worker.\n TanStackDirectiveFunctionsPlugin({\n envLabel: 'Server',\n directive,\n directiveLabel,\n getRuntimeCode: opts.server.getRuntimeCode,\n replacer: opts.server.replacer,\n onDirectiveFnsById,\n }),\n ],\n }\n}\n\nexport interface TanStackServerFnPluginEnvOpts {\n /**\n * The virtual import ID that will be used to import the server function manifest.\n * This virtual import ID will be used in the server build to import the manifest\n * and its modules.\n */\n manifestVirtualImportId: string\n client: {\n envName?: string\n getRuntimeCode: () => string\n replacer: ReplacerFn\n }\n server: {\n envName?: string\n getRuntimeCode: () => string\n replacer: ReplacerFn\n }\n}\n\nexport function TanStackServerFnPluginEnv(\n _opts: TanStackServerFnPluginEnvOpts,\n): Array<Plugin> {\n const opts = {\n ..._opts,\n client: {\n ..._opts.client,\n envName: _opts.client.envName || 'client',\n },\n server: {\n ..._opts.server,\n envName: _opts.server.envName || 'server',\n },\n }\n\n const directiveFnsById: Record<string, DirectiveFn> = {}\n let serverDevEnv: DevEnvironment | undefined\n\n const onDirectiveFnsById = buildOnDirectiveFnsByIdCallback({\n directiveFnsById,\n manifestVirtualImportId: opts.manifestVirtualImportId,\n invalidateModule: (id) => {\n if (serverDevEnv) {\n const mod = serverDevEnv.moduleGraph.getModuleById(id)\n if (mod) {\n if (debug) {\n console.info(\n `invalidating module ${JSON.stringify(mod.id)} in server environment`,\n )\n }\n serverDevEnv.moduleGraph.invalidateModule(mod)\n }\n }\n },\n })\n\n const directive = 'use server'\n const directiveLabel = 'Server Function'\n\n return [\n // The client plugin is used to compile the client directives\n // and save them so we can create a manifest\n TanStackDirectiveFunctionsPluginEnv({\n directive,\n directiveLabel,\n onDirectiveFnsById,\n environments: {\n client: {\n envLabel: 'Client',\n getRuntimeCode: opts.client.getRuntimeCode,\n replacer: opts.client.replacer,\n envName: opts.client.envName,\n },\n server: {\n envLabel: 'Server',\n getRuntimeCode: opts.server.getRuntimeCode,\n replacer: opts.server.replacer,\n envName: opts.server.envName,\n },\n },\n }),\n {\n // On the server, we need to be able to read the server-function manifest from the client build.\n // This is likely used in the handler for server functions, so we can find the server function\n // by its ID, import it, and call it.\n name: 'tanstack-start-server-fn-vite-plugin-manifest-server',\n enforce: 'pre',\n configureServer(viteDevServer) {\n serverDevEnv = viteDevServer.environments[opts.server.envName]\n if (!serverDevEnv) {\n throw new Error(\n `TanStackServerFnPluginEnv: environment \"${opts.server.envName}\" not found`,\n )\n }\n },\n resolveId: {\n filter: { id: new RegExp(opts.manifestVirtualImportId) },\n handler(id) {\n return resolveViteId(id)\n },\n },\n load: {\n filter: { id: new RegExp(resolveViteId(opts.manifestVirtualImportId)) },\n handler() {\n if (this.environment.name !== opts.server.envName) {\n return `export default {}`\n }\n const manifestWithImports = `\n export default {${Object.entries(directiveFnsById)\n .map(\n ([id, fn]: any) =>\n `'${id}': {\n functionName: '${fn.functionName}',\n importer: () => import(${JSON.stringify(fn.extractedFilename)})\n }`,\n )\n .join(',')}}`\n\n return manifestWithImports\n },\n },\n },\n ]\n}\n\nfunction resolveViteId(id: string) {\n return `\\0${id}`\n}\n\nfunction buildOnDirectiveFnsByIdCallback(opts: {\n invalidateModule: (resolvedId: string) => void\n directiveFnsById: Record<string, DirectiveFn>\n manifestVirtualImportId: string\n}) {\n const onDirectiveFnsById = (d: Record<string, DirectiveFn>) => {\n if (debug) {\n console.info(`onDirectiveFnsById received: `, d)\n }\n\n // do we already know all the server functions? if so, we can exit early\n // this could happen if the same file is compiled first in the client and then in the server environment\n const newKeys = Object.keys(d).filter(\n (key) => !(key in opts.directiveFnsById),\n )\n if (newKeys.length > 0) {\n // When directives are compiled, save them to `directiveFnsById`\n // This state will be used both during development to incrementally\n // look up server functions and during build/production to produce a\n // static manifest that can be read by the server build\n Object.assign(opts.directiveFnsById, d)\n if (debug) {\n console.info(`directiveFnsById after update: `, opts.directiveFnsById)\n }\n\n opts.invalidateModule(resolveViteId(opts.manifestVirtualImportId))\n }\n }\n return onDirectiveFnsById\n}\n"],"names":["TanStackDirectiveFunctionsPlugin","id","TanStackDirectiveFunctionsPluginEnv"],"mappings":"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"}