83 lines
3.0 KiB
Markdown
83 lines
3.0 KiB
Markdown
# Config loader — `config.ts`
|
|
|
|
<!-- begin:auto-docs -->
|
|
_(no submodules)_
|
|
<!-- end:auto-docs -->
|
|
|
|
## 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<string, string>` — 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.
|