Skip to content

Commit

Permalink
feat(code connect): add function to get list of figma component ids
Browse files Browse the repository at this point in the history
  • Loading branch information
tylermorrisford committed Dec 16, 2024
1 parent 0f3b31a commit 6644d36
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 5 deletions.
59 changes: 59 additions & 0 deletions get-figma-ids.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const figmaComonentsUrl =
'https://api.figma.com/v1/files/<your-file-key-here>/components';
const figmaToken = 'your-figma-token-here';

/**
* Fetches the figma component ids from the figma api
* @returns {Map<string, {name: string, nodeId: string}>} - 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 };
17 changes: 17 additions & 0 deletions packages/components/link/src/link.figma.tsx
Original file line number Diff line number Diff line change
@@ -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) => <Link>{props.children}</Link>,
}
);
31 changes: 26 additions & 5 deletions scan-figma-connection.js
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -17,15 +18,18 @@ const path = require('path');
*/

// Helpers
const getConnectFileContent = (componentName) => {
const getConnectFileContent = (
componentName,
componentId = '<placeholder>'
) => {
return `// @ts-nocheck
import { ${componentName} } from '@commercetools-frontend/ui-kit';
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=<placeholder>',
'https://www.figma.com/design/UoqmtHSHwxvEZRcbsM4A3Z/CT-product-design-system?node-id=${componentId}',
{
props: {
// *This file was generated from a script*
Expand All @@ -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.
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand Down

0 comments on commit 6644d36

Please sign in to comment.