UNPKG

dropit-figma

Version:

DropIT: A seamless design-to-code solution that integrates with the Figma API to fetch and structure design data, including pages, screens, styles, and images, for streamlined development workflows.

232 lines (205 loc) 7.32 kB
import { _getClient } from './figmaClient.js'; /** * Fetches a Figma file by its ID. * @param {string} fileId - The ID of the Figma file. * @returns {Promise<Object>} - The Figma file data. */ export async function getFigmaFile(fileId) { const { data } = await _getClient().file(fileId); return data; } export function getAllPages(figmaFile) { const pages = []; figmaFile?.document?.children.forEach((page) => { if (page?.type === 'CANVAS') { pages.push(page); } }); return pages; } export function getAllScreens(page) { const screens = []; page?.children.forEach((screen) => { if (screen?.type === 'FRAME' || screen?.type === 'GROUP') { screens.push(screen); } }); return screens; } /** * Fetches the components from a Figma file. * @param {string} fileId - The ID of the Figma file. * @returns {Promise<Object[]>} - List of components in the file. */ export async function getComponents(fileId) { const fileData = await getFigmaFile(fileId); return fileData.components || []; } /** * Fetches the styles by key. * @param {string} key - The key of the style. * @returns {Promise<Object>} - The Figma file Style data. */ export async function getStyle(key) { const { data } = await _getClient().style(key) return data; } /** * Fetches the Images from a Figma file. * @param {string} fileId - The ID of the Figma file. * @param {Array<String>} ids - Array of Component id e.g. ['2:1','3:3] * @returns {Promise<Object>} - The Figma file Images data. */ export async function getImagesByIds(fileId, ids) { const { data } = await _getClient().fileImages(fileId, { ids: ids }) return data; } /** * Extracts all image fills from a Figma file. * This function recursively traverses the Figma document to find nodes with image fills. * * @param {string} fileId - The ID of the Figma file. * @returns {Array<Object>} - An array of objects containing image details (node ID, name, and image reference). */ async function getAllImageFills(figmaFile) { try { // Retrieve the document structure const document = figmaFile.document; /** * Recursively searches nodes to find image fills. * @param {Array<Object>} nodes - List of nodes to search. * @param {Array<Object>} images - Accumulator for found images. * @returns {Array<Object>} - List of image fills found in the document. */ const findImageFills = (nodes, images = []) => { nodes.forEach(node => { // Check if the node has fills and filter IMAGE fills if (node.fills && Array.isArray(node.fills)) { node.fills.forEach(fill => { if (fill.type === 'IMAGE' && fill.imageRef) { images.push({ nodeId: node.id, imageRef: fill.imageRef, name: node.name, }); } }); } // Recursively search child nodes, if present if (node.children) { findImageFills(node.children, images); } }); return images; }; // Start the recursive search from the root node const images = findImageFills(document.children || []); return images; } catch (error) { console.error('Error fetching image fills:', error.message); throw error; } } /** * Fetches public URLs for image fills in a Figma file. * This function uses image references (`imageRef`) to fetch accessible URLs via Figma API. * * @param {string} fileId - The ID of the Figma file. * @param {Array<Object>} images - List of image fills (objects with nodeId, imageRef, and name). * @returns {Promise<Object>} - An array of objects containing image details with public URLs. */ async function getImageUrls(fileId, images, format = 'png', scale = 4) { try { // Extract image keys from the image fills const imageKeys = images.map(image => image.nodeId); // Fetch public URLs for the image references const { data: imageUrls } = await _getClient().fileImages(fileId, { ids: imageKeys, format, scale }); // Map the URLs back to the image details return images.map((key, index) => ({ nodeName: images[index].name, nodeId: images[index].nodeId, imageUrl: imageUrls.images[images[index].nodeId], })); } catch (error) { console.error('Error fetching image URLs:', error.message); throw error; } } export async function getAllImages(figmaFile, fileId, properties) { const images = await getAllImageFills(figmaFile); console.log(images.length); const imageUrls = await getImageUrls(fileId, images, properties?.imageFormat, properties?.imageScale); return imageUrls; } /** * Fetches detailed information about a style by its key. * @param {string} styleKey - The key of the style to fetch. * @returns {Promise<Object>} - Detailed information about the style. * @throws {Error} - If the style key is invalid or fetch fails. */ export async function getStyleDetailsByKey(styleKey) { try { const { data } = await _getClient().style(styleKey); // Fetch style details using figma-js return data; } catch (error) { console.error(`Failed to fetch details for style key: ${styleKey}`, error); throw new Error("Unable to fetch style details. Check the style key."); } } /** * Fetches all local styles with actual styling details (e.g., font, color). * @param {string} fileId - The ID of the Figma file. * @returns {Array<Object>} - List of local styles with their detailed properties. */ export async function getLocalStylesWithDetails(figmaFile) { const styles = figmaFile.styles || {}; const nodes = figmaFile.document.children || []; // Function to find a node by its ID and extract style details const getStyleDetails = (styleId, baseStyle) => { for (const node of nodes) { if (node.styles && node?.styles[baseStyle?.styleType?.toLowerCase()] === styleId) { return node.styles; } // Recursively check child nodes if (node.children) { const childStyle = getStyleDetailsFromChildren(node.children, styleId, baseStyle); if (childStyle) return childStyle; } } return null; }; const getStyleDetailsFromChildren = (children, styleId, baseStyle) => { for (const child of children) { if (child.styles && child?.styles[baseStyle?.styleType?.toLowerCase()] === styleId) { switch (baseStyle?.styleType) { case 'FILL': return { fills: child?.fills }; case 'TEXT': return { textStyle: child?.style }; case 'EFFECT': return { effects: child?.effects }; default: return {}; } } if (child.children) { const nestedStyle = getStyleDetailsFromChildren(child.children, styleId, baseStyle); if (nestedStyle) return nestedStyle; } } return null; }; // Map styles to include detailed properties return Object.keys(styles).map((key) => { const baseStyle = styles[key]; const details = getStyleDetails(key, baseStyle); return { styleId: key, styleKey: baseStyle.key, name: baseStyle.name, type: baseStyle.styleType, description: baseStyle.description || '', details, // Add detailed style properties }; }); }