Files
agentic-dev/docs/guides/scaffolding-a-feature.md
Danijel Martinek 019d4866a0 feat(turbo): turbo gen feature generator (Phase 1, single-entity)
Adds `pnpm turbo gen feature` to scaffold a Lazar-conformant feature
package matching the navigation reference shape: entity + Zod schema,
single use case (`get<Entity>`), controller, mock + Payload-stub real
repository (with span + capture), DI module/container/symbols, and tRPC
router with full BAD_REQUEST/NOT_FOUND error mapping. The generated
`bind-production.ts` and `bind-dev-seed.ts` compose the post-R44
`withSpan(tracer, opts, withCapture(logger, tags, factory(deps)))`
sandwich at bind time.

Verified by generating a sample `packages/example/` feature and running
`pnpm --filter @repo/example lint typecheck test` — all three pass
(9 test files, 25 tests). Cleaned up after verification so no example
package is committed.

Phase-1 limitations (documented in `docs/guides/scaffolding-a-feature.md`
and printed by the generator on success): no Payload CMS templates, no
React Query helpers, faker-driven factories left as stubs, single
entity / single use case, and aggregator wiring (core-api/root,
apps/web-next bindAll) is left as a manual checklist.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 01:09:22 +02:00

4.4 KiB
Raw Blame History

Scaffolding a feature

turbo gen feature produces a Lazar-conformant feature package under packages/<name>/ matching the shape of the reference navigation feature.

Invoking the generator

Interactive (recommended for first runs — Plop will prompt for each value):

pnpm turbo gen feature

Non-interactive (positional bypass — order matches the prompts in turbo/generators/config.ts):

pnpm turbo gen feature --args <name> <Entity> <entities-plural>
Position Prompt Example Conventions
<name> Feature package name widgets kebab-case, becomes @repo/<name> and packages/<name>/
<Entity> Entity name Widget PascalCase singular, drives class/symbol/use-case names
<entities-plural> Entity plural slug widgets kebab-case, used for the future Payload collection slug

Example end-to-end:

pnpm turbo gen feature --args widgets Widget widgets
pnpm install                              # link the new workspace package
pnpm --filter @repo/widgets lint typecheck test

What it generates

  • Package files: package.json, tsconfig.json, vitest.config.ts, eslint.config.js, turbo.json, AGENTS.md
  • One entity (src/entities/models/<entity>.ts) with a Zod schema + unit test
  • One use case (src/application/use-cases/get-<entity>.use-case.ts) with exported input/output schemas, factory function, and tests
  • One controller (src/interface-adapters/controllers/get-<entity>.controller.ts) with the canonical safeParse → presenter shape
  • Mock + real repository (src/infrastructure/repositories/<entity>.repository{,.mock}.ts). Both wrap calls in tracer.startSpan; the real repo also calls logger.captureException on errors. The real repo body is a Phase-1 stub that returns null until you wire a Payload collection.
  • DI: symbols.ts, module.ts, container.ts, plus bind-production.ts and bind-dev-seed.ts that compose withSpan(tracer, opts, withCapture(logger, tags, factory(deps))) at bind time (post-R44 sandwich pattern)
  • tRPC integration: procedures.ts (feature-scoped error middleware) and router.ts exposing get<Entity> with full router tests including BAD_REQUEST / NOT_FOUND mapping
  • Contract suite (__contracts__/), dev seed (__seeds__/dev.ts), and empty stubs for __factories__/ and ui/

Phase-1 scope (intentionally limited)

The generator does NOT yet emit:

  • Payload CMS collection / global templates (integrations/cms/**)
  • React Query option builders (ui/query.ts)
  • Faker-driven defineFactory<Entity> factories (only stubs)
  • Multi-entity / multi-use-case (one get<Entity> only)
  • Aggregator wiring across the monorepo

Add these by hand once the entity stabilises. The generator's stub files clearly mark each TODO.

Manual aggregator wiring (printed on success)

After running the generator, hand-edit these files to mount the new feature on the app's runtime composition graph:

  1. apps/web-next/src/server/bind-production.ts — import bindProduction<Name> and bindDevSeed<Name> and call them from the bindAll() dispatcher (production branch + dev-seed branch).
  2. packages/core-api/src/root.ts — import <name>Router from @repo/<name>/api and mount it on the app router.
  3. packages/core-api/package.json — add "@repo/<name>": "workspace:*" to dependencies.
  4. apps/web-next/package.json — add "@repo/<name>": "workspace:*" to dependencies.
  5. (Later) Payload CMS — add a collection at packages/<name>/src/integrations/cms/collections/<entities-plural>.ts and register it in packages/core-cms/....
  6. Verify: pnpm --filter @repo/<name> lint typecheck test

The same checklist is printed by the generator when it finishes.

  • CLAUDE.md — Key Conventions (factory-style use cases, .toDynamicValue(), schemas-in-use-case, three binding modes per feature, span + capture sandwich)
  • packages/navigation/AGENTS.md — canonical reference shape the templates mirror
  • docs/architecture/vertical-feature-spec.md — design rationale for the layout
  • docs/decisions/adr-012-lazar-conformance.md — file naming + factory pattern
  • docs/decisions/adr-013-input-output-unification.md — schemas-in-use-case + presenter
  • docs/decisions/adr-014-instrumentation-sentry.md — span + capture wiring (R41R44)