import generated from './props.generated.json'; import type { Entry, PropDoc } from './types'; /** * The props table for one entry: what the TypeScript types say, with whatever * the entry says on top. * * `props.generated.json` is written by `scripts/gen-props.mjs` (`npm run * props:gen`, asserted fresh by `npm run verify`) and keyed by the exported * component name — which is what `Entry.name` already is. * * The merge is deliberately one-directional. Generated rows come first, in the * order the generator sorted them (required, then alphabetical); an `Entry.props` * row with the same `name` replaces one in place rather than appending a * duplicate, and a row with a name the generator never saw is appended. So a * hand-written entry is an *edit* to the docgen output, not a competing copy of * it: say only the thing the types cannot say, and the rest stays live. * * That matters most where the generator is silent by design. It only emits props * the kit itself declares, so a component whose whole API is the Radix * primitive's — Tooltip, Popover, Dialog — generates nothing, and `Entry.props` * is the only way it gets a table at all. The reasoning is in the header of * `scripts/gen-props.mjs`. */ /* A JSON import is `string`-typed at every leaf, so the shape has to be asserted once here rather than trusted. The generator and `PropDoc` are kept in step by this line failing to compile if either moves. */ const GENERATED = generated as Record; export function propsFor(entry: Entry): readonly PropDoc[] { const base = GENERATED[entry.name] ?? []; const overrides = entry.props ?? []; if (overrides.length === 0) return base; const byName = new Map(overrides.map((row) => [row.name, row])); const merged = base.map((row) => byName.get(row.name) ?? row); const seen = new Set(base.map((row) => row.name)); return [...merged, ...overrides.filter((row) => !seen.has(row.name))]; }