Update docs.

master
Sir Robert Burbridge 2023-11-15 14:52:05 -05:00
parent 1b922b5155
commit d4a145b96b
6 changed files with 68 additions and 292 deletions

View File

@ -1,6 +1,5 @@
# @thefarce/loom
## What is this?
Loom is a tool for weaving form data with content data in nodejs. It does
@ -39,7 +38,8 @@ console.log(
interpolate(
transform(
parseHtml('<p>Hello, {name}!</p>'),
[],
() => { ... },
[funcA, funcB, funcC],
),
{ name: 'Othello' },
)
@ -55,31 +55,30 @@ Running this script produces the following output:
## API
### renderHtml(ast)
### astToHtml(ast)
### parseHtml(html)
### htmlToAst(html)
### interpolate(ast, data)
### transform(ast, plugins)
See the [transformations documentation](./docs/transformations.md) for more
information.
Transform the AST according to plugins.
#### ast
## Glossary
### ast
The AST to be transformed. This can be any AST that conforms to the
Unified.js specification, especially the
[hast](https://github.com/syntax-tree/hast) AST.
#### plugins
## Contributing to Loom
Plugins are pairs of functions.
The *selector* function
The *transformer* function
## Development
Contributions to Loom are welcome.
### VIM

View File

@ -1,83 +0,0 @@
# Components
## Architecture
Fundamentally, we want to do (approximately) 4 steps, some of which are
optional.
1. Template parsing. Transforms templates into ASTs.
1. Template integration. Combines two templates into a single template.
1. Content parsing. Structures content for template AST rendering.
1. Rendering. Concretize the AST into a document structure with
the available content added.
## Thoughts
It is not at all clear to me that components should be in HTML. It seems
like a natural starting point, though, since we want to stay close to our
bare-metal technologies.
Perhaps components should be in the style of HTML or JSX fragments. I
borrowed heavily from Svelte for this.
I'm somewhat dissatisfied with the syntax of svelte's "directives" like
{#if} and {/if}. They feel like they were put in as a concession to
script interpolation. I think I prefer the JSX approach that uses {}
to run arbitrary script content.
I don't know if there's any concepts we should bring in from XSLT for
this, but it's already a technology mechanis,
```
// This "<loom-module>" tag could be created automatically before inserting.
<loom-module>
<script type="text/loom-module">
/**
* Key script features:
* - Assume ES6?
* - Maybe allow other languages that transpile to JS?
* - Automatically sandbox css?
* - Everything here needs to be available only during interpretation
* of this HTML and css.
*/
</script>
<ul>
<li>Allow multiple comment styles?</li>
<li>Plugin-based transformations (AST)</li>
<li>Some kind of script scoping</li>
<li>Allow some bits of code using JSX-style {} syntax.</li>
</ul>
<style type="test/scss">
/**
* Key styling features:
*
* - Allow all three comment types here? Make this a setting?
* - Bake-in postcss transformers? Maybe project-agnostic...
* - Automatically sandbox css
*/
</style>
</loom-module>
```
If the component defines something like this:
```
<script>
import Item from 'https://.../Item.component.xml';
</script>
<Item data={item}/>
```
Then the "import" node of the AST would be replaced by a function that
provides the requested content (from Item.component.xml) like a macro. When
the `<Item/>` node is hit, it will be interpreted according to the imported
component macro.

View File

@ -1,107 +0,0 @@
# Markup
## Considerations
Hypothetically, markup doesn't really matter for this. A component is a
component if it can be translated into an AST that a renderer can render.
Practically, markup matters a lot. People will have strong preferences
about their favourite versions of things. One solution to this is to open
everything wide: "Just make parsers, transformers, and renderers that suit
your need." The advantage of this is it gives people a lot of power to do
whatever they want. Practically, the disadvantage is that poor taste will
prevail. This is mostly just because good taste requires lots of hard work
and most people haven't had time to do sufficient hard work to have good
taste (disregarding the tendency not to want to do that work). It's
probably worth noting that good taste can have strong aesthetic differences.
Two people who disagree strongly can both have excellent tastes, differing
in influences.
I think the approach I prefer is to leave it open in principle but obscured
from most consumers of the tool. This would be effectively to make the Loom
toolkit work with any style of markup and plugins, but not to popularize
that feature (for quite some time), preferring instead to create
concretizations of the toolkit in the form of frameworks. These will help
guide consumers in taste, while not restricting those who want to apply more
work to develop their own aesthetic sensibilities.
## Key Elements
Fundamentally, we want to integrate _form data_ with _content data_. There
are probably better and worse paradigms for how to accomplish this. I'd say
probably _logic_ style. This is a paradigm that would let
producers ignore the mechanisms of integration.
Another excellent (and opposite) approach could be a _functional_ style. In
this paradigm, the producer is concerned with mechanisms of data
transformation.
## Other thoughts
It is not at all clear to me that components should be in HTML. It seems
like a natural starting point, though, since we want to stay close to our
bare-metal technologies.
Perhaps components should be in the style of HTML or JSX fragments. I
borrowed heavily from Svelte for this.
I'm somewhat dissatisfied with the syntax of svelte's "directives" like
{#if} and {/if}. They feel like they were put in as a concession to
script interpolation. I think I prefer the JSX approach that uses {}
to run arbitrary script content.
I don't know if there's any concepts we should bring in from XSLT for
this, but it's already a technology mechanism that operates in this domain.
```
// This "<loom-module>" tag could be created automatically before inserting.
<loom-module>
<script type="text/loom-module">
/**
* Key script features:
* - Assume ES6?
* - Maybe allow other languages that transpile to JS?
* - Automatically sandbox css?
* - Everything here needs to be available only during interpretation
* of this HTML and css.
*/
</script>
<ul>
<li>Allow multiple comment styles?</li>
<li>Plugin-based transformations (AST)</li>
<li>Some kind of script scoping</li>
<li>Allow some bits of code using JSX-style {} syntax.</li>
</ul>
<style type="test/scss">
/**
* Key styling features:
*
* - Allow all three comment types here? Make this a setting?
* - Bake-in postcss transformers? Maybe project-agnostic...
* - Automatically sandbox css
*/
</style>
</loom-module>
```
If the component defines something like this:
```
<script>
import Item from 'https://.../Item.component.xml';
</script>
<Item data={item}/>
```
Then the "import" node of the AST would be replaced by a function that
provides the requested content (from Item.component.xml) like a macro. When
the `<Item/>` node is hit, it will be interpreted according to the imported
component macro.

View File

@ -1,86 +0,0 @@
# Process
* Load one or more modules
* Load one or more HTML fragments
* Render each fragment against the relevant data.
# Loom Modules
Loom modules are bundles of:
1. Form data
2. Process data
3. Event data
4. Content data
Each element of a module is sandboxed. The data can be accessed externally,
but need not be.
Loom modules are represented by an abstract syntax tree.
The `content` element of a node can be any single value that is one of an
boolean, string, number, object/map, array, or undefined/null.
The `process` element of a node can be any set of functions that can be fully
serialized to a string round-trip.
The `event` element of a node is a dictionary of events keyed to a name with
a value that is always an array of handlers. Each handler takes an event
object as the first (or only) argument. Handler functions can either be
functions or registry names of `process` functions.
In this way, data and processes can be attached to any node as local state.
Consider the following HTML snippet
<div>Title</div>
and (an elided form of) its AST:
```
{
"type" : "root",
"children" : [
{
"type" : "element",
"tagName" : "div",
"properties" : {},
"children" : [
{
"type" : "text",
"value" : "Test",
}
],
},
],
}
```
Going further
The following defines an HTML template with inline process data.
```
<div
loom-process={{
'changeTitle': ($) => { $.content = 'New Title' }
}}
>Title</div>
```
As written, the `changeTitle` process will never be called. Let's attach
it to an event.
```
<div
events={{
click: ($,e) => $.process.changeTitle('New Title'),
}}
loom-process={{
'changeTitle': ($) => { $.content = 'New Title' },
}}
>Title</div>
```

View File

@ -0,0 +1,56 @@
# Transformation
In Loom, *transformation* refers to the process of modifying an abstract
syntax tree (AST), especially through the use of *transformers*.
Transformers are arrays of 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` function. Each of these should be a *transformer function* or
an array of such.
## Transformation functions
Transformer functions take _a copy_ of an AST node. Because it is a copy,
the changes made to the node received by the transformer function will have
no effect on the tree. To change the tree, return a new AST node.
### Return values
Transformation functions may return one of three recognized value types. If
a different value is returned, the `transform` function will throw an error.
#### AST node
If the transformation function 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.createAstNode()` or `util.cloneNode(node)` function. The
determination of whether or not the returned value is an AST node is made
using the `util.isAstNode()` utility function.
#### null
If the transformation function returns `null`, the original node will be
removed from the AST. No further transformation functions will be called on
the same node.
#### undefined
If the transformation function returns `undefined`, the original node will
be left in the AST unchanged. The next transformation function will be
called on the same node.
### _other values_
In the event that a transformation function returns any other value than
`null`, `undefined`, or an AST node--as determined by `util.isAstNode()`,
the `transform()` function will throw an error.

View File

@ -1,3 +0,0 @@
# Loading templates