mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-25 06:04:54 +00:00
Fixed all "Unknown component type" validation errors by: 1. Restored Card sub-components (CardHeader, CardTitle, CardDescription, CardContent, CardFooter) - Restored src/components/ui/card.tsx with all sub-components - Created JSON definitions for all 5 Card sub-components 2. Restored Tabs sub-components (TabsList, TabsTrigger, TabsContent) - Restored src/components/ui/tabs.tsx with all sub-components - Created JSON definitions for all 3 Tabs sub-components 3. Added 20 missing custom page components - Created JSON wrappers for all custom page components - AtomicLibraryShowcase, CodeEditor, ComponentTreeBuilder, etc. - All components properly reference their TypeScript implementations 4. Updated registry system - Enhanced update-registry-from-json.ts to support 'custom' source - Added components directory scanning - Registry now includes all 342 components Results: - ✅ All "Unknown component type" errors resolved - ✅ Registry updated: 322 → 342 components (+20) - ✅ 28 new JSON files created (8 UI sub-components + 20 custom components) - ⚠️ Remaining validation errors are pre-existing schema format issues in page definitions (unrelated to component conversion) Registry Statistics: - Total components: 342 - JSON compatible: 119 - By source: atoms (142), ui (65), molecules (45), icons (38), custom (20), organisms (16), wrappers (10), primitive (6) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
263 lines
7.6 KiB
TypeScript
263 lines
7.6 KiB
TypeScript
import fs from 'node:fs/promises'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
const rootDir = path.resolve(__dirname, '..')
|
|
|
|
interface JSONComponent {
|
|
type: string
|
|
jsonCompatible?: boolean
|
|
wrapperRequired?: boolean
|
|
load?: {
|
|
path: string
|
|
export: string
|
|
lazy?: boolean
|
|
}
|
|
props?: Record<string, unknown>
|
|
metadata?: {
|
|
notes?: string
|
|
}
|
|
}
|
|
|
|
interface RegistryEntry {
|
|
type: string
|
|
name: string
|
|
category: string
|
|
canHaveChildren: boolean
|
|
description: string
|
|
status: 'supported' | 'deprecated'
|
|
source: 'atoms' | 'molecules' | 'organisms' | 'ui' | 'wrappers' | 'custom'
|
|
jsonCompatible: boolean
|
|
wrapperRequired?: boolean
|
|
load?: {
|
|
path: string
|
|
export: string
|
|
lazy?: boolean
|
|
}
|
|
metadata?: {
|
|
conversionDate?: string
|
|
autoGenerated?: boolean
|
|
notes?: string
|
|
}
|
|
}
|
|
|
|
interface Registry {
|
|
version: string
|
|
categories: Record<string, string>
|
|
sourceRoots: Record<string, string[]>
|
|
components: RegistryEntry[]
|
|
statistics: {
|
|
total: number
|
|
supported: number
|
|
jsonCompatible: number
|
|
byCategory: Record<string, number>
|
|
bySource: Record<string, number>
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determine component category based on name and source
|
|
*/
|
|
function determineCategory(componentName: string, source: string): string {
|
|
const name = componentName.toLowerCase()
|
|
|
|
// Layout components
|
|
if (/container|section|stack|flex|grid|layout|panel|sidebar|header|footer/.test(name)) {
|
|
return 'layout'
|
|
}
|
|
|
|
// Input components
|
|
if (/input|select|checkbox|radio|slider|switch|form|textarea|date|file|number|password|search/.test(name)) {
|
|
return 'input'
|
|
}
|
|
|
|
// Navigation components
|
|
if (/nav|menu|breadcrumb|tab|link|pagination/.test(name)) {
|
|
return 'navigation'
|
|
}
|
|
|
|
// Feedback components
|
|
if (/alert|toast|notification|spinner|loading|progress|skeleton|badge|indicator/.test(name)) {
|
|
return 'feedback'
|
|
}
|
|
|
|
// Data display components
|
|
if (/table|list|card|chart|graph|tree|timeline|avatar|image/.test(name)) {
|
|
return 'data'
|
|
}
|
|
|
|
// Display components
|
|
if (/text|heading|label|code|icon|divider|separator|spacer/.test(name)) {
|
|
return 'display'
|
|
}
|
|
|
|
// Default to custom for organisms and complex components
|
|
if (source === 'organisms' || source === 'molecules') {
|
|
return 'custom'
|
|
}
|
|
|
|
return 'display'
|
|
}
|
|
|
|
/**
|
|
* Determine if component can have children
|
|
*/
|
|
function canHaveChildren(componentName: string): boolean {
|
|
const name = componentName.toLowerCase()
|
|
|
|
// These typically don't have children
|
|
const noChildren = /input|select|checkbox|radio|slider|switch|image|icon|divider|separator|spacer|spinner|progress|badge|dot/
|
|
|
|
return !noChildren.test(name)
|
|
}
|
|
|
|
/**
|
|
* Generate description for component
|
|
*/
|
|
function generateDescription(componentName: string, category: string): string {
|
|
const descriptions: Record<string, string> = {
|
|
layout: 'Layout container component',
|
|
input: 'Form input component',
|
|
navigation: 'Navigation component',
|
|
feedback: 'Feedback and status component',
|
|
data: 'Data display component',
|
|
display: 'Display component',
|
|
custom: 'Custom component',
|
|
}
|
|
|
|
return descriptions[category] || 'Component'
|
|
}
|
|
|
|
/**
|
|
* Read all JSON files from a directory and create registry entries
|
|
*/
|
|
async function processDirectory(
|
|
dir: string,
|
|
source: 'atoms' | 'molecules' | 'organisms' | 'ui' | 'custom'
|
|
): Promise<RegistryEntry[]> {
|
|
const entries: RegistryEntry[] = []
|
|
|
|
try {
|
|
const files = await fs.readdir(dir)
|
|
const jsonFiles = files.filter(f => f.endsWith('.json'))
|
|
|
|
for (const file of jsonFiles) {
|
|
const filePath = path.join(dir, file)
|
|
const content = await fs.readFile(filePath, 'utf-8')
|
|
const jsonComponent: JSONComponent = JSON.parse(content)
|
|
|
|
const componentName = jsonComponent.type
|
|
if (!componentName) continue
|
|
|
|
const category = determineCategory(componentName, source)
|
|
|
|
const entry: RegistryEntry = {
|
|
type: componentName,
|
|
name: componentName,
|
|
category,
|
|
canHaveChildren: canHaveChildren(componentName),
|
|
description: generateDescription(componentName, category),
|
|
status: 'supported',
|
|
source,
|
|
jsonCompatible: jsonComponent.jsonCompatible !== false,
|
|
wrapperRequired: jsonComponent.wrapperRequired || false,
|
|
metadata: {
|
|
conversionDate: new Date().toISOString().split('T')[0],
|
|
autoGenerated: true,
|
|
notes: jsonComponent.metadata?.notes,
|
|
},
|
|
}
|
|
|
|
if (jsonComponent.load) {
|
|
entry.load = jsonComponent.load
|
|
}
|
|
|
|
entries.push(entry)
|
|
}
|
|
} catch (error) {
|
|
console.error(`Error processing ${dir}:`, error)
|
|
}
|
|
|
|
return entries
|
|
}
|
|
|
|
/**
|
|
* Update the registry with new components
|
|
*/
|
|
async function updateRegistry() {
|
|
console.log('📝 Updating json-components-registry.json...\n')
|
|
|
|
const registryPath = path.join(rootDir, 'json-components-registry.json')
|
|
|
|
// Read existing registry
|
|
const registryContent = await fs.readFile(registryPath, 'utf-8')
|
|
const registry: Registry = JSON.parse(registryContent)
|
|
|
|
console.log(` Current components: ${registry.components.length}`)
|
|
|
|
// Process each directory
|
|
const newEntries: RegistryEntry[] = []
|
|
|
|
const directories = [
|
|
{ dir: path.join(rootDir, 'src/config/pages/atoms'), source: 'atoms' as const },
|
|
{ dir: path.join(rootDir, 'src/config/pages/molecules'), source: 'molecules' as const },
|
|
{ dir: path.join(rootDir, 'src/config/pages/organisms'), source: 'organisms' as const },
|
|
{ dir: path.join(rootDir, 'src/config/pages/ui'), source: 'ui' as const },
|
|
{ dir: path.join(rootDir, 'src/config/pages/components'), source: 'custom' as const },
|
|
]
|
|
|
|
for (const { dir, source } of directories) {
|
|
const entries = await processDirectory(dir, source)
|
|
newEntries.push(...entries)
|
|
console.log(` Processed ${source}: ${entries.length} components`)
|
|
}
|
|
|
|
// Merge with existing components (remove duplicates)
|
|
const existingTypes = new Set(registry.components.map(c => c.type))
|
|
const uniqueNewEntries = newEntries.filter(e => !existingTypes.has(e.type))
|
|
|
|
console.log(`\n New unique components: ${uniqueNewEntries.length}`)
|
|
console.log(` Skipped duplicates: ${newEntries.length - uniqueNewEntries.length}`)
|
|
|
|
// Add new components
|
|
registry.components.push(...uniqueNewEntries)
|
|
|
|
// Update statistics
|
|
const byCategory: Record<string, number> = {}
|
|
const bySource: Record<string, number> = {}
|
|
|
|
for (const component of registry.components) {
|
|
byCategory[component.category] = (byCategory[component.category] || 0) + 1
|
|
bySource[component.source] = (bySource[component.source] || 0) + 1
|
|
}
|
|
|
|
registry.statistics = {
|
|
total: registry.components.length,
|
|
supported: registry.components.filter(c => c.status === 'supported').length,
|
|
jsonCompatible: registry.components.filter(c => c.jsonCompatible).length,
|
|
byCategory,
|
|
bySource,
|
|
}
|
|
|
|
// Sort components by type
|
|
registry.components.sort((a, b) => a.type.localeCompare(b.type))
|
|
|
|
// Write updated registry
|
|
await fs.writeFile(registryPath, JSON.stringify(registry, null, 2) + '\n')
|
|
|
|
console.log(`\n✅ Registry updated successfully!`)
|
|
console.log(` Total components: ${registry.statistics.total}`)
|
|
console.log(` JSON compatible: ${registry.statistics.jsonCompatible}`)
|
|
console.log(`\n📊 By source:`)
|
|
for (const [source, count] of Object.entries(bySource)) {
|
|
console.log(` ${source.padEnd(12)}: ${count}`)
|
|
}
|
|
console.log(`\n📊 By category:`)
|
|
for (const [category, count] of Object.entries(byCategory)) {
|
|
console.log(` ${category.padEnd(12)}: ${count}`)
|
|
}
|
|
}
|
|
|
|
updateRegistry().catch(console.error)
|