Skip to content

Commit

Permalink
fix: renderHTML must escape textnodes differently from attributes
Browse files Browse the repository at this point in the history
Signed-off-by: Muthu Kumar <[email protected]>
  • Loading branch information
MKRhere committed Aug 25, 2021
1 parent 5ba3899 commit 68df0dc
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 5 deletions.
8 changes: 4 additions & 4 deletions src/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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);

Expand Down Expand Up @@ -92,7 +92,7 @@ const toDOM = function toDOM<N extends Nodeish>(
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)) {
Expand Down
5 changes: 4 additions & 1 deletion src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ const escapables = {
'"': "&quot;",
};

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]);
Expand Down
19 changes: 19 additions & 0 deletions test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: '"&"</div>'`,
},
"<'\"Hello&world\"'>",
),
),
`<div id="hello" class="world" style="content: &#39;&quot;&amp;&quot;&lt;/div&gt;&#39;">&lt;'"Hello&amp;world"'&gt;</div>`,
);
},
});

assertEquals(
renderHTML(
div(
Expand Down

0 comments on commit 68df0dc

Please sign in to comment.