refactor(bgm): use virtual: module specifiers
Rename the plugin's virtual modules to virtual:bgm/packages and virtual:bgm/package/<id> so they read as plugin-provided modules rather than real packages. Update the unit and build integration tests.
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import harbor from 'bgm/harbor';
|
||||
import harbor from 'virtual:bgm/package/harbor';
|
||||
import packages from 'virtual:bgm/packages';
|
||||
|
||||
// Re-export the package data so the test can assert the bundled output.
|
||||
export const parts = [...harbor.parts.keys()];
|
||||
export const surfaces = [...harbor.surfaces.keys()];
|
||||
export const setups = [...harbor.setups.keys()];
|
||||
export const title = harbor.meta.title;
|
||||
export const allIds = packages.map((p) => p.meta.id);
|
||||
|
||||
console.log(title, parts, surfaces, setups);
|
||||
console.log(title, parts, surfaces, setups, allIds);
|
||||
|
||||
@@ -25,6 +25,8 @@ describe('bgm vite plugin (integration)', () => {
|
||||
outDir,
|
||||
write: true,
|
||||
emptyOutDir: true,
|
||||
// Keep identifiers readable so the test can assert on them.
|
||||
minify: false,
|
||||
},
|
||||
plugins: [bgm({ root: gamesRoot })],
|
||||
});
|
||||
@@ -38,6 +40,10 @@ describe('bgm vite plugin (integration)', () => {
|
||||
expect(code).toContain('board#harbor');
|
||||
expect(code).toContain('game#main');
|
||||
expect(code).toContain('Harbor');
|
||||
|
||||
// The `bgm` module lists every discovered package.
|
||||
expect(code).toContain('allIds');
|
||||
expect(code).toContain('"harbor"');
|
||||
} finally {
|
||||
await fs.promises.rm(outDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
/**
|
||||
* Vite plugin: resolve `bgm/<package>` imports to the package's JSON.
|
||||
* Vite plugin: resolve `virtual:bgm/packages` and `virtual:bgm/package/<id>`
|
||||
* imports to 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`.
|
||||
* yaml/json/toml/csv files), collects packages, and this plugin serves them
|
||||
* as modules:
|
||||
*
|
||||
* - `virtual:bgm/packages` — the default export is an array of every
|
||||
* discovered package.
|
||||
* - `virtual:bgm/package/<id>` — the default export is that single package's
|
||||
* assembled JSON.
|
||||
*
|
||||
* The `virtual:` prefix marks these as plugin-provided modules, so they can't
|
||||
* be mistaken for real installed packages. Editing a game definition
|
||||
* hot-reloads the app via `addWatchFile`.
|
||||
*/
|
||||
import * as path from 'node:path';
|
||||
import type { Plugin } from 'vite';
|
||||
@@ -12,6 +21,10 @@ import { collectPackages, loadDefs } from './collect.js';
|
||||
import type { Package } from './types.js';
|
||||
|
||||
const VIRTUAL_PREFIX = '\0bgm:';
|
||||
/** Public specifier for the module that lists every package. */
|
||||
const PACKAGES = 'virtual:bgm/packages';
|
||||
/** Public specifier prefix for a single package module. */
|
||||
const PACKAGE = 'virtual:bgm/package/';
|
||||
|
||||
export interface BgmOptions {
|
||||
/** Absolute path to the games root (e.g. `<repo>/games`). */
|
||||
@@ -36,11 +49,17 @@ export function bgm(options: BgmOptions): Plugin {
|
||||
}
|
||||
},
|
||||
resolveId(id) {
|
||||
if (id.startsWith('bgm/')) return VIRTUAL_PREFIX + id;
|
||||
if (id === PACKAGES) return VIRTUAL_PREFIX + PACKAGES;
|
||||
if (id.startsWith(PACKAGE)) return VIRTUAL_PREFIX + id;
|
||||
},
|
||||
load(id) {
|
||||
if (!id.startsWith(VIRTUAL_PREFIX)) return;
|
||||
const name = id.slice(VIRTUAL_PREFIX.length + 'bgm/'.length);
|
||||
const virtual = id.slice(VIRTUAL_PREFIX.length);
|
||||
if (virtual === PACKAGES) {
|
||||
const packages = collect().map(toJson);
|
||||
return `export default ${JSON.stringify(packages)}`;
|
||||
}
|
||||
const name = virtual.slice(PACKAGE.length);
|
||||
const pkg = collect().find((p) => p.meta.id === name);
|
||||
if (!pkg) {
|
||||
throw new Error(`bgm package "${name}" not found`);
|
||||
|
||||
@@ -24,16 +24,28 @@ function load(plugin: ReturnType<typeof bgm>, id: string): unknown {
|
||||
}
|
||||
|
||||
describe('bgm vite plugin', () => {
|
||||
it('resolves bgm/ imports to the virtual module', () => {
|
||||
it('resolves bgm imports to the virtual module', () => {
|
||||
const plugin = bgm({ root: gamesRoot });
|
||||
expect(resolveId(plugin, 'bgm/harbor')).toBe('\0bgm:bgm/harbor');
|
||||
expect(resolveId(plugin, 'bgm/nope')).toBe('\0bgm:bgm/nope');
|
||||
expect(resolveId(plugin, 'virtual:bgm/packages')).toBe('\0bgm:virtual:bgm/packages');
|
||||
expect(resolveId(plugin, 'virtual:bgm/package/harbor')).toBe('\0bgm:virtual:bgm/package/harbor');
|
||||
expect(resolveId(plugin, 'virtual:bgm/package/nope')).toBe('\0bgm:virtual:bgm/package/nope');
|
||||
expect(resolveId(plugin, 'other')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('loads every package as a JSON module', () => {
|
||||
const plugin = bgm({ root: gamesRoot });
|
||||
const code = load(plugin, '\0bgm:virtual:bgm/packages');
|
||||
expect(code).toBeDefined();
|
||||
expect(String(code).startsWith('export default ')).toBe(true);
|
||||
const packages = JSON.parse(String(code).slice('export default '.length));
|
||||
expect(packages).toHaveLength(1);
|
||||
expect(packages[0].meta.id).toBe('harbor');
|
||||
expect(packages[0].parts).toHaveProperty('token#wood');
|
||||
});
|
||||
|
||||
it('loads a package as a JSON module', () => {
|
||||
const plugin = bgm({ root: gamesRoot });
|
||||
const code = load(plugin, '\0bgm:bgm/harbor');
|
||||
const code = load(plugin, '\0bgm:virtual:bgm/package/harbor');
|
||||
expect(code).toBeDefined();
|
||||
expect(String(code).startsWith('export default ')).toBe(true);
|
||||
const pkg = JSON.parse(String(code).slice('export default '.length));
|
||||
@@ -44,6 +56,6 @@ describe('bgm vite plugin', () => {
|
||||
|
||||
it('errors on an unknown package', () => {
|
||||
const plugin = bgm({ root: gamesRoot });
|
||||
expect(() => load(plugin, '\0bgm:bgm/unknown')).toThrow(/not found/);
|
||||
expect(() => load(plugin, '\0bgm:virtual:bgm/package/unknown')).toThrow(/not found/);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user