keeweb/build/loaders/fontawesome-loader.js

109 lines
3.4 KiB
JavaScript

const fs = require('fs');
const path = require('path');
const SVGIcons2SVGFontStream = require('svgicons2svgfont');
const svg2ttf = require('svg2ttf');
const wawoff2 = require('wawoff2');
const svgBaseDir = path.resolve('node_modules/@fortawesome/fontawesome-free/svgs/');
const svgDirs = ['brands', 'regular', 'solid']
.map((dir) => path.join(svgBaseDir, dir))
.concat('graphics/svg');
const allIcons = {};
for (const svgDir of svgDirs) {
const suffix = svgDir.endsWith('regular') ? '-o' : '';
fs.readdirSync(path.join(svgDir))
.filter((icon) => icon.endsWith('.svg'))
.forEach((icon) => {
const svgIconPath = path.join(svgDir, icon);
const iconName = icon.substr(0, icon.length - 4) + suffix;
allIcons[iconName] = svgIconPath;
});
}
module.exports = function makeFontAwesomeWoff2() {
const callback = this.async();
if (this.cacheable) {
this.cacheable();
}
const iconFontScssPath = path.resolve('app/styles/base/_icon-font.scss');
this.addDependency(iconFontScssPath);
fs.readFile(iconFontScssPath, 'utf-8', async (err, scssSource) => {
if (err) {
return callback(err);
}
process.stdout.write('Building fontawesome.woff2... ');
const startTime = Date.now();
try {
const { fontData, iconsCount } = await buildFont(this, scssSource);
const kb = (fontData.byteLength / 1024).toFixed(2);
const time = Date.now() - startTime;
process.stdout.write(`ok: ${time}ms, ${iconsCount} icons, ${kb} KiB\n`);
const fontCss = fontData.toString('base64');
callback(null, `module.exports = "data:font/woff2;base64,${fontCss}"`);
} catch (ex) {
process.stdout.write('error\n');
callback(ex);
}
});
};
function buildFont(loader, scssSource) {
const includedIcons = {};
const includedIconList = [...scssSource.matchAll(/\n\$fa-var-([\w-]+):/g)].map(
([, name]) => name
);
for (const iconName of includedIconList) {
if (includedIcons[iconName]) {
throw new Error(`Duplicate icon: $fa-var-${iconName}`);
}
if (!allIcons[iconName]) {
throw new Error(`Icon not found: "${iconName}"`);
}
includedIcons[iconName] = true;
}
const fontStream = new SVGIcons2SVGFontStream({
fontName: 'Font Awesome 5 Free',
round: 10e12,
log() {}
});
const fontData = [];
fontStream.on('data', (chunk) => fontData.push(chunk));
let charCode = 0xf000;
for (const iconName of includedIconList) {
++charCode;
const svgIconPath = allIcons[iconName];
loader.addDependency(svgIconPath);
const glyph = fs.createReadStream(svgIconPath);
glyph.metadata = { name: iconName, unicode: [String.fromCharCode(charCode)] };
fontStream.write(glyph);
}
fontStream.end();
return new Promise((resolve, reject) => {
fontStream.on('end', async () => {
try {
let data = Buffer.concat(fontData);
data = Buffer.from(svg2ttf(data.toString('utf8')).buffer);
data = Buffer.from(await wawoff2.compress(data));
resolve({ fontData: data, iconsCount: includedIconList.length });
} catch (ex) {
reject(ex);
}
});
});
}
module.exports.raw = true;