The 'ssl' package seems to be a consistent bad actor in regard to build
breakage wrt. the system openssl dependency (see
https://github.com/savonet/ocaml-ssl/issues/154). My new rule is that
the core Forester tool should not depend on this library until such time
as they adopt a practice that does not cause my build to irreparably
break following every patch release of the system openssl dependency.
I have proposed to change the dream-html library to not depend on dream
here: https://github.com/yawaramin/dream-html/issues/28. Will be very
happy to resume our dependency on dream-html if that idea is adopted.
Signed-off-by: Jon Sterling <jon@jonmsterling.com>
---
dune-project | 2 -
forester.opam | 1 -
forester.opam.locked | 77 +--
lib/frontend/Forest.ml | 3 +-
lib/render/Dream_forester.ml | 4 +-
lib/render/Dream_forester.mli | 2 +-
lib/render/Dream_html_lite.ml | 936 ++++++++++++++++++++++++++++++++
lib/render/Dream_html_lite.mli | 960 +++++++++++++++++++++++++++++++++
lib/render/Render_dream.ml | 2 +-
lib/render/dune | 2 +-
test/dune | 2 +-
11 files changed, 1925 insertions(+), 66 deletions(-)
create mode 100644 lib/render/Dream_html_lite.ml
create mode 100644 lib/render/Dream_html_lite.mli
diff --git a/dune-project b/dune-project
index bfba6d3..09c67b2 100644
--- a/dune-project
+++ b/dune-project
@@ -52,8 +52,6 @@
(>= 2.0.0))
(yojson
(>= 2.1.2))
- (dream-html
- (>= 3.4.1))
(toml
(>= 7.1.0))
(irmin-git
diff --git a/forester.opam b/forester.opam
index ffe4c88..5ee435a 100644
--- a/forester.opam
+++ b/forester.opam
@@ -23,7 +23,6 @@ depends: [
"bwd" {>= "2.3.0"}
"algaeff" {>= "2.0.0"}
"yojson" {>= "2.1.2"}
- "dream-html" {>= "3.4.1"}
"toml" {>= "7.1.0"}
"irmin-git" {>= "3.9.0"}
"odoc" {with-doc}
diff --git a/forester.opam.locked b/forester.opam.locked
index 627ad49..5c7a1cc 100644
--- a/forester.opam.locked
+++ b/forester.opam.locked
@@ -11,7 +11,7 @@ depends: [
"algaeff" {= "2.0.0"}
"angstrom" {= "0.16.0"}
"arp" {= "3.1.1"}
- "asai" {= "0.3.0"}
+ "asai" {= "0.3.1"}
"asn1-combinators" {= "0.2.6"}
"astring" {= "0.8.5"}
"awa" {= "0.3.1"}
@@ -26,15 +26,11 @@ depends: [
"base64" {= "3.5.1"}
"bheap" {= "2.0.0"}
"bigarray-compat" {= "1.1.0"}
- "bigarray-overlap" {= "0.2.1"}
"bigstringaf" {= "0.9.1"}
"bos" {= "0.2.1"}
"bwd" {= "2.3.0"}
"ca-certs" {= "0.2.3"}
- "ca-certs-nss" {= "3.98"}
- "camlp-streams" {= "5.0.1"}
- "caqti" {= "2.1.1"}
- "caqti-lwt" {= "2.1.1"}
+ "ca-certs-nss" {= "3.101"}
"carton" {= "0.7.2"}
"carton-git" {= "0.7.2"}
"carton-lwt" {= "0.7.2"}
@@ -45,14 +41,12 @@ depends: [
"cohttp" {= "5.3.1"}
"cohttp-lwt" {= "5.3.0"}
"cohttp-lwt-unix" {= "5.3.0"}
- "conduit" {= "6.2.2"}
- "conduit-lwt" {= "6.2.2"}
- "conduit-lwt-unix" {= "6.2.2"}
+ "conduit" {= "6.2.3"}
+ "conduit-lwt" {= "6.2.3"}
+ "conduit-lwt-unix" {= "6.2.3"}
"conf-gmp" {= "4"}
"conf-gmp-powm-sec" {= "3"}
- "conf-libev" {= "4-12"}
"conf-libffi" {= "2.0.0"}
- "conf-libssl" {= "4"}
"conf-pkg-config" {= "3"}
"cppo" {= "1.6.9"}
"csexp" {= "1.5.2"}
@@ -63,24 +57,15 @@ depends: [
"ctypes-foreign" {= "0.22.0"}
"decompress" {= "1.5.3"}
"digestif" {= "1.2.0"}
- "dns" {= "7.0.3"}
- "dns-client" {= "7.0.3"}
- "dns-client-lwt" {= "7.0.3"}
- "dns-client-mirage" {= "7.0.3"}
+ "dns" {= "8.0.0"}
+ "dns-client" {= "8.0.0"}
"domain-local-await" {= "1.0.1"}
"domain-name" {= "0.4.0"}
- "dream" {= "1.0.0~alpha6"}
- "dream-html" {= "3.4.1"}
- "dream-httpaf" {= "1.0.0~alpha3"}
- "dream-pure" {= "1.0.0~alpha2"}
"duff" {= "0.5"}
"dune" {= "3.15.3"}
"dune-build-info" {= "3.15.3"}
"dune-configurator" {= "3.15.3"}
- "dune-private-libs" {= "3.15.3"}
- "dune-site" {= "3.15.3"}
"duration" {= "0.2.1"}
- "dyn" {= "3.15.3"}
"eio" {= "1.1"}
"eio_main" {= "1.1"}
"eio_posix" {= "1.1"}
@@ -90,26 +75,23 @@ depends: [
"eqaf" {= "0.9"}
"ethernet" {= "3.2.0"}
"faraday" {= "0.8.2"}
- "faraday-lwt" {= "0.8.2"}
- "faraday-lwt-unix" {= "0.8.2"}
"fmt" {= "0.9.0"}
"fpath" {= "0.7.3"}
"fsevents" {= "0.3.0"}
"fsevents-lwt" {= "0.3.0"}
- "git" {= "3.16.0"}
- "git-mirage" {= "3.16.0"}
- "git-paf" {= "3.16.0"}
- "git-unix" {= "3.16.0"}
+ "git" {= "3.16.1"}
+ "git-mirage" {= "3.16.1"}
+ "git-paf" {= "3.16.1"}
+ "git-unix" {= "3.16.1"}
"gmap" {= "0.3.0"}
- "graphql" {= "0.14.0"}
- "graphql-lwt" {= "0.14.0"}
- "graphql_parser" {= "0.14.0"}
"h2" {= "0.11.0"}
- "happy-eyeballs" {= "0.6.0"}
- "happy-eyeballs-lwt" {= "0.6.0"}
- "happy-eyeballs-mirage" {= "0.6.0"}
+ "happy-eyeballs" {= "1.1.0"}
+ "happy-eyeballs-lwt" {= "1.1.0"}
+ "happy-eyeballs-mirage" {= "1.1.0"}
"hkdf" {= "1.0.4"}
"hmap" {= "0.8.1"}
+ "host-arch-arm64" {= "1"}
+ "host-system-other" {= "1"}
"hpack" {= "0.11.0"}
"httpaf" {= "0.7.1"}
"hxd" {= "0.3.2"}
@@ -124,31 +106,26 @@ depends: [
"irmin-watcher" {= "0.5.0"}
"jsonm" {= "1.0.2"}
"ke" {= "0.6"}
- "lambdasoup" {= "1.0.0"}
"logs" {= "0.7.0"}
"lru" {= "0.3.1"}
"lwt" {= "5.7.0"}
"lwt-dllist" {= "1.0.1"}
- "lwt_ppx" {= "2.1.0"}
- "lwt_ssl" {= "1.2.0"}
"macaddr" {= "5.6.0"}
"macaddr-cstruct" {= "5.6.0"}
"magic-mime" {= "1.3.1"}
- "markup" {= "1.0.3"}
"menhir" {= "20231231"}
"menhirCST" {= "20231231"}
"menhirLib" {= "20231231"}
"menhirSdk" {= "20231231"}
"metrics" {= "0.4.1"}
- "mimic" {= "0.0.7"}
- "mimic-happy-eyeballs" {= "0.0.7"}
+ "mimic" {= "0.0.8"}
+ "mimic-happy-eyeballs" {= "0.0.8"}
"mirage-clock" {= "4.2.0"}
"mirage-clock-unix" {= "4.2.0"}
"mirage-crypto" {= "0.11.3"}
"mirage-crypto-ec" {= "0.11.3"}
"mirage-crypto-pk" {= "0.11.3"}
"mirage-crypto-rng" {= "0.11.3"}
- "mirage-crypto-rng-lwt" {= "0.11.3"}
"mirage-flow" {= "4.0.2"}
"mirage-kv" {= "6.1.1"}
"mirage-net" {= "4.0.0"}
@@ -157,12 +134,10 @@ depends: [
"mirage-time" {= "3.0.0"}
"mirage-unix" {= "5.0.1"}
"mtime" {= "2.0.0"}
- "multipart_form" {= "0.6.0"}
- "multipart_form-lwt" {= "0.6.0"}
"num" {= "1.5"}
- "ocaml" {= "5.1.1"}
- "ocaml-base-compiler" {= "5.1.1"}
- "ocaml-compiler-libs" {= "v0.12.4"}
+ "ocaml" {= "5.2.0"}
+ "ocaml-base-compiler" {= "5.2.0"}
+ "ocaml-compiler-libs" {= "v0.17.0"}
"ocaml-config" {= "3"}
"ocaml-options-vanilla" {= "1"}
"ocaml-syntax-shims" {= "1.0.0"}
@@ -172,12 +147,10 @@ depends: [
"ocamlgraph" {= "2.1.0"}
"ocplib-endian" {= "1.2"}
"optint" {= "0.3.0"}
- "ordering" {= "3.15.3"}
"paf" {= "0.6.0"}
"parsexp" {= "v0.17.0"}
"pbkdf" {= "1.2.0"}
"pecu" {= "0.7"}
- "pp" {= "1.2.0"}
"ppx_derivers" {= "1.2.1"}
"ppx_deriving" {= "6.0.2"}
"ppx_irmin" {= "3.9.0"}
@@ -185,7 +158,6 @@ depends: [
"ppx_sexp_conv" {= "v0.17.0"}
"ppxlib" {= "0.32.1"}
"ppxlib_jane" {= "v0.17.0"}
- "prettym" {= "0.0.3"}
"psq" {= "0.2.1"}
"ptime" {= "1.1.0"}
"randomconv" {= "0.1.3"}
@@ -196,25 +168,20 @@ depends: [
"seq" {= "base"}
"sexplib" {= "v0.17.0"}
"sexplib0" {= "v0.17.0"}
- "ssl" {= "0.7.0"}
"stdlib-shims" {= "0.3.0"}
- "stdune" {= "3.15.3"}
"stringext" {= "1.6.0"}
"tcpip" {= "8.1.0"}
"thread-table" {= "1.0.0"}
"tls" {= "0.17.5"}
- "tls-lwt" {= "0.17.5"}
"tls-mirage" {= "0.17.5"}
"toml" {= "7.1.0"}
"topkg" {= "1.0.7"}
- "uchar" {= "0.0.2"}
- "unstrctrd" {= "0.4"}
"uri" {= "4.4.0"}
"uri-sexp" {= "4.4.0"}
"uucp" {= "15.1.0"}
"uutf" {= "1.0.3"}
"x509" {= "0.16.5"}
- "yojson" {= "2.1.2"}
+ "yojson" {= "2.2.1"}
"yuujinchou" {= "5.2.0"}
"zarith" {= "1.13"}
]
diff --git a/lib/frontend/Forest.ml b/lib/frontend/Forest.ml
index 59b8c95..4929f1a 100644
--- a/lib/frontend/Forest.ml
+++ b/lib/frontend/Forest.ml
@@ -274,7 +274,7 @@ let render_tree ~cfg ~cwd (tree : Sem.tree) =
Format.pp_print_newline fmt ();
Format.fprintf fmt "<?xml-stylesheet type=\"text/xsl\" href=\"%s\"?>" cfg.stylesheet;
Format.pp_print_newline fmt ();
- Dream_html.pp fmt node
+ Dream_html_lite.pp fmt node
end
let render_json ~cwd docs =
@@ -287,7 +287,6 @@ let is_hidden_file fname =
let copy_theme ~env ~theme_dir =
let cwd = Eio.Stdenv.cwd env in
- let fs = Eio.Stdenv.fs env in
Eio.Path.with_open_dir theme_dir @@ fun theme ->
Eio.Path.read_dir theme |> List.iter @@ fun fname ->
if not @@ is_hidden_file fname then
diff --git a/lib/render/Dream_forester.ml b/lib/render/Dream_forester.ml
index edf7878..ab4ae4b 100644
--- a/lib/render/Dream_forester.ml
+++ b/lib/render/Dream_forester.ml
@@ -1,4 +1,4 @@
-open Dream_html
+open Dream_html_lite
let reserved_prefix = "fr"
let forester_xmlns = "http://www.jonmsterling.com/jms-005P.xml"
@@ -92,4 +92,4 @@ let src fmt = uri_attr "src" fmt
let embedded_tex = f_std_tag "embedded-tex"
let embedded_tex_preamble attrs = f_text_tag "embedded-tex-preamble" attrs
let embedded_tex_body attrs = f_text_tag "embedded-tex-body" attrs
-let hash fmt = string_attr "hash" fmt
\ No newline at end of file
+let hash fmt = string_attr "hash" fmt
diff --git a/lib/render/Dream_forester.mli b/lib/render/Dream_forester.mli
index 09dc0d8..bc31f97 100644
--- a/lib/render/Dream_forester.mli
+++ b/lib/render/Dream_forester.mli
@@ -1,4 +1,4 @@
-open Dream_html
+open Dream_html_lite
open Core
val reserved_prefix : string
diff --git a/lib/render/Dream_html_lite.ml b/lib/render/Dream_html_lite.ml
new file mode 100644
index 0000000..a47883e
--- /dev/null
+++ b/lib/render/Dream_html_lite.ml
@@ -0,0 +1,936 @@
+(* 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 <https://www.gnu.org/licenses/>. *)
+
+type attr = string * string
+
+type tag =
+ { name : string;
+ attrs : attr list;
+ children : node list option
+ }
+
+and node =
+ | Tag of tag
+ | Txt of string
+ | Comment of string
+
+type 'a to_attr = 'a -> attr
+type 'a string_attr = ('a, unit, string, attr) format4 -> 'a
+type std_tag = attr list -> node list -> node
+type void_tag = attr list -> node
+type 'a text_tag = attr list -> ('a, unit, string, node) format4 -> 'a
+
+let write_attr p = function
+ | "", _ -> ()
+ | name, "" ->
+ p "\n";
+ p name
+ | name, value ->
+ p "\n";
+ p name;
+ p {|="|};
+ p value;
+ p {|"|}
+
+(* Loosely based on https://www.w3.org/TR/DOM-Parsing/ *)
+let rec write_tag ~xml p = function
+ | Tag { name = ""; children = Some children; _ } ->
+ List.iter (write_tag ~xml p) children
+ | Tag { name; attrs; children = Some [] } when xml ->
+ p "<";
+ p name;
+ List.iter (write_attr p) attrs;
+ p " />"
+ | Tag { name; attrs; children = None } ->
+ p "<";
+ p name;
+ List.iter (write_attr p) attrs;
+ p (if xml then " />" else ">")
+ | Tag { name; attrs; children = Some children } ->
+ if name = "html" then p "<!DOCTYPE html>\n";
+ p "<";
+ p name;
+ List.iter (write_attr p) attrs;
+ p ">";
+ List.iter (write_tag ~xml p) children;
+ p "</";
+ p name;
+ p ">"
+ | Txt str -> p str
+ | Comment str ->
+ p "<!-- ";
+ p str;
+ p " -->"
+
+let to_string ~xml node =
+ let buf = Buffer.create 256 in
+ write_tag ~xml (Buffer.add_string buf) node;
+ Buffer.contents buf
+
+let pp ppf node = node |> to_string ~xml:false |> Format.pp_print_string ppf
+let to_xml = to_string ~xml:true
+let to_string = to_string ~xml:false
+let pp_xml ppf node = node |> to_xml |> Format.pp_print_string ppf
+
+let txt_escape buffer = function
+ | '&' -> Buffer.add_string buffer "&"
+ | '<' -> Buffer.add_string buffer "<"
+ | '>' -> Buffer.add_string buffer ">"
+ | c -> Buffer.add_char buffer c
+
+let txt_escape raw s =
+ if raw then
+ s
+ else
+ let buffer = Buffer.create (String.length s * 2) in
+ String.iter (txt_escape buffer) s;
+ Buffer.contents buffer
+
+let attr_escape buffer = function
+ | '&' -> Buffer.add_string buffer "&"
+ | '"' -> Buffer.add_string buffer """
+ | c -> Buffer.add_char buffer c
+
+let attr_escape raw s =
+ if raw then
+ s
+ else
+ let buffer = Buffer.create (String.length s * 2) in
+ String.iter (attr_escape buffer) s;
+ Buffer.contents buffer
+
+let attr name = name, ""
+
+let string_attr name ?(raw = false) fmt =
+ Printf.ksprintf (fun s -> name, attr_escape raw s) fmt
+
+let uri_attr name fmt =
+ Printf.ksprintf
+ (fun s -> name, s |> Uri.of_string |> Uri.to_string |> attr_escape false)
+ fmt
+
+let bool_attr name value = name, string_of_bool value
+let float_attr name value = name, Printf.sprintf "%f" value
+let int_attr name value = name, string_of_int value
+let std_tag name attrs children = Tag { name; attrs; children = Some children }
+let void_tag name attrs = Tag { name; attrs; children = None }
+
+let text_tag name ?(raw = false) attrs fmt =
+ Printf.ksprintf
+ (fun s -> Tag { name; attrs; children = Some [Txt (txt_escape raw s)] })
+ fmt
+
+let txt ?(raw = false) fmt =
+ Printf.ksprintf (fun s -> Txt (txt_escape raw s)) fmt
+
+let comment str = Comment (txt_escape false str)
+
+let ( +@ ) node attr =
+ match node with
+ | Tag t -> Tag { t with attrs = attr :: t.attrs }
+ | _ -> invalid_arg "cannot add attribute to non-tag node"
+
+let ( -@ ) node attr =
+ match node with
+ | Tag t ->
+ Tag { t with attrs = List.filter (fun (k, _) -> k <> attr) t.attrs }
+ | _ -> invalid_arg "cannot remove attribute from non-tag node"
+
+let ( .@[] ) node attr =
+ match node with
+ | Tag { attrs; _ } -> List.assoc attr attrs
+ | _ -> invalid_arg "cannot get value of attribute from non-tag node"
+
+let is_null = function
+ | Tag { name = ""; _ } -> true
+ | _ -> false
+
+let is_null_ (name, _) = name = ""
+
+module HTML = struct
+ (* Attributes *)
+
+ type method_ =
+ [ `GET
+ | `POST
+ | `dialog ]
+
+ type enctype =
+ [ `urlencoded
+ | `formdata
+ | `text_plain ]
+
+ let enctype_string = function
+ | `urlencoded -> "application/x-www-form-urlencoded"
+ | `formdata -> "multipart/form-data"
+ | `text_plain -> "text/plain"
+
+ let null_ = string_attr "" ""
+ let accept fmt = string_attr "accept" fmt
+ let accept_charset fmt = string_attr "accept-charset" fmt
+ let accesskey fmt = string_attr "accesskey" fmt
+ let action fmt = uri_attr "action" fmt
+ let align fmt = string_attr "align" fmt
+ let allow fmt = string_attr "allow" fmt
+ let alt fmt = string_attr "alt" fmt
+ let async = attr "async"
+
+ let autocapitalize value =
+ ( "autocapitalize",
+ match value with
+ | `off -> "off"
+ | `none -> "none"
+ | `on -> "on"
+ | `sentences -> "sentences"
+ | `words -> "words"
+ | `characters -> "characters" )
+
+ let autocomplete value =
+ ( "autocomplete",
+ match value with
+ | `off -> "off"
+ | `on -> "on"
+ | `name -> "name"
+ | `honorific_prefix -> "honorific-prefix"
+ | `given_name -> "given-name"
+ | `additional_name -> "additional-name"
+ | `honorific_suffix -> "honorific-suffix"
+ | `nickname -> "nickname"
+ | `email -> "email"
+ | `username -> "username"
+ | `new_password -> "new-password"
+ | `current_password -> "current-password"
+ | `one_time_code -> "one-time-code"
+ | `organization_title -> "organization-title"
+ | `organization -> "organization"
+ | `street_address -> "street-address"
+ | `address_line1 -> "address-line1"
+ | `address_line2 -> "address-line2"
+ | `address_line3 -> "address-line3"
+ | `address_level4 -> "address-level4"
+ | `address_level3 -> "address-level3"
+ | `address_level2 -> "address-level2"
+ | `address_level1 -> "address-level1"
+ | `country -> "country"
+ | `country_name -> "country-name"
+ | `postal_code -> "postal-code"
+ | `cc_name -> "cc-name"
+ | `cc_given_name -> "cc-given-name"
+ | `cc_additional_name -> "cc-additional-name"
+ | `cc_family_name -> "cc-family-name"
+ | `cc_number -> "cc-number"
+ | `cc_exp -> "cc-exp"
+ | `cc_exp_month -> "cc-exp-month"
+ | `cc_exp_year -> "cc-exp-year"
+ | `cc_csc -> "cc-csc"
+ | `cc_type -> "cc-type"
+ | `transaction_currency -> "transaction-currency"
+ | `transaction_amount -> "transaction-amount"
+ | `language -> "language"
+ | `bday -> "bday"
+ | `bday_day -> "bday-day"
+ | `bday_month -> "bday-month"
+ | `bday_year -> "bday-year"
+ | `sex -> "sex"
+ | `tel -> "tel"
+ | `tel_country_code -> "tel-country-code"
+ | `tel_national -> "tel-national"
+ | `tel_area_code -> "tel-area-code"
+ | `tel_local -> "tel-local"
+ | `tel_extension -> "tel-extension"
+ | `impp -> "impp"
+ | `url -> "url"
+ | `photo -> "photo"
+ | `webauthn -> "webauthn" )
+
+ let autofocus = attr "autofocus"
+ let autoplay = attr "autoplay"
+ let buffered fmt = string_attr "buffered" fmt
+
+ let capture value =
+ ( "capture",
+ match value with
+ | `user -> "user"
+ | `environment -> "environment" )
+
+ let charset fmt = string_attr "charset" fmt
+ let checked = attr "checked"
+ let cite_ fmt = uri_attr "cite" fmt
+ let class_ fmt = string_attr "class" fmt
+ let color fmt = string_attr "color" fmt
+ let cols = int_attr "cols"
+ let colspan = int_attr "colspan"
+ let content fmt = string_attr "content" fmt
+ let contenteditable = bool_attr "contenteditable"
+ let contextmenu fmt = string_attr "contextmenu" fmt
+ let controls = attr "controls"
+ let coords fmt = string_attr "coords" fmt
+
+ let crossorigin value =
+ ( "crossorigin",
+ match value with
+ | `anonymous -> "anonymous"
+ | `use_credentials -> "use-credentials" )
+
+ let data_ fmt = uri_attr "data" fmt
+ let datetime fmt = string_attr "datetime" fmt
+
+ let decoding value =
+ ( "decoding",
+ match value with
+ | `sync -> "sync"
+ | `async -> "async"
+ | `auto -> "auto" )
+
+ let default = attr "default"
+ let defer = attr "defer"
+
+ let dir value =
+ ( "dir",
+ match value with
+ | `ltr -> "ltr"
+ | `rtl -> "rtl"
+ | `auto -> "auto" )
+
+ let dirname fmt = string_attr "dirname" fmt
+ let disabled = attr "disabled"
+ let download fmt = string_attr "download" fmt
+ let draggable = attr "draggable"
+ let enctype value = "enctype", enctype_string value
+
+ let fetchpriority value =
+ ( "fetchpriority",
+ match value with
+ | `high -> "high"
+ | `low -> "low"
+ | `auto -> "auto" )
+
+ let for_ fmt = string_attr "for" fmt
+ let form_ fmt = string_attr "form" fmt
+ let formaction fmt = string_attr "formaction" fmt
+ let formenctype value = "formenctype", enctype_string value
+
+ let method_to_string = function
+ | `GET -> "get"
+ | `POST -> "post"
+ | `dialog -> "dialog"
+
+ let formmethod value = "formmethod", method_to_string value
+ let formnovalidate = attr "formnovalidate"
+ let formtarget fmt = string_attr "formtarget" fmt
+ let headers fmt = string_attr "headers" fmt
+ let height fmt = string_attr "height" fmt
+
+ let hidden value =
+ ( "hidden",
+ match value with
+ | `hidden -> "hidden"
+ | `until_found -> "until-found" )
+
+ let high = float_attr "high"
+ let href fmt = uri_attr "href" fmt
+ let hreflang fmt = string_attr "hreflang" fmt
+
+ let http_equiv value =
+ ( "http-equiv",
+ match value with
+ | `content_security_policy -> "content-security-policy"
+ | `content_type -> "content-type"
+ | `default_style -> "default-style"
+ | `x_ua_compatible -> "x-ua-compatible"
+ | `refresh -> "refresh" )
+
+ let id fmt = string_attr "id" fmt
+ let integrity fmt = string_attr "integrity" fmt
+
+ let inputmode value =
+ ( "inputmode",
+ match value with
+ | `none -> "none"
+ | `text -> "text"
+ | `decimal -> "decimal"
+ | `numeric -> "numeric"
+ | `tel -> "tel"
+ | `search -> "search"
+ | `email -> "email"
+ | `url -> "url" )
+
+ let ismap = attr "ismap"
+ let itemprop fmt = string_attr "itemprop" fmt
+
+ let kind value =
+ ( "kind",
+ match value with
+ | `subtitles -> "subtitles"
+ | `captions -> "captions"
+ | `descriptions -> "descriptions"
+ | `chapters -> "chapters"
+ | `metadata -> "metadata" )
+
+ let label_ fmt = string_attr "label" fmt
+ let lang fmt = string_attr "lang" fmt
+ let list fmt = string_attr "list" fmt
+ let loading_lazy = string_attr "loading" "lazy"
+ let loop = attr "loop"
+ let low = float_attr "low"
+ let max fmt = string_attr "max" fmt
+ let maxlength = int_attr "maxlength"
+ let media fmt = string_attr "media" fmt
+ let method_ value = "method", method_to_string value
+ let min fmt = string_attr "min" fmt
+ let minlength = int_attr "minlength"
+ let multiple = attr "multiple"
+ let muted = attr "muted"
+ let name fmt = string_attr "name" fmt
+ let novalidate = attr "novalidate"
+ let onblur fmt = string_attr "onblur" ~raw:true fmt
+ let onclick fmt = string_attr "onclick" ~raw:true fmt
+ let open_ = attr "open"
+ let optimum = float_attr "optimum"
+ let pattern fmt = string_attr "pattern" fmt
+ let ping fmt = string_attr "ping" fmt
+ let placeholder fmt = string_attr "placeholder" fmt
+ let playsinline = attr "playsinline"
+ let poster fmt = uri_attr "poster" fmt
+
+ let preload value =
+ ( "preload",
+ match value with
+ | `none -> "none"
+ | `metadata -> "metadata"
+ | `auto -> "auto" )
+
+ let readonly = attr "readonly"
+
+ let referrerpolicy value =
+ ( "referrerpolicy",
+ match value with
+ | `no_referrer -> "no-referrer"
+ | `no_referrer_when_downgrade -> "no-referrer-when-downgrade"
+ | `origin -> "origin"
+ | `origin_when_cross_origin -> "origin-when-cross-origin"
+ | `same_origin -> "same-origin"
+ | `strict_origin -> "strict-origin"
+ | `strict_origin_when_cross_origin -> "strict-origin-when-cross-origin"
+ | `unsafe_url -> "unsafe-url" )
+
+ let rel fmt = string_attr "rel" fmt
+ let required = attr "required"
+ let reversed = attr "reversed"
+
+ let role value =
+ ( "role",
+ match value with
+ | `alert -> "alert"
+ | `alertdialog -> "alertdialog"
+ | `application -> "application"
+ | `article -> "article"
+ | `banner -> "banner"
+ | `button -> "button"
+ | `cell -> "cell"
+ | `checkbox -> "checkbox"
+ | `columnheader -> "columnheader"
+ | `combobox -> "combobox"
+ | `comment -> "comment"
+ | `complementary -> "complementary"
+ | `contentinfo -> "contentinfo"
+ | `definition -> "definition"
+ | `dialog -> "dialog"
+ | `document -> "document"
+ | `feed -> "feed"
+ | `figure -> "figure"
+ | `form -> "form"
+ | `generic -> "generic"
+ | `grid -> "grid"
+ | `gridcell -> "gridcell"
+ | `group -> "group"
+ | `heading -> "heading"
+ | `img -> "img"
+ | `link -> "link"
+ | `list -> "list"
+ | `listbox -> "listbox"
+ | `listitem -> "listitem"
+ | `log -> "log"
+ | `main -> "main"
+ | `mark -> "mark"
+ | `marquee -> "marquee"
+ | `math -> "math"
+ | `menu -> "menu"
+ | `menubar -> "menubar"
+ | `menuitem -> "menuitem"
+ | `menuitemcheckbox -> "menuitemcheckbox"
+ | `menuitemradio -> "menuitemradio"
+ | `meter -> "meter"
+ | `navigation -> "navigation"
+ | `none -> "none"
+ | `note -> "note"
+ | `option -> "option"
+ | `presentation -> "presentation"
+ | `progressbar -> "progressbar"
+ | `radio -> "radio"
+ | `radiogroup -> "radiogroup"
+ | `region -> "region"
+ | `row -> "row"
+ | `rowgroup -> "rowgroup"
+ | `rowheader -> "rowheader"
+ | `scrollbar -> "scrollbar"
+ | `search -> "search"
+ | `searchbox -> "searchbox"
+ | `separator -> "separator"
+ | `slider -> "slider"
+ | `spinbutton -> "spinbutton"
+ | `status -> "status"
+ | `suggestion -> "suggestion"
+ | `switch -> "switch"
+ | `tab -> "tab"
+ | `table -> "table"
+ | `tablist -> "tablist"
+ | `tabpanel -> "tabpanel"
+ | `term -> "term"
+ | `textbox -> "textbox"
+ | `timer -> "timer"
+ | `toolbar -> "toolbar"
+ | `tooltip -> "tooltip"
+ | `tree -> "tree"
+ | `treegrid -> "treegrid"
+ | `treeitem -> "treeitem" )
+
+ let rows = int_attr "rows"
+ let rowspan = int_attr "rowspan"
+ let sandbox fmt = string_attr "sandbox" fmt
+ let scope fmt = string_attr "scope" fmt
+ let selected = attr "selected"
+ let shape fmt = string_attr "shape" fmt
+ let size fmt = string_attr "size" fmt
+ let sizes fmt = string_attr "sizes" fmt
+ let slot_ fmt = string_attr "slot" fmt
+ let span_ = int_attr "span"
+ let spellcheck = bool_attr "spellcheck"
+ let src fmt = uri_attr "src" fmt
+ let srcdoc fmt = string_attr "srcdoc" fmt
+ let srclang fmt = string_attr "srclang" fmt
+ let srcset fmt = string_attr "srcset" fmt
+ let start = int_attr "start"
+ let step fmt = string_attr "step" fmt
+ let style_ fmt = string_attr ~raw:true "style" fmt
+ let tabindex = int_attr "tabindex"
+ let target fmt = string_attr "target" fmt
+ let title_ fmt = string_attr "title" fmt
+
+ let translate value =
+ ( "translate",
+ match value with
+ | `yes -> "yes"
+ | `no -> "no" )
+
+ let type_ fmt = string_attr "type" fmt
+ let usemap fmt = string_attr "usemap" fmt
+ let value fmt = string_attr "value" fmt
+ let width fmt = string_attr "width" fmt
+
+ let wrap value =
+ ( "wrap",
+ match value with
+ | `hard -> "hard"
+ | `soft -> "soft" )
+
+ (* Tags *)
+
+ let null = std_tag "" []
+ let a = std_tag "a"
+ let address = std_tag "address"
+ let abbr = std_tag "abbr"
+ let area = void_tag "area"
+ let article = std_tag "article"
+ let aside = std_tag "aside"
+ let audio = std_tag "audio"
+ let b = std_tag "b"
+ let base = void_tag "base"
+ let bdi = std_tag "bdi"
+ let bdo = std_tag "bdo"
+ let blockquote = std_tag "blockquote"
+ let br = void_tag "br"
+ let body = std_tag "body"
+ let button = std_tag "button"
+ let canvas = std_tag "canvas"
+ let caption = std_tag "caption"
+ let cite = std_tag "cite"
+ let code = std_tag "code"
+ let col = void_tag "col"
+ let colgroup = std_tag "colgroup"
+ let data = std_tag "data"
+ let datalist = std_tag "datalist"
+ let dd = std_tag "dd"
+ let del = std_tag "del"
+ let details = std_tag "details"
+ let dfn = std_tag "dfn"
+ let dialog = std_tag "dialog"
+ let div = std_tag "div"
+ let dl = std_tag "dl"
+ let dt = std_tag "dt"
+ let em = std_tag "em"
+ let embed = void_tag "embed"
+ let fieldset = std_tag "fieldset"
+ let figcaption = std_tag "figcaption"
+ let figure = std_tag "figure"
+ let footer = std_tag "footer"
+ let form = std_tag "form"
+ let h1 = std_tag "h1"
+ let h2 = std_tag "h2"
+ let h3 = std_tag "h3"
+ let h4 = std_tag "h4"
+ let h5 = std_tag "h5"
+ let h6 = std_tag "h6"
+ let head = std_tag "head"
+ let header = std_tag "header"
+ let hgroup = std_tag "hgroup"
+ let hr = void_tag "hr"
+ let html = std_tag "html"
+ let i = std_tag "i"
+ let iframe = std_tag "iframe"
+ let img = void_tag "img"
+ let input = void_tag "input"
+ let ins = std_tag "ins"
+ let kbd = std_tag "kbd"
+ let label = std_tag "label"
+ let legend = std_tag "legend"
+ let li = std_tag "li"
+ let link = void_tag "link"
+ let main = std_tag "main"
+ let map = std_tag "map"
+ let mark = std_tag "mark"
+ let menu = std_tag "menu"
+ let meta = void_tag "meta"
+ let meter = std_tag "meter"
+ let nav = std_tag "nav"
+ let noscript = std_tag "noscript"
+ let object_ = std_tag "object"
+ let ol = std_tag "ol"
+ let optgroup = std_tag "optgroup"
+ let option attrs fmt = text_tag "option" attrs fmt
+ let output = std_tag "output"
+ let p = std_tag "p"
+ let picture = std_tag "picture"
+ let pre = std_tag "pre"
+ let progress = std_tag "progress"
+ let q = std_tag "q"
+ let rp = std_tag "rp"
+ let rt = std_tag "rt"
+ let ruby = std_tag "ruby"
+ let s = std_tag "s"
+ let samp = std_tag "samp"
+ let script attrs fmt = text_tag "script" ~raw:true attrs fmt
+ let section = std_tag "section"
+ let select = std_tag "select"
+ let slot = std_tag "slot"
+ let small = std_tag "small"
+ let source = void_tag "source"
+ let span = std_tag "span"
+ let strong = std_tag "strong"
+ let style attrs fmt = text_tag "style" ~raw:true attrs fmt
+ let sub = std_tag "sub"
+ let sup = std_tag "sup"
+ let summary = std_tag "summary"
+ let table = std_tag "table"
+ let tbody = std_tag "tbody"
+ let td = std_tag "td"
+ let template = std_tag "template"
+ let textarea attrs fmt = text_tag "textarea" attrs fmt
+ let tfoot = std_tag "tfoot"
+ let th = std_tag "th"
+ let thead = std_tag "thead"
+ let time = std_tag "time"
+ let title attrs fmt = text_tag "title" attrs fmt
+ let tr = std_tag "tr"
+ let track = void_tag "track"
+ let u = std_tag "u"
+ let ul = std_tag "ul"
+ let var = std_tag "var"
+ let video = std_tag "video"
+ let wbr = void_tag "wbr"
+end
+
+module SVG = struct
+ (* Attributes *)
+ let d fmt = string_attr "d" fmt
+ let fill fmt = string_attr "fill" fmt
+ let stroke fmt = string_attr "stroke" fmt
+
+ let stroke_linecap value =
+ ( "stroke-linecap",
+ match value with
+ | `butt -> "butt"
+ | `round -> "round"
+ | `square -> "square" )
+
+ let stroke_linejoin value =
+ ( "stroke-linejoin",
+ match value with
+ | `arcs -> "arcs"
+ | `bevel -> "bevel"
+ | `miter -> "miter"
+ | `miter_clip -> "miter-clip"
+ | `round -> "round" )
+
+ let stroke_width fmt = string_attr "stroke-width" fmt
+
+ let viewbox ~min_x ~min_y ~width ~height =
+ "viewbox", Printf.sprintf "%d %d %d %d" min_x min_y width height
+
+ let xmlns = string_attr "xmlns" "http://www.w3.org/2000/svg"
+
+ (* Tags *)
+ let path = std_tag "path"
+ let svg = std_tag "svg"
+end
+
+module Aria = struct
+ let activedescendant fmt = string_attr "aria-activedescendant" fmt
+ let atomic = attr "aria-atomic"
+
+ let autocomplete value =
+ ( "aria-autocomplete",
+ match value with
+ | `inline -> "inline"
+ | `list -> "list"
+ | `both -> "both" )
+
+ let braillelabel fmt = string_attr "aria-braillelabel" fmt
+ let brailleroledescription fmt = string_attr "aria-brailleroledescription" fmt
+ let busy = attr "aria-busy"
+
+ let checked value =
+ ( "aria-checked",
+ match value with
+ | `false_ -> "false"
+ | `true_ -> "true"
+ | `mixed -> "mixed" )
+
+ let colcount = int_attr "aria-colcount"
+ let colindextext fmt = string_attr "aria-colindextext" fmt
+ let colspan = int_attr "aria-colspan"
+ let controls fmt = string_attr "aria-controls" fmt
+
+ let current value =
+ ( "aria-current",
+ match value with
+ | `page -> "page"
+ | `step -> "step"
+ | `location -> "location"
+ | `date -> "date"
+ | `time -> "time"
+ | `true_ -> "true" )
+
+ let describedby fmt = string_attr "aria-describedby" fmt
+ let description fmt = string_attr "aria-description" fmt
+ let details fmt = string_attr "aria-details" fmt
+ let disabled = attr "aria-disabled"
+ let errormessage fmt = string_attr "aria-errormessage" fmt
+ let expanded = bool_attr "aria-expanded"
+ let flowto fmt = string_attr "aria-flowto" fmt
+
+ let haspopup value =
+ ( "aria-haspopup",
+ match value with
+ | `true_ -> "true"
+ | `menu -> "menu"
+ | `listbox -> "listbox"
+ | `tree -> "tree"
+ | `grid -> "grid"
+ | `dialog -> "dialog" )
+
+ let hidden = bool_attr "aria-hidden"
+
+ let invalid value =
+ ( "aria-invalid",
+ match value with
+ | `grammar -> "grammar"
+ | `spelling -> "spelling"
+ | `true_ -> "true" )
+
+ let keyshortcuts fmt = string_attr "aria-keyshortcuts" fmt
+ let label fmt = string_attr "aria-label" fmt
+ let labelledby fmt = string_attr "aria-labelledby" fmt
+ let level = int_attr "aria-level"
+
+ let live value =
+ ( "aria-live",
+ match value with
+ | `assertive -> "assertive"
+ | `polite -> "polite" )
+
+ let modal = attr "aria-modal"
+ let multiline = attr "aria-multiline"
+ let multiselectable = attr "aria-multiselectable"
+
+ let orientation value =
+ ( "aria-orientation",
+ match value with
+ | `horizontal -> "horizontal"
+ | `vertical -> "vertical" )
+
+ let owns fmt = string_attr "aria-owns" fmt
+ let placeholder fmt = string_attr "aria-placeholder" fmt
+ let posinset = int_attr "aria-posinset"
+
+ let pressed value =
+ ( "aria-pressed",
+ match value with
+ | `false_ -> "false"
+ | `mixed -> "mixed"
+ | `true_ -> "true" )
+
+ let readonly = attr "aria-readonly"
+
+ let relevant value =
+ ( "aria-relevant",
+ match value with
+ | `additions -> "additions"
+ | `all -> "all"
+ | `removals -> "removals"
+ | `text -> "text" )
+
+ let required = attr "aria-required"
+ let roledescription fmt = string_attr "aria-roledescription" fmt
+ let rowcount = int_attr "aria-rowcount"
+ let rowindex = int_attr "aria-rowindex"
+ let rowindextext fmt = string_attr "aria-rowindextext" fmt
+ let rowspan = int_attr "aria-rowspan"
+ let selected = bool_attr "aria-selected"
+ let setsize = int_attr "aria-setsize"
+
+ let sort value =
+ ( "aria-sort",
+ match value with
+ | `ascending -> "ascending"
+ | `descending -> "descending"
+ | `other -> "other" )
+
+ let valuemax = float_attr "aria-valuemax"
+ let valuemin = float_attr "aria-valuemin"
+ let valuenow = float_attr "aria-valuenow"
+ let valuetext fmt = string_attr "aria-valuetext" fmt
+end
+
+module Hx = struct
+ let __ fmt = string_attr ~raw:true "_" fmt
+
+ (* This is a boolean because it can be selectively switched off in some parts
+ of the page. *)
+ let boost = bool_attr "data-hx-boost"
+ let confirm fmt = string_attr "data-hx-confirm" fmt
+ let delete fmt = uri_attr "data-hx-delete" fmt
+ let disable = attr "data-hx-disable"
+ let disinherit fmt = string_attr "data-hx-disinherit" fmt
+ let encoding_formdata = "data-hx-encoding", "multipart/form-data"
+ let ext fmt = string_attr "data-hx-ext" fmt
+ let get fmt = uri_attr "data-hx-get" fmt
+ let headers fmt = string_attr "data-hx-headers" fmt
+ let history_false = bool_attr "data-hx-history" false
+ let history_elt = attr "data-hx-history-elt"
+ let include_ fmt = string_attr "data-hx-include" fmt
+ let indicator fmt = string_attr ~raw:true "data-hx-indicator" fmt
+ let on fmt = string_attr "data-hx-on" ~raw:true fmt
+ let on_ ~event fmt = string_attr ("data-hx-on:" ^ event) ~raw:true fmt
+ let params fmt = string_attr "data-hx-params" fmt
+ let patch fmt = uri_attr "data-hx-patch" fmt
+ let post fmt = uri_attr "data-hx-post" fmt
+ let preload = attr "preload"
+ let preserve = attr "data-hx-preserve"
+ let prompt fmt = string_attr "data-hx-prompt" fmt
+ let push_url fmt = uri_attr "data-hx-push-url" fmt
+ let put fmt = uri_attr "data-hx-put" fmt
+ let replace_url fmt = string_attr "data-hx-replace-url" fmt
+ let request fmt = string_attr "data-hx-request" fmt
+ let select fmt = string_attr ~raw:true "data-hx-select" fmt
+ let select_oob fmt = string_attr ~raw:true "data-hx-select-oob" fmt
+ let sse_connect fmt = string_attr "data-sse-connect" fmt
+ let sse_swap fmt = string_attr "data-sse-swap" fmt
+ let swap fmt = string_attr ~raw:true "data-hx-swap" fmt
+ let swap_oob fmt = string_attr ~raw:true "data-hx-swap-oob" fmt
+ let sync fmt = string_attr "data-hx-sync" fmt
+ let target fmt = string_attr ~raw:true "data-hx-target" fmt
+ let trigger fmt = string_attr "data-hx-trigger" ~raw:true fmt
+ let validate = attr "data-hx-validate"
+ let vals fmt = string_attr "data-hx-vals" fmt
+ let ws_connect fmt = string_attr "data-ws-connect" fmt
+ let ws_send = attr "data-ws-send"
+end
+
+module MathML = struct
+ (* Attributes *)
+ let accent = bool_attr "accent"
+ let accentunder = bool_attr "accentunder"
+ let columnspan = int_attr "columnspan"
+ let depth fmt = string_attr "depth" fmt
+
+ let dir value =
+ ( "dir",
+ match value with
+ | `rtl -> "rtl"
+ | `ltr -> "ltr" )
+
+ let display_block = string_attr "display" "block"
+ let displaystyle = bool_attr "displaystyle"
+ let fence = bool_attr "fence"
+ let height fmt = string_attr "height" fmt
+ let largeop = bool_attr "largeop"
+ let linethickness fmt = string_attr "linethickness" fmt
+ let lspace fmt = string_attr "lspace" fmt
+ let mathvariant fmt = string_attr "mathvariant" fmt
+ let maxsize fmt = string_attr "maxsize" fmt
+ let minsize fmt = string_attr "minsize" fmt
+ let movablelimits = bool_attr "movablelimits"
+ let rowspan = int_attr "rowspan"
+ let rspace fmt = string_attr "rspace" fmt
+ let scriptlevel fmt = string_attr "scriptlevel" fmt
+ let separator = bool_attr "separator"
+ let stretchy = bool_attr "stretchy"
+ let symmetric = bool_attr "symmetric"
+ let voffset fmt = string_attr "voffset" fmt
+ let xmlns = string_attr "xmlns" "http://www.w3.1998/Math/MathML"
+
+ (* Tags *)
+ let annotation = std_tag "annotation"
+ let annotation_xml = std_tag "annotation-xml"
+ let math = std_tag "math"
+ let merror = std_tag "merror"
+ let mfrac = std_tag "mfrac"
+ let mi = std_tag "mi"
+ let mmultiscripts = std_tag "mmultiscripts"
+ let mn = std_tag "mn"
+ let mo = std_tag "mo"
+ let mover = std_tag "mover"
+ let mpadded = std_tag "mpadded"
+ let mphantom = std_tag "mphantom"
+ let mroot = std_tag "mroot"
+ let mrow = std_tag "mrow"
+ let ms = std_tag "ms"
+ let mspace = std_tag "mspace"
+ let msqrt = std_tag "msqrt"
+ let mstyle = std_tag "mstyle"
+ let msub = std_tag "msub"
+ let msubsup = std_tag "msubsup"
+ let msup = std_tag "msup"
+ let mtable = std_tag "mtable"
+ let mtd = std_tag "mtd"
+ let mtext = std_tag "mtext"
+ let mtr = std_tag "mtr"
+ let munder = std_tag "munder"
+ let munderover = std_tag "munderover"
+ let semantics = std_tag "semantics"
+end
diff --git a/lib/render/Dream_html_lite.mli b/lib/render/Dream_html_lite.mli
new file mode 100644
index 0000000..870eb8d
--- /dev/null
+++ b/lib/render/Dream_html_lite.mli
@@ -0,0 +1,960 @@
+(* 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 <https://www.gnu.org/licenses/>. *)
+
+(** Constructing HTML. Detailed explanation in
+ {: https://github.com/yawaramin/dream-html}.
+
+ Let's adapt the example from the
+ {{: https://aantron.github.io/dream/} Dream home page}:
+
+
+ {[let hello who =
+ let open Dream_html in
+ let open HTML 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.
+
+ Note that the version of this library installed on your device may have
+ outdated documentation strings. To view the latest documentation, please
+ visit {{: https://github.com/yawaramin/dream-html} the repository page}. *)
+
+(** {2 Core types}
+
+ These are the types of the final values which get rendered. *)
+
+type attr
+(** E.g. [id="toast"]. *)
+
+type node
+(** Either a tag, a comment, or text data in the markup. *)
+
+(** {2 Output} *)
+
+val to_string : node -> string
+
+val to_xml : node -> string
+(** Same as [to_string] but render void tags as XML-style self-closing tags.
+
+ @since 3.3.0. *)
+
+val pp : Format.formatter -> node -> unit
+
+val pp_xml : Format.formatter -> node -> unit
+(** Same as [pp] but render void tags as XML-style self-closing tags.
+
+ @since 3.3.0. *)
+
+(** {2 Constructing nodes and attributes} *)
+
+type 'a to_attr = 'a -> attr
+(** Attributes can be created from typed values. *)
+
+type 'a string_attr = ('a, unit, string, attr) format4 -> 'a
+(** Special handling for string-value attributes so they can use format strings
+ i.e. string interpolation. *)
+
+type std_tag = attr list -> node list -> node
+(** A 'standard' tag with attributes and children. *)
+
+type void_tag = attr list -> node
+(** A 'void element':
+ {: https://developer.mozilla.org/en-US/docs/Glossary/Void_element} with no
+ children. *)
+
+type 'a text_tag = attr list -> ('a, unit, string, node) format4 -> 'a
+(** Tags which can have attributes but can contain only text. The text can be
+ formatted. *)
+
+val attr : string -> attr
+(** [attr name] is a new attribute which does not carry any payload. E.g.
+
+ {[let required = attr "required"]}
+
+ @since 0.1.0. *)
+
+val string_attr : string -> ?raw:bool -> _ string_attr
+(** [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.
+
+ @param raw (default [false]) whether to inject the raw text or to escape it.
+ Note that Dream does not support escaping inline JavaScript nor CSS, so
+ neither does dream-html:
+ {: https://github.com/aantron/dream/tree/master/example/7-template#security}. *)
+
+val uri_attr : string -> _ string_attr
+(** Convenience for attributes whose values should be URIs. Takes care of both
+ URI-encoding and attribute escaping, as recommended in
+ {: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#common-mistake}.
+
+ Examples
+
+ {[a [href "/blog?tags=iamsafe\"></a><script>alert('Pwned')</script>"] [txt "Tags: tag1 | tag2"]
+ ==>
+ <a href="/blog?tags=iamsafe%22%3E%3C/a%3E%3Cscript%3Ealert('Pwned')%3C/script%3E">Tags: tag1 | tag2</a>
+
+ a [href "/foo?a=1&b=2 3&c=4<5&d=6>5"] [txt "Test"]
+ ==>
+ <a href="/foo?a=1&b=2%203&c=4%3C5&d=6%3E5">Test</a>]} *)
+
+val bool_attr : string -> bool to_attr
+val float_attr : string -> float to_attr
+val int_attr : string -> int to_attr
+val std_tag : string -> std_tag
+val void_tag : string -> void_tag
+
+val text_tag : string -> ?raw:bool -> _ text_tag
+(** Build a tag which can contain only text.
+
+ @param raw (default [false]) whether to inject the raw text or to escape it.
+ Note that Dream does not support escaping inline JavaScript nor CSS, so
+ neither does dream-html:
+ {: https://github.com/aantron/dream/tree/master/example/7-template#security}. *)
+
+val txt : ?raw:bool -> ('a, unit, string, node) format4 -> '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. 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]} *)
+
+val comment : string -> node
+(** A comment that will be embedded in the rendered HTML, i.e. [<!-- comment -->].
+ The text is HTML-escaped. *)
+
+
+(** {2 Accessors for tags} *)
+
+val ( +@ ) : node -> attr -> node
+(** Add an attribute to a tag.
+
+ {[let toast msg = p [id "toast"] [txt "%s" msg]
+ let toast_oob = toast "ok." +@ Hx.swap_oob "true"]}
+
+ @raise Invalid_argument if the node is not a tag (i.e. if it is a text or
+ comment node).
+ @since 0.0.3. *)
+
+val ( -@ ) : node -> string -> node
+(** Remove an attribute from a tag.
+
+ @raise Invalid_argument if the node is not a tag (i.e. if it is a text or
+ comment node).
+ @since 0.0.3. *)
+
+val ( .@[] ) : node -> string -> string
+(** Get the value of an existing attribute.
+
+ {[let toast = p [id "toast"] [txt "OK."]
+ let toast_id = toast.@["id"]]}
+
+ @raise Invalid_argument if the node is not a tag (i.e. if it is a text or
+ comment node).
+ @raise Not_found if the tag does not have the given attribute.
+ @since 0.0.3. *)
+
+val is_null : node -> bool
+(** Get whether a node is null (empty) or not. Useful for conditional rendering
+ of UIs when you are passed in a node and you don't know if it's empty or not.
+
+ @since 1.2.0. *)
+
+val is_null_ : attr -> bool
+(** Get whether an attribute is null (empty) or not.
+
+ @since 1.2.0. *)
+
+(** {2 Standard attributes and tags} *)
+
+(** All standard HTML attributes and tags. Some attributes and tags have the same
+ name, e.g. [style]. To disambiguate them, attributes have a [_] (underscore)
+ suffix. *)
+module HTML : sig
+ (** {3 Attributes}
+
+ 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.
+
+ All string-valued attributes allow formatting (interpolation):
+
+ {[div [id "section-%d" section_id] []]}
+
+ Or plain strings:
+
+ {[p [id "toast"] []]}
+
+ Most boolean attributes are plain values and don't need to be constructed
+ with function calls:
+
+ {[input [required]]}
+
+ However, boolean attributes which may be inherited and toggled on/off in
+ children, are constructed by passing in a value:
+
+ {[div [contenteditable true] [
+ p [] [txt "Edit me!"];
+ p [contenteditable false] [txt "Can't edit me!"];
+ ]]}
+
+ Enumerated attributes accept specific values:
+
+ {[input [inputmode `tel]]}
+
+ @since 1.0.0. *)
+
+ type enctype =
+ [ `urlencoded
+ | `formdata
+ | `text_plain ]
+
+ type method_ =
+ [ `GET
+ | `POST
+ | `dialog (** @since 2.1.0 *) ]
+
+ val null_ : attr
+ (** An attribute that will not be rendered in the markup. Useful for conditional
+ logic where you sometimes want to render an attribute and sometimes not.
+
+ {[p [if should_show then null_ else style_ "display:none"] [txt "Show and tell"]]} *)
+
+ val accept : _ string_attr
+ val accept_charset : _ string_attr
+ val accesskey : _ string_attr
+ val action : _ string_attr
+
+ val align : _ string_attr
+ [@@ocaml.deprecated
+ "See https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes"]
+
+ val allow : _ string_attr
+ val alt : _ string_attr
+ val async : attr
+
+ val autocapitalize :
+ [< `off | `none | `on | `sentences | `words | `characters] to_attr
+
+ val autocomplete :
+ [< `off
+ | `on
+ | `name
+ | `honorific_prefix
+ | `given_name
+ | `additional_name
+ | `honorific_suffix
+ | `nickname
+ | `email
+ | `username
+ | `new_password
+ | `current_password
+ | `one_time_code
+ | `organization_title
+ | `organization
+ | `street_address
+ | `address_line1
+ | `address_line2
+ | `address_line3
+ | `address_level4
+ | `address_level3
+ | `address_level2
+ | `address_level1
+ | `country
+ | `country_name
+ | `postal_code
+ | `cc_name
+ | `cc_given_name
+ | `cc_additional_name
+ | `cc_family_name
+ | `cc_number
+ | `cc_exp
+ | `cc_exp_month
+ | `cc_exp_year
+ | `cc_csc
+ | `cc_type
+ | `transaction_currency
+ | `transaction_amount
+ | `language
+ | `bday
+ | `bday_day
+ | `bday_month
+ | `bday_year
+ | `sex
+ | `tel
+ | `tel_country_code
+ | `tel_national
+ | `tel_area_code
+ | `tel_local
+ | `tel_extension
+ | `impp
+ | `url
+ | `photo
+ | `webauthn ]
+ to_attr
+
+ val autofocus : attr
+ val autoplay : attr
+ val buffered : _ string_attr
+ val capture : [< `user | `environment] to_attr
+ val charset : _ string_attr
+ val checked : attr
+ val cite_ : _ string_attr
+ val class_ : _ string_attr
+
+ val color : _ string_attr
+ [@@ocaml.deprecated
+ "See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/font#color"]
+
+ val cols : int to_attr
+ val colspan : int to_attr
+ val content : _ string_attr
+ val contenteditable : bool to_attr
+ val contextmenu : _ string_attr
+ val controls : attr
+ val coords : _ string_attr
+ val crossorigin : [< `anonymous | `use_credentials] to_attr
+ val data_ : _ string_attr
+ val datetime : _ string_attr
+ val decoding : [< `sync | `async | `auto] to_attr
+ val default : attr
+ val defer : attr
+ val dir : [< `ltr | `rtl | `auto] to_attr
+ val dirname : _ string_attr
+ val disabled : attr
+ val download : _ string_attr
+ val draggable : attr
+ val enctype : [< enctype] to_attr
+
+ val fetchpriority : [< `high | `low | `auto] to_attr
+ (** @since 1.2.0. *)
+
+ val for_ : _ string_attr
+ val form_ : _ string_attr
+ val formaction : _ string_attr
+ val formenctype : [< enctype] to_attr
+ val formmethod : [< method_] to_attr
+ val formnovalidate : attr
+ val formtarget : _ string_attr
+ val headers : _ string_attr
+ val height : _ string_attr
+ val hidden : [< `hidden | `until_found] to_attr
+ val high : float to_attr
+ val href : _ string_attr
+ val hreflang : _ string_attr
+
+ val http_equiv :
+ [< `content_security_policy
+ | `content_type
+ | `default_style
+ | `x_ua_compatible
+ | `refresh ]
+ to_attr
+
+ val id : _ string_attr
+ val integrity : _ string_attr
+
+ val inputmode :
+ [< `none | `text | `decimal | `numeric | `tel | `search | `email | `url]
+ to_attr
+
+ val ismap : attr
+ val itemprop : _ string_attr
+
+ val kind :
+ [< `subtitles | `captions | `descriptions | `chapters | `metadata] to_attr
+
+ val label_ : _ string_attr
+ val lang : _ string_attr
+ val list : _ string_attr
+
+ val loading_lazy : attr
+ (** See {: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#loading}.
+ [loading=eager] is the default so no need for specifically that value.
+
+ @since 3.1.0. *)
+
+ val loop : attr
+ val low : float to_attr
+ val max : _ string_attr
+ val maxlength : int to_attr
+ val media : _ string_attr
+ val method_ : [< method_] to_attr
+ val min : _ string_attr
+ val minlength : int to_attr
+ val multiple : attr
+ val muted : attr
+ val name : _ string_attr
+ val novalidate : attr
+
+ val onblur : _ string_attr
+ (** Note that the value of this attribute is not escaped. *)
+
+ val onclick : _ string_attr
+ (** Note that the value of this attribute is not escaped. *)
+
+ val open_ : attr
+ val optimum : float to_attr
+ val pattern : _ string_attr
+ val ping : _ string_attr
+ val placeholder : _ string_attr
+ val playsinline : attr
+ val poster : _ string_attr
+ val preload : [< `none | `metadata | `auto] to_attr
+ val readonly : attr
+
+ val referrerpolicy :
+ [< `no_referrer
+ | `no_referrer_when_downgrade
+ | `origin
+ | `origin_when_cross_origin
+ | `same_origin
+ | `strict_origin
+ | `strict_origin_when_cross_origin
+ | `unsafe_url ]
+ to_attr
+
+ val rel : _ string_attr
+ val required : attr
+ val reversed : attr
+
+ val role :
+ [ `alert
+ | `alertdialog
+ | `application
+ | `article
+ | `banner
+ | `button
+ | `cell
+ | `checkbox
+ | `columnheader
+ | `combobox (* command - do not use *)
+ | `comment
+ | `complementary (* composite - do not use *)
+ | `contentinfo
+ | `definition
+ | `dialog (* directory - deprecated *)
+ | `document
+ | `feed
+ | `figure
+ | `form
+ | `generic
+ | `grid
+ | `gridcell
+ | `group
+ | `heading
+ | `img
+ (* input - do not use *)
+ (* landmark - do not use *)
+ | `link
+ | `list
+ | `listbox
+ | `listitem
+ | `log
+ | `main
+ | `mark
+ | `marquee
+ | `math
+ | `menu
+ | `menubar
+ | `menuitem
+ | `menuitemcheckbox
+ | `menuitemradio
+ | `meter
+ | `navigation
+ | `none
+ | `note
+ | `option
+ | `presentation
+ | `progressbar
+ | `radio
+ | `radiogroup (* range - do not use *)
+ | `region (* roletype - do not use *)
+ | `row
+ | `rowgroup
+ | `rowheader
+ | `scrollbar
+ | `search
+ | `searchbox
+ | (* section - do not use *)
+ (* sectionhead - do not use *)
+ (* select - do not use *)
+ `separator
+ | `slider
+ | `spinbutton
+ | `status
+ | (* structure - do not use *)
+ `suggestion
+ | `switch
+ | `tab
+ | `table
+ | `tablist
+ | `tabpanel
+ | `term
+ | `textbox
+ | `timer
+ | `toolbar
+ | `tooltip
+ | `tree
+ | `treegrid
+ | `treeitem
+ (* widget - do not use *)
+ (* window - do not use *) ]
+ to_attr
+ (** @since 3.0.0 *)
+
+ val rows : int to_attr
+ val rowspan : int to_attr
+ val sandbox : _ string_attr
+ val scope : _ string_attr
+ val selected : attr
+ val shape : _ string_attr
+
+ val size : _ string_attr
+ (** Required for {: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#size}. *)
+
+ val sizes : _ string_attr
+ val slot_ : _ string_attr
+ val span_ : int to_attr
+ val spellcheck : bool to_attr
+ val src : _ string_attr
+ val srcdoc : _ string_attr
+ val srclang : _ string_attr
+ val srcset : _ string_attr
+ val start : int to_attr
+ val step : _ string_attr
+
+ val style_ : _ string_attr
+ (** Note that the value of this attribute is not escaped. *)
+
+ val tabindex : int to_attr
+ val target : _ string_attr
+ val title_ : _ string_attr
+ val translate : [< `yes | `no] to_attr
+
+ val type_ : _ string_attr
+ (** Note: this can't be restricted to just the allowed values for [<input type>],
+ because it's used in other elements e.g. [<link type>]. *)
+
+ val usemap : _ string_attr
+ val value : _ string_attr
+ val width : _ string_attr
+ val wrap : [< `hard | `soft] to_attr
+
+ (** {3 Tags}
+
+ HTML tags. Most (standard tags) are constructed by passing a list of
+ attributes and a list of children:
+
+ {[div [id "my-div"] [p [] [txt "Hello"]]]}
+
+ Some (void elements) are constructed only with a list of attributes:
+
+ {[input [required; type_ "email"; name "email-addr"]]}
+
+ Finally, a few (text elements) are constructed with a list of attributes
+ and a single format string child:
+
+ {[title [] "Document title"
+
+ title [] "My App ・ %s" page_name
+
+ script [] {|alert('Careful, this is not escaped :-)');|}
+ ]} *)
+
+ val null : node list -> node
+ (** A tag that will not be rendered in the markup. Useful for containing a bunch
+ of child nodes inside a single node without having to litter the DOM with an
+ actual node. Also may be called 'splicing'.
+
+ {[null [
+ p [] [txt "This paragraph."];
+ p [] [txt "And this paragraph."];
+ p [] [txt "Are spliced directly into the document without a containing node."];
+ ]]}
+
+ Also useful for constructing a completely empty node that is erased when
+ printing: {[null []]} *)
+
+ val a : std_tag
+ val address : std_tag
+ val area : void_tag
+ val abbr : std_tag
+ val article : std_tag
+ val aside : std_tag
+ val audio : std_tag
+ val b : std_tag
+ val base : void_tag
+ val bdi : std_tag
+ val bdo : std_tag
+ val blockquote : std_tag
+ val body : std_tag
+ val br : void_tag
+ val button : std_tag
+ val canvas : std_tag
+ val caption : std_tag
+ val cite : std_tag
+ val code : std_tag
+ val col : void_tag
+ val colgroup : std_tag
+ val data : std_tag
+ val datalist : std_tag
+ val dd : std_tag
+ val del : std_tag
+ val details : std_tag
+ val dfn : std_tag
+ val dialog : std_tag
+ val div : std_tag
+ val dl : std_tag
+ val dt : std_tag
+ val em : std_tag
+ val embed : void_tag
+ val fieldset : std_tag
+ val figcaption : std_tag
+ val figure : std_tag
+ val form : std_tag
+ val footer : std_tag
+ val h1 : std_tag
+ val h2 : std_tag
+ val h3 : std_tag
+ val h4 : std_tag
+ val h5 : std_tag
+ val h6 : std_tag
+ val head : std_tag
+ val header : std_tag
+ val hgroup : std_tag
+ val hr : void_tag
+
+ val html : std_tag
+ (** A [<!DOCTYPE html>] declaration is automatically prefixed when this tag is
+ printed. *)
+
+ val i : std_tag
+ val iframe : std_tag
+ val img : void_tag
+ val input : void_tag
+ val ins : std_tag
+ val kbd : std_tag
+ val label : std_tag
+ val legend : std_tag
+ val li : std_tag
+ val link : void_tag
+ val main : std_tag
+ val map : std_tag
+ val mark : std_tag
+ val menu : std_tag
+ val meta : void_tag
+ val meter : std_tag
+ val nav : std_tag
+ val noscript : std_tag
+ val object_ : std_tag
+ val ol : std_tag
+ val optgroup : std_tag
+ val option : _ text_tag
+ val output : std_tag
+ val p : std_tag
+ val picture : std_tag
+ val pre : std_tag
+ val progress : std_tag
+ val q : std_tag
+ val rp : std_tag
+ val rt : std_tag
+ val ruby : std_tag
+ val s : std_tag
+ val samp : std_tag
+
+ val script : _ text_tag
+ (** Note that the content of this tag is not escaped. *)
+
+ val section : std_tag
+ val select : std_tag
+ val slot : std_tag
+ val small : std_tag
+ val source : void_tag
+ val span : std_tag
+ val strong : std_tag
+
+ val style : _ text_tag
+ (** Note that the content of this tag is not escaped. *)
+
+ val sub : std_tag
+ val summary : std_tag
+ val sup : std_tag
+ val table : std_tag
+ val tbody : std_tag
+ val td : std_tag
+ val template : std_tag
+ val textarea : _ text_tag
+ val tfoot : std_tag
+ val th : std_tag
+ val thead : std_tag
+ val time : std_tag
+ val title : _ text_tag
+ val tr : std_tag
+ val track : void_tag
+ val u : std_tag
+ val ul : std_tag
+ val var : std_tag
+ val video : std_tag
+ val wbr : void_tag
+end
+
+(** @since 1.1.0. *)
+module SVG : sig
+ val d : _ string_attr
+ val fill : _ string_attr
+ val stroke : _ string_attr
+ val stroke_linecap : [< `butt | `round | `square] to_attr
+
+ val stroke_linejoin :
+ [< `arcs | `bevel | `miter | `miter_clip | `round] to_attr
+
+ val stroke_width : _ string_attr
+ val viewbox : min_x:int -> min_y:int -> width:int -> height:int -> attr
+ val xmlns : attr
+ val path : std_tag
+ val svg : std_tag
+end
+
+(** @since 3.1.0. *)
+module MathML : sig
+ val accent : bool to_attr
+ val accentunder : bool to_attr
+ val columnspan : int to_attr
+ val depth : _ string_attr
+ val dir : [`ltr | `rtl] to_attr
+
+ val display_block : attr
+ (** See {: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/math#display}.
+ [display=inline] is the default, so there is no need to bind its value. *)
+
+ val displaystyle : bool to_attr
+ val fence : bool to_attr
+ val height : _ string_attr
+ val largeop : bool to_attr
+ val linethickness : _ string_attr
+ val lspace : _ string_attr
+ val mathvariant : _ string_attr
+ val maxsize : _ string_attr
+ val minsize : _ string_attr
+ val movablelimits : bool to_attr
+ val rowspan : int to_attr
+ val rspace : _ string_attr
+ val scriptlevel : _ string_attr
+ val separator : bool to_attr
+ val stretchy : bool to_attr
+ val symmetric : bool to_attr
+ val voffset : _ string_attr
+ val xmlns : attr
+ val annotation : std_tag
+ val annotation_xml : std_tag
+ val math : std_tag
+ val merror : std_tag
+ val mfrac : std_tag
+ val mi : std_tag
+ val mmultiscripts : std_tag
+ val mn : std_tag
+ val mo : std_tag
+ val mover : std_tag
+ val mpadded : std_tag
+ val mphantom : std_tag
+ val mroot : std_tag
+ val mrow : std_tag
+ val ms : std_tag
+ val mspace : std_tag
+ val msqrt : std_tag
+ val mstyle : std_tag
+ val msub : std_tag
+ val msubsup : std_tag
+ val msup : std_tag
+ val mtable : std_tag
+ val mtd : std_tag
+ val mtext : std_tag
+ val mtr : std_tag
+ val munder : std_tag
+ val munderover : std_tag
+ val semantics : std_tag
+end
+
+(** {2 ARIA support} *)
+
+(** {: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/}
+
+ @since 3.0.0. *)
+module Aria : sig
+ val activedescendant : _ string_attr
+ val atomic : attr
+ val autocomplete : [`inline | `list | `both] to_attr
+ val braillelabel : _ string_attr
+ val brailleroledescription : _ string_attr
+ val busy : attr
+ val checked : [`false_ | `true_ | `mixed] to_attr
+ val colcount : int to_attr
+ val colindextext : _ string_attr
+ val colspan : int to_attr
+ val controls : _ string_attr
+ val current : [`page | `step | `location | `date | `time | `true_] to_attr
+ val describedby : _ string_attr
+ val description : _ string_attr
+ val details : _ string_attr
+ val disabled : attr
+ val errormessage : _ string_attr
+ val expanded : bool to_attr
+ val flowto : _ string_attr
+ val haspopup : [`true_ | `menu | `listbox | `tree | `grid | `dialog] to_attr
+ val hidden : bool to_attr
+ val invalid : [`grammar | `spelling | `true_] to_attr
+ val keyshortcuts : _ string_attr
+ val label : _ string_attr
+ val labelledby : _ string_attr
+ val level : int to_attr
+ val live : [`assertive | `polite] to_attr
+ val modal : attr
+ val multiline : attr
+ val multiselectable : attr
+ val orientation : [`horizontal | `vertical] to_attr
+ val owns : _ string_attr
+ val placeholder : _ string_attr
+ val posinset : int to_attr
+ val pressed : [`false_ | `mixed | `true_] to_attr
+ val readonly : attr
+ val relevant : [`additions | `all | `removals | `text] to_attr
+ val required : attr
+ val roledescription : _ string_attr
+ val rowcount : int to_attr
+ val rowindex : int to_attr
+ val rowindextext : _ string_attr
+ val rowspan : int to_attr
+ val selected : bool to_attr
+ val setsize : int to_attr
+ val sort : [`ascending | `descending | `other] to_attr
+ val valuemax : float to_attr
+ val valuemin : float to_attr
+ val valuenow : float to_attr
+ val valuetext : _ string_attr
+end
+
+(** {2 htmx support} *)
+
+(** htmx support {: https://htmx.org/reference/} *)
+module Hx : sig
+ val __ : _ string_attr
+ (** This attribute serves as the _ attribute, which is used by Hyperscript.
+ Note that the value of this attribute is not escaped.
+
+ @since 0.1.0. *)
+
+ val boost : bool to_attr
+ val confirm : _ string_attr
+ val delete : _ string_attr
+ val disable : attr
+ val disinherit : _ string_attr
+
+ val encoding_formdata : attr
+ (** Hardcoding of the [hx-encoding] attribute to [multipart/form-data]. *)
+
+ val ext : _ string_attr
+ val get : _ string_attr
+ val headers : _ string_attr
+
+ val history_false : attr
+ (** Hardcoding of the [hx-history] attribute to [false]. *)
+
+ val history_elt : attr
+ val include_ : _ string_attr
+
+ val indicator : _ string_attr
+ (** Note that the value of this attribute is not escaped as it may include a
+ CSS selector. *)
+
+ val on : _ string_attr
+ [@@ocaml.deprecated "See https://htmx.org/attributes/hx-on/#hx-on-deprecated"]
+ (** Note that the value of this attribute is not escaped. *)
+
+ val on_ : event:string -> _ string_attr
+ (** The [hx-on:*] set of attributes, where [*] represents DOM events:
+ {: https://htmx.org/attributes/hx-on/}.
+
+ Note that the value of this attribute is not escaped.
+
+ @since 2.1.0. *)
+
+ val params : _ string_attr
+ val patch : _ string_attr
+ val post : _ string_attr
+
+ val preload : attr
+ (** The preload extension: {: https://htmx.org/extensions/preload/} *)
+
+ val preserve : attr
+ val prompt : _ string_attr
+ val push_url : _ string_attr
+ val put : _ string_attr
+ val replace_url : _ string_attr
+ val request : _ string_attr
+
+ val select : _ string_attr
+ (** Note that the value of this attribute is not escaped as it may include a
+ CSS selector. *)
+
+ val select_oob : _ string_attr
+ (** Note that the value of this attribute is not escaped as it may include a
+ CSS selector. *)
+
+ val sse_connect : _ string_attr
+ val sse_swap : _ string_attr
+
+ val swap : _ string_attr
+ (** Note that the value of this attribute is not escaped as it may include a
+ CSS selector. *)
+
+ val swap_oob : _ string_attr
+ (** Note that the value of this attribute is not escaped as it may include a
+ CSS selector. *)
+
+ val sync : _ string_attr
+
+ val target : _ string_attr
+ (** Note that the value of this attribute is not escaped as it may include a
+ CSS selector. *)
+
+ val trigger : _ string_attr
+ (** Note that the value of this attribute is not escaped. *)
+
+ val validate : attr
+ val vals : _ string_attr
+ val ws_connect : _ string_attr
+ val ws_send : attr
+end
diff --git a/lib/render/Render_dream.ml b/lib/render/Render_dream.ml
index 21d77d9..83ccd05 100644
--- a/lib/render/Render_dream.ml
+++ b/lib/render/Render_dream.ml
@@ -1,7 +1,7 @@
open Core
open Prelude
-open Dream_html
+open Dream_html_lite
module E = Render_effect.Perform
module F = Dream_forester
diff --git a/lib/render/dune b/lib/render/dune
index 7e3bc4a..991a292 100644
--- a/lib/render/dune
+++ b/lib/render/dune
@@ -2,7 +2,7 @@
(name Render)
(preprocess
(pps ppx_deriving.show))
- (libraries forester.prelude forester.core asai algaeff dream-html yojson)
+ (libraries forester.prelude forester.core asai algaeff yojson)
(public_name forester.render))
(env
diff --git a/test/dune b/test/dune
index b66589c..8a8089b 100644
--- a/test/dune
+++ b/test/dune
@@ -1,3 +1,3 @@
(tests
(names parse store)
- (libraries forester.frontend irmin.unix))
+ (libraries forester.frontend irmin.unix lwt.unix))
--
2.45.2