Files
atomic-design-poc/docs/reference/scaffolding.md
T
ehoandClaude Sonnet 5 920ce138cb fix(scaffolding): rename create-ssp to create-frontend, fix 2 bugs it surfaced
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>
2026-07-31 23:26:28 +02:00

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 + parseX parser + spec ("parse, don't validate"). Prompts for context + PascalCase name; emits src/app/{context}/domain/value-objects/{name}.ts and its .spec.ts.
  • npm run gen:form-machine — Elm-style Model/Msg/pure-reduce state machine + spec. Prompts for context + name; emits src/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 $localize id needs an English <target> in src/locale/messages.en.xlf before npm 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 its gen:* 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 returning Result.
  • 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 the new-context skill. Planned, not shipped.
  • create-frontend (WP-45) — mechanised new-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.