@mseep/mcp-figma-to-code
Version:
MCP server for converting Figma designs to React Native components
97 lines (96 loc) • 3.86 kB
JavaScript
import { existsSync } from 'node:fs';
import { normalizeName, toCamelCase, toPascalCase } from './index.js';
import { join } from 'node:path';
const PROJECT_DIR = process.env.PROJECT_DIR || '/';
const componentDir = join(PROJECT_DIR, 'components');
const areSameComponent = (name1, name2) => {
return normalizeName(name1) === normalizeName(name2);
};
function extractComponentChildren(children) {
if (!Array.isArray(children))
return [];
return children.map(({ name, children, type, style, fills }) => ({
name,
type,
style,
fills,
children: extractComponentChildren(children || []),
}));
}
function extractComponentProps(children) {
return children
.flatMap((c) => {
const parts = c.name.split(', ');
return parts.map((prop) => {
const [key, value] = prop.split('=');
return {
name: toCamelCase(key),
type: value === 'True' || value === 'False' ? 'boolean' : value,
};
});
})
.reduce((acc, prop) => {
if (!acc[prop.name])
acc[prop.name] = { ...prop };
else if (!acc[prop.name].type.includes(prop.type))
acc[prop.name].type = `${acc[prop.name].type} | ${prop.type}`;
return acc;
}, {});
}
export async function generateComponent(component, validation = false, componentToExtract = '') {
try {
const { document } = component;
const componentsPage = document.children.find((c) => c.name === 'Components');
if (!componentsPage) {
console.log('No Components page found in document');
throw new Error('Components page not found in Figma file');
}
const page = componentsPage.children;
let componentSets = [];
let processedCount = 0;
const checkExisting = (componentName) => validation ? !existsSync(`${componentDir}/${componentName}`) : true;
const specificComponent = (componentName, componentToExtract) => componentToExtract
? areSameComponent(componentName, componentToExtract)
: true;
for (const section of page) {
const { children } = section;
if (!children)
continue;
for (const item of children) {
const { type, name } = item;
const componentName = toPascalCase(name);
if (type === 'COMPONENT_SET' &&
checkExisting(componentName) &&
specificComponent(componentName, componentToExtract)) {
processedCount++;
try {
const props = extractComponentProps(item.children);
const minified = {
name: componentName,
props,
children: extractComponentChildren(item.children),
};
componentSets.push(minified);
}
catch (processError) {
return {
message: `Error processing component ${name}: ${processError}`,
componentSets: [],
};
}
}
}
}
// Create a formatted result for the user
const message = `Successfully processed ${processedCount} components.\n\nComponent sets: ${componentSets.length}\nComponent paths:\n${componentSets.map((cs) => `- ${cs.name}`).join('\n')}`;
// Return both the result message and the component data
return {
message,
componentSets,
};
}
catch (error) {
console.error(`Error generating component: ${error}`);
throw error;
}
}