Repository navigation
Expand file tree
/
Copy pathvite.config.ts
More file actions
220 lines (209 loc) · 9.43 KB
/
Copy pathvite.config.ts
File metadata and controls
220 lines (209 loc) · 9.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
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,
// Self-hosted Sentry — without `url`, the plugin defaults to sentry.io
// and silently no-ops the upload. Falls back to sentry.io if unset for
// local builds against the SaaS instance.
url: process.env.SENTRY_URL,
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,
// Compatibility floor for the Wry system WebView (#5571). Since the
// CEF→Wry migration (#5456) the desktop app renders in the OS-provided
// engine (WKWebView on macOS, WebView2 on Windows, WebKitGTK on Linux),
// so the bundle must not emit syntax newer than the oldest supported
// engine. macOS 12 (Monterey) ships WebKit ~15.x, so Safari 15 is the
// JS/CSS floor — without it Vite emits untranspiled ES2022+ that fails to
// parse before React mounts, and the window (revealed unconditionally by
// the Rust shell) is shown blank. Safari 15 is a strict subset of
// WebView2/WebKitGTK, so the Windows/Linux bundles lose nothing.
//
// The target lowers *syntax* only; it never polyfills runtime methods. The
// bundle still calls WebKit-15.4 APIs (structuredClone, Object.hasOwn,
// Array.prototype.at), so the real mount floor is WebKit 15.4 = macOS 12.3
// (bundle.macOS.minimumSystemVersion). The one WebKit-16.4 feature we use
// in-source — a RegExp lookbehind — is rewritten to a lookahead at its call
// site (features/share/shareContent.ts) since no Monterey WebKit ships it.
target: "safari15",
cssTarget: "safari15",
// 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: {
"@": resolve(__dirname, "src"),
buffer: "buffer",
process: "process/browser",
util: "util",
os: "os-browserify/browser",
},
},
optimizeDeps: {
include: ["buffer", "process", "util", "os-browserify"],
},
}));