import http from 'node:http' import https from 'node:https' import tailwindcss from '@tailwindcss/vite' import { tanstackStart } from '@tanstack/react-start/plugin/vite' import viteReact from '@vitejs/plugin-react' import { defineConfig, type Plugin } from 'vite' import tsconfigPaths from 'vite-tsconfig-paths' import { nitro } from 'nitro/vite' const flattenPlugins = (plugins: Array): Plugin[] => plugins.flat(Infinity).filter((plugin): plugin is Plugin => Boolean(plugin)) type BuildWarning = { code?: string; id?: string; message: string } function warnBuild(warning: BuildWarning, warn: (warning: BuildWarning) => void) { const dependency = warning.id?.includes('/node_modules/') ?? false const knownDependencyNoise = ( (dependency && warning.code === 'MODULE_LEVEL_DIRECTIVE' && warning.message.includes('use client')) || (warning.code === 'UNUSED_EXTERNAL_IMPORT' && warning.message.includes('@tanstack/')) ) if (!knownDependencyNoise) warn(warning) } const rollupWarningOptions = { onwarn: warnBuild } function creatorHubBuildWarningPlugin(): Plugin { return { name: 'creatorhub:build-warning-filter', config() { return { environments: { ssr: { build: { rollupOptions: rollupWarningOptions } }, }, } }, } } function creatorHubAssetFetchDest(): Plugin { return { name: 'creatorhub:asset-fetch-dest', enforce: 'pre', configureServer(server) { server.middlewares.use((request, _response, next) => { // Chromium can omit Fetch Metadata for LAN requests; Nitro otherwise treats these assets as document routes. if (request.headers['sec-fetch-dest']) return next() const pathname = request.url?.split(/[?#]/, 1)[0] ?? '' if (/\.css$/.test(pathname)) request.headers['sec-fetch-dest'] = 'style' else if (/\.(?:woff2?|eot|ttf|otf)$/.test(pathname)) request.headers['sec-fetch-dest'] = 'font' else if (/\.(?:[cm]?[jt]sx?|mjs)$/.test(pathname)) request.headers['sec-fetch-dest'] = 'script' next() }) } } } function creatorHubApiProxy(): Plugin { const target = process.env.CREATORHUB_CONTROL_PLANE_URL ?? 'http://127.0.0.1:8082' return { name: 'creatorhub:control-plane-proxy', configureServer(server) { server.middlewares.use((request, response, next) => { if (!request.url?.startsWith('/api/')) return next() const upstreamURL = new URL(request.url, target) const transport = upstreamURL.protocol === 'https:' ? https : http const headers = { ...request.headers, host: upstreamURL.host } const upstream = transport.request(upstreamURL, { method: request.method, headers }, (upstreamResponse) => { response.statusCode = upstreamResponse.statusCode ?? 502 for (const [name, value] of Object.entries(upstreamResponse.headers)) { if (value !== undefined) response.setHeader(name, value) } upstreamResponse.pipe(response) }) upstream.on('error', (error) => { server.config.logger.error(`[creatorhub:control-plane-proxy] ${request.method ?? 'GET'} ${request.url}: ${error.message}`) if (!response.headersSent) { response.statusCode = 502 response.setHeader('content-type', 'application/json') response.end(JSON.stringify({ error: '控制面代理不可用' })) } else { response.destroy(error) } }) request.pipe(upstream) }) }, } } export default defineConfig({ server: { host: true, port: 5100, strictPort: true, proxy: { '/api': 'http://127.0.0.1:8082' } }, build: { rollupOptions: rollupWarningOptions }, plugins: flattenPlugins([ creatorHubAssetFetchDest(), creatorHubApiProxy(), tsconfigPaths(), tailwindcss(), tanstackStart(), creatorHubBuildWarningPlugin(), nitro({ preset: process.env.SERVER_PRESET, rollupConfig: rollupWarningOptions }), viteReact() ]) })