91 lines
2.8 KiB
HTML
91 lines
2.8 KiB
HTML
<Container>
|
|
<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.
|
|
<@code lang="rust">
|
|
// From the html_parser crate
|
|
pub enum Node {
|
|
Text(String),
|
|
Element(Element),
|
|
Comment(String),
|
|
}
|
|
|
|
pub struct Element {
|
|
pub name: String,
|
|
pub attributes: HashMap<String, Option<String>>,
|
|
pub children: Vec<Node>,
|
|
// other fields omitted
|
|
}
|
|
</@code>
|
|
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.
|
|
<@code lang="rust">
|
|
// My implementation (fields omitted)
|
|
pub enum HtmlElement {
|
|
DocType,
|
|
Comment(/* */),
|
|
OpenTag { /* */ },
|
|
CloseTag { /* */ },
|
|
Text( /* */ ),
|
|
Script { /* */ },
|
|
Style { /* */ },
|
|
Directive { /* */ },
|
|
}
|
|
</@code>
|
|
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.
|
|
<@code lang="html">
|
|
<!-- Here is an example of a template definition -->
|
|
<Echo>
|
|
<!--
|
|
This means to inherit the 'class' attribute from the invocation
|
|
-->
|
|
<h1 class=@class>
|
|
<!--
|
|
This means place the children of the invocation here
|
|
-->
|
|
<@children/>
|
|
</h1>
|
|
<h2>
|
|
<!-- This means to inherit the 'href' attribute from the invocation -->
|
|
<a href="@href">
|
|
<!-- Children can be duplicated any number of times -->
|
|
<@children/>
|
|
</a>
|
|
</h2>
|
|
<!--
|
|
Attributes can also be duplciated, and boolean attributes
|
|
work exactly as expected
|
|
-->
|
|
<h3 class=@class hidden=@hidden>
|
|
<@children/>
|
|
</h3>
|
|
</Echo>
|
|
<!-- End of template definition -->
|
|
</@code>
|
|
</div>
|
|
</Container>
|