/** * Component Contract Parser — converts .contract.json files to CompiledFragment. * * Maps the framework-agnostic ComponentContract format to the same * CompiledFragment type used by .fragment.tsx, so all downstream consumers * (MCP tools, governance, search) work unchanged. */ import { componentContractSchema } from './component-contract.js'; import type { ComponentContract } from './component-contract.js'; // Use the context package's CompiledFragment type via re-export from types.ts // Since core can't depend on context, we define the minimal output shape here // that matches CompiledFragment structurally. export interface CompiledContractOutput { filePath: string; meta: { name: string; description: string; category: string; tags?: string[]; status?: 'stable' | 'beta' | 'deprecated' | 'experimental'; figma?: string; figmaProps?: Record; }; usage: { when: string[]; whenNot: string[]; guidelines?: string[]; accessibility?: string[]; }; props: Record; relations?: Array<{ component: string; relationship: 'alternative' | 'parent' | 'child' | 'sibling' | 'composition' | 'complementary' | 'used-by'; note: string; }>; variants: Array<{ name: string; description: string; code?: string; args?: Record; }>; contract?: { propsSummary?: string[]; scenarioTags?: string[]; a11yRules?: string[]; bans?: Array<{ pattern: string; message: string }>; compoundChildren?: Record; canonicalUsage?: string[]; performanceBudget?: number; }; /** Framework hint from contract, used by extractor adapter selection */ framework?: string; /** AI metadata for compound component generation */ ai?: { compositionPattern?: 'compound' | 'simple' | 'controlled' | 'wrapper'; subComponents?: string[]; requiredChildren?: string[]; commonPatterns?: string[]; }; /** Top-level compact prop summaries for agent first-pass */ propsSummary?: string[]; /** Provenance tracking */ provenance?: { source: string; verified: boolean; frameworkSupport?: string; sourceHash?: string; extractedAt?: string; }; /** Source file path relative to config root */ sourcePath?: string; /** Named export from source file */ exportName?: string; } /** * Check if a file path is a component contract file. */ export function isContractFile(filePath: string): boolean { return filePath.endsWith('.contract.json'); } /** * Parse a component contract JSON file into CompiledFragment-compatible output. */ export function parseComponentContract( content: string, filePath: string, ): CompiledContractOutput { const raw = JSON.parse(content); const validated: ComponentContract = componentContractSchema.parse(raw); return { filePath, meta: { name: validated.name, description: validated.description, category: validated.category, tags: validated.tags, status: validated.status, figma: validated.figma?.nodeUrl, figmaProps: validated.figma?.propMappings as Record | undefined, }, usage: validated.usage, props: validated.props, relations: validated.relations ?? [], variants: (validated.examples ?? []).map((ex) => ({ name: ex.name, description: ex.description, code: ex.code, args: ex.args, })), contract: validated.contract ? { ...validated.contract, // Merge top-level propsSummary into contract.propsSummary if not already set propsSummary: validated.contract.propsSummary ?? validated.propsSummary, } : { propsSummary: validated.propsSummary, }, framework: validated.framework, ai: validated.ai, propsSummary: validated.propsSummary, provenance: validated.provenance, sourcePath: validated.sourcePath, exportName: validated.exportName, }; }