Skip to content

Commit

Permalink
fix: self closing tags
Browse files Browse the repository at this point in the history
  • Loading branch information
ncpa0cpl committed Sep 16, 2023
1 parent e308bbe commit b958a76
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 63 deletions.
80 changes: 40 additions & 40 deletions __tests__/html-parser/__snapshots__/render-to-html.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
exports[`renderToHTML ErrorBoundary should correctly render the tree that's inside an ErrorBoundary 1`] = `
" <html>
<head>
<meta charset=\\"utf-8\\"></meta>
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\"></meta>
<meta charset=\\"utf-8\\" />
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\" />
<title>Page Title</title>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\"></meta>
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\"></link>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" />
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\" />
</head>
<body>
<div class=\\"main-container\\">
<h2>Hello World!</h2>
<input value=\\"write here\\"></input>
<input value=\\"write here\\" />
<button>Submit</button>
</div>
</body>
Expand Down Expand Up @@ -43,16 +43,16 @@ exports[`renderToHTML ErrorBoundary should render the fallback if the direct chi
exports[`renderToHTML should correctly generate html from component base jsx structure 1`] = `
"<html>
<head>
<meta charset=\\"utf-8\\"></meta>
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\"></meta>
<meta charset=\\"utf-8\\" />
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\" />
<title>Page Title</title>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\"></meta>
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\"></link>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" />
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\" />
</head>
<body>
<div class=\\"main-container\\">
<h2>Hello World!</h2>
<input value=\\"write here\\"></input>
<input value=\\"write here\\" />
<button>Submit</button>
</div>
</body>
Expand All @@ -64,23 +64,23 @@ exports[`renderToHTML should correctly generate html from simple jsx 1`] = `
<h1>Hello World</h1>
<h2>Prop Title</h2>
<button onclick=\\"console.log(&quot;Hello World!&quot;)\\">Click me!</button>
<input autofocus draggable></input>
<input autofocus draggable />
</div>"
`;

exports[`renderToHTML should correctly parse async components 1`] = `
"<html>
<head>
<meta charset=\\"utf-8\\"></meta>
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\"></meta>
<meta charset=\\"utf-8\\" />
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\" />
<title>Page Title</title>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\"></meta>
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\"></link>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" />
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\" />
</head>
<body>
<div class=\\"main-container\\">
<h2>Async title</h2>
<input value=\\"write here\\"></input>
<input value=\\"write here\\" />
<button>Submit</button>
</div>
</body>
Expand Down Expand Up @@ -147,7 +147,7 @@ exports[`renderToHTML should properly handle context data correctly handles enca
<div id=\\"root\\">
<div class=\\"template for-magic\\">
<div>
<input type=\\"text\\" name=\\"repeating_magic_dwarf\\"></input>
<input type=\\"text\\" name=\\"repeating_magic_dwarf\\" />
</div>
</div>
</div>
Expand Down Expand Up @@ -182,11 +182,11 @@ exports[`renderToHTML should properly handle context data should correctly drill
exports[`renderToHTML should properly handle context data should correctly handle provider pattern 1`] = `
"<html>
<head>
<meta charset=\\"utf-8\\"></meta>
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\"></meta>
<meta charset=\\"utf-8\\" />
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\" />
<title>Page Title</title>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\"></meta>
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\"></link>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" />
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\" />
</head>
<body>
<div class=\\"main-container\\">
Expand All @@ -201,27 +201,27 @@ exports[`renderToHTML should properly handle context data should correctly handl
exports[`renderToHTML should properly handle context data should correctly override existing context data 1`] = `
"<html>
<head>
<meta charset=\\"utf-8\\"></meta>
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\"></meta>
<meta charset=\\"utf-8\\" />
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\" />
<title>Page Title</title>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\"></meta>
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\"></link>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" />
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\" />
</head>
<body>
<h1>This title was overridden</h1>
<input placeholder=\\"write here\\"></input>
<input placeholder=\\"write here\\" />
</body>
</html>"
`;

exports[`renderToHTML should properly handle context data should correctly render jsx with context data 1`] = `
"<html>
<head>
<meta charset=\\"utf-8\\"></meta>
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\"></meta>
<meta charset=\\"utf-8\\" />
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\" />
<title>Page Title</title>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\"></meta>
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\"></link>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" />
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\" />
</head>
<body>
<div class=\\"main-container\\">
Expand All @@ -236,17 +236,17 @@ exports[`renderToHTML should properly handle context data should correctly rende
exports[`renderToHTML should properly handle context data should correctly render jsx with context data and arrays in between elements 1`] = `
"<html>
<head>
<meta charset=\\"utf-8\\"></meta>
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\"></meta>
<meta charset=\\"utf-8\\" />
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\" />
<title>Page Title</title>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\"></meta>
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\"></link>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" />
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\" />
</head>
<body>
<div>
<h1>This title is set via the context</h1>
</div>
<input placeholder=\\"write here\\"></input>
<input placeholder=\\"write here\\" />
<button>Submit</button>
</body>
</html>"
Expand All @@ -255,17 +255,17 @@ exports[`renderToHTML should properly handle context data should correctly rende
exports[`renderToHTML should properly handle context data should correctly render jsx with context data and async components 1`] = `
"<html>
<head>
<meta charset=\\"utf-8\\"></meta>
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\"></meta>
<meta charset=\\"utf-8\\" />
<meta http-equiv=\\"x-ua-compatible\\" content=\\"IE=edge\\" />
<title>Page Title</title>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\"></meta>
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\"></link>
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\" />
<link rel=\\"stylesheet\\" type=\\"text/css\\" media=\\"screen\\" href=\\"main.css\\" />
</head>
<body>
<div>
<h1>This title is set via the context</h1>
</div>
<input placeholder=\\"write here\\"></input>
<input placeholder=\\"write here\\" />
<button>Submit</button>
</body>
</html>"
Expand Down
40 changes: 35 additions & 5 deletions src/html-parser/jsx-elem-to-html.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ComponentApi } from "../component-api/component-api";
import { ErrorBoundary } from "../error-boundary/error-boundary";
import { join } from "../utilities/join";
import { SELF_CLOSING_TAG_LIST } from "../utilities/self-closing-tag-list";
import { mapAttributesToHtmlTagString } from "./attribute-to-html-tag-string";
import { getHTMLStruct } from "./get-html-struct";

Expand Down Expand Up @@ -104,6 +105,9 @@ export const jsxElemToHtmlSync = (
}
return join(results);
} else {
const isSelfClosingTag =
htmlStruct.children.length === 0 &&
SELF_CLOSING_TAG_LIST.includes(htmlStruct.tag);
const inlineTag =
htmlStruct.children.length === 0 ||
htmlStruct.children.every(isTextNode);
Expand All @@ -113,9 +117,22 @@ export const jsxElemToHtmlSync = (
mapAttributesToHtmlTagString(htmlStruct.attributes),
" ",
);

const separator = attrString.length ? " " : "";

if (isSelfClosingTag) {
return (
`${indentPadding}<${htmlStruct.tag}` +
separator +
join(mapAttributesToHtmlTagString(htmlStruct.attributes), " ") +
separator +
"/>"
);
}

const startTag =
`${indentPadding}<${htmlStruct.tag}` +
(attrString.length ? " " : "") +
separator +
join(mapAttributesToHtmlTagString(htmlStruct.attributes), " ") +
">";
const endTag = `${inlineTag ? "" : indentPadding}</${htmlStruct.tag}>`;
Expand Down Expand Up @@ -214,6 +231,9 @@ export const jsxElemToHtmlAsync = async (
}
return join(results);
} else {
const isSelfClosingTag =
htmlStruct.children.length === 0 &&
SELF_CLOSING_TAG_LIST.includes(htmlStruct.tag);
const inlineTag =
htmlStruct.children.length === 0 ||
htmlStruct.children.every(isTextNode);
Expand All @@ -223,11 +243,21 @@ export const jsxElemToHtmlAsync = async (
mapAttributesToHtmlTagString(htmlStruct.attributes),
" ",
);

const separator = attrString.length ? " " : "";

if (isSelfClosingTag) {
return (
`${indentPadding}<${htmlStruct.tag}` +
separator +
join(mapAttributesToHtmlTagString(htmlStruct.attributes), " ") +
separator +
"/>"
);
}

const startTag =
`${indentPadding}<${htmlStruct.tag}` +
(attrString.length ? " " : "") +
attrString +
">";
`${indentPadding}<${htmlStruct.tag}` + separator + attrString + ">";
const endTag = `${inlineTag ? "" : indentPadding}</${htmlStruct.tag}>`;
const children: string[] = [];

Expand Down
40 changes: 22 additions & 18 deletions src/string-template-parser/jsx-elem-to-strings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ComponentApi } from "../component-api/component-api";
import { ErrorBoundary } from "../error-boundary/error-boundary";
import { createElement } from "../jsx-runtime";
import { SELF_CLOSING_TAG_LIST } from "../utilities/self-closing-tag-list";
import { Interpolate, InterpolateTag } from "./interpolate";
import { mapAttributeName } from "./map-attribute-name";
import type { StringTemplateParserOptions } from "./render-to-string-template-tag";
Expand Down Expand Up @@ -136,13 +137,12 @@ export const jsxElemToTagFuncArgsSync = (

return results;
} else {
const results: TagFunctionArgs = [[], []];
const isSelfClosingTag =
children.length === 0 && SELF_CLOSING_TAG_LIST.includes(element.tag);

const part1 = `<${element.tag}`;
const part2 = ">";
const part3 = `</${element.tag}>`;
const results: TagFunctionArgs = [[], []];

results[0].push(part1);
results[0].push(`<${element.tag}`);

const attrList = Object.entries(attributes);
for (let index = 0; index < attrList.length; index++) {
Expand All @@ -161,24 +161,28 @@ export const jsxElemToTagFuncArgsSync = (
results[0].push('"');
}

concatToLastStringOrPush(results, part2);
if (isSelfClosingTag) {
concatToLastStringOrPush(results, "/>");
} else {
concatToLastStringOrPush(results, ">");

for (let i = 0; i < children.length; i++) {
const child = children[i]!;
const [[first, ...strings], tagParams] = jsxElemToTagFuncArgsSync(
child,
options,
componentApi,
);

for (let i = 0; i < children.length; i++) {
const child = children[i]!;
const [[first, ...strings], tagParams] = jsxElemToTagFuncArgsSync(
child,
options,
componentApi,
);
concatToLastStringOrPush(results, first);

concatToLastStringOrPush(results, first);
results[0] = results[0].concat(strings);
results[1] = results[1].concat(tagParams);
}

results[0] = results[0].concat(strings);
results[1] = results[1].concat(tagParams);
concatToLastStringOrPush(results, `</${element.tag}>`);
}

concatToLastStringOrPush(results, part3);

return results;
}
}
Expand Down
16 changes: 16 additions & 0 deletions src/utilities/self-closing-tag-list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const SELF_CLOSING_TAG_LIST = [
"area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"link",
"meta",
"param",
"source",
"track",
"wbr",
];

0 comments on commit b958a76

Please sign in to comment.