Files
ServerKit/frontend/vite.config.js
T
Juan DenisandClaude Opus 4.8 c7d81db64e build(deps-dev): upgrade vite 5→8 and @vitejs/plugin-react 4→6
Applies dependabot #68 (resolves the high-severity dev-server vite
advisories) and fixes the release-build break it caused.

Vite 8 bundles with rolldown, which only accepts the FUNCTION form of
`build.rollupOptions.output.manualChunks`. The existing object form
threw "manualChunks is not a function" and failed the Build release
tarball CI job. Convert the vendor grouping to the equivalent
node_modules path-matching function; unmatched modules fall through to
default chunking so shared deps (e.g. d3) still de-duplicate.

Verified: `npm run build` green, `npm audit` 0 vulnerabilities, lint 0
errors, dev server boots on vite 8.1.4, runtime-extension importmap +
vendor shims still injected into dist/index.html.

Note: the lazy TramoEditor chunk grows because rolldown groups the
non-tree-shakeable `import * as simpleIcons from 'simple-icons'` set
(via @tramo/editor) into it. Same bytes as before, still lazy-loaded on
/automations — a chunk-grouping change, not new bloat.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 16:57:11 -04:00

141 lines
6.4 KiB
JavaScript

/* global process */
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
import { fileURLToPath } from 'url'
// Runtime-extension import map (plan 25 Decision 2). A runtime-loaded extension
// bundle externalizes exactly these bare specifiers and resolves them at load
// time to the panel's own singleton instances via the `/serverkit-vendor/*.mjs`
// shims (fed by src/plugins/runtime/vendorShare.js). Injected into index.html at
// BUILD time only: Vite's dev server does its own bare-specifier resolution, and
// runtime loading is a production concern (dev uses the build-time glob path).
// The map is inert for the host's own code (bundled to hashed relative chunks,
// never bare specifiers) — it only affects the extension blobs.
const VENDOR_IMPORTMAP = {
imports: {
'react': '/serverkit-vendor/react.mjs',
'react-dom': '/serverkit-vendor/react-dom.mjs',
'react-dom/client': '/serverkit-vendor/react-dom-client.mjs',
'react/jsx-runtime': '/serverkit-vendor/react-jsx-runtime.mjs',
'react-router-dom': '/serverkit-vendor/react-router-dom.mjs',
'serverkit-sdk': '/serverkit-vendor/serverkit-sdk.mjs',
},
}
function serverkitImportmap() {
return {
name: 'serverkit-vendor-importmap',
apply: 'build',
transformIndexHtml() {
return [{
tag: 'script',
attrs: { type: 'importmap' },
children: JSON.stringify(VENDOR_IMPORTMAP),
injectTo: 'head-prepend',
}]
},
}
}
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '')
const frontendPort = Number(env.SERVERKIT_FRONTEND_PORT) || 41921
const apiTarget = (env.VITE_API_URL || 'http://localhost:47927/api/v1').replace(/\/api\/v1\/?$/, '')
const rawAllowedHosts = (env.VITE_ALLOWED_HOSTS || '').trim()
let allowedHosts
if (rawAllowedHosts === 'all') {
allowedHosts = true
} else if (rawAllowedHosts) {
allowedHosts = rawAllowedHosts.split(',').map(s => s.trim()).filter(Boolean)
}
return {
plugins: [react(), serverkitImportmap()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
// Stable import path for plugin code:
// import { api, useAuth } from 'serverkit-sdk';
// Internal restructures of src/plugins/sdk are invisible
// to plugins as long as the named exports stay stable.
'serverkit-sdk': fileURLToPath(new URL('./src/plugins/sdk/index.js', import.meta.url)),
},
// Force every `react` / `react-dom` / `react-router-dom`
// import (host code AND plugin code loaded via
// import.meta.glob) to resolve to one copy. Without this
// Vite can hand plugins a different React instance if dep
// optimization runs mid-session, producing the
// "Invalid hook call / ReactCurrentDispatcher is null"
// crash inside the contributions hook.
//
// Do NOT add resolve.alias entries for these — aliasing to
// the package directory bypasses Vite's optimizeDeps and
// serves raw ESM, which (combined with pre-bundled
// react-dom) recreates the same two-copies problem from the
// other direction.
dedupe: ['react', 'react-dom', 'react-router-dom'],
},
optimizeDeps: {
// Pre-bundle the renderer at server start instead of
// discovering it lazily. Lazy discovery is what triggers the
// mid-session re-bundle that strands the open browser tab
// on a stale React URL.
include: ['react', 'react-dom', 'react-router-dom'],
},
server: {
port: frontendPort,
...(allowedHosts !== undefined ? { allowedHosts } : {}),
proxy: {
'/api': {
target: apiTarget,
changeOrigin: true,
},
'/socket.io': {
target: apiTarget,
changeOrigin: true,
ws: true,
},
},
// Enable polling for WSL (Windows filesystem doesn't support inotify)
watch: {
usePolling: true,
interval: 1000,
},
},
css: {
preprocessorOptions: {
scss: {
// Silence Dart Sass deprecation warnings for @import and slash-div
// These are expected during migration from LESS and will be addressed
// when moving to @use/@forward module system
silenceDeprecations: ['import', 'slash-div', 'legacy-js-api', 'global-builtin', 'color-functions', 'strict-unary'],
},
},
},
build: {
sourcemap: false,
rollupOptions: {
output: {
// Vite 8 bundles with rolldown, which only accepts the
// FUNCTION form of manualChunks (the object form that
// Rollup allowed throws "manualChunks is not a function").
// Match on node_modules path so each listed package — plus
// its private deps — lands in one cohesive vendor chunk.
// Anything unmatched falls through to Vite's default
// chunking, so shared deps (e.g. d3 used by both charts and
// flow) get their own common chunk instead of duplicating.
manualChunks(id) {
if (!id.includes('node_modules')) return
if (/[\\/]node_modules[\\/](react|react-dom|react-router|react-router-dom|@remix-run[\\/]router|scheduler)[\\/]/.test(id)) return 'vendor-react'
if (/[\\/]node_modules[\\/]recharts[\\/]/.test(id)) return 'vendor-charts'
if (/[\\/]node_modules[\\/]@xyflow[\\/]/.test(id)) return 'vendor-flow'
if (/[\\/]node_modules[\\/]@xterm[\\/]/.test(id)) return 'vendor-xterm'
if (/[\\/]node_modules[\\/]lucide-react[\\/]/.test(id)) return 'vendor-icons'
},
},
},
},
}
})