feat(bgm): add vite plugin and build integration test

Move the bgm vite plugin into the package so it can be tested in
isolation from the web app. Serve each package as a JSON module,
serializing its maps, and verify resolution through a real vite build
against a self-contained fixture.
This commit is contained in:
2026-08-09 19:07:14 +08:00
parent c0967ab71f
commit ba16f17d8a
11 changed files with 357 additions and 18 deletions
+64
View File
@@ -0,0 +1,64 @@
/**
* Vite plugin: resolve `bgm/<package>` imports to the package's JSON.
*
* The loader reads a games root (markdown code blocks + real
* yaml/json/toml/csv files), collects packages, and this plugin serves each
* package as a module whose default export is the assembled JSON. Editing a
* game definition hot-reloads the app via `addWatchFile`.
*/
import * as path from 'node:path';
import type { Plugin } from 'vite';
import { collectPackages, loadDefs } from './collect.js';
import type { Package } from './types.js';
const VIRTUAL_PREFIX = '\0bgm:';
export interface BgmOptions {
/** Absolute path to the games root (e.g. `<repo>/games`). */
root: string;
}
export function bgm(options: BgmOptions): Plugin {
const root = options.root;
const collect = (): Package[] => {
const defMap = loadDefs('', root);
return collectPackages(defMap, root);
};
return {
name: 'bgm',
buildStart() {
// Watch every source file so edits trigger a reload/re-collect.
const defMap = loadDefs('', root);
for (const name of defMap.files.keys()) {
this.addWatchFile(path.join(root, name));
}
},
resolveId(id) {
if (id.startsWith('bgm/')) return VIRTUAL_PREFIX + id;
},
load(id) {
if (!id.startsWith(VIRTUAL_PREFIX)) return;
const name = id.slice(VIRTUAL_PREFIX.length + 'bgm/'.length);
const pkg = collect().find((p) => p.meta.id === name);
if (!pkg) {
throw new Error(`bgm package "${name}" not found`);
}
return `export default ${JSON.stringify(toJson(pkg))}`;
},
};
}
/**
* Serialize a `Package` for JSON emission. The parts/surfaces/setups are
* `Map`s, which `JSON.stringify` would otherwise turn into `{}`.
*/
function toJson(pkg: Package): Record<string, unknown> {
return {
meta: pkg.meta,
parts: Object.fromEntries(pkg.parts),
surfaces: Object.fromEntries(pkg.surfaces),
setups: Object.fromEntries(pkg.setups),
};
}