html-templater/hyper-build/html-templating.html
2024-04-28 17:06:53 -05:00

88 lines
9.1 KiB
HTML

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"/><meta content="width=device-width, initial-scale=1" name="viewport"/><link type="image/x-icon" rel="icon" href="resources/favicon.svg"/><!-- --><!-- --></head><body><div class="pure-g"><div class="home-menu pure-menu pure-menu-horizontal"><ul class="pure-menu-list"><li class="pure-menu-item"><a class="pure-menu-link" href="/home"> Home </a></li><li class="pure-menu-item"><a href="/projects" class="pure-menu-link"> Projects </a></li><li class="pure-menu-item"><a href="/blog" class="pure-menu-link"> Blog </a></li><li class="pure-menu-item"><a class="pure-menu-link" href="/about"> About </a></li></ul></div><div class="pure-u-1-1"><div class="header"><h1> Rust HTML Templating Engine </h1><h2> Front End - Parser Design </h2></div><div class="content"><h2 class="distinct"> Motivation </h2>
HTML is a powerful tool for creating static web pages, but
is not easily modular or scalable. Front end frameworks like
React provide reusable components. I want to use components
without the overhead of a front-end framework. I want the final
output to be statically generated rather than generated on the fly
by the server or the client, minified and with inlined CSS and
JavaScript.
<h2 class="distinct"> Approach </h2>
After looking at some of the HTML parsing libraries, I was
unsatisfied with the approaches most developers took. The DOM
is represented as a tree structure, where each node keeps a
pointer to its children.
<div class="code-block">
<pre><code>
<span class="comment">&#x2f;&#x2f; From the html_parser crate</span>
<span class="keyword">pub</span> <span class="keyword storage type">enum</span> <span class="type">Node</span> <span class="punctuation bracket">{</span>
<span class="type enum variant">Text</span><span class="punctuation bracket">(</span><span class="type">String</span><span class="punctuation bracket">)</span><span class="punctuation delimiter">,</span>
<span class="type enum variant">Element</span><span class="punctuation bracket">(</span><span class="type">Element</span><span class="punctuation bracket">)</span><span class="punctuation delimiter">,</span>
<span class="type enum variant">Comment</span><span class="punctuation bracket">(</span><span class="type">String</span><span class="punctuation bracket">)</span><span class="punctuation delimiter">,</span>
<span class="punctuation bracket">}</span>
<span class="keyword">pub</span> <span class="keyword storage type">struct</span> <span class="type">Element</span> <span class="punctuation bracket">{</span>
<span class="keyword">pub</span> <span class="variable other member">name</span>: <span class="type">String</span><span class="punctuation delimiter">,</span>
<span class="keyword">pub</span> <span class="variable other member">attributes</span>: <span class="type">HashMap</span><span class="punctuation bracket">&lt;</span><span class="type">String</span><span class="punctuation delimiter">,</span> <span class="type">Option</span><span class="punctuation bracket">&lt;</span><span class="type">String</span><span class="punctuation bracket">&gt;</span><span class="punctuation bracket">&gt;</span><span class="punctuation delimiter">,</span>
<span class="keyword">pub</span> <span class="variable other member">children</span>: <span class="type">Vec</span><span class="punctuation bracket">&lt;</span><span class="type">Node</span><span class="punctuation bracket">&gt;</span><span class="punctuation delimiter">,</span>
<span class="comment">&#x2f;&#x2f; other fields omitted</span>
<span class="punctuation bracket">}</span>
</code></pre>
</div>
This is bad for cache locality and effectively forces the use
of recursion in order to traverse the DOM. Recursion is undesirable
for performance and robustness reasons. My approach is to create a
flat array of elements.
<div class="code-block">
<pre><code>
<span class="comment">&#x2f;&#x2f; My implementation (fields omitted)</span>
<span class="keyword">pub</span> <span class="keyword storage type">enum</span> <span class="type">HtmlElement</span> <span class="punctuation bracket">{</span>
<span class="type enum variant">DocType</span><span class="punctuation delimiter">,</span>
<span class="type enum variant">Comment</span><span class="punctuation bracket">(</span><span class="comment">&#x2f;* *&#x2f;</span><span class="punctuation bracket">)</span><span class="punctuation delimiter">,</span>
<span class="type enum variant">OpenTag</span> <span class="punctuation bracket">{</span> <span class="comment">&#x2f;* *&#x2f;</span> <span class="punctuation bracket">}</span><span class="punctuation delimiter">,</span>
<span class="type enum variant">CloseTag</span> <span class="punctuation bracket">{</span> <span class="comment">&#x2f;* *&#x2f;</span> <span class="punctuation bracket">}</span><span class="punctuation delimiter">,</span>
<span class="type enum variant">Text</span><span class="punctuation bracket">(</span> <span class="comment">&#x2f;* *&#x2f;</span> <span class="punctuation bracket">)</span><span class="punctuation delimiter">,</span>
<span class="type enum variant">Script</span> <span class="punctuation bracket">{</span> <span class="comment">&#x2f;* *&#x2f;</span> <span class="punctuation bracket">}</span><span class="punctuation delimiter">,</span>
<span class="type enum variant">Style</span> <span class="punctuation bracket">{</span> <span class="comment">&#x2f;* *&#x2f;</span> <span class="punctuation bracket">}</span><span class="punctuation delimiter">,</span>
<span class="type enum variant">Directive</span> <span class="punctuation bracket">{</span> <span class="comment">&#x2f;* *&#x2f;</span> <span class="punctuation bracket">}</span><span class="punctuation delimiter">,</span>
<span class="punctuation bracket">}</span>
</code></pre>
</div>
This approach lends itself to linear iterative algorithms. An
element's children can be represented as a slice of the DOM array,
from the opening tag to its close tag.
<h2 class="distinct"> Templates </h2>
I define a "template" as a user defined element which expands into
a larger piece of HTML. Templates can pass on children and attributes
to the final output. Some special templates can perform other functions,
like inlining a CSS file or syntax highlighting a code block.
<div class="code-block">
<pre><code>
<span class="comment">&lt;!-- Here is an example of a template definition --&gt;</span>
<span class="punctuation bracket">&lt;</span><span class="tag">Echo</span><span class="punctuation bracket">&gt;</span>
<span class="comment">&lt;!--
This means to inherit the &#x27;class&#x27; attribute from the invocation
--&gt;</span>
<span class="punctuation bracket">&lt;</span><span class="tag">h1</span> <span class="attribute">class</span><span class="punctuation delimiter">=</span><span class="string">@class</span><span class="punctuation bracket">&gt;</span>
<span class="comment">&lt;!--
This means place the children of the invocation here
--&gt;</span>
<span class="punctuation bracket">&lt;</span>@children<span class="punctuation bracket">&#x2f;&gt;</span>
<span class="punctuation bracket">&lt;&#x2f;</span><span class="tag">h1</span><span class="punctuation bracket">&gt;</span>
<span class="punctuation bracket">&lt;</span><span class="tag">h2</span><span class="punctuation bracket">&gt;</span>
<span class="comment">&lt;!-- This means to inherit the &#x27;href&#x27; attribute from the invocation --&gt;</span>
<span class="punctuation bracket">&lt;</span><span class="tag">a</span> <span class="attribute">href</span><span class="punctuation delimiter">=</span><span class="string">&quot;</span><span class="string">@href</span><span class="string">&quot;</span><span class="punctuation bracket">&gt;</span>
<span class="comment">&lt;!-- Children can be duplicated any number of times --&gt;</span>
<span class="punctuation bracket">&lt;</span>@children<span class="punctuation bracket">&#x2f;&gt;</span>
<span class="punctuation bracket">&lt;&#x2f;</span><span class="tag">a</span><span class="punctuation bracket">&gt;</span>
<span class="punctuation bracket">&lt;&#x2f;</span><span class="tag">h2</span><span class="punctuation bracket">&gt;</span>
<span class="comment">&lt;!--
Attributes can also be duplciated, and boolean attributes
work exactly as expected
--&gt;</span>
<span class="punctuation bracket">&lt;</span><span class="tag">h3</span> <span class="attribute">class</span><span class="punctuation delimiter">=</span><span class="string">@class</span> <span class="attribute">hidden</span><span class="punctuation delimiter">=</span><span class="string">@hidden</span><span class="punctuation bracket">&gt;</span>
<span class="punctuation bracket">&lt;</span>@children<span class="punctuation bracket">&#x2f;&gt;</span>
<span class="punctuation bracket">&lt;&#x2f;</span><span class="tag">h3</span><span class="punctuation bracket">&gt;</span>
<span class="punctuation bracket">&lt;&#x2f;</span><span class="tag">Echo</span><span class="punctuation bracket">&gt;</span>
<span class="comment">&lt;!-- End of template definition --&gt;</span>
</code></pre>
</div></div></div></div></body></html>