diff --git a/lib/forge-std b/lib/forge-std index 27e14b7..9825609 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 27e14b7f2448e5f5ac32719f51fe652aa0b0733e +Subproject commit 98256095f37d83653ba1617d06ccd9604cbd7c6d diff --git a/packages/inspector/hooks/useFormattedSVG.tsx b/packages/inspector/hooks/useFormattedSVG.tsx index b5d8d4e..b64a50b 100644 --- a/packages/inspector/hooks/useFormattedSVG.tsx +++ b/packages/inspector/hooks/useFormattedSVG.tsx @@ -1,7 +1,8 @@ import { useState, useEffect } from "react"; -// import babelParser from 'prettier/parser-babel' import htmlParser from "prettier/parser-html"; import prettier from "prettier/standalone"; +import { parse } from "svg-parser"; +import { svgElementAttributes } from "svg-element-attributes"; const options = { parser: "html", @@ -25,7 +26,12 @@ export const useFormattedSvg = (svg: string) => { if (svg === null) { setFormattedSvg(""); } else { - setFormattedSvg(formatSVG(svg)); + const ast = parse(svg); + + validateTagsAndProperties(ast); + + const formatted = formatSVG(svg); + setFormattedSvg(formatted); } } catch (e) { setError(e.message); @@ -34,3 +40,41 @@ export const useFormattedSvg = (svg: string) => { return { formattedSvg, error }; }; + +// Overrides +svgElementAttributes.svg.push("xmlns", "xmlns:xlink"); +svgElementAttributes.mpath.push("xlink:href"); + +const validTags = Object.keys(svgElementAttributes).filter((tag) => tag !== "*"); + +function validateTagsAndProperties(node) { + if (node.children) { + node.children.forEach((child) => { + + if (child.type === "element") { + + // Check tag names first + if (validTags.includes(child.tagName) === false) { + // somehow write a traceback that ascends the tree + throw Error(`Invalid tag name: ${child.tagName}`); + } + + // Check attributes + const validPropertiesForThisTag = [ + ...svgElementAttributes[child.tagName], + ...svgElementAttributes["*"], + ]; + const invalidProperties = Object.keys(child.properties).filter( + (attr) => validPropertiesForThisTag.includes(attr) === false + ); + + if (invalidProperties.length > 0) { + throw Error( + `Invalid attributes for tag ${child.tagName}: ${invalidProperties.join(", ")}` + ); + } + } + validateTagsAndProperties(child); + }); + } +} diff --git a/packages/inspector/package.json b/packages/inspector/package.json index a2f799c..b570989 100644 --- a/packages/inspector/package.json +++ b/packages/inspector/package.json @@ -26,6 +26,8 @@ "react-dom": "^18.0.0", "react-icons": "^4.3.1", "react-syntax-highlighter": "^15.5.0", + "svg-element-attributes": "^2.0.1", + "svg-parser": "^2.0.4", "ts-node": "^10.7.0", "typescript": "^4.6.3", "xml-formatter": "^2.6.1" diff --git a/packages/inspector/pages/index.tsx b/packages/inspector/pages/index.tsx index b06b54c..6473510 100644 --- a/packages/inspector/pages/index.tsx +++ b/packages/inspector/pages/index.tsx @@ -68,8 +68,8 @@ export default function Index() { {error.length === 0 ? null : ( <> -