From d30041b4f950659ea6a59044b6a540a032a98d23 Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Sun, 5 Jul 2026 17:27:54 +1000 Subject: [PATCH] Route tier and group taxonomy through package-docs catalog (#67) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hoists DOC_GROUPS into package-docs-catalog.ts so the docs group set lives in one place instead of being duplicated between turbo-generators and the meta.json. Adds tierBucket() so render-index and render-llms-full share one classification function instead of maintaining independent tier-to-section mappings. Adds @tier validation in resolveEntry that throws when a component export has no @tier tag — missing tags were silently defaulting to 'composed'. Changes: - package-docs-catalog.ts: export DOC_GROUPS, tierBucket(), @tier check - render-index.ts: use tierBucket (switch over 4 buckets) - render-llms-full.ts: use tierBucket, drop bucketFor - turbo-generators/config.ts: import DOC_GROUPS, remove local copy - turbo-generators/package.json: add @luke-ui/docs-tools dependency --- .../docs-tools/src/package-docs-catalog.ts | 20 ++++++++++- .../@luke-ui/docs-tools/src/render-index.ts | 20 ++++++----- .../docs-tools/src/render-llms-full.ts | 35 ++++++------------- packages/turbo-generators/config.ts | 7 ++-- packages/turbo-generators/package.json | 3 ++ pnpm-lock.yaml | 4 +++ 6 files changed, 52 insertions(+), 37 deletions(-) diff --git a/packages/@luke-ui/docs-tools/src/package-docs-catalog.ts b/packages/@luke-ui/docs-tools/src/package-docs-catalog.ts index d31e865b..8307dd61 100644 --- a/packages/@luke-ui/docs-tools/src/package-docs-catalog.ts +++ b/packages/@luke-ui/docs-tools/src/package-docs-catalog.ts @@ -50,6 +50,18 @@ export type PackageDocsCatalogEntry = | PackageDocsComponentEntry | PackageDocsBarrelEntry; +export const DOC_GROUPS = ['actions', 'feedback', 'forms', 'typography', 'visuals'] as const; +export type DocGroup = (typeof DOC_GROUPS)[number]; + +export type TierBucket = 'atom' | 'composed' | 'primitive' | 'barrel' | 'asset'; + +export function tierBucket(entry: { shape: string; tier: string }): TierBucket { + if (entry.shape === 'asset' || entry.shape === 'barrel') return entry.shape; + if (entry.tier === 'atom') return 'atom'; + if (entry.tier === 'primitive') return 'primitive'; + return 'composed'; +} + export type PackageDocsCatalogMetadata = Pick< PackageDocsCatalogEntry, 'path' | 'target' | 'slug' | 'shape' | 'pageKind' | 'tier' | 'title' | 'description' @@ -103,7 +115,13 @@ function resolveEntry(entry: DiscoveredExport): PackageDocsCatalogEntry { } const parsed = parseComponent(entry.sourcePath); - const tier = parsed.tier ?? entry.tier; + if (!parsed.tier) { + throw new Error( + `Export "${entry.path}" is missing a @tier JSDoc tag. ` + + `Add /** @tier atom */ or /** @tier composed */ to its props interface.`, + ); + } + const tier = parsed.tier; return { ...entry, description: parsed.description, diff --git a/packages/@luke-ui/docs-tools/src/render-index.ts b/packages/@luke-ui/docs-tools/src/render-index.ts index 0412db55..bf2ea324 100644 --- a/packages/@luke-ui/docs-tools/src/render-index.ts +++ b/packages/@luke-ui/docs-tools/src/render-index.ts @@ -1,4 +1,5 @@ import type { PackageDocsCatalogMetadata } from './package-docs-catalog.js'; +import { tierBucket } from './package-docs-catalog.js'; export interface IndexEntry extends PackageDocsCatalogMetadata { href?: string; @@ -29,16 +30,19 @@ export function renderIndex(input: RenderIndexInput): string { const assets: Array = []; for (const e of entries) { - if (e.shape === 'component') { - if (e.tier === 'primitive') { + switch (tierBucket(e)) { + case 'primitive': primitives.push(e); - } else { + break; + case 'barrel': + barrels.push(e); + break; + case 'asset': + assets.push(e); + break; + default: primary.push(e); - } - } else if (e.shape === 'barrel') { - barrels.push(e); - } else if (e.shape === 'asset') { - assets.push(e); + break; } } diff --git a/packages/@luke-ui/docs-tools/src/render-llms-full.ts b/packages/@luke-ui/docs-tools/src/render-llms-full.ts index 7d38dfe0..608b6390 100644 --- a/packages/@luke-ui/docs-tools/src/render-llms-full.ts +++ b/packages/@luke-ui/docs-tools/src/render-llms-full.ts @@ -1,4 +1,5 @@ import type { PackageDocsCatalogMetadata } from './package-docs-catalog.js'; +import { tierBucket } from './package-docs-catalog.js'; export interface LlmsFullEntry extends Pick { md: string; @@ -31,33 +32,19 @@ export function renderLlmsFull(entries: Array): string { * Entries that don't classify into a bucket are dropped. */ export function sortLlmsFullEntries(entries: Array): Array { - const buckets: Record> = { - atom: [], - barrel: [], - composed: [], - primitive: [], - }; + const buckets: Record> = {}; for (const entry of entries) { - const bucket = bucketFor(entry); - if (bucket) buckets[bucket].push(entry); + if (entry.shape !== 'component' && entry.shape !== 'barrel') continue; + if (entry.shape === 'component' && !entry.tier) continue; + + const bucket = tierBucket(entry); + (buckets[bucket] ??= []).push(entry); } const bySlug = (a: T, b: T) => a.slug.localeCompare(b.slug); return [ - ...buckets.atom.sort(bySlug), - ...buckets.composed.sort(bySlug), - ...buckets.barrel.sort(bySlug), - ...buckets.primitive.sort(bySlug), + ...(buckets.atom ?? []).sort(bySlug), + ...(buckets.composed ?? []).sort(bySlug), + ...(buckets.barrel ?? []).sort(bySlug), + ...(buckets.primitive ?? []).sort(bySlug), ]; } - -type LlmsFullBucket = 'atom' | 'composed' | 'barrel' | 'primitive'; - -function bucketFor(entry: LlmsFullEntry): LlmsFullBucket | undefined { - if (entry.shape === 'barrel') return 'barrel'; - if (entry.shape !== 'component') return; - - if (entry.tier === 'atom') return 'atom'; - if (entry.tier === 'primitive') return 'primitive'; - if (entry.tier === 'composed') return 'composed'; - return; -} diff --git a/packages/turbo-generators/config.ts b/packages/turbo-generators/config.ts index 58493f32..a9023ab4 100644 --- a/packages/turbo-generators/config.ts +++ b/packages/turbo-generators/config.ts @@ -1,16 +1,15 @@ +import { DOC_GROUPS } from '@luke-ui/docs-tools/package-docs-catalog'; import type { PlopTypes } from '@turbo/gen'; import * as z from 'zod'; import { applyComponentCreationPlan } from './src/apply-component-creation-plan.js'; import type { CreateComponentInput } from './src/component-creation-plan.js'; import { createComponentPlan } from './src/component-creation-plan.js'; - -const DOCS_GROUPS = ['actions', 'feedback', 'forms', 'typography', 'visuals'] as const; const COMPONENT_NAME_RE = /^[A-Za-z][A-Za-z0-9-]*$/; const COMPONENT_TIERS = ['atom', 'composed'] as const; const COMPONENT_STYLING = ['none', 'recipe'] as const; const componentAnswersSchema = z.object({ - docsGroup: z.enum(DOCS_GROUPS), + docsGroup: z.enum(DOC_GROUPS), name: z.string().min(1), styling: z.enum(COMPONENT_STYLING), tier: z.enum(COMPONENT_TIERS), @@ -44,7 +43,7 @@ export default function generator(plop: PlopTypes.NodePlopAPI): void { type: 'list', }, { - choices: [...DOCS_GROUPS], + choices: [...DOC_GROUPS], message: 'Docs group:', name: 'docsGroup', type: 'list', diff --git a/packages/turbo-generators/package.json b/packages/turbo-generators/package.json index 951b2b71..eefdbe1b 100644 --- a/packages/turbo-generators/package.json +++ b/packages/turbo-generators/package.json @@ -10,6 +10,9 @@ "generate:component": "tsx scripts/generate-component.ts", "test": "vp test run" }, + "dependencies": { + "@luke-ui/docs-tools": "workspace:*" + }, "devDependencies": { "@types/node": "catalog:", "tsx": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e70e6cc1..a7437107 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -456,6 +456,10 @@ importers: version: 4.4.3 packages/turbo-generators: + dependencies: + '@luke-ui/docs-tools': + specifier: workspace:* + version: link:../@luke-ui/docs-tools devDependencies: '@types/node': specifier: 'catalog:' -- 2.51.2