Files
1c7d2912-f512-4bf2-acfc-9da…/transform-registry.js
2026-01-14 13:35:00 +02:00

243 lines
9.5 KiB
JavaScript

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`);