4.9 KiB
Spec Delta — react-ui-i18n deferred tooling
ADDED Requirements
Requirement: Stories ship for every i18n primitive
Every primitive added by the react-ui-i18n capability SHALL ship a sibling .stories.tsx alongside its implementation. Each story file SHALL contain a default export plus a playground and at least one variants story (the chassis-wide stories convention). Stories SHALL render inside <TranslationProvider> + <RegistryProvider> + <ReactUiI18nBridge> so the rendered surface matches production-runtime composition. Story-snapshot tests SHALL run as part of the package's test suite.
The covered surfaces are: <FormattedNumber>, <FormattedCurrency>, <FormattedDate>, <FormattedList>, <FormattedRelativeTime>, <Plural>, <FormattedMessage>, <ReactUiI18nBridge>, formattedCell, useValidationMessageBridge, and <InputNumber>.
Verified by: per-feature story-snapshot tests (one *.stories.test.tsx per primitive) plus a presence check in scripts/check-i18n-stories.test.ts (or equivalent).
Scenario: every i18n primitive has a .stories.tsx sibling
- GIVEN the directory
src/i18n/<primitive>/ - WHEN the chassis
*.stories.tsxwalker runs - THEN every public primitive listed in the capability spec has a matching
<primitive>.stories.tsxfile
Requirement: bun run check:i18n-keys catches translation-key drift
The package SHALL ship scripts/check-i18n-keys.ts invoked via bun run check:i18n-keys. The script SHALL AST-walk every *.{ts,tsx} under src/components/** and src/i18n/** looking for translation-key references — t("react-ui.…"), tDynamic("react-ui.…"), <FormattedMessage i18nKey="react-ui.…" />, <Trans i18nKey="react-ui.…" /> — and SHALL diff the discovered key set against src/locales/en-GB.json. The script SHALL exit non-zero when any key is referenced in code but absent from the bundle (MISSING) or present in the bundle but referenced nowhere in code (ORPHAN). The script SHALL be wired into bunx turbo lint so CI fails on drift.
Verified by: scripts/check-i18n-keys.test.ts — fixture directory exercising the missing, orphan, and clean paths.
Scenario: missing key in code fails the gate
- GIVEN a source file referencing
t("react-ui.foo.absent")anden-GB.jsonlacking that key - WHEN
bun run check:i18n-keysruns - THEN the script exits non-zero and prints
MISSING: react-ui.foo.absentwith the source path
Scenario: orphan key in bundle fails the gate
- GIVEN
en-GB.jsoncontainingreact-ui.foo.unusedand no source reference - WHEN
bun run check:i18n-keysruns - THEN the script exits non-zero and prints
ORPHAN: react-ui.foo.unused
Scenario: clean source + bundle pair passes
- GIVEN every key in
en-GB.jsonis referenced in code AND every code-referenced key is in the bundle - WHEN
bun run check:i18n-keysruns - THEN the script exits zero with no diagnostics
Requirement: bun run check:registry-eject verifies shadcn ejection
The package SHALL ship scripts/check-registry-eject.ts invoked via bun run check:registry-eject. For every registry/r/<name>.json entry the script SHALL:
- Resolve the listed source files.
- Apply the registry transform (rewriting
@suaveplan/react-uiimports to local relative paths, preserving@suaveplan/react-i18nimports). - Run
tsc --noEmitagainst the transformed tree using a synthesized consumertsconfig.jsonthat declares@suaveplan/react-i18nand React peers viacompilerOptions.paths.
The script SHALL exit non-zero when any registry entry's transformed source fails to type-check, surfacing the underlying tsc diagnostics with the entry name as context. The script SHALL also detect missing registry entries — public components / primitives in src/components/** and src/i18n/** lacking a corresponding registry/r/<name>.json SHALL trigger a MISSING REGISTRY ENTRY diagnostic. The script SHALL be wired into bunx turbo lint.
Verified by: scripts/check-registry-eject.test.ts — fixture-driven happy-path + failure-path tests.
Scenario: ejected primitive type-checks against react-i18n
- GIVEN the registry entry
formatted-number.json - WHEN the transform is applied to its files and the result is type-checked
- THEN
tsc --noEmitexits 0 and the importimport { useFormatter } from "@suaveplan/react-i18n"resolves
Scenario: missing registry entry for a public primitive fails the gate
- GIVEN a public primitive at
src/i18n/<new>/<new>.tsxwith no correspondingregistry/r/<new>.json - WHEN
bun run check:registry-ejectruns - THEN the script exits non-zero with
MISSING REGISTRY ENTRY: src/i18n/<new>/<new>.tsx
Scenario: corrupted transform output fails the gate
- GIVEN a registry entry whose source references a symbol the transform fails to rewrite correctly
- WHEN the transformed source is type-checked
- THEN the script exits non-zero with
EJECT FAILURE: <name>and the underlying tsc diagnostics