SP

Engineering

Design Document
Author: Krista Simmons Status: Draft Scope: shared-components only
Proposal

catalog.json — Auto-Generated Package Catalog

Goal: Zero-effort component documentation by extracting metadata from source
TL;DR: Auto-generate a catalog.json during build that contains component props, composables, and tokens. The solidprofessor-packages skill reads this for accurate documentation. Zero developer effort required.

Key Principle: Zero Developer Effort

Adding or modifying components requires no extra steps. The catalog is generated automatically from source code during the build process.

Problem

Current State

The solidprofessor-packages skill contains a manually-maintained list of components with their props. This creates two problems:

  1. Drift — Props get added/changed in code but the skill isn't updated
  2. Extra work — Devs have to remember to update the skill when adding components

We want accurate prop documentation without any manual maintenance.

Solution

Auto-Generated Catalog

1
Dev adds component
→
2
npm run build
→
3
catalog.json generated
→
4
Published with package

What Gets Generated (automatically)

What Stays in the Skills (manually maintained)

Why keep detection patterns separate?

Detection patterns rarely change (how often do we add totally new component categories?). They're easier to maintain as a simple table in the shelf skill than as JSON that devs would need to remember to update.

Schema

catalog.json Structure

{
  "name": "@solidprofessorhub/shared-components",
  "version": "1.29.3",
  "generatedAt": "2026-10-21T15:30:00Z",

  "components": {
    "BaseButton": {
      "props": {
        "variant": {
          "type": "'primary' | 'secondary' | 'ghost' | 'danger' | 'link' | 'icon'",
          "required": false,
          "default": "'primary'"
        },
        "size": {
          "type": "'default' | 'small' | 'xs'",
          "required": false,
          "default": "'default'"
        },
        "loading": {
          "type": "boolean",
          "required": false,
          "default": "false"
        },
        "icon": {
          "type": "string",
          "required": false
        },
        "to": {
          "type": "string | RouteLocationRaw",
          "required": false,
          "description": "Vue Router path - renders as router-link"
        },
        "href": {
          "type": "string",
          "required": false,
          "description": "External link - renders as anchor"
        }
      }
    },
    "TextField": {
      "props": {
        "modelValue": { "type": "string", "required": true },
        "label": { "type": "string", "required": true },
        "error": { "type": "string", "required": false },
        "type": { "type": "string", "default": "'text'" },
        "size": { "type": "'default' | 'small'" }
      }
    }
    // ... all other components auto-extracted
  },

  "composables": {
    "useModal": {
      "returns": ["isOpen", "open", "close"]
    },
    "useForm": {
      "returns": ["values", "errors", "validate", "reset"]
    },
    "useFormModal": {
      "returns": ["isOpen", "open", "close", "submit"]
    }
  },

  "utilities": ["cn", "getBaseUrl", "getUrlWithQueryParameters"],

  "icons": {
    "tokenSet": ["angle-down", "angle-up", "arrow-left", "arrow-right", "ellipsis", "magnifying-glass", "plus", "xmark", "eye", "eye-slash"]
  },

  "tokens": {
    "colors": {
      "neutral-0": "#FFFFFF",
      "neutral-5": "#F9FAFB",
      "neutral-10": "#F3F5F7",
      "neutral-50": "#606D80",
      "neutral-90": "#121A26",
      "primary-50": "#479E5B",
      "primary-60": "#2D833F",
      "primary-70": "#1E6C2E"
      // ... extracted from theme.css
    }
  }
}
Implementation

File Structure

shared-frontend/packages/shared-components/ ├── src/ │ ├── components/ │ │ ├── BaseButton.vue │ │ ├── TextField.vue │ │ └── ... │ ├── composables/ │ ├── styles/ │ │ └── theme.css │ └── index.ts ├── scripts/generate-catalog.ts # New: extraction script ├── package.json └── dist/ ├── index.js └── catalog.json # Generated output

Generation Script

// scripts/generate-catalog.ts
// Uses vue-component-meta to extract props from Vue SFCs

import { createComponentMetaChecker } from 'vue-component-meta'
import { readFileSync, writeFileSync } from 'fs'
import { globSync } from 'glob'
import { resolve } from 'path'
import pkg from '../package.json'

// 1. Set up the Vue component meta checker
const checker = createComponentMetaChecker(
  resolve(__dirname, '../tsconfig.json')
)

// 2. Find all Vue components
const componentFiles = globSync('src/components/**/*.vue', {
  cwd: resolve(__dirname, '..')
})

// 3. Extract props from each component
const components: Record<string, any> = {}

for (const file of componentFiles) {
  const fullPath = resolve(__dirname, '..', file)
  const meta = checker.getComponentMeta(fullPath)

  // Skip internal components (not exported)
  if (meta.name.startsWith('_')) continue

  components[meta.name] = {
    props: Object.fromEntries(
      meta.props
        .filter(p => !p.global) // Skip global props like class, style
        .map(p => [p.name, {
          type: p.type,
          required: p.required,
          default: p.default,
          description: p.description
        }])
    )
  }
}

// 4. Extract composables from index.ts exports
const composables = extractComposablesFromIndex()

// 5. Extract utilities
const utilities = extractUtilitiesFromIndex()

// 6. Extract icon token set from BaseIcon
const icons = extractIconTokens()

// 7. Extract design tokens from theme.css
const tokens = extractTokensFromCSS('src/styles/theme.css')

// 8. Build the catalog
const catalog = {
  name: pkg.name,
  version: pkg.version,
  generatedAt: new Date().toISOString(),
  components,
  composables,
  utilities,
  icons,
  tokens
}

// 9. Write to dist/
writeFileSync(
  resolve(__dirname, '../dist/catalog.json'),
  JSON.stringify(catalog, null, 2)
)

console.log(`✓ Generated catalog.json for ${pkg.name}@${pkg.version}`)
console.log(`  ${Object.keys(components).length} components`)
console.log(`  ${Object.keys(composables).length} composables`)
console.log(`  ${Object.keys(tokens.colors || {}).length} color tokens`)

Build Integration

// package.json
{
  "scripts": {
    "build": "vite build && npm run generate-catalog",
    "generate-catalog": "tsx scripts/generate-catalog.ts"
  },
  "files": [
    "dist"   // catalog.json included automatically
  ],
  "devDependencies": {
    "vue-component-meta": "^2.0.0",
    "tsx": "^4.0.0"
  }
}
Fully Automated

The existing release workflow runs npm run build, which now includes catalog generation. No changes to CI/CD needed.

Skill Updates

How Skills Use the Catalog

solidprofessor-packages skill

Instead of hardcoded prop lists, the skill reads from the installed package:

# In SKILL.md

## Component Documentation

For accurate, up-to-date component props, read the catalog:

```bash
cat node_modules/@solidprofessorhub/shared-components/dist/catalog.json
```

Use `components[name].props` for prop details.
Use `composables` for available composables.
Use `tokens.colors` for design token values.

shelf skill (unchanged)

Shelf keeps its detection patterns as simple tables in the SKILL.md. No changes needed.

Skill Uses catalog.json? What for
solidprofessor-packages Yes Component props, composables, tokens
shelf No Keeps detection patterns in skill (stable, rarely change)
Developer Experience

What Changes for Devs

Scenario Before After
Add new component Update skill with props Nothing — auto-extracted
Add/change props Update skill with new props Nothing — auto-extracted
Add new composable Update skill with exports Nothing — auto-extracted
Add design token Update skill with value Nothing — auto-extracted from CSS
Add icon to BaseIcon token set Update both skills Nothing — auto-extracted

The Only Manual Update

If you add a completely new component category that shelf should detect (rare), add one row to shelf's detection table. Example: if we add a Toast component and want to detect hand-rolled toasts.

Rollout

Implementation Plan

Step Task Effort
1 Add vue-component-meta and tsx to devDependencies 5 min
2 Create scripts/generate-catalog.ts 2-3 hours
3 Test locally: npm run generate-catalog 30 min
4 Add to build script in package.json 5 min
5 Publish new version (catalog.json included) Normal release
6 Update solidprofessor-packages skill to read from catalog 30 min
Benefits

Summary