Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 36 additions & 16 deletions packages/server-functions-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
TanStackDirectiveFunctionsPlugin,
TanStackDirectiveFunctionsPluginEnv,
} from '@tanstack/directive-functions-plugin'
import type { Plugin } from 'vite'
import type { Plugin, ViteDevServer } from 'vite'
import type {
DirectiveFn,
ReplacerFn,
Expand All @@ -14,11 +14,6 @@ export type CreateRpcFn = (
splitImportFn?: string,
) => any

declare global {
// eslint-disable-next-line no-var
var TSR_directiveFnsById: Record<string, DirectiveFn>
}

export type ServerFnPluginOpts = {
/**
* The virtual import ID that will be used to import the server function manifest.
Expand All @@ -41,16 +36,19 @@ export function createTanStackServerFnPlugin(opts: ServerFnPluginOpts): {
ssr: Array<Plugin>
server: Array<Plugin>
} {
const ROOT = process.cwd()

globalThis.TSR_directiveFnsById = {}
const directiveFnsById: Record<string, DirectiveFn> = {}
let viteDevServer: ViteDevServer | undefined

const onDirectiveFnsById = (d: Record<string, DirectiveFn>) => {
// When directives are compiled, save them to our global variable
// This variable will be used both during development to incrementally
// look up server functions and during build/production to produce a
// static manifest that can be read by the server build
Object.assign(globalThis.TSR_directiveFnsById, d)
Object.assign(directiveFnsById, d)
invalidateVirtualModule(
viteDevServer,
resolveViteId(opts.manifestVirtualImportId),
)
}

const directive = 'use server'
Expand Down Expand Up @@ -87,6 +85,9 @@ export function createTanStackServerFnPlugin(opts: ServerFnPluginOpts): {
// by its ID, import it, and call it.
name: 'tanstack-start-server-fn-vite-plugin-manifest-server',
enforce: 'pre',
configureServer(server) {
viteDevServer = server
},
resolveId(id) {
if (id === opts.manifestVirtualImportId) {
return resolveViteId(id)
Expand All @@ -100,7 +101,7 @@ export function createTanStackServerFnPlugin(opts: ServerFnPluginOpts): {
}

const manifestWithImports = `
export default {${Object.entries(globalThis.TSR_directiveFnsById)
export default {${Object.entries(directiveFnsById)
.map(
([id, fn]: any) =>
`'${id}': {
Expand Down Expand Up @@ -163,16 +164,20 @@ export function TanStackServerFnPluginEnv(
},
}

const root = process.cwd()

globalThis.TSR_directiveFnsById = {}
const directiveFnsById: Record<string, DirectiveFn> = {}
let viteDevServer: ViteDevServer | undefined

const onDirectiveFnsById = (d: Record<string, DirectiveFn>) => {
// When directives are compiled, save them to our global variable
// This variable will be used both during development to incrementally
// look up server functions and during build/production to produce a
// static manifest that can be read by the server build
Object.assign(globalThis.TSR_directiveFnsById, d)
Object.assign(directiveFnsById, d)

invalidateVirtualModule(
viteDevServer,
resolveViteId(opts.manifestVirtualImportId),
)
}

const directive = 'use server'
Expand Down Expand Up @@ -207,6 +212,9 @@ export function TanStackServerFnPluginEnv(
// so the manifest is like a serialized state from the client build to the server build
name: 'tanstack-start-server-fn-vite-plugin-manifest-server',
enforce: 'pre',
configureServer(server) {
viteDevServer = server
},
applyToEnvironment(environment) {
return environment.name === opts.server.envName
},
Expand All @@ -223,7 +231,7 @@ export function TanStackServerFnPluginEnv(
}

const manifestWithImports = `
export default {${Object.entries(globalThis.TSR_directiveFnsById)
export default {${Object.entries(directiveFnsById)
.map(
([id, fn]: any) =>
`'${id}': {
Expand All @@ -242,3 +250,15 @@ export function TanStackServerFnPluginEnv(
function resolveViteId(id: string) {
return `\0${id}`
}

function invalidateVirtualModule(
viteDevServer: ViteDevServer | undefined,
resolvedId: string,
) {
if (viteDevServer) {
const mod = viteDevServer.moduleGraph.getModuleById(resolvedId)
if (mod) {
viteDevServer.moduleGraph.invalidateModule(mod)
}
}
}
23 changes: 11 additions & 12 deletions packages/start-plugin-core/src/dev-server-plugin/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { VITE_ENVIRONMENT_NAMES } from '../constants'
import { extractHtmlScripts } from './extract-html-scripts'
import type { Connect, DevEnvironment, Plugin, ViteDevServer } from 'vite'

/* eslint-disable no-var */
declare global {
// eslint-disable-next-line no-var
var TSS_INJECTED_HEAD_SCRIPTS: string | undefined
var TSS_VITE_DEV_SERVER: ViteDevServer | undefined
}

export function devServerPlugin(): Plugin {
Expand All @@ -24,13 +25,13 @@ export function devServerPlugin(): Plugin {
return
}

;(globalThis as any).viteDevServer = viteDevServer

globalThis.TSS_VITE_DEV_SERVER = viteDevServer
// upon server restart, reset the injected scripts
globalThis.TSS_INJECTED_HEAD_SCRIPTS = undefined
return () => {
remove_html_middlewares(viteDevServer.middlewares)
let cachedScripts: string | undefined

viteDevServer.middlewares.use(async (req, res) => {
viteDevServer.middlewares.use(async (req, res, next) => {
// Create an H3Event to have it passed into the server entry
// i.e: event => defineEventHandler(event)
const event = createEvent(req, res)
Expand All @@ -46,14 +47,8 @@ export function devServerPlugin(): Plugin {
)
}

if (!isRunnableDevEnvironment(serverEnv)) {
throw new Error(
`Expected server environment ${VITE_ENVIRONMENT_NAMES.server} to be a RunnableDevEnvironment. This can be caused by multiple vite versions being installed in the project.`,
)
}

// Extract the scripts that Vite plugins would inject into the initial HTML
if (cachedScripts === undefined) {
if (globalThis.TSS_INJECTED_HEAD_SCRIPTS === undefined) {
const templateHtml = `<html><head></head><body></body></html>`
const transformedHtml = await viteDevServer.transformIndexHtml(
req.url || '/',
Expand All @@ -65,6 +60,10 @@ export function devServerPlugin(): Plugin {
.join(';')
}

if (!isRunnableDevEnvironment(serverEnv)) {
return next()
}

// Import and resolve the request by running the server entry point
// i.e export default defineEventHandler((event) => { ... })
const serverEntry = await serverEnv.runner.import(
Expand Down