From 840bd25d8341f9c63c239296d3ffcf77874630ec Mon Sep 17 00:00:00 2001 From: Luke Boswell Date: Fri, 10 Jan 2025 12:13:40 +1100 Subject: [PATCH 1/3] update flake --- flake.lock | 53 ++++++++++++++++++++++++++--------------------------- flake.nix | 2 +- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/flake.lock b/flake.lock index 827dfe9..76d16d5 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1696426674, - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "lastModified": 1733328505, + "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", "owner": "edolstra", "repo": "flake-compat", - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", "type": "github" }, "original": { @@ -21,11 +21,11 @@ "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1730504689, - "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", + "lastModified": 1736143030, + "narHash": "sha256-+hu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "506278e768c2a08bec68eb62932193e341f55c90", + "rev": "b905f6fc23a9051a6e1b741e1438dbfc0634c6de", "type": "github" }, "original": { @@ -39,11 +39,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1726560853, - "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -79,11 +79,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1732521221, - "narHash": "sha256-2ThgXBUXAE1oFsVATK1ZX9IjPcS4nKFOAjhPNKuiMn0=", + "lastModified": 1736344531, + "narHash": "sha256-8YVQ9ZbSfuUk2bUf2KRj60NRraLPKPS0Q4QFTbc+c2c=", "owner": "nixos", "repo": "nixpkgs", - "rev": "4633a7c72337ea8fd23a4f2ba3972865e3ec685d", + "rev": "bffc22eb12172e6db3c5dde9e3e5628f8e3e7912", "type": "github" }, "original": { @@ -95,29 +95,29 @@ }, "nixpkgs-lib": { "locked": { - "lastModified": 1730504152, - "narHash": "sha256-lXvH/vOfb4aGYyvFmZK/HlsNsr/0CVWlwYvo2rxJk3s=", + "lastModified": 1735774519, + "narHash": "sha256-CewEm1o2eVAnoqb6Ml+Qi9Gg/EfNAxbRx1lANGVyoLI=", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/e9b51731911566bbf7e4895475a87fe06961de0b.tar.gz" }, "original": { "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" + "url": "https://github.com/NixOS/nixpkgs/archive/e9b51731911566bbf7e4895475a87fe06961de0b.tar.gz" } }, "nixpkgs_2": { "locked": { - "lastModified": 1712163089, - "narHash": "sha256-Um+8kTIrC19vD4/lUCN9/cU9kcOsD1O1m+axJqQPyMM=", + "lastModified": 1722403750, + "narHash": "sha256-tRmn6UiFAPX0m9G1AVcEPjWEOc9BtGsxGcs7Bz3MpsM=", "owner": "nixos", "repo": "nixpkgs", - "rev": "fd281bd6b7d3e32ddfa399853946f782553163b5", + "rev": "184957277e885c06a505db112b35dfbec7c60494", "type": "github" }, "original": { "owner": "nixos", "repo": "nixpkgs", - "rev": "fd281bd6b7d3e32ddfa399853946f782553163b5", + "rev": "184957277e885c06a505db112b35dfbec7c60494", "type": "github" } }, @@ -130,17 +130,16 @@ "rust-overlay": "rust-overlay" }, "locked": { - "lastModified": 1732671688, - "narHash": "sha256-+bg0orL8tiIdK6hi1eiZ6Un7N+dhiInQfiVmXsPatXY=", + "lastModified": 1736460403, + "narHash": "sha256-Q+CVTjdbBCBLyoRQJ1K5VIun+03mlYo2LuplyYeWw4Q=", "owner": "roc-lang", "repo": "roc", - "rev": "0295bb58da3de1dec84f63b5a1847ff32a3fd290", + "rev": "a69a326161a49c19186b8bf07b50c625d3b95686", "type": "github" }, "original": { "owner": "roc-lang", "repo": "roc", - "rev": "0295bb58da3de1dec84f63b5a1847ff32a3fd290", "type": "github" } }, @@ -159,11 +158,11 @@ ] }, "locked": { - "lastModified": 1727490462, - "narHash": "sha256-OrrPiNBiikv9BR464XTT75FzOq7tKAvMbMi7YOKVIeg=", + "lastModified": 1736303309, + "narHash": "sha256-IKrk7RL+Q/2NC6+Ql6dwwCNZI6T6JH2grTdJaVWHF0A=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "11a13e50debafae4ae802f1d6b8585101516dd93", + "rev": "a0b81d4fa349d9af1765b0f0b4a899c13776f706", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index d7b1299..73959e0 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; - roc.url = "github:roc-lang/roc?rev=0295bb58da3de1dec84f63b5a1847ff32a3fd290"; + roc.url = "github:roc-lang/roc"; }; nixConfig = { From 5eb986f94519bcbeb681fcf071412d2b6cdc1e08 Mon Sep 17 00:00:00 2001 From: Luke Boswell Date: Fri, 10 Jan 2025 12:16:28 +1100 Subject: [PATCH 2/3] upgrade to snake_case builtins and PNC --- examples/example.roc | 38 +++-- src/Attribute.roc | 274 ++++++++++++++--------------- src/Html.roc | 398 ++++++++++++++++++++++--------------------- src/SafeStr.roc | 98 ++++++----- 4 files changed, 417 insertions(+), 391 deletions(-) diff --git a/examples/example.roc b/examples/example.roc index 46a734b..1a1b083 100644 --- a/examples/example.roc +++ b/examples/example.roc @@ -1,5 +1,6 @@ -app [main] { - cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.17.0/lZFLstMUCUvd5bjnnpYromZJXkQUrdhbva4xdBInicE.tar.br", +app [main!] { + # TODO replace with release URL + cli: platform "../../basic-cli/platform/main.roc", html: "../src/main.roc", } @@ -7,16 +8,25 @@ import cli.Stdout import html.Html import html.Attribute -main = - page = Html.html [] [ - Html.body [] [ - Html.h1 [] [Html.text "Roc"], - Html.p [] [ - Html.text "My favourite language is ", - Html.a [Attribute.href "https://roc-lang.org/"] [Html.text "Roc"], - Html.text "!", - ], +main! = \_args -> + page = Html.html( + [], + [ + Html.body( + [], + [ + Html.h1([], [Html.text("Roc")]), + Html.p( + [], + [ + Html.text("My favourite language is "), + Html.a([Attribute.href("https://roc-lang.org/")], [Html.text("Roc")]), + Html.text("!"), + ], + ), + ], + ), ], - ] - renderedHtml = Html.render page - Stdout.line renderedHtml + ) + rendered_html = Html.render(page) + Stdout.line!(rendered_html) diff --git a/src/Attribute.roc b/src/Attribute.roc index 05cbe90..a73d83c 100644 --- a/src/Attribute.roc +++ b/src/Attribute.roc @@ -2,7 +2,7 @@ module [ Attribute, attribute, accept, - acceptCharset, + accept_charset, accesskey, action, align, @@ -60,7 +60,7 @@ module [ high, href, hreflang, - httpEquiv, + http_equiv, icon, id, importance, @@ -139,529 +139,529 @@ Attribute : [Attribute Str Str] ## Define a non-standard attribute. ## You can use this to add attributes that are not already supported. attribute : Str -> (Str -> Attribute) -attribute = \attrName -> - \attrValue -> Attribute attrName attrValue +attribute = \attr_name -> + \attr_value -> Attribute(attr_name, attr_value) ## Construct a `accept` attribute. accept : Str -> Attribute -accept = attribute "accept" +accept = attribute("accept") ## Construct a `accept-charset` attribute. -acceptCharset : Str -> Attribute -acceptCharset = attribute "accept-charset" +accept_charset : Str -> Attribute +accept_charset = attribute("accept-charset") ## Construct a `accesskey` attribute. accesskey : Str -> Attribute -accesskey = attribute "accesskey" +accesskey = attribute("accesskey") ## Construct a `action` attribute. action : Str -> Attribute -action = attribute "action" +action = attribute("action") ## Construct a `align` attribute. align : Str -> Attribute -align = attribute "align" +align = attribute("align") ## Construct a `allow` attribute. allow : Str -> Attribute -allow = attribute "allow" +allow = attribute("allow") ## Construct a `alt` attribute. alt : Str -> Attribute -alt = attribute "alt" +alt = attribute("alt") ## Construct a `async` attribute. async : Str -> Attribute -async = attribute "async" +async = attribute("async") ## Construct a `autocapitalize` attribute. autocapitalize : Str -> Attribute -autocapitalize = attribute "autocapitalize" +autocapitalize = attribute("autocapitalize") ## Construct a `autocomplete` attribute. autocomplete : Str -> Attribute -autocomplete = attribute "autocomplete" +autocomplete = attribute("autocomplete") ## Construct a `autofocus` attribute. autofocus : Str -> Attribute -autofocus = attribute "autofocus" +autofocus = attribute("autofocus") ## Construct a `autoplay` attribute. autoplay : Str -> Attribute -autoplay = attribute "autoplay" +autoplay = attribute("autoplay") ## Construct a `background` attribute. background : Str -> Attribute -background = attribute "background" +background = attribute("background") ## Construct a `bgcolor` attribute. bgcolor : Str -> Attribute -bgcolor = attribute "bgcolor" +bgcolor = attribute("bgcolor") ## Construct a `border` attribute. border : Str -> Attribute -border = attribute "border" +border = attribute("border") ## Construct a `buffered` attribute. buffered : Str -> Attribute -buffered = attribute "buffered" +buffered = attribute("buffered") ## Construct a `capture` attribute. capture : Str -> Attribute -capture = attribute "capture" +capture = attribute("capture") ## Construct a `challenge` attribute. challenge : Str -> Attribute -challenge = attribute "challenge" +challenge = attribute("challenge") ## Construct a `charset` attribute. charset : Str -> Attribute -charset = attribute "charset" +charset = attribute("charset") ## Construct a `checked` attribute. checked : Str -> Attribute -checked = attribute "checked" +checked = attribute("checked") ## Construct a `cite` attribute. cite : Str -> Attribute -cite = attribute "cite" +cite = attribute("cite") ## Construct a `class` attribute. class : Str -> Attribute -class = attribute "class" +class = attribute("class") ## Construct a `code` attribute. code : Str -> Attribute -code = attribute "code" +code = attribute("code") ## Construct a `codebase` attribute. codebase : Str -> Attribute -codebase = attribute "codebase" +codebase = attribute("codebase") ## Construct a `color` attribute. color : Str -> Attribute -color = attribute "color" +color = attribute("color") ## Construct a `cols` attribute. cols : Str -> Attribute -cols = attribute "cols" +cols = attribute("cols") ## Construct a `colspan` attribute. colspan : Str -> Attribute -colspan = attribute "colspan" +colspan = attribute("colspan") ## Construct a `content` attribute. content : Str -> Attribute -content = attribute "content" +content = attribute("content") ## Construct a `contenteditable` attribute. contenteditable : Str -> Attribute -contenteditable = attribute "contenteditable" +contenteditable = attribute("contenteditable") ## Construct a `contextmenu` attribute. contextmenu : Str -> Attribute -contextmenu = attribute "contextmenu" +contextmenu = attribute("contextmenu") ## Construct a `controls` attribute. controls : Str -> Attribute -controls = attribute "controls" +controls = attribute("controls") ## Construct a `coords` attribute. coords : Str -> Attribute -coords = attribute "coords" +coords = attribute("coords") ## Construct a `crossorigin` attribute. crossorigin : Str -> Attribute -crossorigin = attribute "crossorigin" +crossorigin = attribute("crossorigin") ## Construct a `csp` attribute. csp : Str -> Attribute -csp = attribute "csp" +csp = attribute("csp") ## Construct a `data` attribute. data : Str -> Attribute -data = attribute "data" +data = attribute("data") ## Construct a `datetime` attribute. datetime : Str -> Attribute -datetime = attribute "datetime" +datetime = attribute("datetime") ## Construct a `decoding` attribute. decoding : Str -> Attribute -decoding = attribute "decoding" +decoding = attribute("decoding") ## Construct a `default` attribute. default : Str -> Attribute -default = attribute "default" +default = attribute("default") ## Construct a `defer` attribute. defer : Str -> Attribute -defer = attribute "defer" +defer = attribute("defer") ## Construct a `dir` attribute. dir : Str -> Attribute -dir = attribute "dir" +dir = attribute("dir") ## Construct a `dirname` attribute. dirname : Str -> Attribute -dirname = attribute "dirname" +dirname = attribute("dirname") ## Construct a `disabled` attribute. disabled : Str -> Attribute -disabled = attribute "disabled" +disabled = attribute("disabled") ## Construct a `download` attribute. download : Str -> Attribute -download = attribute "download" +download = attribute("download") ## Construct a `draggable` attribute. draggable : Str -> Attribute -draggable = attribute "draggable" +draggable = attribute("draggable") ## Construct a `enctype` attribute. enctype : Str -> Attribute -enctype = attribute "enctype" +enctype = attribute("enctype") ## Construct a `enterkeyhint` attribute. enterkeyhint : Str -> Attribute -enterkeyhint = attribute "enterkeyhint" +enterkeyhint = attribute("enterkeyhint") ## Construct a `for` attribute. for : Str -> Attribute -for = attribute "for" +for = attribute("for") ## Construct a `form` attribute. form : Str -> Attribute -form = attribute "form" +form = attribute("form") ## Construct a `formaction` attribute. formaction : Str -> Attribute -formaction = attribute "formaction" +formaction = attribute("formaction") ## Construct a `formenctype` attribute. formenctype : Str -> Attribute -formenctype = attribute "formenctype" +formenctype = attribute("formenctype") ## Construct a `formmethod` attribute. formmethod : Str -> Attribute -formmethod = attribute "formmethod" +formmethod = attribute("formmethod") ## Construct a `formnovalidate` attribute. formnovalidate : Str -> Attribute -formnovalidate = attribute "formnovalidate" +formnovalidate = attribute("formnovalidate") ## Construct a `formtarget` attribute. formtarget : Str -> Attribute -formtarget = attribute "formtarget" +formtarget = attribute("formtarget") ## Construct a `headers` attribute. headers : Str -> Attribute -headers = attribute "headers" +headers = attribute("headers") ## Construct a `height` attribute. height : Str -> Attribute -height = attribute "height" +height = attribute("height") ## Construct a `hidden` attribute. hidden : Str -> Attribute -hidden = attribute "hidden" +hidden = attribute("hidden") ## Construct a `high` attribute. high : Str -> Attribute -high = attribute "high" +high = attribute("high") ## Construct a `href` attribute. href : Str -> Attribute -href = attribute "href" +href = attribute("href") ## Construct a `hreflang` attribute. hreflang : Str -> Attribute -hreflang = attribute "hreflang" +hreflang = attribute("hreflang") ## Construct a `http-equiv` attribute. -httpEquiv : Str -> Attribute -httpEquiv = attribute "http-equiv" +http_equiv : Str -> Attribute +http_equiv = attribute("http-equiv") ## Construct a `icon` attribute. icon : Str -> Attribute -icon = attribute "icon" +icon = attribute("icon") ## Construct a `id` attribute. id : Str -> Attribute -id = attribute "id" +id = attribute("id") ## Construct a `importance` attribute. importance : Str -> Attribute -importance = attribute "importance" +importance = attribute("importance") ## Construct a `inputmode` attribute. inputmode : Str -> Attribute -inputmode = attribute "inputmode" +inputmode = attribute("inputmode") ## Construct a `integrity` attribute. integrity : Str -> Attribute -integrity = attribute "integrity" +integrity = attribute("integrity") ## Construct a `intrinsicsize` attribute. intrinsicsize : Str -> Attribute -intrinsicsize = attribute "intrinsicsize" +intrinsicsize = attribute("intrinsicsize") ## Construct a `ismap` attribute. ismap : Str -> Attribute -ismap = attribute "ismap" +ismap = attribute("ismap") ## Construct a `itemprop` attribute. itemprop : Str -> Attribute -itemprop = attribute "itemprop" +itemprop = attribute("itemprop") ## Construct a `keytype` attribute. keytype : Str -> Attribute -keytype = attribute "keytype" +keytype = attribute("keytype") ## Construct a `kind` attribute. kind : Str -> Attribute -kind = attribute "kind" +kind = attribute("kind") ## Construct a `label` attribute. label : Str -> Attribute -label = attribute "label" +label = attribute("label") ## Construct a `lang` attribute. lang : Str -> Attribute -lang = attribute "lang" +lang = attribute("lang") ## Construct a `language` attribute. language : Str -> Attribute -language = attribute "language" +language = attribute("language") ## Construct a `list` attribute. list : Str -> Attribute -list = attribute "list" +list = attribute("list") ## Construct a `loading` attribute. loading : Str -> Attribute -loading = attribute "loading" +loading = attribute("loading") ## Construct a `loop` attribute. loop : Str -> Attribute -loop = attribute "loop" +loop = attribute("loop") ## Construct a `low` attribute. low : Str -> Attribute -low = attribute "low" +low = attribute("low") ## Construct a `manifest` attribute. manifest : Str -> Attribute -manifest = attribute "manifest" +manifest = attribute("manifest") ## Construct a `max` attribute. max : Str -> Attribute -max = attribute "max" +max = attribute("max") ## Construct a `maxlength` attribute. maxlength : Str -> Attribute -maxlength = attribute "maxlength" +maxlength = attribute("maxlength") ## Construct a `media` attribute. media : Str -> Attribute -media = attribute "media" +media = attribute("media") ## Construct a `method` attribute. method : Str -> Attribute -method = attribute "method" +method = attribute("method") ## Construct a `min` attribute. min : Str -> Attribute -min = attribute "min" +min = attribute("min") ## Construct a `minlength` attribute. minlength : Str -> Attribute -minlength = attribute "minlength" +minlength = attribute("minlength") ## Construct a `multiple` attribute. multiple : Str -> Attribute -multiple = attribute "multiple" +multiple = attribute("multiple") ## Construct a `muted` attribute. muted : Str -> Attribute -muted = attribute "muted" +muted = attribute("muted") ## Construct a `name` attribute. name : Str -> Attribute -name = attribute "name" +name = attribute("name") ## Construct a `novalidate` attribute. novalidate : Str -> Attribute -novalidate = attribute "novalidate" +novalidate = attribute("novalidate") ## Construct a `open` attribute. open : Str -> Attribute -open = attribute "open" +open = attribute("open") ## Construct a `optimum` attribute. optimum : Str -> Attribute -optimum = attribute "optimum" +optimum = attribute("optimum") ## Construct a `pattern` attribute. pattern : Str -> Attribute -pattern = attribute "pattern" +pattern = attribute("pattern") ## Construct a `ping` attribute. ping : Str -> Attribute -ping = attribute "ping" +ping = attribute("ping") ## Construct a `placeholder` attribute. placeholder : Str -> Attribute -placeholder = attribute "placeholder" +placeholder = attribute("placeholder") ## Construct a `poster` attribute. poster : Str -> Attribute -poster = attribute "poster" +poster = attribute("poster") ## Construct a `preload` attribute. preload : Str -> Attribute -preload = attribute "preload" +preload = attribute("preload") ## Construct a `radiogroup` attribute. radiogroup : Str -> Attribute -radiogroup = attribute "radiogroup" +radiogroup = attribute("radiogroup") ## Construct a `readonly` attribute. readonly : Str -> Attribute -readonly = attribute "readonly" +readonly = attribute("readonly") ## Construct a `referrerpolicy` attribute. referrerpolicy : Str -> Attribute -referrerpolicy = attribute "referrerpolicy" +referrerpolicy = attribute("referrerpolicy") ## Construct a `rel` attribute. rel : Str -> Attribute -rel = attribute "rel" +rel = attribute("rel") ## Construct a `required` attribute. required : Str -> Attribute -required = attribute "required" +required = attribute("required") ## Construct a `reversed` attribute. reversed : Str -> Attribute -reversed = attribute "reversed" +reversed = attribute("reversed") ## Construct a `role` attribute. role : Str -> Attribute -role = attribute "role" +role = attribute("role") ## Construct a `rows` attribute. rows : Str -> Attribute -rows = attribute "rows" +rows = attribute("rows") ## Construct a `rowspan` attribute. rowspan : Str -> Attribute -rowspan = attribute "rowspan" +rowspan = attribute("rowspan") ## Construct a `sandbox` attribute. sandbox : Str -> Attribute -sandbox = attribute "sandbox" +sandbox = attribute("sandbox") ## Construct a `scope` attribute. scope : Str -> Attribute -scope = attribute "scope" +scope = attribute("scope") ## Construct a `scoped` attribute. scoped : Str -> Attribute -scoped = attribute "scoped" +scoped = attribute("scoped") ## Construct a `selected` attribute. selected : Str -> Attribute -selected = attribute "selected" +selected = attribute("selected") ## Construct a `shape` attribute. shape : Str -> Attribute -shape = attribute "shape" +shape = attribute("shape") ## Construct a `size` attribute. size : Str -> Attribute -size = attribute "size" +size = attribute("size") ## Construct a `sizes` attribute. sizes : Str -> Attribute -sizes = attribute "sizes" +sizes = attribute("sizes") ## Construct a `slot` attribute. slot : Str -> Attribute -slot = attribute "slot" +slot = attribute("slot") ## Construct a `span` attribute. span : Str -> Attribute -span = attribute "span" +span = attribute("span") ## Construct a `spellcheck` attribute. spellcheck : Str -> Attribute -spellcheck = attribute "spellcheck" +spellcheck = attribute("spellcheck") ## Construct a `src` attribute. src : Str -> Attribute -src = attribute "src" +src = attribute("src") ## Construct a `srcdoc` attribute. srcdoc : Str -> Attribute -srcdoc = attribute "srcdoc" +srcdoc = attribute("srcdoc") ## Construct a `srclang` attribute. srclang : Str -> Attribute -srclang = attribute "srclang" +srclang = attribute("srclang") ## Construct a `srcset` attribute. srcset : Str -> Attribute -srcset = attribute "srcset" +srcset = attribute("srcset") ## Construct a `start` attribute. start : Str -> Attribute -start = attribute "start" +start = attribute("start") ## Construct a `step` attribute. step : Str -> Attribute -step = attribute "step" +step = attribute("step") ## Construct a `style` attribute. style : Str -> Attribute -style = attribute "style" +style = attribute("style") ## Construct a `summary` attribute. summary : Str -> Attribute -summary = attribute "summary" +summary = attribute("summary") ## Construct a `tabindex` attribute. tabindex : Str -> Attribute -tabindex = attribute "tabindex" +tabindex = attribute("tabindex") ## Construct a `target` attribute. target : Str -> Attribute -target = attribute "target" +target = attribute("target") ## Construct a `title` attribute. title : Str -> Attribute -title = attribute "title" +title = attribute("title") ## Construct a `translate` attribute. translate : Str -> Attribute -translate = attribute "translate" +translate = attribute("translate") ## Construct a `type` attribute. type : Str -> Attribute -type = attribute "type" +type = attribute("type") ## Construct a `usemap` attribute. usemap : Str -> Attribute -usemap = attribute "usemap" +usemap = attribute("usemap") ## Construct a `value` attribute. value : Str -> Attribute -value = attribute "value" +value = attribute("value") ## Construct a `width` attribute. width : Str -> Attribute -width = attribute "width" +width = attribute("width") ## Construct a `wrap` attribute. wrap : Str -> Attribute -wrap = attribute "wrap" +wrap = attribute("wrap") diff --git a/src/Html.roc b/src/Html.roc index af2be55..a57fe8f 100644 --- a/src/Html.roc +++ b/src/Html.roc @@ -2,10 +2,10 @@ module [ Node, text, element, - voidElement, + void_element, render, - renderWithoutDocType, - dangerouslyIncludeUnescapedHtml, + render_without_doc_type, + dangerously_include_unescaped_html, # Content sectioning address, article, @@ -137,7 +137,7 @@ module [ ] import Attribute exposing [Attribute, attribute] -import SafeStr exposing [SafeStr, escape, dangerouslyMarkSafe] +import SafeStr exposing [SafeStr, escape, dangerously_mark_safe] ## An HTML node, either an HTML element or some text inside an HTML element. Node : [Element Str U64 (List Attribute) (List Node), Text Str, UnescapedHtml Str] @@ -155,8 +155,8 @@ text : Str -> Node text = Text expect - textNode = text "" - renderWithoutDocType textNode == "<script>alert('hi')</script>" + text_node = text("") + render_without_doc_type(text_node) == "<script>alert('hi')</script>" ## Mark a string as safe for HTML without actually escaping it. ## @@ -169,12 +169,12 @@ expect ## htmlNode = Html.dangerouslyIncludeUnescapedHtml "" ## Html.renderWithoutDocType htmlNode == "" ## ``` -dangerouslyIncludeUnescapedHtml : Str -> Node -dangerouslyIncludeUnescapedHtml = UnescapedHtml +dangerously_include_unescaped_html : Str -> Node +dangerously_include_unescaped_html = UnescapedHtml expect - htmlNode = dangerouslyIncludeUnescapedHtml "" - renderWithoutDocType htmlNode == "" + html_node = dangerously_include_unescaped_html("") + render_without_doc_type(html_node) == "" ## Define a non-standard HTML element. ## You can use this to add elements that are not already supported. @@ -188,43 +188,55 @@ expect ## blink [] [ text "This text is blinking!" ] ## ``` element : Str -> (List Attribute, List Node -> Node) -element = \tagName -> +element = \tag_name -> \attrs, children -> # While building the node tree, calculate the size of Str it will render to - withTag = 2 * (3 + Str.countUtf8Bytes tagName) - withAttrs = List.walk attrs withTag \acc, Attribute name val -> - acc + Str.countUtf8Bytes name + Str.countUtf8Bytes val + 4 - totalSize = List.walk children withAttrs \acc, child -> - acc + nodeSize child - - Element tagName totalSize attrs children + with_tag = 2 * (3 + Str.count_utf8_bytes(tag_name)) + with_attrs = List.walk( + attrs, + with_tag, + \acc, Attribute(name, val) -> + acc + Str.count_utf8_bytes(name) + Str.count_utf8_bytes(val) + 4, + ) + total_size = List.walk( + children, + with_attrs, + \acc, child -> + acc + node_size(child), + ) + + Element(tag_name, total_size, attrs, children) ## Define a non-standard HTML [void element](https://developer.mozilla.org/en-US/docs/Glossary/Void_element). ## A void element is an element that cannot have any children. -voidElement : Str -> (List Attribute -> Node) -voidElement = \tagName -> +void_element : Str -> (List Attribute -> Node) +void_element = \tag_name -> \attrs -> # While building the node tree, calculate the size of Str it will render to - withTag = 2 * (3 + Str.countUtf8Bytes tagName) - withAttrs = List.walk attrs withTag \acc, Attribute name val -> - acc + Str.countUtf8Bytes name + Str.countUtf8Bytes val + 4 + with_tag = 2 * (3 + Str.count_utf8_bytes(tag_name)) + with_attrs = List.walk( + attrs, + with_tag, + \acc, Attribute(name, val) -> + acc + Str.count_utf8_bytes(name) + Str.count_utf8_bytes(val) + 4, + ) - Element tagName withAttrs attrs [] + Element(tag_name, with_attrs, attrs, []) ## Internal helper to calculate the size of a node -nodeSize : Node -> U64 -nodeSize = \node -> +node_size : Node -> U64 +node_size = \node -> when node is - Text content -> + Text(content) -> # We allocate more bytes than the original string had because we'll need extra bytes # if there are any characters we need to escape. My choice of the proportion 3/2 # was arbitrary. - Str.countUtf8Bytes content |> Num.divCeil 2 |> Num.mul 3 + Str.count_utf8_bytes(content) |> Num.div_ceil(2) |> Num.mul(3) - UnescapedHtml content -> - Str.countUtf8Bytes content + UnescapedHtml(content) -> + Str.count_utf8_bytes(content) - Element _ size _ _ -> + Element(_, size, _, _) -> size ## Render a Node to an HTML string @@ -234,566 +246,566 @@ nodeSize = \node -> ## See also `renderWithoutDocType`. render : Node -> Str render = \node -> - buffer = SafeStr.reserve (dangerouslyMarkSafe "") (nodeSize node) + buffer = SafeStr.reserve(dangerously_mark_safe(""), node_size(node)) - renderHelp buffer node - |> SafeStr.toStr + render_help(buffer, node) + |> SafeStr.to_str expect - exampleDocument = html [] [body [] [p [(attribute "example") "test"] [text "Hello, World!"]]] - out = render exampleDocument + example_document = html([], [body([], [p([(attribute("example"))("test")], [text("Hello, World!")])])]) + out = render(example_document) out == "

Hello, World!

" expect - exampleDocument = html [] [body [] [p [(attribute "example") "test"] [text ""]]] - out = render exampleDocument + example_document = html([], [body([], [p([(attribute("example"))("test")], [text("")])])]) + out = render(example_document) out == "

<script>alert('hi')</script>

" expect - exampleDocument = html [] [body [] [p [(attribute "example") "test"] [dangerouslyIncludeUnescapedHtml ""]]] - out = render exampleDocument + example_document = html([], [body([], [p([(attribute("example"))("test")], [dangerously_include_unescaped_html("")])])]) + out = render(example_document) out == "

" expect - exampleDocument = html [] [body [] [base [], link [], meta [], embed [], source [], input [], area [], img [], track [], br [], wbr [], col [], hr []]] - out = render exampleDocument + example_document = html([], [body([], [base([]), link([]), meta([]), embed([]), source([]), input([]), area([]), img([]), track([]), br([]), wbr([]), col([]), hr([])])]) + out = render(example_document) out == "

" ## Render a Node to a string, without a `!DOCTYPE` tag. -renderWithoutDocType : Node -> Str -renderWithoutDocType = \node -> - buffer = SafeStr.withCapacity (nodeSize node) +render_without_doc_type : Node -> Str +render_without_doc_type = \node -> + buffer = SafeStr.with_capacity(node_size(node)) - renderHelp buffer node - |> SafeStr.toStr + render_help(buffer, node) + |> SafeStr.to_str ## An internal helper to render a node to a string buffer. -renderHelp : SafeStr, Node -> SafeStr -renderHelp = \buffer, node -> +render_help : SafeStr, Node -> SafeStr +render_help = \buffer, node -> when node is - Text content -> - SafeStr.concat buffer (escape content) + Text(content) -> + SafeStr.concat(buffer, escape(content)) - UnescapedHtml content -> - SafeStr.concat buffer (dangerouslyMarkSafe content) + UnescapedHtml(content) -> + SafeStr.concat(buffer, dangerously_mark_safe(content)) - Element tagName _ attrs children -> - when tagName is + Element(tag_name, _, attrs, children) -> + when tag_name is # Special case for void elements "base" | "link" | "meta" | "embed" | "source" | "input" | "area" | "img" | "track" | "br" | "wbr" | "col" | "hr" -> buffer - |> SafeStr.concat (dangerouslyMarkSafe "<") - |> SafeStr.concat (dangerouslyMarkSafe tagName) - |> \withTagName -> - if List.isEmpty attrs then - withTagName + |> SafeStr.concat(dangerously_mark_safe("<")) + |> SafeStr.concat(dangerously_mark_safe(tag_name)) + |> \with_tag_name -> + if List.is_empty(attrs) then + with_tag_name else - List.walk attrs withTagName renderAttr - |> SafeStr.concat (dangerouslyMarkSafe ">") # Don't use self-closing tag syntax for void elements + List.walk(attrs, with_tag_name, render_attr) + |> SafeStr.concat(dangerously_mark_safe(">")) # Don't use self-closing tag syntax for void elements _ -> buffer - |> SafeStr.concat (dangerouslyMarkSafe "<") - |> SafeStr.concat (dangerouslyMarkSafe tagName) - |> \withTagName -> - if List.isEmpty attrs then - withTagName + |> SafeStr.concat(dangerously_mark_safe("<")) + |> SafeStr.concat(dangerously_mark_safe(tag_name)) + |> \with_tag_name -> + if List.is_empty(attrs) then + with_tag_name else - List.walk attrs withTagName renderAttr - |> SafeStr.concat (dangerouslyMarkSafe ">") - |> \withTag -> List.walk children withTag renderHelp - |> SafeStr.concat (dangerouslyMarkSafe " SafeStr.concat (dangerouslyMarkSafe tagName) - |> SafeStr.concat (dangerouslyMarkSafe ">") + List.walk(attrs, with_tag_name, render_attr) + |> SafeStr.concat(dangerously_mark_safe(">")) + |> \with_tag -> List.walk(children, with_tag, render_help) + |> SafeStr.concat(dangerously_mark_safe(" SafeStr.concat(dangerously_mark_safe(tag_name)) + |> SafeStr.concat(dangerously_mark_safe(">")) ## An internal helper to render an attribute to a string buffer. -renderAttr : SafeStr, Attribute -> SafeStr -renderAttr = \buffer, Attribute key value -> +render_attr : SafeStr, Attribute -> SafeStr +render_attr = \buffer, Attribute(key, value) -> buffer - |> SafeStr.concat (dangerouslyMarkSafe " ") - |> SafeStr.concat (dangerouslyMarkSafe key) - |> SafeStr.concat (dangerouslyMarkSafe "=\"") - |> SafeStr.concat (escape value) - |> SafeStr.concat (dangerouslyMarkSafe "\"") + |> SafeStr.concat(dangerously_mark_safe(" ")) + |> SafeStr.concat(dangerously_mark_safe(key)) + |> SafeStr.concat(dangerously_mark_safe("=\"")) + |> SafeStr.concat(escape(value)) + |> SafeStr.concat(dangerously_mark_safe("\"")) # Content sectioning ## Construct a `address` element. address : List Attribute, List Node -> Node -address = element "address" +address = element("address") ## Construct a `article` element. article : List Attribute, List Node -> Node -article = element "article" +article = element("article") ## Construct a `aside` element. aside : List Attribute, List Node -> Node -aside = element "aside" +aside = element("aside") ## Construct a `footer` element. footer : List Attribute, List Node -> Node -footer = element "footer" +footer = element("footer") ## Construct a `h1` element. h1 : List Attribute, List Node -> Node -h1 = element "h1" +h1 = element("h1") ## Construct a `h2` element. h2 : List Attribute, List Node -> Node -h2 = element "h2" +h2 = element("h2") ## Construct a `h3` element. h3 : List Attribute, List Node -> Node -h3 = element "h3" +h3 = element("h3") ## Construct a `h4` element. h4 : List Attribute, List Node -> Node -h4 = element "h4" +h4 = element("h4") ## Construct a `h5` element. h5 : List Attribute, List Node -> Node -h5 = element "h5" +h5 = element("h5") ## Construct a `h6` element. h6 : List Attribute, List Node -> Node -h6 = element "h6" +h6 = element("h6") ## Construct a `header` element. header : List Attribute, List Node -> Node -header = element "header" +header = element("header") ## Construct a `main` element. main : List Attribute, List Node -> Node -main = element "main" +main = element("main") ## Construct a `nav` element. nav : List Attribute, List Node -> Node -nav = element "nav" +nav = element("nav") ## Construct a `section` element. section : List Attribute, List Node -> Node -section = element "section" +section = element("section") # Demarcating edits ## Construct a `del` element. del : List Attribute, List Node -> Node -del = element "del" +del = element("del") ## Construct a `ins` element. ins : List Attribute, List Node -> Node -ins = element "ins" +ins = element("ins") # Document metadata ## Construct a `base` element. base : List Attribute -> Node -base = voidElement "base" +base = void_element("base") ## Construct a `head` element. head : List Attribute, List Node -> Node -head = element "head" +head = element("head") ## Construct a `link` element. link : List Attribute -> Node -link = voidElement "link" +link = void_element("link") ## Construct a `meta` element. meta : List Attribute -> Node -meta = voidElement "meta" +meta = void_element("meta") ## Construct a `style` element. style : List Attribute, List Node -> Node -style = element "style" +style = element("style") ## Construct a `title` element. title : List Attribute, List Node -> Node -title = element "title" +title = element("title") # Embedded content ## Construct a `embed` element. embed : List Attribute -> Node -embed = voidElement "embed" +embed = void_element("embed") ## Construct a `iframe` element. iframe : List Attribute, List Node -> Node -iframe = element "iframe" +iframe = element("iframe") ## Construct a `object` element. object : List Attribute, List Node -> Node -object = element "object" +object = element("object") ## Construct a `picture` element. picture : List Attribute, List Node -> Node -picture = element "picture" +picture = element("picture") ## Construct a `portal` element. portal : List Attribute, List Node -> Node -portal = element "portal" +portal = element("portal") ## Construct a `source` element. source : List Attribute -> Node -source = voidElement "source" +source = void_element("source") # Forms ## Construct a `button` element. button : List Attribute, List Node -> Node -button = element "button" +button = element("button") ## Construct a `datalist` element. datalist : List Attribute, List Node -> Node -datalist = element "datalist" +datalist = element("datalist") ## Construct a `fieldset` element. fieldset : List Attribute, List Node -> Node -fieldset = element "fieldset" +fieldset = element("fieldset") ## Construct a `form` element. form : List Attribute, List Node -> Node -form = element "form" +form = element("form") ## Construct a `input` element. input : List Attribute -> Node -input = voidElement "input" +input = void_element("input") ## Construct a `label` element. label : List Attribute, List Node -> Node -label = element "label" +label = element("label") ## Construct a `legend` element. legend : List Attribute, List Node -> Node -legend = element "legend" +legend = element("legend") ## Construct a `meter` element. meter : List Attribute, List Node -> Node -meter = element "meter" +meter = element("meter") ## Construct a `optgroup` element. optgroup : List Attribute, List Node -> Node -optgroup = element "optgroup" +optgroup = element("optgroup") ## Construct a `option` element. option : List Attribute, List Node -> Node -option = element "option" +option = element("option") ## Construct a `output` element. output : List Attribute, List Node -> Node -output = element "output" +output = element("output") ## Construct a `progress` element. progress : List Attribute, List Node -> Node -progress = element "progress" +progress = element("progress") ## Construct a `select` element. select : List Attribute, List Node -> Node -select = element "select" +select = element("select") ## Construct a `textarea` element. textarea : List Attribute, List Node -> Node -textarea = element "textarea" +textarea = element("textarea") # Image and multimedia ## Construct a `area` element. area : List Attribute -> Node -area = voidElement "area" +area = void_element("area") ## Construct a `audio` element. audio : List Attribute, List Node -> Node -audio = element "audio" +audio = element("audio") ## Construct a `img` element. img : List Attribute -> Node -img = voidElement "img" +img = void_element("img") ## Construct a `map` element. map : List Attribute, List Node -> Node -map = element "map" +map = element("map") ## Construct a `track` element. track : List Attribute -> Node -track = voidElement "track" +track = void_element("track") ## Construct a `video` element. video : List Attribute, List Node -> Node -video = element "video" +video = element("video") # Inline text semantics ## Construct a `a` element. a : List Attribute, List Node -> Node -a = element "a" +a = element("a") ## Construct a `abbr` element. abbr : List Attribute, List Node -> Node -abbr = element "abbr" +abbr = element("abbr") ## Construct a `b` element. b : List Attribute, List Node -> Node -b = element "b" +b = element("b") ## Construct a `bdi` element. bdi : List Attribute, List Node -> Node -bdi = element "bdi" +bdi = element("bdi") ## Construct a `bdo` element. bdo : List Attribute, List Node -> Node -bdo = element "bdo" +bdo = element("bdo") ## Construct a `br` element. br : List Attribute -> Node -br = voidElement "br" +br = void_element("br") ## Construct a `cite` element. cite : List Attribute, List Node -> Node -cite = element "cite" +cite = element("cite") ## Construct a `code` element. code : List Attribute, List Node -> Node -code = element "code" +code = element("code") ## Construct a `data` element. data : List Attribute, List Node -> Node -data = element "data" +data = element("data") ## Construct a `dfn` element. dfn : List Attribute, List Node -> Node -dfn = element "dfn" +dfn = element("dfn") ## Construct a `em` element. em : List Attribute, List Node -> Node -em = element "em" +em = element("em") ## Construct a `i` element. i : List Attribute, List Node -> Node -i = element "i" +i = element("i") ## Construct a `kbd` element. kbd : List Attribute, List Node -> Node -kbd = element "kbd" +kbd = element("kbd") ## Construct a `mark` element. mark : List Attribute, List Node -> Node -mark = element "mark" +mark = element("mark") ## Construct a `q` element. q : List Attribute, List Node -> Node -q = element "q" +q = element("q") ## Construct a `rp` element. rp : List Attribute, List Node -> Node -rp = element "rp" +rp = element("rp") ## Construct a `rt` element. rt : List Attribute, List Node -> Node -rt = element "rt" +rt = element("rt") ## Construct a `ruby` element. ruby : List Attribute, List Node -> Node -ruby = element "ruby" +ruby = element("ruby") ## Construct a `s` element. s : List Attribute, List Node -> Node -s = element "s" +s = element("s") ## Construct a `samp` element. samp : List Attribute, List Node -> Node -samp = element "samp" +samp = element("samp") ## Construct a `small` element. small : List Attribute, List Node -> Node -small = element "small" +small = element("small") ## Construct a `span` element. span : List Attribute, List Node -> Node -span = element "span" +span = element("span") ## Construct a `strong` element. strong : List Attribute, List Node -> Node -strong = element "strong" +strong = element("strong") ## Construct a `sub` element. sub : List Attribute, List Node -> Node -sub = element "sub" +sub = element("sub") ## Construct a `sup` element. sup : List Attribute, List Node -> Node -sup = element "sup" +sup = element("sup") ## Construct a `time` element. time : List Attribute, List Node -> Node -time = element "time" +time = element("time") ## Construct a `u` element. u : List Attribute, List Node -> Node -u = element "u" +u = element("u") ## Construct a `var` element. var : List Attribute, List Node -> Node -var = element "var" +var = element("var") ## Construct a `wbr` element. wbr : List Attribute -> Node -wbr = voidElement "wbr" +wbr = void_element("wbr") # Interactive elements ## Construct a `details` element. details : List Attribute, List Node -> Node -details = element "details" +details = element("details") ## Construct a `dialog` element. dialog : List Attribute, List Node -> Node -dialog = element "dialog" +dialog = element("dialog") ## Construct a `summary` element. summary : List Attribute, List Node -> Node -summary = element "summary" +summary = element("summary") # Main root ## Construct a `html` element. html : List Attribute, List Node -> Node -html = element "html" +html = element("html") # SVG and MathML ## Construct a `math` element. math : List Attribute, List Node -> Node -math = element "math" +math = element("math") ## Construct a `svg` element. svg : List Attribute, List Node -> Node -svg = element "svg" +svg = element("svg") # Scripting ## Construct a `canvas` element. canvas : List Attribute, List Node -> Node -canvas = element "canvas" +canvas = element("canvas") ## Construct a `noscript` element. noscript : List Attribute, List Node -> Node -noscript = element "noscript" +noscript = element("noscript") ## Construct a `script` element. script : List Attribute, List Node -> Node -script = element "script" +script = element("script") # Sectioning root ## Construct a `body` element. body : List Attribute, List Node -> Node -body = element "body" +body = element("body") # Table content ## Construct a `caption` element. caption : List Attribute, List Node -> Node -caption = element "caption" +caption = element("caption") ## Construct a `col` element. col : List Attribute -> Node -col = voidElement "col" +col = void_element("col") ## Construct a `colgroup` element. colgroup : List Attribute, List Node -> Node -colgroup = element "colgroup" +colgroup = element("colgroup") ## Construct a `table` element. table : List Attribute, List Node -> Node -table = element "table" +table = element("table") ## Construct a `tbody` element. tbody : List Attribute, List Node -> Node -tbody = element "tbody" +tbody = element("tbody") ## Construct a `td` element. td : List Attribute, List Node -> Node -td = element "td" +td = element("td") ## Construct a `tfoot` element. tfoot : List Attribute, List Node -> Node -tfoot = element "tfoot" +tfoot = element("tfoot") ## Construct a `th` element. th : List Attribute, List Node -> Node -th = element "th" +th = element("th") ## Construct a `thead` element. thead : List Attribute, List Node -> Node -thead = element "thead" +thead = element("thead") ## Construct a `tr` element. tr : List Attribute, List Node -> Node -tr = element "tr" +tr = element("tr") # Text content ## Construct a `blockquote` element. blockquote : List Attribute, List Node -> Node -blockquote = element "blockquote" +blockquote = element("blockquote") ## Construct a `dd` element. dd : List Attribute, List Node -> Node -dd = element "dd" +dd = element("dd") ## Construct a `div` element. div : List Attribute, List Node -> Node -div = element "div" +div = element("div") ## Construct a `dl` element. dl : List Attribute, List Node -> Node -dl = element "dl" +dl = element("dl") ## Construct a `dt` element. dt : List Attribute, List Node -> Node -dt = element "dt" +dt = element("dt") ## Construct a `figcaption` element. figcaption : List Attribute, List Node -> Node -figcaption = element "figcaption" +figcaption = element("figcaption") ## Construct a `figure` element. figure : List Attribute, List Node -> Node -figure = element "figure" +figure = element("figure") ## Construct a `hr` element. hr : List Attribute -> Node -hr = voidElement "hr" +hr = void_element("hr") ## Construct a `li` element. li : List Attribute, List Node -> Node -li = element "li" +li = element("li") ## Construct a `menu` element. menu : List Attribute, List Node -> Node -menu = element "menu" +menu = element("menu") ## Construct a `ol` element. ol : List Attribute, List Node -> Node -ol = element "ol" +ol = element("ol") ## Construct a `p` element. p : List Attribute, List Node -> Node -p = element "p" +p = element("p") ## Construct a `pre` element. pre : List Attribute, List Node -> Node -pre = element "pre" +pre = element("pre") ## Construct a `ul` element. ul : List Attribute, List Node -> Node -ul = element "ul" +ul = element("ul") # Web components ## Construct a `slot` element. slot : List Attribute, List Node -> Node -slot = element "slot" +slot = element("slot") ## Construct a `template` element. template : List Attribute, List Node -> Node -template = element "template" +template = element("template") diff --git a/src/SafeStr.roc b/src/SafeStr.roc index 069fc1e..788fac2 100644 --- a/src/SafeStr.roc +++ b/src/SafeStr.roc @@ -1,9 +1,9 @@ module [ SafeStr, - toStr, + to_str, escape, - dangerouslyMarkSafe, - withCapacity, + dangerously_mark_safe, + with_capacity, concat, reserve, ] @@ -15,83 +15,87 @@ SafeStr := Str ## This is the function that should usually be used for converting a Str to a SafeStr. escape : Str -> SafeStr escape = \str -> - (encodedBytes, isOriginalStringFine) = + (encoded_bytes, is_original_string_fine) = # We allocate more bytes than the original string had because we'll need extra bytes # if there are any characters we need to escape. My choice of the proportion 3/2 # was arbitrary. - capacity = Str.countUtf8Bytes str |> Num.divCeil 2 |> Num.mul 3 - bytes = List.withCapacity capacity + capacity = Str.count_utf8_bytes(str) |> Num.div_ceil(2) |> Num.mul(3) + bytes = List.with_capacity(capacity) # Look at each byte in the string. It's important to look at each codepoint, not the # whole graheme. (See the test below.) Because we're only looking for `"`, `&`, `'`, # `<`, and `>`, each of which are one byte, it's fine to just iterate over the bytes. # In UTF-8, the only bytes that ever start with a 0 are the single-byte codepoints, so # these bytes will never appear in the middle of a codepoint. - Str.walkUtf8 str (bytes, Bool.true) \(bytesSoFar, isOriginalStringFineSoFar), byte -> - when byte is - 34 -> (List.concat bytesSoFar (Str.toUtf8 """), Bool.false) # " must be escaped - 38 -> (List.concat bytesSoFar (Str.toUtf8 "&"), Bool.false) # & must be escaped - 39 -> (List.concat bytesSoFar (Str.toUtf8 "'"), Bool.false) # ' must be escaped - 60 -> (List.concat bytesSoFar (Str.toUtf8 "<"), Bool.false) # < must be escaped - 62 -> (List.concat bytesSoFar (Str.toUtf8 ">"), Bool.false) # > must be escaped - _ -> (List.append bytesSoFar byte, isOriginalStringFineSoFar) # All other bytes are fine! - if isOriginalStringFine then + Str.walk_utf8( + str, + (bytes, Bool.true), + \(bytes_so_far, is_original_string_fine_so_far), byte -> + when byte is + 34 -> (List.concat(bytes_so_far, Str.to_utf8(""")), Bool.false) # " must be escaped + 38 -> (List.concat(bytes_so_far, Str.to_utf8("&")), Bool.false) # & must be escaped + 39 -> (List.concat(bytes_so_far, Str.to_utf8("'")), Bool.false) # ' must be escaped + 60 -> (List.concat(bytes_so_far, Str.to_utf8("<")), Bool.false) # < must be escaped + 62 -> (List.concat(bytes_so_far, Str.to_utf8(">")), Bool.false) # > must be escaped + _ -> (List.append(bytes_so_far, byte), is_original_string_fine_so_far), + ) # All other bytes are fine! + if is_original_string_fine then # If we didn't do any replacements, we might as well use the original string # so that Roc can free encodedBytes and avoid re-validating the UTF-8. - @SafeStr str + @SafeStr(str) else - when Str.fromUtf8 encodedBytes is - Ok s -> @SafeStr s - Err (BadUtf8 _ _) -> - crash "SafeStr.escape: bad utf8. This should not be possible; please report this bug to roc-html." + when Str.from_utf8(encoded_bytes) is + Ok(s) -> @SafeStr(s) + Err(BadUtf8(_)) -> + crash("SafeStr.escape: bad utf8. This should not be possible; please report this bug to roc-html.") ## Convert a SafeStr to a regular Str. -toStr : SafeStr -> Str -toStr = \@SafeStr str -> str +to_str : SafeStr -> Str +to_str = \@SafeStr(str) -> str -expect toStr (escape "

abc

") == "<h1>abc</h1>" -expect toStr (escape "abc") == "abc" -expect toStr (escape "折り紙🕊") == "折り紙🕊" -expect toStr (escape "é é") == "é é" -expect toStr (escape "﷽ᄀᄀᄀ각ᆨᆨ🇺🇸각नीநி") == "﷽ᄀᄀᄀ각ᆨᆨ🇺🇸각नीநி" -expect toStr (escape "﷽&ᄀᄀᄀ각ᆨᆨ🇺🇸각नीநி") == "﷽&ᄀᄀᄀ각ᆨᆨ🇺🇸각नीநி" -expect toStr (escape "'&\"<>") == "'&"<>" +expect to_str(escape("

abc

")) == "<h1>abc</h1>" +expect to_str(escape("abc")) == "abc" +expect to_str(escape("折り紙🕊")) == "折り紙🕊" +expect to_str(escape("é é")) == "é é" +expect to_str(escape("﷽ᄀᄀᄀ각ᆨᆨ🇺🇸각नीநி")) == "﷽ᄀᄀᄀ각ᆨᆨ🇺🇸각नीநி" +expect to_str(escape("﷽&ᄀᄀᄀ각ᆨᆨ🇺🇸각नीநி")) == "﷽&ᄀᄀᄀ각ᆨᆨ🇺🇸각नीநி" +expect to_str(escape("'&\"<>")) == "'&"<>" # Note: This sometimes displays incorrectly in VSCode. It's a family emoji. -expect toStr (escape "👩‍👩‍👦‍👦") == "👩‍👩‍👦‍👦" -expect toStr (escape "&👩‍👩‍👦‍👦&") == "&👩‍👩‍👦‍👦&" -expect toStr (escape "`~!@#$%^&*()-=_+[]\\{}|;':\",./<>?") == "`~!@#$%^&*()-=_+[]\\{}|;':",./<>?" +expect to_str(escape("👩‍👩‍👦‍👦")) == "👩‍👩‍👦‍👦" +expect to_str(escape("&👩‍👩‍👦‍👦&")) == "&👩‍👩‍👦‍👦&" +expect to_str(escape("`~!@#$%^&*()-=_+[]\\{}|;':\",./<>?")) == "`~!@#$%^&*()-=_+[]\\{}|;':",./<>?" # Even though this string doesn't contain the *grapheme* "<" or the grapheme ">", # it does contain the *codepoints* "<" and ">". The browser still interprets them # as HTML tags, so it's important to escape them. -expect toStr (escape "something؀

͏bad؀

͏something") == "something؀<h1>͏bad؀</h1>͏something" +expect to_str(escape("something؀

͏bad؀

͏something")) == "something؀<h1>͏bad؀</h1>͏something" ## Mark a string as safe for HTML without actually escaping it. ## DO NOT use this function unless you know the input string is safe. ## NEVER use this function on user input. -dangerouslyMarkSafe : Str -> SafeStr -dangerouslyMarkSafe = \str -> @SafeStr str +dangerously_mark_safe : Str -> SafeStr +dangerously_mark_safe = \str -> @SafeStr(str) expect - badStr = "" - toStr (dangerouslyMarkSafe badStr) == badStr + bad_str = "" + to_str(dangerously_mark_safe(bad_str)) == bad_str ## The SafeStr equivalent of Str.withCapacity -withCapacity : U64 -> SafeStr -withCapacity = \capacity -> - @SafeStr (Str.withCapacity capacity) +with_capacity : U64 -> SafeStr +with_capacity = \capacity -> + @SafeStr(Str.with_capacity(capacity)) -expect toStr (withCapacity 10) == "" +expect to_str(with_capacity(10)) == "" ## The SafeStr equivalent of Str.reserve reserve : SafeStr, U64 -> SafeStr -reserve = \@SafeStr str, additionalCapacity -> - @SafeStr (Str.reserve str additionalCapacity) +reserve = \@SafeStr(str), additional_capacity -> + @SafeStr(Str.reserve(str, additional_capacity)) -expect "abc>" |> escape |> reserve 50 |> toStr == "abc>" +expect "abc>" |> escape |> reserve(50) |> to_str == "abc>" ## The SafeStr equivalent of Str.concat concat : SafeStr, SafeStr -> SafeStr -concat = \@SafeStr str1, @SafeStr str2 -> +concat = \@SafeStr(str1), @SafeStr(str2) -> # If two strings are HTML-safe, their concatenation must be too. - @SafeStr (Str.concat str1 str2) + @SafeStr(Str.concat(str1, str2)) -expect escape "3>2" |> concat (escape "2<3") |> toStr == "3>22<3" +expect escape("3>2") |> concat(escape("2<3")) |> to_str == "3>22<3" From 64ea410dc3e8f523a38c68aa25d43161c6e6095a Mon Sep 17 00:00:00 2001 From: Luke Boswell Date: Sat, 11 Jan 2025 12:16:14 +1100 Subject: [PATCH 3/3] fix PNC in doc comments --- src/Html.roc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Html.roc b/src/Html.roc index a57fe8f..f82174d 100644 --- a/src/Html.roc +++ b/src/Html.roc @@ -148,8 +148,8 @@ Node : [Element Str U64 (List Attribute) (List Node), Text Str, UnescapedHtml St ## ## ``` ## expect -## textNode = Html.text "" -## Html.renderWithoutDocType textNode == "<script>alert('hi')</script>" +## textNode = Html.text("") +## Html.render_without_doc_type(textNode) == "<script>alert('hi')</script>" ## ``` text : Str -> Node text = Text @@ -166,8 +166,8 @@ expect ## ## ``` ## expect -## htmlNode = Html.dangerouslyIncludeUnescapedHtml "" -## Html.renderWithoutDocType htmlNode == "" +## htmlNode = Html.dangerously_include_unescaped_html("") +## Html.render_without_doc_type(htmlNode) == "" ## ``` dangerously_include_unescaped_html : Str -> Node dangerously_include_unescaped_html = UnescapedHtml @@ -183,9 +183,9 @@ expect ## ## ``` ## blink : List Attribute, List Node -> Node -## blink = element "blink" +## blink = element("blink") ## -## blink [] [ text "This text is blinking!" ] +## blink [] [ text("This text is blinking!") ] ## ``` element : Str -> (List Attribute, List Node -> Node) element = \tag_name -> @@ -243,7 +243,7 @@ node_size = \node -> ## ## The output has no whitespace between nodes, to make it small. ## This is intended for generating full HTML documents, so it automatically adds `` to the start of the string. -## See also `renderWithoutDocType`. +## See also `render_without_doc_type`. render : Node -> Str render = \node -> buffer = SafeStr.reserve(dangerously_mark_safe(""), node_size(node))