tools/__tasks__/compile/images/icons.mjs (117 lines of code) (raw):
import { paths } from '../../config.mjs';
import fs from 'node:fs';
import path from 'node:path';
import glob from 'glob';
import btoa from 'btoa';
import { optimize, extendDefaultPlugins } from 'svgo';
import mkdirp from 'mkdirp';
import { fileURLToPath } from 'node:url';
const getSVG = (iconPath) =>
new Promise((resolve, reject) => {
// eslint-disable-next-line consistent-return
fs.readFile(iconPath, { encoding: 'utf-8' }, (err, data) => {
if (err) return reject(err);
try {
resolve({
name: path.parse(iconPath).name,
data: optimize(data, {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
removeViewBox: false,
},
},
},
],
}),
});
} catch (e) {
return reject(e);
}
});
});
const sortSVGs = (svgs) =>
svgs.sort((a, b) => {
const aInfo = a.data.info;
const bInfo = b.data.info;
if (aInfo.height !== bInfo.height) {
return aInfo.height - bInfo.height;
} else if (aInfo.width !== bInfo.width) {
return bInfo.width - aInfo.width;
}
return a.name.localeCompare(b.name);
});
const generateSassForSVG = (svg) => {
const {
name,
data: fileData,
data: {
info: { width = 0, height = 0 },
},
} = svg;
return `
%svg-i-${name},
.svg-i-${name} {
background-image: url(data:image/svg+xml;base64,${btoa(
fileData.data,
)});
background-position: 0 0;
width: ${width}px;
height: ${height}px;
}
.svg .i-${name} {
@extend %svg-i-${name} !optional;
}
`.replace(/ {8}/g, '');
};
const saveSass = (sass, dest, fileName) =>
new Promise((resolve, reject) => {
fs.writeFile(
path.join(dest, fileName),
`
// THIS FILE WAS AUTOMATICALLY GENERATED BY
// ${path.relative(paths.root, fileURLToPath(import.meta.url))}
// DO NOT EDIT IT!
@if ($svg-support) {
${sass}
}
`
.trim()
.replace(/ {16}/g, ''),
(err) => {
if (err) return reject(err);
return resolve();
},
);
});
/** @type {import('listr2').ListrTask} */
const task = {
title: 'Create sprites',
task: (ctx, task) =>
task.newListr(
['commercial', 'global', 'membership', 'video'].map((target) => ({
title: `Spriting ${target}`,
task: () => {
const srcPath = path.join(paths.src, 'images', target);
const destPath = path.join(
paths.src,
'stylesheets',
'icons',
);
const fileName = `_${target}-icons-svg.scss`;
const iconPaths = glob.sync(path.join(srcPath, '*.svg'));
mkdirp.sync(destPath);
return Promise.all(iconPaths.map(getSVG))
.then(sortSVGs)
.then((svgs) =>
svgs.map(generateSassForSVG).join('').trim(),
)
.then((sass) => saveSass(sass, destPath, fileName));
},
})),
{ concurrent: !!ctx.verbose ? false : true },
),
};
export default task;