diff --git a/.tool-versions b/.tool-versions index 7033136..b825aa9 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1 @@ elm 0.19.1 -python 3.13.1-v2 diff --git a/README.md b/README.md index 3bd2ea6..0429641 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ Next, we concatenate this with the Elm JavaScript output and an app launcher sni ## Limitations * No event loop -* Hence, no [TEA](https://guide.elm-lang.org/architecture/); the `main` function must return a static view -* Hence, no Time, no Random, no Json Encoder/Decoder (!), no Http +* Hence, no [TEA](https://guide.elm-lang.org/architecture/); no `update` can be triggered +* Hence, no Time, no Random, no Http * Nodes can only have one parent (this should always be the case) * Does not scale well : creating thousands of Nodes consumes a [lot of RAM](#Performances) @@ -37,19 +37,73 @@ Next, we concatenate this with the Elm JavaScript output and an app launcher sni ```elm module Hello exposing(main) -import Html exposing (p, text) +import Html exposing (text) -main = p [] [text "Hello World!"] +main = text "Hello World!" ``` -4. Run `./build.sh src/Hello.elm`; this generates the corresponding HTML code: +4. Run `./elmscript.sh src/Hello.elm` -```html -

+This produces the expected output: + +```text Hello World! -

``` +## passing input + +Input content can be passed to the script via the standard input. The content is then passed to the Elm script as "flags". The script must then use `Browser.element` and implement `init flags` function. + + +Create `src/Greet.elm` with the following content: + +```elm +module Greet exposing (main) + +import Browser +import Html exposing (text) + + +main = + Browser.element + { init = \f -> (f, Cmd.none) + , update = \m _ -> (m, Cmd.none) + , view = \m -> text ("hello " ++ m) + , subscriptions = always Sub.none + } +``` + +Run `./elmscript.sh src/Greet.elm <<< 'Elm'` + +This produces the expected output: `hello Elm`. + + +I's even more natural when passing file : + +* run `elm install elm-explorations/markdown` +* create `src/MdRender.elm` with the following content: + +```elm +module MdRender exposing (main) + +import Browser +import Html.Attributes exposing (class) +import Markdown + +main = + Browser.element + { init = \s -> (s, Cmd.none) + , update = \m c -> (m, Cmd.none) + , view = \m -> Markdown.toHtml [class "content"] m + , subscriptions = always Sub.none + } + +``` + +* run `./elmscript.sh src/MdRender.elm < README.md > README.html` + + + ## Performances Acceptable for small scripts : 250ms on a modest x86_64 CPU and 64MB RAM for a 500 records into a table ; but is does not scale well as everything is loaded before processing ; no streaming contrary to the usual Unix way. diff --git a/TODO.md b/TODO.md index c10d2a5..5ed91ff 100644 --- a/TODO.md +++ b/TODO.md @@ -1,8 +1,8 @@ # Major -- find a way to process external data - - [ ] from stdout (use case : json) +- [ ] find a way to process external data + - [x] from stdout (use case : json), use flags - [ ] from files (use case : SSG) - [ ] from http (use case : spider) @@ -19,10 +19,10 @@ # Minor - [ ] support for Elm debug (`-d`) mode (qjs does not implement `console.warn`, only `console.log`) -- [ ] in debug mode, keep the output + - [ ] in debug mode, keep the output - [x] allow to specify or guess the main module name - [x] if `innerHTML` attribute is set, output its value instead of the node tree -- [ ] allow to create standalone (`-s`) HTML document instead of fragment -- [ ] silence elm compiler message when successful +- [x] allow to create standalone (`-s`) HTML document instead of fragment => use template engine instead +- [x] silence elm compiler message when successful - [ ] skip compilation steps if source has not been modified ; run directly ; this mean keeping the output -- [ ] add proper copyright and license +- [x] add proper copyright and license diff --git a/build.debug.sh b/build.debug.sh deleted file mode 100755 index 2d16c97..0000000 --- a/build.debug.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh -eu - -w1="$(mktemp out_$$_1_XXXX.js)" - -elm make --output=${w1} $1 1>&2 -sed -i -e 's!console.warn(!console.log(!g' ${w1} - -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} diff --git a/build.sh b/build.sh deleted file mode 100755 index 9af20a9..0000000 --- a/build.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -eu - -CMDD=$(dirname $(realpath 0)) - -w1="$(mktemp out_$$_1_XXXX.js)" - -elm make --optimize --output=${w1} $1 1>&2 - -w2="$(mktemp out_$$_XXXX.js)" - -cat ${CMDD}/dom.js ${CMDD}/preelm.js ${w1} ${CMDD}/postelm.js > ${w2} - -rm ${w1} - -qjs --std ${w2} - -# if qjs fails, output file will stay for debugging, else - -rm ${w2} diff --git a/dom.js b/dom.js index 7e70b6b..c72b75c 100644 --- a/dom.js +++ b/dom.js @@ -5,6 +5,11 @@ function Node(parent, 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 + // but Html.Attributes.attribute/2 uses Element.setAttribute + this.setAttribute = (key, val) => { + // as of now, there is no check if key conflict with the ones defined by Dom implementation + this[key] = val; + } this.children = []; // List of Node this.replaceChild = (newNode, domNode) => { this.children[domNode.pos-1] = newNode; @@ -16,6 +21,9 @@ function Node(parent, tag) { this.appendChild = (node) => { node.pos = this.children.push(node); } + this.replaceData = (offset, count, data) => { + this.text = data; // elm runtime will always replace the whole text + } this.dump = (d=0) => { if (this.text != null) { print(this.text); diff --git a/elmscript.sh b/elmscript.sh new file mode 100755 index 0000000..c647cb6 --- /dev/null +++ b/elmscript.sh @@ -0,0 +1,41 @@ +#!/bin/sh -eu + +CMDD=$(dirname $(realpath 0)) + +w1="$(mktemp out_$$_1_XXXX.js)" + +elm make --optimize --output=${w1} $1 1>/dev/null + +w2="$(mktemp out_$$_XXXX.js)" + +cat ${CMDD}/dom.js >> ${w2} +cat << EEE >> ${w2} +try { +EEE +cat ${w1} >> ${w2} +cat << EEE >> ${w2} + Elm[Object.keys(Elm)[0]].init({ + node: document.getElementById("elm") +EEE +flags=$(grep -c 'main = $elm$browser$Browser$element' ${w1} || true) +if [ $flags -eq 1 ]; +then +cat << EEE >> ${w2}; + , flags: std.in.readAsString() +EEE +fi +cat << EEE >> ${w2} + }); + document.children[0].dump(); +} catch(e) { + throw e; +} +EEE + +rm ${w1} + +qjs --std ${w2} + +# if qjs fails, output file will stay for debugging, else + +rm ${w2} diff --git a/postelm.js b/postelm.js deleted file mode 100644 index 20c6fd8..0000000 --- a/postelm.js +++ /dev/null @@ -1,13 +0,0 @@ - - - // above is the DOM implementation - // and the Elm app code - - // here we lanch the app - Elm[Object.keys(Elm)[0]].init({ node: document.getElementById("elm") }); - - document.children[0].dump(); - -} catch(e) { - throw e; -} diff --git a/preelm.js b/preelm.js deleted file mode 100644 index 6d86e51..0000000 --- a/preelm.js +++ /dev/null @@ -1,6 +0,0 @@ - -try { - -// here will come the Elm app code - - diff --git a/uloc.sh b/uloc.sh deleted file mode 100755 index bcbbca5..0000000 --- a/uloc.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -eu - -countuniq0 build.sh dom.js postelm.js preelm.js