xodus is a templating language that is compatible with fully formed, (server-rendered) HTML.
It can be reverse-engineered into moustache-style template syntax, such as the syntax proposed for template instantiation.
It can also be viewed as an "intermediate" templating language, generated from JS template literals, or JSX, that can be reverse compiled into other templating languages.
The goals are:
- The output should impose as little extra overhead on the server-rendered HTML as possible.
- Can be reverse engineered into the data structures from which the HTML was generated. (h2oExtract)
- Provides enough information to apply well-performing updates on the rendered HTML DOM as client-side data changes.
- Could be used as an HTML-based templating language on the server.
- Compatible with any language capable of parsing HTML and JSON.
Example 1:
<a x-f=network>cnn</a>
function h2oExtract takes this DOM node, and generates object: {network: 'cnn'}.
x-f stands for "expand from", or "expanded from", depending on the context.
function toTempl takes this node and generates (in memory):
<template><a>{{network}}</a></template>
function xodus takes as input:
- DOM node:
<a>{{network}}</a>
and
- Object:
{"network": "cnn"}
and generates
<a x-f=network>cnn</a>
Example 1a:
<a data-xf=network>cnn</a>
The functions described above do the same thing.
xodus supports an option to use this syntax.
Example 2: Facing the Music, Part I
The interpolation requirement seems problematic, however you look at it. It is tempting to say "why not just wrap all dynamic content inside a span"? or something, but the concept of interpolation applies to attributes as well. But let's start with innerHTML / textContent
<a x-f='{"network": [8]}'>This is cnn</a>
8 indicates the network starts at the eighth position of the string (numbers are estimates, prone to human error). A second number [8,13] indicates the end position.
function h2oExtract(tbd) takes this DOM node, and generates object: {network: 'cnn'}.
function toTempl(tbd) takes this DOM node, and generates:
<template><a>This is {{network}}</a></template>
function xodus takes the DOM node:
<a>This is {{network}}</a>
and
{"network": "cnn"}
and generates
<a x-f='{"network": [8]}'>This is cnn</a>
If innerHTML contains inner nodes as well as interpolating strings, split the interpolating strings into spans (not applicable to attributes). I.e. support for interpolation of textContent is only taken as far as supporting textContent for the entire contents of the tag.
Example 3:
<a href=//cnn.com x-f='{".textContent":"network", "href":"networkURL"}'>cnn</a>
h2oExtract generates {network: 'cnn', networkURL: '//cnn.com'}
toTempl generates
<template><a href={{networkURL}}>{{network}}</a></template>
xodus takes DOM node:
<a href={{networkURL}}>{{network}}</a>
and object
{"network": "cnn", "networkURL": "//cnn.com"}
and generates
<a href=//cnn.com x-f='{".textContent":"network", "href":"networkURL"}'>cnn</a>
Example 4: Facing the music, Part II
<a x-f='{".textContent":{"network": [8]}, "href":{"networkURL": [0,9], "articleID": [9]}}' href=//cnn.com/2021/08/04/us/florida-school-mask-mandate-law/index.html>This is cnn</a>
h2oExtract generates {network: 'cnn', networkURL: '//cnn.com', articleID: '/2021/08/04/us/florida-school-mask-mandate-law/index.html'}
toTempl generates
<template><a href={{networkURL}}{{articleID}}>This is {{network}}</a></template>
xodus takes DOM input:
<template><a href={{networkURL}}{{articleID}}>This is {{network}}</a></template>
and object {network: 'cnn', networkURL: '//cnn.com', articleID: '/2021/08/04/us/florida-school-mask-mandate-law/index.html'}
and generates
<a x-f='{".textContent":{"network": [8]}, "href":{"networkURL": [0,9], "articleID": [9]}}' href=//cnn.com/2021/08/04/us/florida-school-mask-mandate-law/index.html>This is cnn</a>
TODO I
For Example 3 ... If a key starts with a period, like ".textContent" then it refers to a property of the element. Otherwise, the key refers to the attribute.
This raises a tricky conundrum for xodus. There are performance benefits to using properties instead of attributes, on the client, especially for non string properties.
(Perhaps the issue isn't that significant as long as template instantiation only binds to attributes and textContent).
During server-side rendering, properties don't make sense (so the .textContent will need to be hard-coded to mean "this goes inside the tag)".
Example 5: Dry Loops
<template x-f="start-of {{newsStations}}"><a href={{networkURL}}{{articleID}}>{{network}}</a></template>
<a x-f='[8, [0,9], [9]]' href=//cnn.com/2021/08/04/us/florida-school-mask-mandate-law/index.html>This is cnn</a>
<a x-f='[18, [0, 13], [13]]' href=//foxnews.com/politics/desantis-biden-do-job-secure-border>Fair and Balanced Fox News</a>
<a x-f='[13, [0, 11], [11]]' href=//msnbc.com/opinion/why-tucker-carlson-s-trip-budapest-bad-news-america-n1275881>Lean Forward MSNBC</a>
<template x-f="end-of {{newsStations}}">
h2oExtract generates
{
"newsStations": [
{"network": "Fox News", "networkURL": "//foxnews.com", "articleID": "/politics/desantis-biden-do-job-secure-border"},
{"network": "MSNBC", "networkURL": "//msnbc.com", "articleID": "/opinion/why-tucker-carlson-s-trip-budapest-bad-news-america-n1275881"},
{"network": "CNN", "networkURL": "//cnn.com", "articleID": "/2021/08/04/us/florida-school-mask-mandate-law/index.html"}
]
}
toTempl generates ?
<template for:each={{newsStations}}><a href={{networkURL}}{{articleID}}>{{network}}</a></template>
...or whatever syntax template instantiation settles on.
xodus takes DOM input:
<template for:each={{newsStations}}><a href={{networkURL}}{{articleID}}>{{network}}</a></template>
and object
{
"newsStations": [
{"network": "Fox News", "networkURL": "//foxnews.com", "articleID": "/politics/desantis-biden-do-job-secure-border"},
{"network": "MSNBC", "networkURL": "//msnbc.com", "articleID": "/opinion/why-tucker-carlson-s-trip-budapest-bad-news-america-n1275881"},
{"network": "CNN", "networkURL": "//cnn.com", "articleID": "/2021/08/04/us/florida-school-mask-mandate-law/index.html"}
]
}
and generates
<template x-f="start-of {{newsStations}}"><a href={{networkURL}}{{articleID}}>{{network}}</a></template>
<a x-f='[8, [0,9], [9]]' href=//cnn.com/2021/08/04/us/florida-school-mask-mandate-law/index.html>This is cnn</a>
<a x-f='[18, [0, 13], [13]]' href=//foxnews.com/politics/desantis-biden-do-job-secure-border>Fair and Balanced Fox News</a>
<a x-f='[13, [0, 11], [11]]' href=//msnbc.com/opinion/why-tucker-carlson-s-trip-budapest-bad-news-america-n1275881>Lean Forward MSNBC</a>
<template x-f="end-of {{newsStations}}">
Example 6
<ul>
<li>Warm weather clothing</li>
<template x-f="show 3 items if {{isWarmOutside}}"></template>
<li>t-shirt</li>
<li>shorts</li>
<li>sandals</li>
<li>Cool weather clothing</li>
</ul>
h2oExtract generates {isWarmOutside: true}
<ul>
<li>Warm weather clothing</li>
<template x-f="show if {{isWarmOutside}}"></template>
<template>
<li>t-shirt</li>
<li>shorts</li>
<li>sandals</li>
</template>
<li>Cool weather clothing</li>
</ul>
h2oExtract generates {isWarmOutside: false}
Name inspired by this funny comment.