1 import { mkdir, readFile, rm, writeFile } from 'fs/promises';
2 import { JSDOM } from 'jsdom';
3 import mustache from 'mustache';
4 import path from 'path';
5 import * as prettier from 'prettier';
7 function capitalize(string: string) {
8 return string.charAt(0).toUpperCase() + string.slice(1);
11 function camelize(str: string) {
13 .replace(/(?:^\w|[A-Z]|[\b\-_]\w)/g, function (word, index) {
14 return index === 0 ? word.toLowerCase() : word.toUpperCase().replace('-', '').replace('_', '');
19 function convertToJsx(html: string): string {
20 return html.replace(/<(\w+)([^>]*)\/?>/g, (_, tagName: string, attributes: string) => {
21 // Convert attributes to camelCase
22 const camelCaseAttributes = attributes
24 .replace(/[\w-]+="[^"]*"/g, (attr) => {
25 const [key, value] = attr.split('=');
26 return `${camelize(key)}=${value}`;
28 .replace(/class=/, 'className=');
29 return `<${tagName} ${camelCaseAttributes}>`;
35 * This file is auto-generated. Do not modify it manually!
36 * Run 'yarn workspace @proton/icons build' to update the icons react components.
40 const iconsDir = path.join(__dirname, '../icons');
42 async function run() {
43 const iconTemplate = await readFile(path.join(__dirname, './IconTemplate.tsx.mustache'), { encoding: 'utf-8' });
45 const iconFile = await readFile('./assets/sprite-icons.svg', 'utf8');
46 const iconNodes = Array.from(new JSDOM(iconFile).window.document.querySelectorAll('g[id]'));
48 const icons = iconNodes.map((node) => {
49 const iconName = capitalize(camelize(node.id));
51 const renderedContent = mustache.render(iconTemplate, { iconName, innerHTML: convertToJsx(node.innerHTML) });
55 content: [disclaimer, renderedContent].join('\n'),
59 await rm(iconsDir, { recursive: true, force: true });
60 await mkdir(iconsDir);
62 const prettierOptions = (await prettier.resolveConfig(path.join(__dirname, '../../../prettier.config.mjs'))) || {};
64 const writeFilesPromises = icons.map(async (icon) => {
66 `${iconsDir}/${icon.name}.tsx`,
67 await prettier.format(icon.content, { ...prettierOptions, parser: 'typescript' })
71 const generateIndex = async () => {
74 `export * from './types';`,
76 `export type IconName = ${iconNodes.map((node) => `'${node.id.replace('ic-', '')}'`).join('|')}`,
78 ...icons.map((icon) => `export { ${icon.name} } from './icons/${icon.name}';`),
82 path.join(__dirname, '../index.ts'),
83 await prettier.format(index, { ...prettierOptions, parser: 'typescript' })
86 writeFilesPromises.push(generateIndex());
88 await Promise.all(writeFilesPromises);