rollup-plugin-svgsprite-generator
Version:
Create external svg sprite
203 lines (188 loc) • 6.69 kB
JavaScript
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>`;
}