Page
Library
Module
Module type
Parameter
Class
Class type
Source
Dream_html
SourceConstructing HTML. Detailed explanation in https://github.com/yawaramin/dream-html.
Let's adapt the example from the Dream home page:
let hello who =
let open Dream_html in
let open Tag in
html [] [body [] [h1 [] [txt "Hello, %s!" who]]]
let () =
Dream.run
@@ Dream.logger
@@ Dream.router [Dream.get "/" (fun _ -> Dream_html.respond (hello "world"))]
More examples shown below.
These are the types of the final values which get rendered.
E.g. id="toast"
.
Either a tag, a comment, or text data in the markup.
val respond :
?status:[< Dream.status ] ->
?code:int ->
?headers:(string * string) list ->
node ->
Dream.response Dream.promise
Type-safe wrapper for Dream.set_body
. Sets the body to the given node
and sets the Content-Type
header to text/html
.
Type-safe wrapper for Dream.write
.
Special handling for string-value attributes so they can use format strings i.e. string interpolation.
A 'void element': https://developer.mozilla.org/en-US/docs/Glossary/Void_element with no children.
Tags which can have attributes but can contain only text. The text can be formatted.
attr name
is a new attribute which does not carry any payload. E.g.
let required = attr "required"
string_attr name fmt
is a new string-valued attribute which allows formatting i.e. string interpolation of the value. Note, the fmt
argument is required due to the value restriction.
Convenience for attributes whose values should be URIs. Takes care of URI- encoding.
a [href "/blog?tags=iamsafe\"></a><script>alert('Pwned')</script>"] [txt "Tags: tag1 | tag2"]
Output:
<a href="/blog?tags=iamsafe%22%3E%3C/a%3E%3Cscript%3Ealert('Pwned')%3C/script%3E">Tags: tag1 | tag2</a>
A text node inside the DOM e.g. the 'hi' in <b>hi</b>
. Allows string interpolation using the same formatting features as Printf.sprintf
:
b [] [txt "Hello, %s!" name]
Or without interpolation:
b [] [txt "Bold of you."]
HTML-escapes the text value using Dream.html_escape
. You can use the ~raw
param to bypass escaping:
let user_input = "<script>alert('I like HTML injection')</script>" in
txt ~raw:true "%s" user_input
A comment that will be embedded in the rendered HTML, i.e. <!-- comment -->
. The text is HTML-escaped.
Convenience to add a CSRF token generated by Dream into your form. Type-safe wrapper for Dream.csrf_tag
.
Tag.form
[action "/foo"]
[csrf_tag req; input [name "bar"]; input [type_ "submit"]]
Get the value of an existing attribute.
let toast = p [id "toast"] [txt "OK."]
let toast_id = toast.@["id"]
Standard, most non-deprecated attributes from https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes. Where an attribute name conflicts with an OCaml keyword, the name is suffixed with _
. Most attributes are constructed by passing in a value of some type.
HTML tags. Most (standard tags) are constructed by passing a list of attributes and a list of children:
htmx attributes https://htmx.org/reference/#attributes
In this section we show an extended example of interoperability of dream-html with other formats, e.g. the Markdown format defined by the omd
library:
open Dream_html
open Tag
open Attr
let omd_attr (k, opt_v) =
string_attr k "%s" @@ Option.fold ~none:"" ~some:Fun.id opt_v
let rec omd_node elements = elements |> List.map element_node |> Tag.null
and item omd = li [] [omd_node omd]
and element_node = function
| Omd.H1 omd -> h1 [] [omd_node omd]
| H2 omd -> h2 [] [omd_node omd]
| H3 omd -> h3 [] [omd_node omd]
| H4 omd -> h4 [] [omd_node omd]
| H5 omd -> h5 [] [omd_node omd]
| H6 omd -> h6 [] [omd_node omd]
| Paragraph omd -> p [] [omd_node omd]
| Text str -> txt "%s" str
| Emph omd -> em [] [omd_node omd]
| Bold omd -> b [] [omd_node omd]
| Ul omds | Ulp omds -> ul [] @@ List.map item omds
| Ol omds | Olp omds -> ol [] @@ List.map item omds
| Code (cls, str) -> code [class_ "%s" cls] [txt "%s" str]
| Code_block (cls, str) ->
pre [class_ "%s" cls] [code [class_ "%s" cls] [txt "%s" str]]
| Br -> br []
| Hr -> hr []
| NL -> txt "\n"
| Url (hre, omd, titl) ->
a
[ href "%s" hre;
target "_blank";
class_ "after:content-['_↗️']";
title "%s" titl ]
[omd_node omd]
| Html ("details", attrs, omd) ->
details (List.map omd_attr attrs) [omd_node omd]
| Html ("summary", attrs, omd) ->
summary (List.map omd_attr attrs) [omd_node omd]
| Html_block ("details", attrs, omd) ->
details (List.map omd_attr attrs) [omd_node omd]
| Html_block ("summary", attrs, omd) ->
summary (List.map omd_attr attrs) [omd_node omd]
| Ref (_, _, _, _)
| Img_ref (_, _, _, _)
| Html (_, _, _)
| Html_block (_, _, _)
| X _ -> Tag.null []
| Html_comment str -> comment str
| Raw str -> txt ~raw:true "%s" str
| Raw_block str -> pre [] [txt ~raw:true "%s" str]
| Blockquote omd -> blockquote [] [omd_node omd]
| Img (al, sr, titl) -> img [alt "%s" al; src "%s" sr; title "%s" titl]
The entrypoint of the functionality is the omd_node
function, which converts from the Omd.t
value which can be obtained by, for example, parsing Markdown with the Omd.of_string
function, to a type-safe node
value.
In the element_node
function we traverse the Markdown structure and convert it into specific nodes–tags, comments, etc.. Text is escaped as appropriate and some specific conversions are handled specially:
<details>
and <summary>
HTML tags, but no others.This is just an example, but it shows the idea that we can take various formats and interop with them using type-sdfe HTML generated by dream-html.