86 lines
3.2 KiB
Markdown
86 lines
3.2 KiB
Markdown
# 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`.
|