Phases 0-3 of the JS/JSX -> TS/TSX migration. No runtime behaviour changes:
every converted file emits byte-identical JavaScript, verified file by file.
Phase 0 - harness hardening, before any rename:
- scripts/ssr-resolve.mjs wraps `ssrLoadModule` so the ~170 literal module
paths in the check scripts resolve .js/.jsx/.ts/.tsx. Without it the first
rename would have silently destroyed the 1642-check suite that guards the
Owliver flow.
- eslint.config.js gains a TypeScript block. Its `files` globs listed only
{js,mjs,cjs,jsx}, so a renamed file would have dropped out of the run while
`eslint .` went on exiting 0 - the quietest failure mode available.
- MIGRATION_BASELINE.md records the measured starting point, including the
pre-existing seed-fixture failure and the already-broken standalone
owliver-baseline.mjs, so neither is later mistaken for migration damage.
Phase 1 - tsconfig.json succeeds jsconfig.json, carrying every option across at
its old value. `types` moves from [] to ["vite/client"], which fixes the eight
import.meta errors; @types/node is deliberately excluded so setTimeout stays a
number in browser code. allowJs and checkJs stay on, strict stays off.
Phase 2 - src/types/{api,entities,user}.ts. Transport envelope, error shape,
the entity-name union (the same 18 names are written down twice today, in
httpClient and base44Client, with nothing checking they agree), and the user
record. Every field transcribed from the API contract, the migrations and the
/me projection in me.go - not inferred. Entity record shapes are deliberately
absent: derivable, but nothing consumes them yet.
Phase 3 - nine leaf utilities renamed to .ts with annotations added only where
they could be established from existing usage. Entity records are typed `any`
with a comment naming what they are, rather than a guessed interface.
Verified: tsc 64 errors (65 before; one pre-existing TS2559 genuinely fixed,
none introduced), lint unchanged at 0 errors, skill-check 1641/1642 with only
the known failure, Owliver baseline section 59/59 green, production build
succeeds with the API origin inlined.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBG1wnuRfJKCstGB8Fekr8
77 lines
3.9 KiB
JavaScript
77 lines
3.9 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';
|
|
import { withSourceResolution } from './ssr-resolve.mjs';
|
|
|
|
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',
|
|
});
|
|
/* `buildFixture` loads `/src/api/seed.js`, which the TypeScript migration
|
|
will rename. The fixture it generates is unchanged either way. */
|
|
withSourceResolution(server, process.cwd());
|
|
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.');
|
|
}
|
|
}
|