Comparing trees
Comparing/Diffing/Merging trees is a core part of StaticShape. This is what powers all of StaticShape’s templating and variables.
#Manual comparisons
For the first example, imagine trying to build a layout out of two pages (Home and About):
index.html
<h1>Home</h1>
about.html
<h1>About</h1>
As a human, we can see the text in the first page is different from the text in the second page and the rest is the same. Doing migrations manually, we take the text from the files and make a layout which outputs that data. The allows us to keep data and markup separate, for example:
index.html
---
text: Home
---
about.html
---
text: About
---
layout.html
<h1>{{ text }}</h1>
#Automated comparisons
For a computer to do this, we need to read the HTML into a format that can be interfaced with. This interface is a set of objects that represent the HTML nodes. These objects contain metadata about themselves and are organised into arrays. Some nodes have “children” which is an array of nodes that are inside the node. Putting this all together creates a tree structure which is like a DOM. If you have every written any CSS selectors or used JavaScript this should feel familiar.
#Extra nodes
In the StaticShape world, there are all the “basic” nodes and some additional types to represent the following items:
- Variables
- Text variables
- Markdown variables
- Inline markdown variables
- Loops
- Conditionals
- Page Content
#Example tree
The rest of the docs will represent trees and nodes as JSON. For the example above we can represent it as:
index.html
[
{
"type": "element",
"name": "h1",
"attrs": {},
"children": [
{
"type": "text",
"value": "Home"
}
]
}
]
about.html
[
{
"type": "element",
"name": "h1",
"attrs": {},
"children": [
{
"type": "text",
"value": "About"
}
]
}
]
#Process for diffing trees
In each file, the tree (root array) has one element node which has a text node as a child. To compare these two trees we take the following steps:
- Create a new array for the merged tree
- Collect the first node from each tree
- Compare them to determine if they are a “good match”:
- If the nodes match: Merge the two nodes together. If the two nodes have children, run this merge recursively.
- Else: Add a conditional node
- Keep going until there are no nodes, If one tree runs out first, all remaining nodes are added as conditional nodes.
- Return the new merged tree and data from variables.
#Example diffing trees
Step by step for this example would be:
- Create a new array
- Compare
h1
node fromindex.html
to theh1
node fromabout.html
- Combine the two
h1
nodes to create a newh1
node, recursively merge the child trees:- Create a new array
- Compare
text
node fromindex.html
to thetext
node fromabout.html
- Combine the two
text
nodes by creating avariable
node - Keep track of the new variable for
index.html
andabout.html
- Add the new node to our merged array
- No more nodes to compare, return the merged tree
- Add the new node to our merged array
- No more nodes to compare, return the merged tree
This gives a similar result to the manual example in JSON:
index.html
{
"text": "Home"
}
about.html
{
"text": "About"
}
layout
[
{
"type": "element",
"name": "h1",
"attrs": {},
"children": [
{
"type": "variable",
"reference": [
"text"
]
}
]
}
]