# Feature templates — `features.ts` ## Purpose Owns every renderer that produces a per-feature file in the scaffolded output: `i18n`, `networking` (basic + full), `api`, `api-schemas`, `storage`, `auth`, `form-builder`, and `r3f`. The renderers are pure functions over `ProjectOptions`; the `r3f` renderer additionally branches on `options.r3fModules` to wire camera / physics / particles / character / pathfinding subsystems into the emitted scene. The companion `templates.ts` module covers the project-level renderers; the two together replace the upstream `src/templates/*.ts` and `src/templates/features/*.ts` blob set. `renderFeature(feature, options)` is the single dispatch entry the generator uses; individual `render*Feature` helpers are exported for tools that want to render a single feature in isolation (for example, a "show me the API skeleton" preview pane in an IDE extension). ## Features - Eight per-feature renderers — every closed `FeatureModule` value has a dedicated implementation. - `renderNetworkFeature` vs `renderNetworkFullFeature` selected by `options.networkingComplexity`. - `renderR3fFeature` composes the camera / physics / particles imports + scene fragments based on `options.r3fModules`. - `featurePath(feature)` returns the canonical output path for each feature (the generator uses this to assemble the file list). ## Usage: Basic ```ts import { renderFeature, featurePath } from "@suaveplan/electrobun/templates"; const content = renderFeature("i18n", opts); const path = featurePath("i18n"); // path === "src/features/i18n.ts" ``` ## Usage: Advanced ```ts import { featurePath, renderFeature, } from "@suaveplan/electrobun/templates"; const featureFiles = opts.features.map((f) => ({ path: featurePath(f), content: renderFeature(f, opts), })); ``` ## API Reference - `renderI18nFeature()`, `renderNetworkFeature()`, `renderNetworkFullFeature()`, `renderApiFeature()`, `renderApiSchemasFeature()`, `renderStorageFeature()`, `renderAuthFeature()`, `renderFormBuilderFeature()`, `renderR3fFeature(options)`. - `renderFeature(feature: FeatureModule, options: ProjectOptions): string` — dispatch table. - `featurePath(feature: FeatureModule): string`. ## Implementation Notes The `r3f` renderer is the only one whose output is materially shaped by `ProjectOptions` beyond the basic "the feature is enabled" toggle. The imports and scene fragments are assembled by joining arrays of strings, which makes the relative ordering deterministic and the diff-noise low when a new R3F module is added: the test for "r3f renderer emits the requested modules" pins the order so a refactor cannot silently re-shuffle imports. The `networking` renderer dispatches on `options.networkingComplexity` rather than `options.networking` because the upstream contract used `"basic"` / `"full"` to gate the full mesh layer. Keeping the same contract means downstream electrobun projects already on the `networkingComplexity` shape do not need to migrate. `featurePath` lives here rather than in the generator because the mapping is per-feature; the generator consumes it as a thin lookup. If a future feature lands with a non-default output path, the change is local to `features.ts`.