Renamed scripts/create-ssp.mjs -> create-frontend.mjs (+ its WP-45 doc, npm
script, and every prose/command reference) since "ssp" reads as an acronym
where "create-frontend" says what it does.
Also fixes two real bugs found while running it for real during WP-61:
scripts/ci-local.sh was missing from RENAME_CONTENT_FILES (any --name'd
clone that keeps a backend would break `npm run ci`, still hardcoding
BigRegister.slnx), and plopfile.mjs's `gen:context` insertion into
.dependency-cruiser.js anchored on the `showcase: null,` line, which
create-ssp/create-frontend has already stripped by the time gen:context
runs in the same invocation — silently leaving a freshly scaffolded
context with no CONTEXT_ALLOWED fence entry at all. Re-anchored on the
`const CONTEXT_ALLOWED = {` line instead, which never moves.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4.7 KiB
Scaffolding & code generation — how it works
How new code and boilerplate get generated in this repo. Three mechanisms, by design:
runnable plop generators for the pure-TS leaf patterns, standalone node generators
for the two committed-but-generated artifacts (gen:api, gen:snippets), and
skill-driven recipes (.claude/skills/) for the patterns too awkward to templatize.
The split is deliberate — WP-43
shipped only the two leaf generators and kept the harder patterns as skills.
The rule: generate the boilerplate, not the judgement
Anything mechanical and shape-stable gets a generator (value objects, form machines — each
ships with its co-located spec so the "pure logic must have a spec" rule holds by default).
Anything that spans a template {{ }} clash, the C# backend, or a design decision stays a
skill recipe — a generator there adds little over the prose.
Plop generators (runnable)
Config: plopfile.mjs; templates: plop-templates/*.hbs (Handlebars, prettier-ignored).
npm run gen:value-object— branded value object +parseXparser + spec ("parse, don't validate"). Prompts forcontext+ PascalCasename; emitssrc/app/{context}/domain/value-objects/{name}.tsand its.spec.ts.npm run gen:form-machine— Elm-style Model/Msg/pure-reducestate machine + spec. Prompts forcontext+name; emitssrc/app/{context}/domain/{name}.machine.ts+ spec.npm run gen— interactive menu (pick a generator).- Positional args skip the prompts:
npx plop value-object registratie KvkNummer.
⚠ Each generated
$localizeid needs an English<target>insrc/locale/messages.en.xlfbeforenpm run ci— the localize gate fails otherwise.
Typed API client — npm run gen:api
Regenerates the FE⇄BE seam from the backend's OpenAPI doc (config: nswag.json).
The script builds BigRegister.Api, emits backend/swagger.json via dotnet swagger, then
NSwag regenerates the Fetch-based client into
src/app/shared/infrastructure/api-client.ts. Both swagger.json and api-client.ts are
committed and never hand-edited — a CI drift gate fails if they're stale. Run it after
any backend contract change (see the bff-endpoint skill).
Showcase snippets — npm run gen:snippets
scripts/gen-snippets.mjs extracts // #region showcase:<name> … // #endregion blocks
from real source files into src/app/showcase/snippets.generated.ts, so the teaching
showcase shows code identical to what ships. Committed, prettier-ignored, and drift-gated in
CI (gen:snippets && git diff --exit-code).
Skill recipes (.claude/skills/)
Patterns kept as recipes rather than generators — invoke by name (/<skill>):
new-feature— the house pipeline: domain → infrastructure → application → UI last.new-context— scaffold a new bounded context (folders, alias, boundary rule, route).value-object/form-machine— the two above; each points at itsgen:*command.bff-endpoint— screen-shaped read: C# endpoint + DTO +gen:api+ adapter + store +<app-async>. Skill, not generator (spans backend + regen).mutation-command— a write op: adapter method +submit-*command returningResult.ui-component— shared atom/molecule/organism + Storybook story. Skill because the Angular template{{ }}collides with Handlebars.new-ssp— bootstrap a whole new portal from this template (keep/strip/rename/re-seed).document-feature— ship docs in the same diff (this doc came from it).test-strategy— where specs live by layer.
Bootstrapping a whole new SSP
Today the new-ssp skill is a manual fork-and-strip recipe (keep shared/ + tooling,
strip the business contexts, re-seed the backend, re-run gen:api). See the skill for the
step-by-step and its GREEN-at-every-step verify gate.
Not yet built
gen:context(WP-44) — a runnable version of thenew-contextskill. Planned, not shipped.create-frontend(WP-45) — mechanisednew-ssp. Planned, not shipped.
Don't assume either command exists — use the corresponding skill until they land.
See also
plopfile.mjs+plop-templates/— the leaf generators..claude/skills/— the recipe library.nswag.json— the typed-client generation config.scripts/gen-snippets.mjs— the showcase snippet extractor.- ARCHITECTURE.md — the NSwag client seam in context.
- WP-43 — why only leaves got generators.