diff --git a/.gitignore b/.gitignore index c3f3a00..d3a1666 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ pnpm* dist tmp *.log -.out* \ No newline at end of file +.out* +test \ No newline at end of file diff --git a/src/html.spec.tsx b/src/html.spec.tsx index 41956a5..f380a7b 100644 --- a/src/html.spec.tsx +++ b/src/html.spec.tsx @@ -5,8 +5,8 @@ import { html as h } from "./html" describe("HTML", () => { it("should generate a string", () => { - let s = h("a", { href: "example.com" }, "Welcome") - expect(s).toEqual('Welcome') + let s = h("a", { href: "example.com" }, "Welcome & Hello & Ciao") + expect(s).toEqual('Welcome & Hello &amp; Ciao') // the second & is correct, because plain string should be unescaped }) it("should nest", () => { diff --git a/src/html.ts b/src/html.ts index b04e48e..c58d286 100644 --- a/src/html.ts +++ b/src/html.ts @@ -27,18 +27,8 @@ export const SELF_CLOSING_TAGS = [ "command", ] -let USED_JSX: Record = {} // HACK:dholtwick:2016-08-23 - -export function CDATA(s: string) { - s = "" - USED_JSX[s] = true - return s -} - -export function HTML(s: string) { - USED_JSX[s] = true - return s -} +export const CDATA = (s: string) => "" +export const HTML = (s: string) => s // export function prependXMLIdentifier(s) { // return '\n' + s @@ -51,15 +41,17 @@ export function markup( attrs: any = {}, children?: any[] ) { - // console.log('markup', xmlMode, tag, attrs, children) const hasChildren = children && children.length > 0 - let s = "" + + let parts: string[] = [] tag = tag.replace(/__/g, ":") - if (tag !== "noop") { + + // React fragment <>... and ours: ... + if (tag !== "noop" && tag !== "") { if (tag !== "cdata") { - s += `<${tag}` + parts.push(`<${tag}`) } else { - s += " v[k] != null) - .map((k) => { - let vv = v[k] - vv = typeof vv === "number" ? vv + "px" : vv - return `${k - .replace(/([a-z])([A-Z])/g, "$1-$2") - .toLowerCase()}:${vv}` - }) - .join(";")}"` + parts.push( + ` ${name}="${Object.keys(v) + .filter((k) => v[k] != null) + .map((k) => { + let vv = v[k] + vv = typeof vv === "number" ? vv + "px" : vv + return `${k + .replace(/([a-z])([A-Z])/g, "$1-$2") + .toLowerCase()}:${vv}` + }) + .join(";")}"` + ) } else if (v !== false && v != null) { - s += ` ${name}="${escapeHTML(v.toString())}"` + parts.push(` ${name}="${escapeHTML(v.toString())}"`) } } } if (tag !== "cdata") { if (xmlMode && !hasChildren) { - s += " />" - USED_JSX[s] = true - return s + parts.push(" />") + return parts.join("") } else { - s += `>` + parts.push(">") } } - if (!xmlMode) { - if (SELF_CLOSING_TAGS.includes(tag)) { - USED_JSX[s] = true - return s - } + if (!xmlMode && SELF_CLOSING_TAGS.includes(tag)) { + return parts.join("") } } @@ -118,10 +108,10 @@ export function markup( child = [child] } for (let c of child) { - if (USED_JSX[c] || tag === "script" || tag === "style") { - s += c + if (c.startsWith("<") || tag === "script" || tag === "style") { + parts.push(c) } else { - s += escapeHTML(c.toString()) + parts.push(escapeHTML(c.toString())) } } } @@ -129,18 +119,17 @@ export function markup( } if (attrs.html) { - s += attrs.html + parts.push(attrs.html) } if (tag !== "noop") { if (tag !== "cdata") { - s += `` + parts.push(``) } else { - s += "]]>" + parts.push("]]>") } } - USED_JSX[s] = true - return s + return parts.join("") } export function html(itag: string, iattrs?: object, ...ichildren: any[]) {