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.
|
// Re-export the package data so the test can assert the bundled output.
|
||||||
export const parts = [...harbor.parts.keys()];
|
export const parts = [...harbor.parts.keys()];
|
||||||
export const surfaces = [...harbor.surfaces.keys()];
|
export const surfaces = [...harbor.surfaces.keys()];
|
||||||
export const setups = [...harbor.setups.keys()];
|
export const setups = [...harbor.setups.keys()];
|
||||||
export const title = harbor.meta.title;
|
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,
|
outDir,
|
||||||
write: true,
|
write: true,
|
||||||
emptyOutDir: true,
|
emptyOutDir: true,
|
||||||
|
// Keep identifiers readable so the test can assert on them.
|
||||||
|
minify: false,
|
||||||
},
|
},
|
||||||
plugins: [bgm({ root: gamesRoot })],
|
plugins: [bgm({ root: gamesRoot })],
|
||||||
});
|
});
|
||||||
@@ -38,6 +40,10 @@ describe('bgm vite plugin (integration)', () => {
|
|||||||
expect(code).toContain('board#harbor');
|
expect(code).toContain('board#harbor');
|
||||||
expect(code).toContain('game#main');
|
expect(code).toContain('game#main');
|
||||||
expect(code).toContain('Harbor');
|
expect(code).toContain('Harbor');
|
||||||
|
|
||||||
|
// The `bgm` module lists every discovered package.
|
||||||
|
expect(code).toContain('allIds');
|
||||||
|
expect(code).toContain('"harbor"');
|
||||||
} finally {
|
} finally {
|
||||||
await fs.promises.rm(outDir, { recursive: true, force: true });
|
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
|
* The loader reads a games root (markdown code blocks + real
|
||||||
* yaml/json/toml/csv files), collects packages, and this plugin serves each
|
* yaml/json/toml/csv files), collects packages, and this plugin serves them
|
||||||
* package as a module whose default export is the assembled JSON. Editing a
|
* as modules:
|
||||||
* game definition hot-reloads the app via `addWatchFile`.
|
*
|
||||||
|
* - `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 * as path from 'node:path';
|
||||||
import type { Plugin } from 'vite';
|
import type { Plugin } from 'vite';
|
||||||
@@ -12,6 +21,10 @@ import { collectPackages, loadDefs } from './collect.js';
|
|||||||
import type { Package } from './types.js';
|
import type { Package } from './types.js';
|
||||||
|
|
||||||
const VIRTUAL_PREFIX = '\0bgm:';
|
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 {
|
export interface BgmOptions {
|
||||||
/** Absolute path to the games root (e.g. `<repo>/games`). */
|
/** Absolute path to the games root (e.g. `<repo>/games`). */
|
||||||
@@ -36,11 +49,17 @@ export function bgm(options: BgmOptions): Plugin {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
resolveId(id) {
|
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) {
|
load(id) {
|
||||||
if (!id.startsWith(VIRTUAL_PREFIX)) return;
|
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);
|
const pkg = collect().find((p) => p.meta.id === name);
|
||||||
if (!pkg) {
|
if (!pkg) {
|
||||||
throw new Error(`bgm package "${name}" not found`);
|
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', () => {
|
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 });
|
const plugin = bgm({ root: gamesRoot });
|
||||||
expect(resolveId(plugin, 'bgm/harbor')).toBe('\0bgm:bgm/harbor');
|
expect(resolveId(plugin, 'virtual:bgm/packages')).toBe('\0bgm:virtual:bgm/packages');
|
||||||
expect(resolveId(plugin, 'bgm/nope')).toBe('\0bgm:bgm/nope');
|
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();
|
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', () => {
|
it('loads a package as a JSON module', () => {
|
||||||
const plugin = bgm({ root: gamesRoot });
|
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(code).toBeDefined();
|
||||||
expect(String(code).startsWith('export default ')).toBe(true);
|
expect(String(code).startsWith('export default ')).toBe(true);
|
||||||
const pkg = JSON.parse(String(code).slice('export default '.length));
|
const pkg = JSON.parse(String(code).slice('export default '.length));
|
||||||
@@ -44,6 +56,6 @@ describe('bgm vite plugin', () => {
|
|||||||
|
|
||||||
it('errors on an unknown package', () => {
|
it('errors on an unknown package', () => {
|
||||||
const plugin = bgm({ root: gamesRoot });
|
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