3.0 KiB
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_MAPPINGconstant so consumers can introspect the flag set (used by the--helptext builder).
Usage: Basic
import { loadConfig } from "@suaveplan/electrobun/config";
const cfg = loadConfig(process.argv.slice(2));
console.log(cfg.name ?? "(no name set)");
Usage: Advanced
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.