4.3 KiB
Transformation
In Loom, transformation refers to the process of modifying an abstract syntax tree (AST), especially through the use of transformers (or transformer functions).
The mechanism provided by loom for these transformations is the
transform()
function.
The first argument to the transform
function is an AST. Each node in the
AST will be considered for transformation.
After the AST, any number of additional arguments may be passed to the
transform(ast, ...transformers)
function. Each of these should be a
transformer.
Transformerss
Signature
transformer(node)
node
- AST node to be transformed. The node includes information about its children, but not its parent.
Return values
Transformerss may return one of three recognized value types. If
a different value is returned, the transform
function will throw an error.
null
Returning null from a transformer will cause the node to be removed from the tree. No further transformers will be run against the node.
undefined
Returning undefined from a transformer will leave the node in the tree unchanged. The next transformer will be called on the same node.
AST node
If the transformers returns an AST node, that node will be used to replace the original node in the original tree. Subsequent transformer functions will recieve a copy of the new node, not the original.
For convenience, new tree nodes can be created with the
util.createNode(data)
or util.cloneNode(node)
function. The
determination of whether or not the returned value is an AST node is made
using the util.isNode(obj)
utility function.
other values
In the event that a transformers returns any other value than
null
, undefined
, or an AST node--as determined by util.isNode()
,
the transform(ast, ...transformers)
function will throw an error.
Examples
In the following examples, we will use the AST corresponding to the following HTML for high intelligibility.
// An HTML document with fixture data that includes an article, a header, // several paragraphs, and an unordered list. Some of the elements have ids // or class names, or other data attributes. // The paragraphs are about fauna
<html>
<body>
<article>
<h1 id="title">Fun Facts about Stuff</h1>
<ul id="fun-facts">
<li id="lights">The Northern Lights are beautiful at night.</li>
<li id="bears">Grizzly bears are scary.</li>
<li id="stones">Ancient megaliths cover the valley.</li>
</ul>
</article>
</body>
</html>
We will use the variable ast
to refer to the root of the AST.
Deleting nodes
When a transformer returns null, it means the node should be deleted. No further transformations are performed on the same node.
Let's delete any fun facts about bears.
import ast from './my-html-ast.mjs';
ast = transform(
ast,
(node) => (node.id === 'bears') ? null : undefined,
);
Adding nodes from a remote data source
Let's add more fun facts from an API.
import ast from './my-html-ast.mjs';
ast = transform(
ast,
(node) => {
if (node.property.id === 'fun-facts') {
// These have a structure like ["...", "...", ...]
let facts = JSON.parse(await fetch('https://example.com/facts'));
facts.forEach((fact) => {
// Clone the first existing fact.
let newNode = cloneNode(facts[0]);
// Replace the text
newNode.children[0].value = fact;
// Remove the id
delete newNode.children[0].property.id;
// Append it to the list
node.children.push(newNode);
});
}
},
)
The resulting AST will have additional facts attached to the ul
element.
Multiple transformers
In this script we will use the hastscript
library to create new html nodes
conveniently.
import { h as htmlTag } from 'hastscript';
import ast from './my-html-ast.mjs';
ast = transform(
ast,
// Delete any node with id 'bears'. No other transformers will be run
// against bear nodes. Other nodes are left unchanged.
(node) => (node.id === 'bears') ? null : undefined,
// Change the article tag to a section tag. Leave the node in place.
(node) => {
if (node.tagName === 'article') {
node.tagName = 'section';
}
}
// Add a new paragraph talking about fun facts.
(node) => {
if (node.tagName === 'article') {
let intro = htmlTag('p', { id: 'intro' }, 'Fun facts about stuff');
node.children.push(intro);
}
},
);