From 5df222edeac4f2ed6aeccd2752da0b1037170e9e Mon Sep 17 00:00:00 2001
From: Atul Varma
Date: Sat, 6 Feb 2021 07:50:51 -0500
Subject: [PATCH] Process multiple children of
);
};
@@ -64,6 +91,7 @@ const App: React.FC<{}> = () => {
{SvgVocabulary.map((symbolData) => (
;
+ }
+ | {
+ tagName: "path";
+ props: SVGProps;
+ }
+) & {
+ children: SvgSymbolElement[];
+};
+
+const SUPPORTED_SVG_TAG_ARRAY: SvgSymbolElement["tagName"][] = ["g", "path"];
+const SUPPORTED_SVG_TAGS = new Set(SUPPORTED_SVG_TAG_ARRAY);
+
const MY_DIR = __dirname;
const SVG_DIR = path.join(MY_DIR, "..", "svg");
const VOCAB_PATH = path.join(MY_DIR, "svg-vocabulary.json");
const SVG_EXT = ".svg";
-function removeAttrIfNotNone(
- attr: string,
- $: cheerio.Root,
- g: cheerio.Cheerio
-) {
- const items = g.find(`[${attr}]`);
- items.each(function (this: any, i, el) {
- if ($(this).attr(attr) !== "none") {
- $(this).removeAttr(attr);
+function onlyTags(
+ elements: cheerio.Element[] | cheerio.Cheerio
+): cheerio.TagElement[] {
+ const result: cheerio.TagElement[] = [];
+
+ for (let i = 0; i < elements.length; i++) {
+ const el = elements[i];
+ if (el.type === "tag") {
+ result.push(el);
}
- });
+ }
+
+ return result;
+}
+
+function isSupportedSvgTag(
+ tagName: string
+): tagName is SvgSymbolElement["tagName"] {
+ return SUPPORTED_SVG_TAGS.has(tagName as any);
+}
+
+const SVG_ATTRIB_TO_PROP_MAP: {
+ [key: string]: keyof SvgSymbolElement["props"] | undefined;
+} = {
+ id: "id",
+ fill: "fill",
+ stroke: "stroke",
+ d: "d",
+ "stroke-linejoin": "strokeLinejoin",
+ "stroke-linecap": "strokeLinecap",
+ "stroke-width": "strokeWidth",
+ "fill-rule": "fillRule",
+};
+
+function attribsToProps(el: cheerio.TagElement): any {
+ const { attribs } = el;
+ const result: any = {};
+
+ for (let attrib of Object.keys(attribs)) {
+ const prop = SVG_ATTRIB_TO_PROP_MAP[attrib];
+
+ if (!prop) {
+ throw new Error(`Unknown SVG attribute '${attrib}' in <${el.tagName}>!`);
+ }
+
+ result[prop] = attribs[attrib];
+ }
+
+ return result;
+}
+
+function serializeSvgSymbolElement(
+ $: cheerio.Root,
+ el: cheerio.TagElement
+): SvgSymbolElement {
+ let children = onlyTags(el.children).map((child) =>
+ serializeSvgSymbolElement($, child)
+ );
+ const { tagName } = el;
+ if (isSupportedSvgTag(tagName)) {
+ return {
+ tagName,
+ props: attribsToProps(el) as any,
+ children,
+ };
+ }
+ throw new Error(`Unsupported SVG element: <${tagName}>`);
}
function getSvgPixelDimension($: cheerio.Root, attr: string): number {
@@ -35,7 +108,7 @@ function getSvgPixelDimension($: cheerio.Root, attr: string): number {
const match = value.match(/^(\d+)px$/);
if (!match) {
throw new Error(
- `unable to parse ${attr} attribute value '${value}'!`
+ `Unable to parse ${attr} attribute value '${value}'!`
);
}
return parseInt(match[1]);
@@ -53,19 +126,17 @@ export function build() {
const $ = cheerio.load(svgMarkup);
const width = getSvgPixelDimension($, "width");
const height = getSvgPixelDimension($, "height");
- const g = $("svg > g");
- removeAttrIfNotNone("fill", $, g);
- removeAttrIfNotNone("stroke", $, g);
+ const svgEl = $("svg");
const name = path.basename(filename, SVG_EXT);
- const svg = g.html();
- if (!svg) {
- throw new Error(`${filename} has no with child elements!`);
- }
+ const layers = onlyTags(svgEl.children()).map((ch) =>
+ serializeSvgSymbolElement($, ch)
+ );
+
const symbol: SvgSymbolData = {
name,
- svg,
width,
height,
+ layers,
};
vocab.push(symbol);
}