源码快照 @ 2026-09-02 (source-only, 剔除非源码素材)
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import os from "node:os";
|
||||
import { resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
|
||||
const portValue = Number.parseInt(process.env.PORT ?? "", 10);
|
||||
const devPort = Number.isFinite(portValue) && portValue > 0 ? portValue : 5173;
|
||||
const allowedHosts = new Set<string>();
|
||||
const envAllowedHosts = process.env.VITE_ALLOWED_HOSTS ?? "";
|
||||
|
||||
const addHost = (value?: string | null) => {
|
||||
const trimmed = value?.trim();
|
||||
if (!trimmed) return;
|
||||
allowedHosts.add(trimmed);
|
||||
};
|
||||
|
||||
envAllowedHosts.split(",").forEach(addHost);
|
||||
addHost(process.env.OPENWORK_PUBLIC_HOST ?? null);
|
||||
const hostname = os.hostname();
|
||||
addHost(hostname);
|
||||
const shortHostname = hostname.split(".")[0];
|
||||
if (shortHostname && shortHostname !== hostname) {
|
||||
addHost(shortHostname);
|
||||
}
|
||||
const appRoot = resolve(fileURLToPath(new URL(".", import.meta.url)));
|
||||
const appPackagePath = resolve(appRoot, "package.json");
|
||||
const desktopPackagePath = resolve(appRoot, "..", "desktop", "package.json");
|
||||
|
||||
function firstNonEmpty(values: Array<string | null | undefined>): string | null {
|
||||
for (const value of values) {
|
||||
const trimmed = value?.trim();
|
||||
if (trimmed) return trimmed;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function readLocalGitSha(): string | null {
|
||||
try {
|
||||
const output = execFileSync("git", ["rev-parse", "HEAD"], {
|
||||
cwd: appRoot,
|
||||
encoding: "utf8",
|
||||
stdio: "pipe",
|
||||
});
|
||||
return output.trim() || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function readPackageVersion(packagePath: string): string | null {
|
||||
if (!existsSync(packagePath)) return null;
|
||||
|
||||
const parsed = JSON.parse(readFileSync(packagePath, "utf8")) as { version?: string };
|
||||
return parsed.version?.trim() || null;
|
||||
}
|
||||
|
||||
const buildAppVersion =
|
||||
process.env.VITE_OPENWORK_APP_VERSION?.trim() ||
|
||||
readPackageVersion(desktopPackagePath) ||
|
||||
readPackageVersion(appPackagePath) ||
|
||||
"0.0.0";
|
||||
const buildSha = firstNonEmpty([
|
||||
process.env.VITE_OPENWORK_BUILD_SHA,
|
||||
process.env.OPENWORK_GIT_SHA,
|
||||
process.env.GITHUB_SHA,
|
||||
]) ?? readLocalGitSha();
|
||||
const shortBuildSha = buildSha ? buildSha.slice(0, 7) : "";
|
||||
|
||||
// Load the Tauri → Electron migration-release fragment if present. Written
|
||||
// by scripts/migration/01-cut-migration-release.mjs for the specific
|
||||
// release commit; absent otherwise so every other build has the migration
|
||||
// prompt dormant. Pre-parsed here so Vite's define/import.meta.env picks
|
||||
// up the keys without a custom plugin.
|
||||
function loadMigrationReleaseEnv(): Record<string, string> {
|
||||
const fragmentPath = resolve(appRoot, ".env.migration-release");
|
||||
if (!existsSync(fragmentPath)) return {};
|
||||
const out: Record<string, string> = {};
|
||||
const raw = readFileSync(fragmentPath, "utf8");
|
||||
for (const line of raw.split(/\r?\n/)) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed || trimmed.startsWith("#")) continue;
|
||||
const eq = trimmed.search("=");
|
||||
if (eq < 0) continue;
|
||||
const key = trimmed.slice(0, eq).trim();
|
||||
if (!key.startsWith("VITE_")) continue;
|
||||
out[key] = trimmed.slice(eq + 1).trim();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
const migrationReleaseEnv = loadMigrationReleaseEnv();
|
||||
|
||||
// Electron packaged builds load index.html via `file://`, so asset URLs
|
||||
// must be relative. Tauri serves via its own protocol so absolute paths
|
||||
// work there. Gate on an env var the electron build script sets.
|
||||
const isElectronPackagedBuild = process.env.OPENWORK_ELECTRON_BUILD === "1";
|
||||
|
||||
// Headless-web dev (scripts/dev-headless-web.ts): serve /api/den same-origin
|
||||
// from the dev server, proxied to the Den control plane, so the browser never
|
||||
// issues cross-origin Den API calls (Den does not serve CORS). The app is
|
||||
// pointed here via VITE_DEN_API_BASE_URL; sign-in still opens the real Den
|
||||
// web app. Inert unless the launcher sets the target env. No gateway marker:
|
||||
// that runtime implies a provisioned cloud instance, which local dev lacks.
|
||||
const headlessDenTarget = (process.env.OPENWORK_DEV_HEADLESS_DEN_TARGET ?? "").trim();
|
||||
|
||||
export default defineConfig({
|
||||
base: isElectronPackagedBuild ? "./" : "/",
|
||||
define: {
|
||||
...Object.fromEntries(
|
||||
Object.entries(migrationReleaseEnv).map(([k, v]) => [
|
||||
`import.meta.env.${k}`,
|
||||
JSON.stringify(v),
|
||||
]),
|
||||
),
|
||||
"import.meta.env.VITE_OPENWORK_APP_VERSION": JSON.stringify(buildAppVersion),
|
||||
"import.meta.env.VITE_OPENWORK_BUILD_SHA": JSON.stringify(shortBuildSha),
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
name: "openwork-dev-server-id",
|
||||
configureServer(server) {
|
||||
server.middlewares.use("/__openwork_dev_server_id", (_req, res) => {
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
res.end(JSON.stringify({ appRoot }));
|
||||
});
|
||||
},
|
||||
},
|
||||
tailwindcss(),
|
||||
react({
|
||||
babel: {
|
||||
plugins: [["babel-plugin-react-compiler", { compilationMode: "annotation" }]],
|
||||
},
|
||||
}),
|
||||
],
|
||||
server: {
|
||||
port: devPort,
|
||||
strictPort: true,
|
||||
...(allowedHosts.size > 0 ? { allowedHosts: Array.from(allowedHosts) } : {}),
|
||||
...(headlessDenTarget
|
||||
? {
|
||||
proxy: {
|
||||
"/api/den": { target: headlessDenTarget, changeOrigin: true },
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
build: {
|
||||
target: "esnext",
|
||||
rollupOptions: {
|
||||
input: {
|
||||
app: resolve(appRoot, "index.html"),
|
||||
overlay: resolve(appRoot, "overlay.html"),
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": resolve(appRoot, "src"),
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user