73 lines
3.6 KiB
JavaScript
73 lines
3.6 KiB
JavaScript
/**
|
|
* Writes the backend's seed fixture from the frontend's seed module.
|
|
*
|
|
* node scripts/seed-fixture.mjs # check: exits non-zero if stale
|
|
* node scripts/seed-fixture.mjs --write # regenerate
|
|
*
|
|
* `src/api/seed.js` is the authored source: it has the helpers, the reasoning in
|
|
* comments, and the values a person edits. `seed/fixtures/seed.json` is what the
|
|
* Go seeder loads. They used to be maintained in parallel with nothing checking
|
|
* them, which is a duplication that only fails quietly — the demo and the API
|
|
* would answer the same question differently, and the first symptom would be a
|
|
* number on a page disagreeing with a number from an agent.
|
|
*
|
|
* `ShiftRecord` is deliberately not carried across. Its dates are anchored to
|
|
* the day the seeder runs rather than to this file's fixed calendar, so the Go
|
|
* side generates it (internal/seeder/shifts.go, a port of attendanceSeed.js).
|
|
* A frozen snapshot of it would read as permanently empty a fortnight later.
|
|
*/
|
|
import { createServer } from 'vite';
|
|
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { pathToFileURL } from 'node:url';
|
|
|
|
export const FIXTURE_PATH = join(process.cwd(), '..', 'krow-backend', 'seed', 'fixtures', 'seed.json');
|
|
|
|
/** The fixture the frontend seed implies. Pure — takes the loaded module. */
|
|
export const GENERATED_NOTE =
|
|
'Generated from krow-demo/src/api/seed.js — do not edit by hand. ' +
|
|
'Regenerate with: npm run seed:fixture';
|
|
|
|
export function fixtureFrom(seedModule) {
|
|
const { ShiftRecord, ...entities } = seedModule.seedData;
|
|
/* First key, so it is the first thing anyone opening the file reads. The Go
|
|
loader unmarshals into a struct of demoUser + entities and ignores the
|
|
rest, so this costs nothing on the reading side. */
|
|
return { _generated: GENERATED_NOTE, demoUser: seedModule.DEMO_USER, entities };
|
|
}
|
|
|
|
/** Serialised exactly as the committed file is: 2-space indent, no trailing newline. */
|
|
export const serialise = (fixture) => JSON.stringify(fixture, null, 2);
|
|
|
|
export async function buildFixture(server) {
|
|
return serialise(fixtureFrom(await server.ssrLoadModule('/src/api/seed.js')));
|
|
}
|
|
|
|
/* ── CLI ──────────────────────────────────────────────────────────────────── */
|
|
/* pathToFileURL, not string concatenation: this repo lives under a path with a
|
|
space in it, and import.meta.url percent-encodes it while `file://${path}`
|
|
does not — the guard silently never fires and the script does nothing. */
|
|
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
const server = await createServer({
|
|
root: process.cwd(), server: { middlewareMode: true }, appType: 'custom', logLevel: 'error',
|
|
});
|
|
const built = await buildFixture(server);
|
|
await server.close();
|
|
|
|
if (process.argv.includes('--write')) {
|
|
writeFileSync(FIXTURE_PATH, built);
|
|
const { entities } = JSON.parse(built);
|
|
const n = Object.values(entities).reduce((s, v) => s + v.length, 0);
|
|
console.log(`seed.json written — ${Object.keys(entities).length} collections, ${n} records.`);
|
|
} else if (!existsSync(FIXTURE_PATH)) {
|
|
console.error('seed.json is missing. Run: node scripts/seed-fixture.mjs --write');
|
|
process.exit(1);
|
|
} else if (readFileSync(FIXTURE_PATH, 'utf8') !== built) {
|
|
console.error('seed.json is stale — it no longer matches src/api/seed.js.');
|
|
console.error('Run: node scripts/seed-fixture.mjs --write');
|
|
process.exit(1);
|
|
} else {
|
|
console.log('seed.json is in step with src/api/seed.js.');
|
|
}
|
|
}
|