import fs from "fs"; import path from "path"; import cheerio from "cheerio"; import { getSvgBoundingBox } from "./bounding-box"; import { extractSpecs } from "./specs"; import { SvgSymbolData, SvgSymbolElement } from "./svg-symbol"; 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 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}>`); } export function build() { const filenames = fs.readdirSync(SVG_DIR); const vocab: SvgSymbolData[] = []; for (let filename of filenames) { if (path.extname(filename) === SVG_EXT) { console.log(`Adding ${filename} to vocabulary.`); const svgMarkup = fs.readFileSync(path.join(SVG_DIR, filename), { encoding: "utf-8", }); const $ = cheerio.load(svgMarkup); const svgEl = $("svg"); const name = path.basename(filename, SVG_EXT).toLowerCase(); const rawLayers = onlyTags(svgEl.children()).map((ch) => serializeSvgSymbolElement($, ch) ); const [specs, layers] = extractSpecs(rawLayers); const bbox = getSvgBoundingBox(layers); const symbol: SvgSymbolData = { name, bbox, layers, specs, }; vocab.push(symbol); } } console.log(`Writing ${VOCAB_PATH}.`); fs.writeFileSync(VOCAB_PATH, JSON.stringify(vocab, null, 2)); }