Files
skills/docs/devtools/tooling/electrobun/src/config/config.md
2026-05-29 08:20:56 +01:00

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_MAPPING constant so consumers can introspect the flag set (used by the --help text 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.