generator-react-native-vector-icons
Version:
Generates React Native vector icons font library
81 lines (80 loc) • 3.09 kB
JavaScript
import fs from 'node:fs';
import postcss from 'postcss';
import parser from 'postcss-selector-parser';
const extractGlyphMapFromCodepoints = (fileName) => {
const codepoints = fs.readFileSync(fileName, { encoding: 'utf8' }).split('\n');
const glyphMap = {};
codepoints.forEach((point) => {
const parts = point.split(' ');
if (parts[0] && parts[1]) {
glyphMap[parts[0].replace(/_/g, '-')] = Number.parseInt(parts[1], 16);
}
});
return glyphMap;
};
const extractGlyphMapFromCss = (fileName, selectorPrefix) => {
const css = fs.readFileSync(fileName, 'utf-8');
const glyphMap = {};
const selectorPattern = `${escapeRegExp(selectorPrefix)}([A-Za-z0-9_-]+)::?before`;
postcss.parse(css).walkRules((rule) => {
const iconNames = [];
const transform = (selectors) => {
selectors.walk((selector) => {
const md = selector.toString().match(selectorPattern);
if (md?.[1]) {
iconNames.push(md[1]);
}
});
};
parser(transform).processSync(rule.selector);
const contents = [];
rule.walkDecls('content', (decl) => {
const content = decl.value.replace(/['"]/g, ''); // Remove quotes
contents.push(content);
});
const content = contents[0];
if (!content || content === 'var(--fa)') {
return;
}
const codePoint = Number.parseInt(content.slice(1), 16);
iconNames.forEach((iconName) => {
glyphMap[iconName] = codePoint;
});
});
// TODO: Quick hack for fontawesome - refactor this to be more general
const selectorPatternFA = `${escapeRegExp(selectorPrefix)}([A-Za-z0-9_-]+)$`;
postcss.parse(css).walkRules((rule) => {
const iconNames = [];
const transform = (selectors) => {
selectors.walk((selector) => {
const md = selector.toString().match(selectorPatternFA);
if (md?.[1]) {
iconNames.push(md[1]);
}
});
};
parser(transform).processSync(rule.selector);
const contents = [];
rule.walkDecls('--fa', (decl) => {
const content = decl.value.replace(/['"]/g, ''); // Remove quotes
contents.push(content);
});
const content = contents[0];
if (!content) {
return;
}
const codePoint = Number.parseInt(content.slice(1), 16);
iconNames.forEach((iconName) => {
glyphMap[iconName] = codePoint;
});
});
return glyphMap;
};
const escapeRegExp = (str) => str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
export const generateGlyphmap = (mode, fileName, selectorPrefix = '.icon-') => {
if (!fileName) {
throw new Error('No files provided');
}
const glyphMap = mode === 'css' ? extractGlyphMapFromCss(fileName, selectorPrefix) : extractGlyphMapFromCodepoints(fileName);
return JSON.stringify(glyphMap, null, ' ');
};