figma-code-handler.ts•4.2 kB
import { z } from "zod/v3";
import prettier from "prettier";
import {
logger,
FigmaServerError,
ErrorType,
createErrorResponse,
} from "../utils/error-handling.js";
import type { FigmaApiClient } from "../api/figma-client.js";
import type { GetFileNodesResponse } from "@figma/rest-api-spec";
import type { PseudoCodeOptions } from "../types/generator.js";
import { getSimplifiedFigmaData } from "../processors/node-processor.js";
import { flattenElements } from "../processors/component-processor.js";
import { generateCSS } from "../generators/css-generator.js";
import { generateJSX } from "../generators/jsx-generator.js";
/**
* Tool definition for generating code from Figma frames
*/
export const generateCodeFromFigmaFrameTool = {
name: "generate_pseudo_code_from_figma_frame",
description:
"It will generates JSX and CSS pseudo-code for a Figma frame or node, given its file key and node ID. Useful for converting Figma designs to code. Can also be used to generate code for specific components or frames in Figma.",
inputSchema: {
fileKey: z.string().describe("The key of the Figma file to fetch, Can be found in a provided URL like `figma.com/(file|design)/<fileKey>/...`"),
nodeId: z.string().describe("The ID of the node to fetch, can be found as URL parameter like `node-id=<nodeId>`, Must use if provided."),
},
} as const;
/**
* Generates pseudo-code from Figma API response.
* Processes the raw Figma data and generates CSS and JSX code.
*/
export async function generatePseudoCode(
response: GetFileNodesResponse,
fileKey: string,
nodeId: string
): Promise<{ content: string }> {
const options: PseudoCodeOptions = {
includeComments: true,
semanticHTML: true,
groupStyles: true,
minifyProps: true,
};
const simplifiedData = getSimplifiedFigmaData(response);
const flatElements = flattenElements(simplifiedData);
const styles = generateCSS({ elements: flatElements, options });
const jsx = generateJSX({ elements: flatElements, options });
let sanitizedJSX;
try {
const prettierJSX = await prettier.format(jsx, {
semi: false,
parser: "babel",
});
sanitizedJSX = prettierJSX.replace(/;/g, "");
} catch (error) {
sanitizedJSX = jsx;
}
const augmentedStyles = `
/* Styles */
Stack {
display: flex;
flex-direction: column;
}
Flex {
display: flex;
flex-direction: row;
}
${styles}`;
const pseudoCode = {
styles: augmentedStyles,
jsx: sanitizedJSX,
};
const arrayOfPseudoCode = Object.values(pseudoCode);
const mdx = `
# Following is Pseudo Code for Figma Design of - File Key: ${fileKey}, Node ID: ${nodeId} \n
${new Array(25).fill("=-").join("")}
${arrayOfPseudoCode.join("\n\n")}
`;
return { content: mdx };
}
/**
* Handler function for generating code from Figma frames
* Generates pseudo-code (JSX and CSS) for a given Figma frame or node.
*/
export const generateCodeFromFigmaFrameHandler = async (
figmaClient: FigmaApiClient,
{ fileKey, nodeId }: { fileKey: string; nodeId: string }
) => {
try {
if (!fileKey || !nodeId) {
throw new FigmaServerError(
"Invalid input. Please provide both file key and node ID.",
ErrorType.VALIDATION_ERROR,
{
operation: "generateCodeFromFigmaFrame",
input: { fileKey, nodeId },
timestamp: new Date().toISOString(),
},
400
);
}
const frameData = await figmaClient.getFileNodes(fileKey, nodeId);
if (!frameData.nodes || Object.keys(frameData.nodes).length === 0) {
throw new FigmaServerError(
`No frame found for node ID ${nodeId} in file ${fileKey}`,
ErrorType.NOT_FOUND,
{
operation: "generateCodeFromFigmaFrame",
fileKey,
nodeId,
timestamp: new Date().toISOString(),
},
404
);
}
const pseudoCode = await generatePseudoCode(frameData, fileKey, nodeId);
return {
content: [
{
type: "text" as const,
text: pseudoCode.content,
},
],
};
} catch (error) {
return createErrorResponse(error as Error, "generateCodeFromFigmaFrame");
}
};