diff --git a/src/render.ts b/src/render.ts index 7335261..3ff3130 100644 --- a/src/render.ts +++ b/src/render.ts @@ -2,7 +2,7 @@ import { Node, Nodeish, HTMLNode } from "./node.ts"; import { EmptyElements } from "./emptyElements.ts"; import { Attr } from "./attributes.ts"; import { isState } from "./state.ts"; -import { Falsy, isFalsy, escapeHTML } from "./util.ts"; +import { Falsy, isFalsy, escapeAttr, escapeTextNode } from "./util.ts"; import { guessEnv } from "./guessEnv.ts"; import { HtmlElement } from "./domTypes.ts"; @@ -28,14 +28,14 @@ function attrifyHTML(attrs: AttributeObject, prefix = ""): string { if (typeof value === "boolean") if (value) return `${prefix + attr}`; else return ""; - if (value) return `${prefix + attr}="${value}"`; + if (value) return `${prefix + attr}="${escapeAttr(String(value))}"`; }) .join(" "); } export function renderHTML(node: Nodeish): string { if (isFalsy(node)) return ""; - if (typeof node === "string") return escapeHTML(node); + if (typeof node === "string") return escapeTextNode(node); if (node instanceof HTMLNode) return node.htmlString; if (isState(node)) return renderHTML(node.init); @@ -92,7 +92,7 @@ const toDOM = function toDOM( opts: { emptyTextNodes?: boolean } = {}, ): "" | ChildNode | HtmlElement { if (typeof node === "string" && (node !== "" || opts.emptyTextNodes)) - return document.createTextNode(escapeHTML(node)); + return document.createTextNode(node); if (isFalsy(node)) return ""; if (node instanceof HTMLNode) return htmlStringToElement(node.htmlString); if (isState(node)) { diff --git a/src/util.ts b/src/util.ts index bdf9100..00dee2a 100644 --- a/src/util.ts +++ b/src/util.ts @@ -6,9 +6,12 @@ const escapables = { '"': """, }; -export const escapeHTML = (s: string) => +export const escapeAttr = (s: string) => s.replace(/<|>|&|"|'/g, r => escapables[r as keyof typeof escapables] || r); +export const escapeTextNode = (s: string) => + s.replace(/<|>|&/g, r => escapables[r as keyof typeof escapables] || r); + export type Falsy = false | "" | 0 | 0n | undefined | null; export const Falsy = new Set([false, "", 0, 0n, undefined, null]); diff --git a/test.ts b/test.ts index 19be201..d5cbcc9 100644 --- a/test.ts +++ b/test.ts @@ -14,6 +14,25 @@ Deno.test({ }, }); +Deno.test({ + name: "renderHTML simple -- escaping attribute and text nodes", + fn: () => { + assertEquals( + renderHTML( + div( + { + id: "hello", + class: "world", + style: `content: '"&"'`, + }, + "<'\"Hello&world\"'>", + ), + ), + `
<'"Hello&world"'>
`, + ); + }, +}); + assertEquals( renderHTML( div(