Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/yawaramin/dream-html
Render HTML, SVG, MathML, htmx markup from your OCaml app
https://github.com/yawaramin/dream-html
html htmx mathml ocaml svg
Last synced: about 23 hours ago
JSON representation
Render HTML, SVG, MathML, htmx markup from your OCaml app
- Host: GitHub
- URL: https://github.com/yawaramin/dream-html
- Owner: yawaramin
- License: gpl-3.0
- Created: 2023-04-22T00:47:10.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-09-09T01:54:09.000Z (2 months ago)
- Last Synced: 2024-09-09T02:59:51.165Z (2 months ago)
- Topics: html, htmx, mathml, ocaml, svg
- Language: OCaml
- Homepage: https://yawaramin.github.io/dream-html/
- Size: 850 KB
- Stars: 153
- Watchers: 4
- Forks: 14
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGES.md
- License: COPYING.txt
Awesome Lists containing this project
README
## dream-html - generate HTML markup from your Dream backend server
Copyright 2023 Yawar Amin
This file is part of dream-html.
dream-html is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.dream-html is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.You should have received a copy of the GNU General Public License along with
dream-html. If not, see .## What
An HTML, SVG, and MathML rendering library that is closely integrated with
[Dream](https://aantron.github.io/dream). Most HTML elements and attributes from
the [Mozilla Developer Network
references](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference) are
included. Almost all non-standard or deprecated tags/attributes deliberately
omitted. CSS support is out of scope. [htmx](https://htmx.org/) attributes
supported out of the box.> [!NOTE]
> Don't want to use Dream? You can use the eDSL without it! Just use the
> `pure-html` package instead of `dream-html`.## Why
- TyXML is a bit too complex.
- Dream's built-in eml (Embedded ML) has some drawbacks like no editor support,
quirky syntax that can be hard to debug and refactor, and manual dune rule
setup for each view file
- In general string-based HTML templating is
[suboptimal](https://www.devever.net/~hl/stringtemplates) and mostly driven by
[familiarity](https://github.com/tavisrudd/throw_out_your_templates).## First look
```ocaml
let page req =
let open Dream_html in
let open HTML in
(* automatically injects *)
html [lang "en"] [
head [] [
title [] "Dream-html" ];
body [] [
h1 [] [txt "Dream-html"];
p [] [txt "Is cool!"];
form [method_ `POST; action "/feedback"] [
(* Integrated with Dream's CSRF token generation *)
csrf_tag req;label [for_ "what-you-think"] [txt "Tell us what you think!"];
input [name "what-you-think"; id "what-you-think"];
input [type_ "submit"; value "Send"] ] ] ](* Integrated with Dream response *)
let handler req = Dream_html.respond (page req)
```## Security (HTML escaping)
Attribute and text values are escaped using rules very similar to standards-
compliant web browsers:```
utop # open Dream_html;;
utop # open HTML;;
utop # #install_printer pp;;utop # let user_input = "alert('You have been pwned')";;
val user_input : string = "alert('You have been pwned')"utop # p [] [txt "%s" user_input];;
- : node =<script>alert('You have been pwned')</script>
utop # div [title_ {|"%s|} user_input] [];;
- : node =
```## How to install
Make sure your local copy of the opam repository is up-to-date first:
```
opam update
opam install dream-html
```Alternatively, to install the latest commit that may not have been released yet:
```
opam pin add dream-html git+https://github.com/yawaramin/dream-html
```## Usage
A convenience is provided to respond with an HTML node from a handler:
```ocaml
Dream_html.respond greeting
```You can compose multiple HTML nodes together into a single node without an extra
DOM node, like [React fragments](https://react.dev/reference/react/Fragment):```ocaml
let view = null [p [] [txt "Hello"]; p [] [txt "World"]]
```You can do string interpolation of text nodes using `txt` and any attribute which
takes a string value:```ocaml
let greet name = p [id "greet-%s" name] [txt "Hello, %s!" name]
```You can conditionally render an attribute, and
[void elements](https://developer.mozilla.org/en-US/docs/Glossary/Void_element)
are statically enforced as childless:```ocaml
let entry =
input
[ if should_focus then autofocus else null_;
id "email";
name "email";
value "Email address" ]
```You can also embed HTML comments in the generated document:
```ocaml
div [] [comment "TODO: xyz."; p [] [txt "Hello!"]]
(*Hello!*)
```You have precise control over whitespace in the rendered HTML; dream-html does
not insert any whitespace by itself–all whitespace must be inserted inside text
nodes explicitly:```ocaml
p [] [txt "hello, "; txt "world!"];;
(*hello, world!
*)
```You can also conveniently hot-reload the webapp in the browser using the
`Dream_html.Livereload` module. See the API reference for details.## Import HTML
One issue that you may come across is that the syntax of HTML is different from
the syntax of dream-html markup. To ease this problem, you may use the
bookmarklet `import_html.js` provided in this project. Simply create a new
bookmark in your browser with any name, and set the URL to the content of that
file (make sure it is exactly the given content).Then, whenever you have a web page open, just click on the bookmarklet to copy
its markup to the clipboard in dream-html format. From there you can simple
paste it into your project.Note that the dream-html version is not formatted nicely, because the
expectation is that you will use ocamlformat to fix the formatting.Also note that the translation done by this bookmarklet is on a best-effort
basis. Many web pages don't strictly conform to the rules of correct HTML
markup, so you will likely need to fix those issues for your build to work.## Test
Run the test and print out diff if it fails:
dune runtest # Will also exit 1 on failure
Set the new version of the output as correct:
dune promote
## Prior art/design notes
Surface design obviously lifted straight from
[elm-html](https://package.elm-lang.org/packages/elm/html/latest/).Implementation inspired by both elm-html and
[ScalaTags](https://com-lihaoyi.github.io/scalatags/).Many languages and libraries have similar HTML embedded DSLs:
- [Phlex](https://www.phlex.fun/) - Ruby
- [Arbre](https://activeadmin.github.io/arbre/) - Ruby
- [hiccl](https://github.com/garlic0x1/hiccl) - Common Lisp
- [scribble-html-lib](https://docs.racket-lang.org/scribble-pp/html-html.html) -
Racket
- [hiccup](https://github.com/weavejester/hiccup) - Clojure
- [std/htmlgen](https://nim-lang.org/docs/htmlgen.html) - Nim
- [Falco.Markup](https://github.com/pimbrouwers/Falco.Markup) - F#
- [htpy](https://htpy.dev/) - Python
- [HTML::Tiny](https://metacpan.org/pod/HTML::Tiny) - Perl
- [j2html](https://j2html.com/) - Java
- [Lucid](https://github.com/chrisdone/lucid) - Haskell