Skip to content

Instantly share code, notes, and snippets.

@f1lander
Created February 11, 2022 05:16
Show Gist options
  • Save f1lander/19c2472c59b512c623f784f8b0f839f8 to your computer and use it in GitHub Desktop.
Save f1lander/19c2472c59b512c623f784f8b0f839f8 to your computer and use it in GitHub Desktop.
import fs from 'fs';
import path from 'path';
import dotenv from 'dotenv';
import * as Figma from 'figma-api';
dotenv.config();
//#region constants
const FIGMA_FILE_KEY = 'L4oZwYVUdpgacZtwipGaYe';
const COLORS_DOCUMENT = '3542:11605';
const FILL = 'FILL';
const RECTANGLE = 'RECTANGLE';
//#endregion constants
//#region Types
type Styles = {
name: string;
id: string;
};
type ColorNode = {
name: string;
color: string;
};
//#endregion Types
//#region Utils functions
const rgbToHex = (r: number, g: number, b: number) => {
const color =
'#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
if (color.length > 7) {
return color.slice(0, 7);
}
return color;
};
const noChildren = (node: any) => node != null && !('children' in node);
const isRectangle = (node: any) => node != null && node.type === RECTANGLE;
const findStyleInTree = (root: any, styleId: any) => {
if (noChildren(root)) {
return isRectangle(root) && root.styles && root.styles.fill === styleId
? root
: undefined;
} else {
return root.children
.map((item: any) => findStyleInTree(item, styleId))
.reduce(
(accumulator: any, current: any) =>
accumulator != null ? accumulator : current,
undefined
);
}
};
const mapStyleToNode = (file: any, styles: Styles[]) => {
const colorNodes: ColorNode[] = [];
styles.forEach(({ name, id }) => {
const node = findStyleInTree(file.document, id);
const color =
isRectangle(node) && node.fills[0] ? node.fills[0].color : undefined;
if (color) {
const { r, g, b } = color;
colorNodes.push({ name, color: rgbToHex(r * 255, g * 255, b * 255) });
}
});
return colorNodes;
};
const generateFile = (content: ColorNode[], fileName = 'colors.ts') => {
if (!content) {
throw new Error('No styles found');
}
const colorsGroupName: string[] = [];
const colors = content.reduce((prev, curr) => {
let colorGroupName;
if (!colorsGroupName.includes(curr.name.split('/')[0])) {
colorsGroupName.push(curr.name.split('/')[0]);
colorGroupName = curr.name.split('/')[0];
}
return (
prev +
(colorGroupName ? `/** ${colorGroupName} */\n` : '') +
` '${curr.name}': '${curr.color}',\n`
);
}, '');
const fileContents = `/* Updated at ${new Date().toUTCString()}*/
export const colors = {
${colors}
};`;
fs.writeFileSync(path.resolve(`./src/ui/${fileName}`), fileContents);
// eslint-disable-next-line no-console
console.log(`Wrote ${content.length} colors to colors.ts`);
};
//#endregion Utils functions
async function main() {
const api = new Figma.Api({
personalAccessToken: process.env.FIGMA_ACCESS_TOKEN || 'token',
});
const file = await api.getFile(FIGMA_FILE_KEY, {
ids: [COLORS_DOCUMENT],
});
const styles: Styles[] = Object.entries(file.styles)
.filter(([, { styleType }]) => styleType === FILL)
.map(([id, { name }]) => ({ name: name.replace(/\s/g, ''), id }))
.sort((a, b) => a.name.localeCompare(b.name));
const result = mapStyleToNode(file, styles);
// eslint-disable-next-line no-console
generateFile(result);
}
(async function () {
await main()
.catch(error => {
console.error(error);
process.exit(1);
})
.then(() => {
process.exit(0);
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment