fenghuo/packages/icons/scripts/generate.mjs

156 lines
4.7 KiB
JavaScript

import path from 'path';
import { transform } from '@svgr/core';
import axios from 'axios';
import chalk from 'chalk';
import dotenv from 'dotenv';
import fs from 'fs-extra';
import _ from 'lodash';
import * as Figma from 'figma-js';
dotenv.config();
const componentsDir = 'src/components';
// Add .env file
const FIGMA_API_TOKEN = process.env.FIGMA_API_TOKEN;
const FIGMA_FILE_ID = process.env.FIGMA_FILE_ID;
const FIGMA_CANVAS = process.env.FIGMA_CANVAS;
if (!FIGMA_API_TOKEN) {
throw new Error('Missing environment variable FIGMA_API_TOKEN');
}
if (!FIGMA_FILE_ID) {
throw new Error('Missing environment variable FIGMA_FILE_ID');
}
if (!FIGMA_CANVAS) {
throw new Error('Missing environment variable FIGMA_CANVAS');
}
const figmaApi = Figma.Client({ personalAccessToken: FIGMA_API_TOKEN });
const getSvgs = async ({ fileId, canvas, group }) => {
const file = await figmaApi.file(fileId);
const { document } = file.data;
const iconsNode = document.children.find(({ name }) => name === canvas);
if (!iconsNode) {
throw new Error(`Couldn't find page with name ${canvas}`);
}
const usingIconNodes = iconsNode.children.find(({ name }) => name === group)?.children || [];
const usingNodeId = usingIconNodes.map(({ id }) => id);
const svgs = await figmaApi.fileImages(fileId, {
format: 'svg',
ids: usingNodeId,
});
return usingIconNodes.map(({ id, name }) => ({ id, name, url: svgs.data.images[id] }));
};
const downloadSVGsData = async (data, batchSize = 20, delayBetweenBatches = 500) => {
const results = [];
const batchCount = Math.ceil(data.length / batchSize);
for (let i = 0; i < batchCount; i++) {
const batchData = data.slice(i * batchSize, (i + 1) * batchSize);
console.log(`Processing batch ${i + 1}/${batchCount}, containing ${batchData.length} requests`);
const batchResults = await Promise.all(
batchData.map(async (dataItem) => {
try {
const downloadedSvg = await axios.get(dataItem.url);
return {
...dataItem,
data: downloadedSvg.data,
success: true,
};
} catch (error) {
console.error(`Failed to download ${dataItem.url}:`, error.message);
return {
...dataItem,
success: false,
};
}
})
);
results.push(...batchResults);
if (i < batchCount - 1) {
await new Promise((resolve) => setTimeout(resolve, delayBetweenBatches));
}
}
return results;
};
const transformReactComponent = (svgList) => {
if (!fs.existsSync(componentsDir)) {
fs.mkdirSync(componentsDir);
}
svgList.forEach((svg) => {
if (!svg.success) return;
const svgCode = svg.data;
const svgName = svg.name.split('/').pop();
const camelCaseInput = _.camelCase(svgName);
const componentName = camelCaseInput.charAt(0).toUpperCase() + camelCaseInput.slice(1);
const componentFileName = `${componentName}.tsx`;
// Converts SVG code into React code using SVGR library
const componentCode = transform.sync(
svgCode,
{
typescript: true,
icon: true,
replaceAttrValues: {
'#000': 'currentColor',
},
plugins: [
// Clean SVG files using SVGO
'@svgr/plugin-svgo',
// Generate JSX
'@svgr/plugin-jsx',
// Format the result using Prettier
'@svgr/plugin-prettier',
],
},
{ componentName }
);
// 6. Write generated component to file system
fs.outputFileSync(path.resolve(componentsDir, componentFileName), componentCode);
// fs.outputFileSync(path.resolve('src/icons', `${svgName}.svg`), svg.data);
});
};
const genIndexContent = () => {
let indexContent = '';
const indexPath = path.resolve('src/index.ts');
fs.readdirSync(componentsDir).forEach((componentFileName) => {
// Convert name to pascal case
const componentName = componentFileName.split('.')[0];
// Export statement
const componentExport = `export { default as ${componentName} } from './components/${componentName}';\n`;
indexContent += componentExport;
});
// Write the content to file system
fs.writeFileSync(indexPath, indexContent);
};
const generate = async () => {
console.log(chalk.magentaBright('-> Fetching icons metadata'));
const svgs = await getSvgs({ fileId: FIGMA_FILE_ID, canvas: FIGMA_CANVAS, group: 'using' });
console.log(chalk.blueBright('-> Downloading SVG code'));
const svgsData = await downloadSVGsData(svgs);
console.log(chalk.cyanBright('-> Converting to React components'));
transformReactComponent(svgsData);
console.log(chalk.yellowBright('-> Writing exports components'));
genIndexContent();
console.log(chalk.greenBright('-> All done! ✅'));
};
generate();