Ctrl K
ComunidadGuías…

vite.config.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import {
	cpSync,
	mkdirSync,
	readFileSync,
	rmSync,
	writeFileSync,
} from "node:fs";
import { join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { defineConfig, loadEnv, type Plugin } from "vite";
import {
	ART_PACKS,
	PACK_IDS,
	resolvePacks,
	unknownPacks,
} from "./src/protocol/castPacks.js";
import {
	CATALOG_FILE,
	catalogFromManifests,
	docAsset,
	droppedAvatarAtlases,
	SKILL_DOCS,
	SKILL_REFERENCE,
	SKILL_THUMBS,
	shippedManifests,
} from "./src/studio/catalogJson.js";
import { artUrls, moveArt, rewriteArtUrls } from "./tools/artHash.js";
import {
	type OperatorValues,
	robotsTxt,
	sitemapXml,
	substituteOperator,
} from "./tools/operatorHtml.js";

const entry = (file: string): string =>
	fileURLToPath(new URL(file, import.meta.url));

const AVATAR_MANIFEST = "assets/avatars/manifest.json";
const BACKDROP_MANIFEST = "assets/backgrounds/manifest.json";

// a copy of public/ holding only the shipped cast, handed to vite as publicDir
function shippedPublicDir(mode: string): string {
	const value = loadEnv(mode, entry("."), "").CHARACTER_PACKS;
	const unknown = unknownPacks(value);
	if (unknown.length > 0)
		throw new Error(
			`CHARACTER_PACKS names no such pack: ${unknown.join(", ")}. Use ${PACK_IDS.join(", ")}, all, or none`,
		);
	const packs = resolvePacks(value);
	const source = entry("public");
	const read = (file: string) => readFileSync(join(source, file), "utf8");
	const dropped = new Set([
		...droppedAvatarAtlases(read(AVATAR_MANIFEST), packs).map((file) =>
			resolve(source, "assets/avatars", file),
		),
		...ART_PACKS.filter((pack) => !packs.has(pack.id)).flatMap((pack) =>
			pack.backdrops.map((name) =>
				resolve(source, "assets/backgrounds", `${name}.png`),
			),
		),
	]);
	const staged = entry("node_modules/.cache/art-packs/public");
	rmSync(staged, { recursive: true, force: true });
	cpSync(source, staged, {
		recursive: true,
		filter: (file) => !dropped.has(resolve(file)),
	});
	const manifests = shippedManifests(
		read(AVATAR_MANIFEST),
		read(BACKDROP_MANIFEST),
		packs,
	);
	const moved = moveArt(
		staged,
		artUrls(manifests.avatars, manifests.backgrounds),
	);
	writeFileSync(
		join(staged, AVATAR_MANIFEST),
		rewriteArtUrls(manifests.avatars, moved),
	);
	writeFileSync(
		join(staged, BACKDROP_MANIFEST),
		rewriteArtUrls(manifests.backgrounds, moved),
	);
	writeFileSync(
		join(staged, CATALOG_FILE),
		JSON.stringify(
			catalogFromManifests(
				manifests.avatars,
				manifests.backgrounds,
				readFileSync(entry(`${SKILL_REFERENCE}/cast.md`), "utf8"),
			),
		),
	);
	for (const doc of SKILL_DOCS)
		writeFileSync(
			join(staged, docAsset(doc)),
			readFileSync(entry(`${SKILL_REFERENCE}/${doc}`), "utf8"),
		);
	for (const thumb of SKILL_THUMBS) {
		const dest = join(staged, docAsset(thumb));
		mkdirSync(join(dest, ".."), { recursive: true });
		writeFileSync(dest, readFileSync(entry(`${SKILL_REFERENCE}/${thumb}`)));
	}
	return staged;
}

// the empty prefix takes plain names, so Workers Builds vars and a local .env read the same
function operatorPages(): Plugin {
	let values: OperatorValues = {};
	return {
		name: "operator-pages",
		configResolved(config) {
			values = loadEnv(config.mode, config.root, "");
		},
		transformIndexHtml: {
			order: "pre",
			handler: (html) => substituteOperator(html, values),
		},
		generateBundle() {
			this.emitFile({
				type: "asset",
				fileName: "robots.txt",
				source: robotsTxt(values.SITE_ORIGIN),
			});
			const sitemap = sitemapXml(values.SITE_ORIGIN);
			if (sitemap)
				this.emitFile({
					type: "asset",
					fileName: "sitemap.xml",
					source: sitemap,
				});
		},
	};
}

export default defineConfig(({ mode }) => ({
	publicDir: shippedPublicDir(mode),
	plugins: [operatorPages()],
	build: {
		// art keeps /assets, so the immutable cache header covers these hashed bundles alone
		assetsDir: "build",
		rollupOptions: {
			input: {
				main: entry("index.html"),
				studio: entry("studio.html"),
				legal: entry("legal.html"),
				report: entry("report.html"),
				agentSkill: entry("agent-skill.html"),
			},
		},
	},
	// so `vite` (HMR) can borrow the worker's /api + room websockets from a `wrangler dev` on :8787
	server: {
		proxy: {
			"/api": {
				target: "http://127.0.0.1:8787",
				changeOrigin: true,
				ws: true,
			},
		},
	},
}));