UNPKG

rollup-plugin-svgsprite-generator

Version:

Create external svg sprite

203 lines (188 loc) 6.69 kB
const path = require("path"); const fs = require("fs"); const cheerio = require("cheerio"); const { optimize } = require("svgo"); const crypto = require("crypto"); exports.svgSprite = function (options = {}) { const { minify = true, doctype = true, xml = true, inlineStyles = true, idConvert = (n) => n, styleModification = (s) => s, postGeneration = () => {}, output, input, hash = false } = options; let outputVar = output; if (!output) { throw new Error('"output" should be specified'); } if (!input) { throw new Error('"input" should be specified'); } const svgo_config = { js2svg: { pretty: !minify, indent: 2, }, plugins: [ "removeUselessStrokeAndFill", "removeEmptyText", "removeEmptyContainers", "removeEmptyAttrs", "removeDimensions", "removeUselessDefs", "minifyStyles", "convertPathData", "removeComments", ], }; let ids = []; return { name: "svg-sprite", generateBundle() { ids = []; const fileName = writeBundle( fs.readdirSync(input).map((file) => { try { const filePath = path.join(input, file); const svgid = path.parse(filePath).name; const code = fs.readFileSync(filePath, { encoding: "utf-8", }); return createSymbol(code, svgid); } catch (e) { console.error(file + ":" + e); } }) ); postGeneration(ids.sort(), fileName); }, transform() { this.addWatchFile(input); }, }; function writeBundle(symbols) { if (symbols.length > 0) { let result = ""; if (xml) result += '<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'; if (doctype) result += '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >\n'; if (minify && symbols.length > 1) // svgo bug optimization when only one symbol result += optimize(createSprite(symbols), svgo_config).data; else result += createSprite(symbols); let fileName = path.basename(outputVar); if (hash) { const hashStr = crypto .createHash("md5") .update(result) .digest("hex") .substring(0, 10); const outputData = path.parse(outputVar); fileName = `${outputData.name}-${hashStr}${outputData.ext}`; outputVar = path.join( outputData.dir, fileName ); } fs.writeFileSync(outputVar, result); return fileName; } return null; } function createSymbol(code, id) { const markup = cheerio.load(code, { xmlMode: true }); const svgMarkup = markup("svg"); const symbolId = idConvert(svgMarkup.find("title").text() || id); ids.push(symbolId); markup("svg").replaceWith("<symbol>"); const children = svgMarkup.children(); const style = getStyles(); markup("symbol") .attr("id", symbolId) .attr("viewBox", svgMarkup.attr("viewBox")) .append( children.filter((s) => { if (inlineStyles) { const child = children.get(s); if (child.tagName == "style") return false; modify(child); } return true; }) ); return markup.xml("symbol"); function getStyles() { const find = svgMarkup.find("style").get(0); var result = {}; if (find) { var regex = new RegExp( "\\.(?<name>[\\w-]+)\\s*{(?<value>([^}])+)", "g" ); var match; while ((match = regex.exec(find.children[0].data)) != null) { if (result[match.groups["name"]]) { console.error( "Double definition of " + match.groups["name"] + " at " + id + ".svg" ); } const styles = {}; match.groups["value"] .trim() .split(";") .forEach((v) => { const s = v.split(":"); if (s.length > 1) styles[s[0].trim()] = s[1].trim(); }); result[match.groups["name"]] = styleModification(styles); } } return result; } function modify(child) { if (child.attribs) { const className = child.attribs.class; if (className) { if (style[className]) { strigifyStyle(style[className]); } else if (child.tagName == "path") { console.error( "Class " + className + " not found in style at " + id + ".svg" ); strigifyStyle(styleModification({})); } } } if (child.children) child.children.forEach(modify); if (child.attribs && child.attribs.class) delete child.attribs.class; function strigifyStyle(styleObject) { child.attribs.style = ""; Object.entries(styleObject).forEach(([key, value]) => { child.attribs.style += `${key} : ${value};`; }); } } } }; function createSprite(symbols) { return `<svg xmlns="http://www.w3.org/2000/svg">${symbols.join( "\n" )}</svg>`; }