Skip to content

ui-primitive-shape

A folder primitive must ship its proof siblings; a flat primitive must carry none at the ui root.

Runs under: @noctcore/harness lint-meta, not ESLint · Factory: createUiPrimitiveShapeRule from @noctcore/lint-meta-rules · Category: source-text · Fails CI by default: yes

The primitives root is the one place exempt from folder-per-component: flat single files are fine for presentational primitives. But once a primitive graduates to its own folder, it is a real component and must carry the same proof-of-behavior siblings (a test, a story) as any feature component. This closes the “the ui folder mixes two shapes with no rule” gap: flat = pure presentational; folder = tested + storied.

  • Folder primitives — for each <uiRoot>/<Name>/<barrelFile>, each configured role must exist as <Name>.<role><extension>; a missing one is flagged.
  • Flat primitives — a flat <uiRoot>/<Name><extension> (capitalized) that already has a sibling <Name>.<role><extension> at the ui root is flagged: those proof files belong inside a <Name>/ folder.
  • A bare flat primitive (<uiRoot>/Button.tsx) with no proof sibling at the ui root.
  • A folder primitive that ships every configured role (Dialog/Dialog.test.tsx, Dialog/Dialog.stories.tsx).
  • A folder without the barrelFile, and flat files whose name does not start with a capital letter.
  • Anything outside uiRoot.
createUiPrimitiveShapeRule(options?: UiPrimitiveShapeOptions): IMetaRule
Option Type Default Meaning
uiRoot string 'apps/web/src/components/ui' The primitives root.
barrelFile string 'index.ts' Barrel file that marks a folder-primitive.
roles string[] ['test', 'stories'] Proof-of-behavior sibling roles a folder-primitive must ship.
extension string '.tsx' Extension of primitive and proof files.
ciCritical boolean true Whether a violation fails CI.

If you do not maintain a flat-vs-folder primitive convention or do not colocate stories/tests, skip the rule.