new registry
This commit is contained in:
242
transform-registry.js
Normal file
242
transform-registry.js
Normal file
@@ -0,0 +1,242 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Read the original registry
|
||||
const registryPath = path.join(__dirname, 'registry.json');
|
||||
const registry = JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
||||
|
||||
// Create output directories
|
||||
const registryDir = path.join(__dirname, 'registry');
|
||||
const componentsDir = path.join(registryDir, 'components');
|
||||
const schemasDir = path.join(registryDir, 'schemas');
|
||||
|
||||
[componentsDir, schemasDir].forEach(dir => {
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
// Extract intent from description/details
|
||||
function extractIntent(description, details, name) {
|
||||
const text = `${description} ${details}`.toLowerCase();
|
||||
|
||||
// More specific intent extraction
|
||||
if (text.includes('vertical stack') && text.includes('alternating')) return 'sequential vertical steps with alternating layout';
|
||||
if (text.includes('horizontal') && text.includes('timeline')) return 'horizontal timeline';
|
||||
if (text.includes('vertical') && text.includes('sticky') && text.includes('scroll')) return 'vertical sticky scroll timeline';
|
||||
if (text.includes('hero') || text.includes('landing')) return 'hero with media';
|
||||
if (text.includes('sequential') || (text.includes('step') && text.includes('process'))) return 'sequential process';
|
||||
if (text.includes('feature') && !text.includes('hero')) return 'feature showcase';
|
||||
if (text.includes('pricing') || text.includes('plan')) return 'pricing plans';
|
||||
if (text.includes('testimonial') || text.includes('review')) return 'testimonials';
|
||||
if (text.includes('about') || text.includes('company')) return 'about section';
|
||||
if (text.includes('blog') || text.includes('article')) return 'content listing';
|
||||
if (text.includes('contact') || text.includes('form')) return 'contact form';
|
||||
if (text.includes('faq') || text.includes('question')) return 'faq section';
|
||||
if (text.includes('footer')) return 'footer';
|
||||
if (text.includes('metric') || text.includes('kpi') || text.includes('statistic')) return 'metrics display';
|
||||
if (text.includes('product') && !text.includes('feature')) return 'product showcase';
|
||||
if (text.includes('team') || text.includes('member')) return 'team section';
|
||||
if (text.includes('carousel') || text.includes('gallery')) return 'media carousel';
|
||||
if (text.includes('grid') && text.includes('card')) return 'card grid';
|
||||
if (text.includes('timeline')) return 'timeline';
|
||||
if (text.includes('split') && text.includes('layout')) return 'split layout';
|
||||
if (text.includes('background')) return 'background';
|
||||
if (text.includes('button')) return 'button';
|
||||
if (text.includes('navbar') || text.includes('navigation')) return 'navigation';
|
||||
if (text.includes('text') && !text.includes('button')) return 'text component';
|
||||
if (text.includes('form') || text.includes('input')) return 'form';
|
||||
|
||||
return 'general component';
|
||||
}
|
||||
|
||||
// Extract bestFor from description/details
|
||||
function extractBestFor(description, details) {
|
||||
const text = `${description} ${details}`.toLowerCase();
|
||||
const bestFor = [];
|
||||
|
||||
if (text.includes('landing page') || text.includes('hero')) bestFor.push('landing pages');
|
||||
if (text.includes('process') || text.includes('step')) bestFor.push('process flows', 'roadmaps', 'step-by-step explanation');
|
||||
if (text.includes('feature')) bestFor.push('feature showcases', 'capability displays');
|
||||
if (text.includes('pricing')) bestFor.push('pricing pages', 'subscription tiers');
|
||||
if (text.includes('portfolio') || text.includes('gallery')) bestFor.push('portfolios', 'image galleries');
|
||||
if (text.includes('testimonial')) bestFor.push('social proof', 'customer reviews');
|
||||
if (text.includes('about')) bestFor.push('about pages', 'company information');
|
||||
if (text.includes('blog')) bestFor.push('blog listings', 'article grids');
|
||||
if (text.includes('contact')) bestFor.push('contact pages', 'lead generation');
|
||||
if (text.includes('faq')) bestFor.push('help pages', 'support sections');
|
||||
if (text.includes('metric') || text.includes('kpi')) bestFor.push('statistics displays', 'achievement showcases');
|
||||
if (text.includes('product')) bestFor.push('product catalogs', 'e-commerce');
|
||||
if (text.includes('team')) bestFor.push('team pages', 'staff directories');
|
||||
|
||||
return bestFor.length > 0 ? bestFor : ['general use'];
|
||||
}
|
||||
|
||||
// Extract avoidWhen from constraints and details
|
||||
function extractAvoidWhen(details, constraints) {
|
||||
const avoidWhen = [];
|
||||
const text = details.toLowerCase();
|
||||
|
||||
if (text.includes('requires') && text.includes('5+')) avoidWhen.push('less than 5 items');
|
||||
if (text.includes('requires') && text.includes('3-5')) avoidWhen.push('less than 3 items', 'more than 5 items');
|
||||
if (text.includes('single') && !text.includes('multiple')) avoidWhen.push('multiple items');
|
||||
if (text.includes('sequential') || text.includes('step')) avoidWhen.push('non-sequential content', 'single item');
|
||||
if (text.includes('grid') && !text.includes('carousel')) avoidWhen.push('more than 4 items');
|
||||
if (text.includes('carousel') && !text.includes('grid')) avoidWhen.push('less than 5 items');
|
||||
|
||||
if (constraints?.itemRules) {
|
||||
if (constraints.itemRules.minItems > 1) {
|
||||
avoidWhen.push(`less than ${constraints.itemRules.minItems} items`);
|
||||
}
|
||||
if (constraints.itemRules.maxItems) {
|
||||
avoidWhen.push(`more than ${constraints.itemRules.maxItems} items`);
|
||||
}
|
||||
}
|
||||
|
||||
return avoidWhen.length > 0 ? avoidWhen : [];
|
||||
}
|
||||
|
||||
// Extract requires from constraints and propsSchema
|
||||
function extractRequires(constraints, propsSchema) {
|
||||
const requires = [];
|
||||
|
||||
// Check propsSchema for array props (main data requirements)
|
||||
if (propsSchema) {
|
||||
for (const [key, value] of Object.entries(propsSchema)) {
|
||||
if (typeof value === 'string' && value.includes('Array') && !key.includes('ClassName') && key !== 'className') {
|
||||
requires.push(`${key}[]`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add item constraints if no array props found
|
||||
if (requires.length === 0 && constraints?.itemRules?.minItems) {
|
||||
requires.push(`minimum ${constraints.itemRules.minItems} items`);
|
||||
}
|
||||
|
||||
return requires;
|
||||
}
|
||||
|
||||
// Extract import path from import statement
|
||||
function extractImportPath(importStatement) {
|
||||
if (!importStatement) return '';
|
||||
// Extract path from: import Component from '@/path/to/Component';
|
||||
const match = importStatement.match(/from\s+['"]([^'"]+)['"]/);
|
||||
return match ? match[1] : importStatement;
|
||||
}
|
||||
|
||||
// Simplify propsSchema - remove className props
|
||||
function simplifyPropsSchema(propsSchema) {
|
||||
if (!propsSchema) return {};
|
||||
|
||||
const simplified = {};
|
||||
for (const [key, value] of Object.entries(propsSchema)) {
|
||||
// Skip className props
|
||||
if (!key.includes('ClassName') && key !== 'className') {
|
||||
simplified[key] = value;
|
||||
}
|
||||
}
|
||||
return simplified;
|
||||
}
|
||||
|
||||
// Process all components
|
||||
const indexData = {};
|
||||
const intentsMap = {};
|
||||
const allComponents = [];
|
||||
|
||||
// Process componentRegistry
|
||||
Object.keys(registry.componentRegistry || {}).forEach(category => {
|
||||
registry.componentRegistry[category].forEach(component => {
|
||||
allComponents.push({ ...component, category, type: 'component' });
|
||||
});
|
||||
});
|
||||
|
||||
// Process sectionRegistry
|
||||
Object.keys(registry.sectionRegistry || {}).forEach(category => {
|
||||
registry.sectionRegistry[category].forEach(component => {
|
||||
allComponents.push({ ...component, category, type: 'section' });
|
||||
});
|
||||
});
|
||||
|
||||
// Process each component
|
||||
allComponents.forEach(component => {
|
||||
const name = component.name;
|
||||
const intent = extractIntent(component.description, component.details, name);
|
||||
const bestFor = extractBestFor(component.description, component.details);
|
||||
const avoidWhen = extractAvoidWhen(component.details, component.constraints);
|
||||
const requires = extractRequires(component.constraints, component.propsSchema);
|
||||
const importPath = extractImportPath(component.import);
|
||||
|
||||
// Add to index.json (lightweight catalog)
|
||||
indexData[name] = {
|
||||
category: component.category,
|
||||
intent: intent,
|
||||
bestFor: bestFor,
|
||||
avoidWhen: avoidWhen,
|
||||
requires: requires,
|
||||
import: importPath
|
||||
};
|
||||
|
||||
// Add to intents map
|
||||
if (!intentsMap[intent]) {
|
||||
intentsMap[intent] = [];
|
||||
}
|
||||
if (!intentsMap[intent].includes(name)) {
|
||||
intentsMap[intent].push(name);
|
||||
}
|
||||
|
||||
// Create component detail file
|
||||
const componentData = {
|
||||
name: name,
|
||||
description: component.description,
|
||||
constraints: component.constraints || {},
|
||||
propsSchema: simplifyPropsSchema(component.propsSchema),
|
||||
usageExample: component.usage || '',
|
||||
do: [
|
||||
...bestFor.map(bf => `Use for ${bf}`),
|
||||
...requires.map(r => `Requires ${r}`)
|
||||
],
|
||||
dont: [
|
||||
...avoidWhen.map(aw => `Do not use ${aw}`)
|
||||
],
|
||||
editRules: {
|
||||
textOnly: true,
|
||||
layoutLocked: true,
|
||||
styleLocked: true
|
||||
}
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(componentsDir, `${name}.json`),
|
||||
JSON.stringify(componentData, null, 2)
|
||||
);
|
||||
|
||||
// Create schema file (full propsSchema)
|
||||
const schemaData = {
|
||||
name: name,
|
||||
propsSchema: component.propsSchema || {}
|
||||
};
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(schemasDir, `${name}.schema.json`),
|
||||
JSON.stringify(schemaData, null, 2)
|
||||
);
|
||||
});
|
||||
|
||||
// Write index.json
|
||||
fs.writeFileSync(
|
||||
path.join(registryDir, 'index.json'),
|
||||
JSON.stringify(indexData, null, 2)
|
||||
);
|
||||
|
||||
// Write intents.json
|
||||
fs.writeFileSync(
|
||||
path.join(registryDir, 'intents.json'),
|
||||
JSON.stringify(intentsMap, null, 2)
|
||||
);
|
||||
|
||||
console.log(`✅ Created LLM-friendly registry structure:`);
|
||||
console.log(` - ${Object.keys(indexData).length} components in index.json`);
|
||||
console.log(` - ${Object.keys(intentsMap).length} intent mappings in intents.json`);
|
||||
console.log(` - ${allComponents.length} component detail files`);
|
||||
console.log(` - ${allComponents.length} schema files`);
|
||||
Reference in New Issue
Block a user