diff --git a/get-figma-ids.js b/get-figma-ids.js new file mode 100644 index 0000000000..587368ff69 --- /dev/null +++ b/get-figma-ids.js @@ -0,0 +1,59 @@ +const figmaComonentsUrl = + 'https://api.figma.com/v1/files//components'; +const figmaToken = 'your-figma-token-here'; + +/** + * Fetches the figma component ids from the figma api + * @returns {Map} - A map of component names to their corresponding node ids + * @throws {Error} - If the fetch request fails + * @example + * const componentIds = await fetchFigmaComponentIds(); + * console.log(componentIds.get('button')); + * // Output: { name: 'button', nodeId: '0:1' } + **/ + +async function fetchFigmaComponentIds() { + try { + const response = await fetch(figmaComonentsUrl, { + method: 'GET', + headers: { + 'X-FIGMA-TOKEN': figmaToken, + }, + }); + + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + const data = await response.json(); + + const componentListArray = data.meta.components + // Filter out components that do not have a `containingStateGroup` property + // as it contains the correct nodeId for the component + .filter((component) => + component.containing_frame.hasOwnProperty('containingStateGroup') + ) + // create a new array with a normalized name of the component + // and the encoded node id of the component + .map((component) => { + return { + name: component.containing_frame.containingStateGroup.name + .split('/') + .pop() + .toLowerCase() + .replace(/[\s\-]/g, ''), + nodeId: encodeURIComponent( + component.containing_frame.containingStateGroup.nodeId + ), + }; + }); + // Use a Map arranged by name for lookups TODO: needs to be deduped + return new Map( + componentListArray.map((component) => [component.name, component]) + ); + } catch (error) { + console.error(`Error fetching data: ${error}`); + } +} + +module.exports = { fetchFigmaComponentIds }; diff --git a/packages/components/link/src/link.figma.tsx b/packages/components/link/src/link.figma.tsx new file mode 100644 index 0000000000..c6a157143c --- /dev/null +++ b/packages/components/link/src/link.figma.tsx @@ -0,0 +1,17 @@ +// @ts-nocheck +import { Link } from '@commercetools-frontend/ui-kit'; +import figma from '@figma/code-connect'; + +// REQUIRED: supply node id for the figma component +figma.connect( + Link, + 'https://www.figma.com/design/UoqmtHSHwxvEZRcbsM4A3Z/CT-product-design-system?node-id=118%3A17594', + { + props: { + // *This file was generated from a script* + // TODO: manually map props here, see https://www.figma.com/code-connect-docs/react/#figmaconnect + children: figma.children('*'), + }, + example: (props) => {props.children}, + } +); diff --git a/scan-figma-connection.js b/scan-figma-connection.js index a043ec8303..ecd56a299d 100644 --- a/scan-figma-connection.js +++ b/scan-figma-connection.js @@ -1,5 +1,6 @@ const fs = require('node:fs/promises'); const path = require('path'); +const { fetchFigmaComponentIds } = require('./get-figma-ids'); /* * This script is used to assist with maintaining figma connect files for components @@ -17,7 +18,10 @@ const path = require('path'); */ // Helpers -const getConnectFileContent = (componentName) => { +const getConnectFileContent = ( + componentName, + componentId = '' +) => { return `// @ts-nocheck import { ${componentName} } from '@commercetools-frontend/ui-kit'; import figma from '@figma/code-connect'; @@ -25,7 +29,7 @@ import figma from '@figma/code-connect'; // REQUIRED: supply node id for the figma component figma.connect( ${componentName}, - 'https://www.figma.com/design/UoqmtHSHwxvEZRcbsM4A3Z/CT-product-design-system?node-id=', + 'https://www.figma.com/design/UoqmtHSHwxvEZRcbsM4A3Z/CT-product-design-system?node-id=${componentId}', { props: { // *This file was generated from a script* @@ -38,13 +42,17 @@ figma.connect( `; }; -const getComponentName = (str) => { +const getReactComponentName = (str) => { return str .split('-') .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) .join(''); }; +const normalizeComponentName = (str) => { + return str.toLowerCase().replace(/[^a-z0-9]/g, ''); +}; + /** * `scanConnectedComponents` recursively searches for the root React component file in a given directory. * For each root file found, it checks for a corresponding `.figma.tsx` file. @@ -56,6 +64,9 @@ const getComponentName = (str) => { */ const scanConnectedComponents = async (dir) => { + // Fetch figma component ids + const figmaComponentMap = await fetchFigmaComponentIds(); + // Read the given directory const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { @@ -101,12 +112,22 @@ const scanConnectedComponents = async (dir) => { } } catch (err) { if (err.code === 'ENOENT') { + // check if our figma component map has the component + const matchingFigmaComponent = figmaComponentMap.get( + normalizeComponentName(componentName) + ); + if (matchingFigmaComponent === undefined) { + continue; + } // `.figma.tsx` file does not exist, create it await fs.writeFile( figmaFilePath, - getConnectFileContent(getComponentName(componentName)) + getConnectFileContent( + getReactComponentName(componentName), + matchingFigmaComponent.nodeId + ) ); - console.log(`Created Figma connect file: ${figmaFilePath}`); + console.log(`Created Connect file at ${figmaFilePath}`); } else { console.error(`Error checking file: ${figmaFilePath}`, err); }