# Config loader — `config.ts` _(no submodules)_ ## Purpose `loadConfig(cliArgs, deps?)` builds an `ElectrobunConfig` by layering hard-coded defaults, an optional on-disk `.suaveplan-electrobun.json`, and CLI argument overrides on top of one another. The loader sits on `@suaveplan/config`'s five-layer pipeline (defaults / file / object / env / CLI), wiring three of the five for the scaffolder's needs. The `--name`, `--id`, `--icon`, and `--version` CLI flags map to the `name`, `identifier`, `iconSource`, and `version` fields respectively. Defaults fill `version="0.0.1"`, `features=[]`, `networkingComplexity="basic"`, and `r3fModules=[]` so callers can rely on the final shape having those fields populated even when no file or flag supplies them. The `ConfigDiscoveryAdapter` seam (defined in `src/fs/fs.ts`) controls where the loader looks for the config file. Tests inject an in-memory adapter; production code uses the default `nodeConfigDiscoveryAdapter` that probes `process.cwd()` then `homedir()` via real `node:fs` calls. ## Features - Three-layer build: defaults → file → CLI. - Synchronous (no async lift required by callers). - Wraps malformed JSON as `ELECTROBUN.CONFIG_PARSE_FAILED`. - Pluggable file discovery through `ConfigDiscoveryAdapter`. - Exported `CLI_MAPPING` constant so consumers can introspect the flag set (used by the `--help` text builder). ## Usage: Basic ```ts import { loadConfig } from "@suaveplan/electrobun/config"; const cfg = loadConfig(process.argv.slice(2)); console.log(cfg.name ?? "(no name set)"); ``` ## Usage: Advanced ```ts import { loadConfig } from "@suaveplan/electrobun/config"; import type { ConfigDiscoveryAdapter } from "@suaveplan/electrobun/fs"; const inMemory: ConfigDiscoveryAdapter = { cwd: () => "/proj", homedir: () => "/home/user", exists: (p) => p === "/proj/.suaveplan-electrobun.json", readFile: () => JSON.stringify({ name: "ci-app", features: ["api"] }), }; const cfg = loadConfig(["--icon", "/icons/app.png"], { discovery: inMemory, }); // cfg.name === "ci-app"; cfg.iconSource === "/icons/app.png" ``` ## API Reference - `loadConfig(cliArgs: readonly string[] = [], deps?: { discovery?: ConfigDiscoveryAdapter }): ElectrobunConfig`. - `HARDCODED_DEFAULTS: ElectrobunConfig` — frozen defaults applied at the bottom of the layer stack. - `CLI_MAPPING: Record` — exact flag-to-field mapping used by the CLI layer. ## Implementation Notes `@suaveplan/config`'s `cliLayer` takes mapping keys without the leading `--`; the constants here mirror that contract verbatim. `coerceValue` inside the cli layer parses booleans, numbers, and JSON literals, so a caller can pass `--features '["api","i18n"]'` and have the array land on the right field without bespoke wiring. The file layer is skipped entirely when `findConfigFile` returns `undefined`. This keeps the layer stack short for the common "no config file" case and avoids a no-op fileLayer reload subscription.