mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-28 13:32:23 +00:00
198 lines
8.0 KiB
TypeScript
198 lines
8.0 KiB
TypeScript
import { defineConfig, type PluginOption } from "vite";
|
|
import react from "@vitejs/plugin-react";
|
|
import { sentryVitePlugin } from "@sentry/vite-plugin";
|
|
|
|
import { readFileSync } from "node:fs";
|
|
import { dirname, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
import { nodePolyfills } from "vite-plugin-node-polyfills";
|
|
|
|
const host = process.env.TAURI_DEV_HOST;
|
|
|
|
// Optional override so parallel `dev:app:win` runs across worktrees can
|
|
// avoid the hardcoded 1420 collision. Default 1420 preserves prior behavior;
|
|
// HMR companion port is dev port + 1 (used only when TAURI_DEV_HOST is set).
|
|
const devPort = Number(process.env.OPENHUMAN_DEV_PORT) || 1420;
|
|
const hmrPort = devPort + 1;
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const pkg = JSON.parse(
|
|
readFileSync(resolve(__dirname, "package.json"), "utf8"),
|
|
) as { version: string };
|
|
|
|
// Canonical Sentry release — must stay in sync with the string produced by
|
|
// `SENTRY_RELEASE` in app/src/utils/config.ts and the core sidecar's
|
|
// `sentry::init` in src/main.rs so events from every surface group together.
|
|
function computeSentryRelease(): string {
|
|
const raw = (process.env.SENTRY_RELEASE ?? "").trim();
|
|
if (raw) return raw;
|
|
const sha = (process.env.VITE_BUILD_SHA ?? "").trim().slice(0, 12);
|
|
return sha
|
|
? `openhuman@${pkg.version}+${sha}`
|
|
: `openhuman@${pkg.version}`;
|
|
}
|
|
|
|
// Gate source-map upload on the presence of SENTRY_AUTH_TOKEN so local dev
|
|
// and CI jobs that don't ship to users skip the plugin silently. The
|
|
// companion `SENTRY_ORG` / `SENTRY_PROJECT` come from CI env.
|
|
function maybeSentryPlugin(): PluginOption | null {
|
|
const authToken = process.env.SENTRY_AUTH_TOKEN;
|
|
if (!authToken) return null;
|
|
return sentryVitePlugin({
|
|
authToken,
|
|
org: process.env.SENTRY_ORG,
|
|
project: process.env.SENTRY_PROJECT,
|
|
release: {
|
|
name: computeSentryRelease(),
|
|
// The frontend already passes this release to Sentry.init(). Keeping the
|
|
// plugin's virtual release module enabled can be transformed by the node
|
|
// polyfill injector into startup code that calls Rollup helpers before
|
|
// they are initialized in the generated desktop bundle.
|
|
inject: false,
|
|
},
|
|
sourcemaps: {
|
|
// Vite emits hashed asset files into `app/dist/assets/`. Upload every
|
|
// .js / .map the build produces.
|
|
//
|
|
// `assets` is resolved by sentry-vite-plugin against `process.cwd()`,
|
|
// not the Vite `root` — so a relative path like `../dist/**` would
|
|
// miss when `pnpm tauri build` runs with cwd=`app/` and silently emit
|
|
// `Didn't find any matching sources for debug ID upload`. Use absolute
|
|
// paths anchored at this config file's directory (`app/`) to be
|
|
// immune to whatever cwd the parent process sets.
|
|
assets: [
|
|
resolve(__dirname, "dist/**/*.js"),
|
|
resolve(__dirname, "dist/**/*.map"),
|
|
],
|
|
// Never ship raw .map files to end users; the upload keeps a copy
|
|
// server-side for symbolication while the bundled app strips them.
|
|
filesToDeleteAfterUpload: [resolve(__dirname, "dist/**/*.map")],
|
|
},
|
|
// Release tagging + commits are handled by sentry-cli / the plugin
|
|
// itself when AUTH_TOKEN and CI env (GITHUB_SHA etc.) are present.
|
|
telemetry: false,
|
|
});
|
|
}
|
|
|
|
function guardCefRelListSupportsPlugin(): PluginOption {
|
|
return {
|
|
name: "openhuman:guard-cef-rel-list-supports",
|
|
enforce: "post",
|
|
renderChunk(code) {
|
|
const unsafe =
|
|
'relList && relList.supports && relList.supports("modulepreload")';
|
|
const guarded =
|
|
'relList && typeof relList.supports === "function" && relList.supports("modulepreload")';
|
|
const next = code.split(unsafe).join(guarded);
|
|
return next === code ? null : { code: next, map: null };
|
|
},
|
|
};
|
|
}
|
|
|
|
// `VITE_OPENHUMAN_TARGET=web` switches the build to the browser-hosted
|
|
// flavor: output lands in `dist-web/` so the desktop build artifact in
|
|
// `dist/` (consumed by `cargo tauri build`) is never clobbered, and the
|
|
// `import.meta.env.VITE_OPENHUMAN_TARGET` value is exposed to runtime code
|
|
// that wants a build-time signal in addition to the runtime `isTauri()`
|
|
// check. Default (`undefined` / `desktop`) keeps the historical behavior.
|
|
const buildTarget = (process.env.VITE_OPENHUMAN_TARGET ?? "desktop").trim();
|
|
const isWebTarget = buildTarget === "web";
|
|
|
|
// https://vite.dev/config/
|
|
export default defineConfig(async () => ({
|
|
root: "src",
|
|
publicDir: "../public",
|
|
// Read env files from the repo root (not `app/src/`, which is the vite
|
|
// `root` and would be the default `envDir`). Lets `pnpm dev:app` pick up
|
|
// `VITE_BACKEND_URL` / `VITE_OPENHUMAN_APP_ENV` from the same root `.env`
|
|
// the Rust shell uses, instead of needing a separate `app/.env.local`.
|
|
// Without this, `import.meta.env.VITE_*` is empty in dev (Vite does not
|
|
// inherit `process.env` for VITE_-prefixed vars), so `BACKEND_URL` falls
|
|
// through to the production fallback in `src/utils/config.ts` even when
|
|
// the shell exports staging URLs.
|
|
envDir: resolve(__dirname, ".."),
|
|
build: {
|
|
outDir: isWebTarget ? "../dist-web" : "../dist",
|
|
emptyOutDir: true,
|
|
// Desktop CEF has surfaced a runtime where `link.relList.supports` is
|
|
// truthy but not callable. Vite calls it both in the modulepreload
|
|
// polyfill and the dynamic-import preload helper, before React mounts.
|
|
modulePreload: false,
|
|
// Emit source maps so @sentry/vite-plugin can upload them; the plugin
|
|
// deletes the on-disk .map files after upload so users don't receive
|
|
// them in the shipped bundle.
|
|
sourcemap: true,
|
|
},
|
|
plugins: [
|
|
nodePolyfills({
|
|
include: ["buffer", "process", "util", "os", "crypto", "stream"],
|
|
globals: {
|
|
Buffer: true,
|
|
process: true,
|
|
global: true,
|
|
},
|
|
}),
|
|
guardCefRelListSupportsPlugin(),
|
|
react(),
|
|
maybeSentryPlugin(),
|
|
].filter(Boolean) as PluginOption[],
|
|
|
|
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
|
//
|
|
// 1. prevent Vite from obscuring rust errors
|
|
clearScreen: false,
|
|
// 2. tauri expects a fixed port, fail if that port is not available
|
|
server: {
|
|
port: devPort,
|
|
strictPort: true,
|
|
// `false` lets Vite pick its own loopback default; on Windows that lands
|
|
// on `::1` only, leaving 127.0.0.1 unbound. The Tauri dev-server proxy
|
|
// (vendored tauri-cef, reqwest under the hood) resolves `localhost` and
|
|
// can pick either stack — when it picks 127.0.0.1 the request fails,
|
|
// which surfaces as a blank webview / white screen because the SPA
|
|
// bundle never loads. `true` maps to `server.listen('0.0.0.0')` in Vite,
|
|
// binding **every network adapter** (loopback + LAN) so whichever stack
|
|
// reqwest's DNS picks for `localhost` has a listener. Side effect: the
|
|
// dev HMR websocket and bundled sources are reachable from other
|
|
// machines on the same network — fine for `tauri dev`, but on a shared
|
|
// or corporate Wi-Fi consider overriding with `host: 'localhost'` (and
|
|
// accepting the dual-stack hazard) instead. Production builds are
|
|
// unaffected.
|
|
host: host || true,
|
|
allowedHosts: [
|
|
"frontend-runner-openhuman-git-main-vezuresxyz.vercel.app",
|
|
],
|
|
hmr: host
|
|
? {
|
|
protocol: "ws",
|
|
host,
|
|
port: hmrPort,
|
|
}
|
|
: {
|
|
// Tauri CEF loads the app from tauri.localhost; without this the
|
|
// HMR client tries ws://tauri.localhost/ and gets ERR_CONNECTION_REFUSED.
|
|
// Force the client to connect to the Vite dev server directly.
|
|
protocol: "ws",
|
|
host: "localhost",
|
|
port: devPort,
|
|
clientPort: devPort,
|
|
},
|
|
watch: {
|
|
// 3. tell Vite to ignore watching `src-tauri` directory (includes src-tauri/ai)
|
|
ignored: ["**/src-tauri/**"],
|
|
},
|
|
},
|
|
resolve: {
|
|
alias: {
|
|
buffer: "buffer",
|
|
process: "process/browser",
|
|
util: "util",
|
|
os: "os-browserify/browser",
|
|
},
|
|
},
|
|
optimizeDeps: {
|
|
include: ["buffer", "process", "util", "os-browserify"],
|
|
},
|
|
}));
|