Skip to main content
Glama
node-processor.ts7.34 kB
import type { GetFileNodesResponse, Node, Component, ComponentSet, Style, } from "@figma/rest-api-spec"; import type { SimplifiedNode } from "../types/figma.js"; import { isVisible, sanitizeComponents, sanitizeComponentSets, } from "../utils/figma-utils.js"; /** * Transforms a raw Figma node into a simplified representation. * Filters out unnecessary properties and normalizes the structure. */ export const parseNode = ({ node }: { node: Node }): SimplifiedNode | null => { if (node.visible === false) { return null; } const simplified: SimplifiedNode = { id: node.id, name: node.name, type: node.type, }; if ("absoluteBoundingBox" in node) { simplified.absoluteBoundingBox = node.absoluteBoundingBox; } if (node.type === "TEXT" || node.type === "TEXT_PATH") { if ("characters" in node) { simplified.text = node.characters; simplified.characters = node.characters; } if ("style" in node) { simplified.textStyle = node.style; simplified.style = node.style; } } if ("fills" in node) simplified.fills = node.fills; if ("styles" in node) simplified.styles = node.styles; if ("strokes" in node) simplified.strokes = node.strokes; if ("effects" in node) simplified.effects = node.effects; if ("opacity" in node) simplified.opacity = node.opacity; if ("cornerRadius" in node) simplified.borderRadius = node.cornerRadius; if ("rectangleCornerRadii" in node && node.rectangleCornerRadii) simplified.borderRadius = node.rectangleCornerRadii; if ("strokeWeight" in node) { simplified.strokeWeight = node.strokeWeight; } if ("strokeAlign" in node) simplified.strokeAlign = node.strokeAlign; if ("strokeDashes" in node) simplified.strokeDashes = node.strokeDashes; if ("cornerSmoothing" in node) simplified.cornerSmoothing = node.cornerSmoothing; if ("individualStrokeWeights" in node) simplified.individualStrokeWeights = node.individualStrokeWeights; if ("layoutMode" in node) simplified.layoutMode = node.layoutMode; if ("layoutAlign" in node) simplified.layoutAlign = node.layoutAlign; if ("layoutGrow" in node) simplified.layoutGrow = node.layoutGrow; if ("layoutPositioning" in node) simplified.layoutPositioning = node.layoutPositioning; if ("layoutSizingHorizontal" in node) simplified.layoutSizingHorizontal = node.layoutSizingHorizontal; if ("layoutSizingVertical" in node) simplified.layoutSizingVertical = node.layoutSizingVertical; if ("constraints" in node) simplified.constraints = node.constraints; if ("minWidth" in node) simplified.minWidth = node.minWidth; if ("maxWidth" in node) simplified.maxWidth = node.maxWidth; if ("minHeight" in node) simplified.minHeight = node.minHeight; if ("maxHeight" in node) simplified.maxHeight = node.maxHeight; if ("clipsContent" in node) simplified.clipsContent = node.clipsContent; if ("overflowDirection" in node) simplified.overflowDirection = node.overflowDirection; if ("primaryAxisSizingMode" in node) simplified.primaryAxisSizingMode = node.primaryAxisSizingMode; if ("counterAxisSizingMode" in node) simplified.counterAxisSizingMode = node.counterAxisSizingMode; if ("primaryAxisAlignItems" in node) simplified.primaryAxisAlignItems = node.primaryAxisAlignItems; if ("counterAxisAlignItems" in node) simplified.counterAxisAlignItems = node.counterAxisAlignItems; if ("paddingLeft" in node) simplified.paddingLeft = node.paddingLeft; if ("paddingRight" in node) simplified.paddingRight = node.paddingRight; if ("paddingTop" in node) simplified.paddingTop = node.paddingTop; if ("paddingBottom" in node) simplified.paddingBottom = node.paddingBottom; if ("itemSpacing" in node) simplified.itemSpacing = node.itemSpacing; if ("layoutWrap" in node) simplified.layoutWrap = node.layoutWrap; if ("counterAxisSpacing" in node) simplified.counterAxisSpacing = node.counterAxisSpacing; if ("size" in node && node.size) { simplified.size = { width: node.size.x, height: node.size.y, }; } if ("componentId" in node) simplified.componentId = node.componentId; if ("componentSetId" in node && typeof node.componentSetId === "string") simplified.componentSetId = node.componentSetId; if ("componentProperties" in node) simplified.componentProperties = node.componentProperties; if ("componentPropertyDefinitions" in node) simplified.componentPropertyDefinitions = node.componentPropertyDefinitions; if ("componentPropertyReferences" in node) simplified.componentPropertyReferences = node.componentPropertyReferences; if ("exposedInstances" in node) simplified.exposedInstances = node.exposedInstances; if ( "isExposedInstance" in node && typeof node.isExposedInstance === "boolean" ) simplified.isExposedInstance = node.isExposedInstance; if ("overrides" in node) simplified.overrides = node.overrides; if ("children" in node && Array.isArray(node.children)) { simplified.children = node.children .map((childNode) => parseNode({ node: childNode })) .filter((child): child is SimplifiedNode => child !== null); } return simplified; }; /** * Processes the raw Figma API response into a simplified data structure. * Extracts components, styles, and nodes for easier processing. */ export const getSimplifiedFigmaData = ( response: GetFileNodesResponse ): { components: Record< string, { id: string; key: string; name: string; componentSetId: string | null | undefined; } >; componentSets: Record< string, { id: string; key: string; name: string; description: string | undefined } >; styles: Record<string, { [key: string]: Style }>; nodes: SimplifiedNode[]; } => { const aggregatedComponents: Record<string, Component> = {}; const aggregatedComponentSets: Record<string, ComponentSet> = {}; const aggregatedStyles: Record<string, { [key: string]: Style }> = {}; const nodeResponses = Object.values(response.nodes); nodeResponses.forEach((nodeResponse) => { if (nodeResponse.components) { Object.assign(aggregatedComponents, nodeResponse.components); } if (nodeResponse.componentSets) { Object.assign(aggregatedComponentSets, nodeResponse.componentSets); } if (nodeResponse.styles) { Object.assign(aggregatedStyles, nodeResponse.styles); } }); const nodesToParse = nodeResponses.map((n) => n.document); const sanitizedComponents = sanitizeComponents(aggregatedComponents); const sanitizedComponentSets = sanitizeComponentSets(aggregatedComponentSets); const simplifiedNodes = nodesToParse .filter(isVisible) .map((n) => parseNode({ node: n })) .filter((child) => child !== null && child !== undefined); return { components: sanitizedComponents, componentSets: sanitizedComponentSets, styles: aggregatedStyles, nodes: simplifiedNodes, }; }; /** * Maps Figma node types to simplified component types for code generation. */ export const simplifyType = (type: string): string => { const typeMap: Record<string, string> = { FRAME: "Container", TEXT: "Text", RECTANGLE: "Box", INSTANCE: "Component", "IMAGE-SVG": "Icon", VECTOR: "Icon", GROUP: "Group", }; return typeMap[type] || type; };

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/toddle-edu/figma-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server