first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,96 @@
# Generator — `generator.ts`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Owns the two-call surface that scaffolds an Electrobun project on disk.
`collectFiles(options, overrides?)` is pure — it returns the
`GeneratedFile[]` list for a given `ProjectOptions` without touching the
filesystem. `generate(options, deps?)` writes the file list through the
`FsAdapter` seam, snapshots the `.suaveplan-electrobun.json` config, and
optionally runs the icon pipeline when `options.iconSource` is set.
The generator is the only place that knows the canonical file order: the
nine core files come first (`electrobun.config.ts`, `package.json`,
`tsconfig.json`, the main-process Bun entry, three mainview files, the
shared types module, the README), then one file per selected feature in
the `FEATURE_ORDER` (`i18n`, `networking`, `api`, `api-schemas`,
`storage`, `auth`, `form-builder`, `r3f`). Input order does not matter
— the iterator walks `FEATURE_ORDER` and includes the feature only when
it is present in the input set.
The `overrides` argument lets tests substitute a faulty renderer to
exercise the `ELECTROBUN.TEMPLATE_RENDER_FAILED` wrapper without forcing
the production renderer table to throw. Production callers never set
overrides.
## Features
- Deterministic file ordering across runs.
- Zod-validated input via `ProjectOptionsSchema`.
- Wraps every renderer throw as `ELECTROBUN.TEMPLATE_RENDER_FAILED`.
- Optional icon pipeline gated by `options.iconSource`.
- OTel: `recordProject(featuresCount)` increment +
`recordGenerateDuration(ms, featuresCount)` observation per call.
- `CollectOverrides` seam for test-only renderer substitution.
## Usage: Basic
```ts
import { generate } from "@suaveplan/electrobun/generator";
const report = await generate({
name: "my-app",
identifier: "com.example.my-app",
description: "demo",
version: "0.1.0",
outputDir: "/tmp/my-app",
features: ["i18n", "api"],
networkingComplexity: "basic",
r3fModules: [],
});
console.log(`Wrote ${report.filesCreated.length} files in ${report.durationMs}ms`);
```
## Usage: Advanced
```ts
import { collectFiles } from "@suaveplan/electrobun/generator";
import type { FsAdapter } from "@suaveplan/electrobun/types";
import { writeFiles } from "@suaveplan/electrobun/fs";
const fs: FsAdapter = /* in-memory adapter */;
const files = collectFiles(opts);
await writeFiles(opts.outputDir, files, fs);
```
## API Reference
- `collectFiles(options: ProjectOptions, overrides?: CollectOverrides):
GeneratedFile[]`.
- `generate(options: ProjectOptions, deps?: GenerateDeps):
Promise<GenerateReport>`.
- `GenerateDeps = { fs?, renderer?, fileExists?, now?, overrides? }`.
- `CollectOverrides = { renderProjectFile?, renderFeatureFile? }` — the
test seam.
## Implementation Notes
`collectFiles` validates with Zod first, then iterates the file list.
Each renderer is wrapped in a try/catch that converts native errors into
`ElectrobunError(ELECTROBUN.TEMPLATE_RENDER_FAILED)` carrying the
faulty path (or feature name) in `details`. The wrap is essential
because a renderer failure surfaces as a string-typed crash at the call
site that consumed `collectFiles`; the typed code lets the caller branch
on "template bug" vs "Zod bug" vs "fs bug" without parsing messages.
The icon-pipeline integration is gated by a truthy
`parsed.iconSource` rather than `!= undefined`, so an empty-string
`iconSource` (a common prompt default) skips icon generation entirely.
This matches the upstream behaviour: the prompt layer collapses an
empty answer to `undefined` before passing it down, but the generator
defends against the empty-string case too in case a config file
materialises one.