mirror of
https://codeberg.org/setop/elm-scripting
synced 2025-11-08 21:49:57 +00:00
first working implementation
This commit is contained in:
1
.tool-versions
Normal file
1
.tool-versions
Normal file
@@ -0,0 +1 @@
|
||||
elm 0.19.1
|
||||
51
README.md
Normal file
51
README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
A tool to generate HTML code from Elm source in the terminal using [QuickJS](https://bellard.org/quickjs/).
|
||||
|
||||
# Design
|
||||
|
||||
QuickJS (Qjs) is a [JavaScript runtime](https://en.wikipedia.org/wiki/List_of_JavaScript_engines), similar to V8 or SpiderMonkey, but lighter and faster.
|
||||
|
||||
As any runtime, Qjs can interpret JavaScript code, but it is not a web browser. It has no concept of an HTML document.
|
||||
|
||||
To bridge this gap, we add a minimal [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) implementation.
|
||||
|
||||
Next, we concatenate this with the Elm JavaScript output and an app launcher snippet, then ask Qjs to interpret all of it.
|
||||
|
||||
## Limitations
|
||||
|
||||
* No event loop
|
||||
* Hence, no [TEA](https://guide.elm-lang.org/architecture/); the `main` function must return a static view
|
||||
* The Elm app module must be called "Main"
|
||||
* Nodes can only have one parent (this should always be the case)
|
||||
|
||||
# Usage
|
||||
|
||||
1. Clone this repository and navigate into your local copy
|
||||
2. Run `elm init`.
|
||||
3. Create `src/Main.elm` with the following content:
|
||||
|
||||
```elm
|
||||
module Main exposing(main)
|
||||
|
||||
import Html exposing (p, text)
|
||||
|
||||
main = p [] [text "Hello World!"]
|
||||
```
|
||||
|
||||
4. Run `./build.sh`; this generates the corresponding HTML code:
|
||||
|
||||
```html
|
||||
<p>
|
||||
Hello World!
|
||||
</p>
|
||||
```
|
||||
|
||||
# Prior Work
|
||||
|
||||
There are more complete tools for generating static sites with Elm:
|
||||
|
||||
* [elm-pages](https://elm-pages.com/)
|
||||
* [elmstatic](https://korban.net/elm/elmstatic)
|
||||
* [elm-starter](https://github.com/lucamug/elm-starter)
|
||||
* [siteelm](https://github.com/nikueater/siteelm)
|
||||
|
||||
You should probably consider using one of them instead of this one. :)
|
||||
17
build.sh
Executable file
17
build.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh -eu
|
||||
|
||||
w1="$(mktemp out_$$_1_XXXX.js)"
|
||||
|
||||
elm make --optimize --output=${w1} src/Main.elm 1>&2
|
||||
|
||||
w2="$(mktemp out_$$_XXXX.js)"
|
||||
|
||||
cat dom.js ${w1} launch.js > ${w2}
|
||||
|
||||
rm ${w1}
|
||||
|
||||
qjs --std ${w2}
|
||||
|
||||
# if qjs fails, output file will stay for debugging, else
|
||||
|
||||
rm ${w2}
|
||||
58
dom.js
Normal file
58
dom.js
Normal file
@@ -0,0 +1,58 @@
|
||||
function Node(parent, tag) {
|
||||
this.parentNode = parent; // Node
|
||||
this.pos = 0; // position in siblings list
|
||||
this.tagName = tag;
|
||||
this.text = null;
|
||||
// attributes are stored directly on the node object
|
||||
// see https://github.com/elm/virtual-dom/blob/master/src/Elm/Kernel/VirtualDom.js#L511
|
||||
this.children = []; // List of Node
|
||||
this.replaceChild = (newNode, domNode) => {
|
||||
this.children[domNode.pos-1] = newNode;
|
||||
newNode.pos = domNode.pos;
|
||||
};
|
||||
if (parent != null) {
|
||||
this.pos = parent.children.push(this);
|
||||
}
|
||||
this.appendChild = (node) => {
|
||||
node.pos = this.children.push(node);
|
||||
}
|
||||
this.dump = (d=0) => {
|
||||
if (this.text != null) {
|
||||
print(this.text);
|
||||
return
|
||||
}
|
||||
std.printf("<%s", this.tagName)
|
||||
// Set.difference(other) is not avalable in qjs
|
||||
for (a of Object.keys(this)) {
|
||||
if (!NodeKeys.has(a)) {
|
||||
std.printf(' %s="%s"', a, this[a])
|
||||
}
|
||||
}
|
||||
if (this.children.length==0) {
|
||||
print("/>")
|
||||
} else {
|
||||
print(">")
|
||||
for (c of this.children) {
|
||||
c.dump(d+1)
|
||||
}
|
||||
print("</"+this.tagName+">");
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
var document = new Node(null, "document");
|
||||
var target = new Node(document, "target");
|
||||
const NodeKeys = new Set(Object.keys(target));
|
||||
|
||||
// getElementById is only used once, to get node Elm must hook into.
|
||||
// so we don't need a full fledge implementation with lookup facilities
|
||||
// just to return the target node
|
||||
document.getElementById = (_id) => { return target}
|
||||
|
||||
document.createElement = (tag) => new Node(null, tag);
|
||||
document.createTextNode = (text) => { t = new Node(null, "#text" ); t.text = text; return t }
|
||||
|
||||
try {
|
||||
|
||||
// here will come the Elm app code
|
||||
Reference in New Issue
Block a user