mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:10:42 +00:00
7d5ad0d0ac
Hash raw public assets first, then render derived assets from the manifest, hash the rewritten content, and emit that rewritten output. This replaces the manifest.json special case with a small derived-asset registry and adds BMFont XML rewriting so <page file=...> entries point to hashed relative atlas page paths in production. Also move asset-manifest.mjs emission to the end of the Vite asset sync step and add regression tests for rewritten web manifest hashing, BMFont XML page rewrite, BMFont XML hash invalidation when page images change, nested relative BMFont page paths, and hard failure on missing derived asset references.
184 lines
5.5 KiB
TypeScript
184 lines
5.5 KiB
TypeScript
import tailwindcss from "@tailwindcss/vite";
|
|
import path from "path";
|
|
import { fileURLToPath } from "url";
|
|
import { defineConfig, loadEnv } from "vite";
|
|
import { createHtmlPlugin } from "vite-plugin-html";
|
|
import { viteStaticCopy } from "vite-plugin-static-copy";
|
|
import tsconfigPaths from "vite-tsconfig-paths";
|
|
import { type AssetManifest, buildAssetUrl } from "./src/core/AssetUrls";
|
|
import {
|
|
buildPublicAssetManifest,
|
|
copyRootPublicFiles,
|
|
createHashedPublicAssetFiles,
|
|
getResourcesDir,
|
|
writePublicAssetManifestModule,
|
|
} from "./src/server/PublicAssetManifest";
|
|
|
|
// Vite already handles these, but its good practice to define them explicitly
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
export default defineConfig(({ mode }) => {
|
|
const env = loadEnv(mode, process.cwd(), "");
|
|
const isProduction = mode === "production";
|
|
const resourcesDir = getResourcesDir(__dirname);
|
|
const assetManifest: AssetManifest = isProduction
|
|
? buildPublicAssetManifest(resourcesDir)
|
|
: {};
|
|
const htmlAssetData = {
|
|
assetManifest: JSON.stringify(assetManifest),
|
|
gameEnv: JSON.stringify(env.GAME_ENV ?? "dev"),
|
|
manifestHref: buildAssetUrl("manifest.json", assetManifest),
|
|
faviconHref: buildAssetUrl("images/Favicon.svg", assetManifest),
|
|
gameplayScreenshotUrl: buildAssetUrl(
|
|
"images/GameplayScreenshot.png",
|
|
assetManifest,
|
|
),
|
|
backgroundImageUrl: buildAssetUrl("images/background.webp", assetManifest),
|
|
desktopLogoImageUrl: buildAssetUrl("images/OpenFront.webp", assetManifest),
|
|
mobileLogoImageUrl: buildAssetUrl("images/OF.webp", assetManifest),
|
|
};
|
|
|
|
const syncHashedPublicAssets = () => ({
|
|
name: "sync-hashed-public-assets",
|
|
apply: "build" as const,
|
|
closeBundle() {
|
|
const outDir = path.join(__dirname, "static");
|
|
copyRootPublicFiles(resourcesDir, outDir);
|
|
createHashedPublicAssetFiles(resourcesDir, outDir, assetManifest);
|
|
writePublicAssetManifestModule(outDir, assetManifest);
|
|
},
|
|
});
|
|
|
|
// In dev, redirect visits to /w*/game/* to "/" so Vite serves the index.html.
|
|
const devGameHtmlBypass = (req?: {
|
|
url?: string;
|
|
method?: string;
|
|
headers?: { accept?: string | string[] };
|
|
}) => {
|
|
if (req?.method !== "GET") return undefined;
|
|
const accept = req.headers?.accept;
|
|
const acceptValue = Array.isArray(accept)
|
|
? accept.join(",")
|
|
: (accept ?? "");
|
|
if (!acceptValue.includes("text/html")) return undefined;
|
|
if (!req.url) return undefined;
|
|
if (/^\/w\d+\/game\/[^/]+/.test(req.url)) {
|
|
return "/";
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
return {
|
|
test: {
|
|
globals: true,
|
|
environment: "jsdom",
|
|
setupFiles: "./tests/setup.ts",
|
|
},
|
|
root: "./",
|
|
base: "/",
|
|
publicDir: isProduction ? false : "resources",
|
|
|
|
resolve: {
|
|
alias: {
|
|
"protobufjs/minimal": path.resolve(
|
|
__dirname,
|
|
"node_modules/protobufjs/minimal.js",
|
|
),
|
|
resources: path.resolve(__dirname, "resources"),
|
|
},
|
|
},
|
|
|
|
plugins: [
|
|
tsconfigPaths(),
|
|
...(isProduction
|
|
? []
|
|
: [
|
|
createHtmlPlugin({
|
|
minify: false,
|
|
entry: "/src/client/Main.ts",
|
|
template: "index.html",
|
|
inject: {
|
|
data: {
|
|
gitCommit: JSON.stringify("DEV"),
|
|
...htmlAssetData,
|
|
},
|
|
},
|
|
}),
|
|
]),
|
|
viteStaticCopy({
|
|
targets: [
|
|
{
|
|
src: "proprietary/*",
|
|
dest: ".",
|
|
},
|
|
],
|
|
}),
|
|
...(isProduction ? [syncHashedPublicAssets()] : []),
|
|
tailwindcss(),
|
|
],
|
|
|
|
define: {
|
|
__ASSET_MANIFEST__: JSON.stringify(assetManifest),
|
|
"process.env.WEBSOCKET_URL": JSON.stringify(
|
|
isProduction ? "" : "localhost:3000",
|
|
),
|
|
"process.env.GAME_ENV": JSON.stringify(isProduction ? "prod" : "dev"),
|
|
"process.env.STRIPE_PUBLISHABLE_KEY": JSON.stringify(
|
|
env.STRIPE_PUBLISHABLE_KEY,
|
|
),
|
|
"process.env.API_DOMAIN": JSON.stringify(env.API_DOMAIN),
|
|
// Add other process.env variables if needed, OR migrate code to import.meta.env
|
|
},
|
|
|
|
build: {
|
|
outDir: "static", // Webpack outputs to 'static', assuming we want to keep this.
|
|
emptyOutDir: true,
|
|
assetsDir: "assets", // Sub-directory for assets
|
|
rollupOptions: {
|
|
output: {
|
|
manualChunks: {
|
|
vendor: ["pixi.js", "howler", "zod", "protobufjs"],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
server: {
|
|
port: 9000,
|
|
// Automatically open the browser when the server starts
|
|
open: process.env.SKIP_BROWSER_OPEN !== "true",
|
|
proxy: {
|
|
"/lobbies": {
|
|
target: "ws://localhost:3000",
|
|
ws: true,
|
|
changeOrigin: true,
|
|
},
|
|
// Worker proxies
|
|
"/w0": {
|
|
target: "ws://localhost:3001",
|
|
ws: true,
|
|
secure: false,
|
|
changeOrigin: true,
|
|
bypass: (req) => devGameHtmlBypass(req),
|
|
rewrite: (path) => path.replace(/^\/w0/, ""),
|
|
},
|
|
"/w1": {
|
|
target: "ws://localhost:3002",
|
|
ws: true,
|
|
secure: false,
|
|
changeOrigin: true,
|
|
bypass: (req) => devGameHtmlBypass(req),
|
|
rewrite: (path) => path.replace(/^\/w1/, ""),
|
|
},
|
|
// API proxies
|
|
"/api": {
|
|
target: "http://localhost:3000",
|
|
changeOrigin: true,
|
|
secure: false,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
});
|