diff --git a/apps/builder/app/builder/features/settings-panel/controls/code.tsx b/apps/builder/app/builder/features/settings-panel/controls/code.tsx
index 61bbc9e58ffb..9c46f490783d 100644
--- a/apps/builder/app/builder/features/settings-panel/controls/code.tsx
+++ b/apps/builder/app/builder/features/settings-panel/controls/code.tsx
@@ -67,30 +67,58 @@ const ErrorInfo = ({
type Error = { message: string; value: string; expected: string };
+/**
+ * Use DOMParser in xml mode to parse potential svg
+ */
+const parseSvg = (value: string) => {
+ const doc = new DOMParser().parseFromString(value, "application/xml");
+ const errorNode = doc.querySelector("parsererror");
+ if (errorNode) {
+ return "";
+ }
+ return doc.documentElement.outerHTML;
+};
+
+const parseHtml = (value: string) => {
+ const div = document.createElement("div");
+ div.innerHTML = value;
+ return div.innerHTML;
+};
+
// The problem is to identify broken HTML and because browser is flexible and always tries to fix it we never
// know if something is actually broken.
-// 1. Parse the HTML using DOM
-// 2. Get HTML via innerHTML
-// 3. Compare the original HTML with innerHTML
-// 4. We try to minimize the amount of false positives by removing
+// 1. Parse potential SVG with XML parser and serialize
+// 2. Compare the original SVG with resulting value
+// 3. Parse the HTML using DOM parser and serialize
+// 4. Compare the original HTML with resulting value
+// 5. We try to minimize the amount of false positives by removing
// - different amount of whitespace
// - unifying `boolean=""` is the same as `boolean`
+// - xmlns attirbute which is always reordered first
const validateHtml = (value: string): Error | undefined => {
- const div = document.createElement("div");
- div.innerHTML = value;
- const expected = div.innerHTML;
const clean = (value: string) => {
return (
value
// Compare without whitespace to avoid false positives
- .replace(/\s/g, "")
+ .replaceAll(/\s/g, "")
// normalize boolean attributes by turning `boolean=""` into `boolean`
- .replace('=""', "")
+ .replaceAll('=""', "")
+ // namespace attribute is always reordered first
+ .replaceAll('xmlns="http://www.w3.org/2000/svg"', "")
);
};
- if (clean(value) !== clean(expected)) {
- return { message: "Invalid HTML detected", value, expected };
+ // in many cases svg is valid xml so serialize in xml mode first
+ // to avoid false positive of auto closing svg tags, for example
+ // ->
+ const xml = parseSvg(value);
+ if (clean(xml) === clean(value)) {
+ return;
+ }
+ const html = parseHtml(value);
+ if (clean(html) === clean(value)) {
+ return;
}
+ return { message: "Invalid HTML detected", value, expected: html ?? "" };
};
export const CodeControl = ({