├── .envrc ├── .github ├── dependabot.yaml ├── release-drafter.yaml └── workflows │ ├── bundle.yaml │ ├── check-pr-labels.yaml │ ├── generate-docs.yaml │ ├── run-pre-commit.yaml │ ├── test.yaml │ └── update-draft-release.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── Justfile ├── LICENCE ├── README.md ├── examples ├── .gitignore └── example.roc ├── flake.lock ├── flake.nix └── src ├── Attribute.roc ├── Html.roc ├── SafeStr.roc └── main.roc /.envrc: -------------------------------------------------------------------------------- 1 | strict_env 2 | if ! has nix_direnv_version || ! nix_direnv_version 3.0.6; then 3 | source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.6/direnvrc" "sha256-45871425a44c7fca05e4bce70eb9425db90e42bcb09b41c3bf556360668919d3" 4 | fi 5 | use flake 6 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: github-actions 5 | # Look for workflow files in .github/workflows/ 6 | directory: / 7 | # Check for updates once a month 8 | schedule: 9 | interval: monthly 10 | -------------------------------------------------------------------------------- /.github/release-drafter.yaml: -------------------------------------------------------------------------------- 1 | # The name of the release 2 | name-template: v$RESOLVED_VERSION 3 | # The tag of the release 4 | tag-template: v$RESOLVED_VERSION 5 | # Template for category headings 6 | category-template: "### $TITLE" 7 | # Template for each individual change 8 | change-template: "- $TITLE @$AUTHOR (#$NUMBER)" 9 | change-title-escapes: \<*_& 10 | categories: 11 | - title: Features 12 | label: feature 13 | - title: Bug fixes 14 | labels: fix 15 | - title: Maintenance 16 | label: chore 17 | version-resolver: 18 | major: 19 | labels: 20 | - major 21 | minor: 22 | labels: 23 | - minor 24 | patch: 25 | labels: 26 | - patch 27 | default: patch # Default increment if no PRs are found 28 | template: | 29 | ## Changelog 30 | 31 | $CHANGES 32 | -------------------------------------------------------------------------------- /.github/workflows/bundle.yaml: -------------------------------------------------------------------------------- 1 | name: Bundle 2 | 3 | on: 4 | # Run when a release is published 5 | release: 6 | types: 7 | - published 8 | 9 | jobs: 10 | bundle: 11 | name: Bundle 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: write 15 | steps: 16 | - name: Check out the repository 17 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 18 | - name: Install Roc 19 | uses: hasnep/setup-roc@9866c6fdc971ee9f4b3eeba03d825dc32a5efa7f # ratchet:hasnep/setup-roc@v0.5.0 20 | with: 21 | roc-version: 0.0.0-alpha2-rolling 22 | - name: Bundle and release the library 23 | uses: hasnep/bundle-roc-library@4364d15b4ae83c99e0bc0caab8a254a5d0a9369f # ratchet:hasnep/bundle-roc-library@v0.1.0 24 | with: 25 | library: src/main.roc 26 | token: ${{ github.token }} 27 | -------------------------------------------------------------------------------- /.github/workflows/check-pr-labels.yaml: -------------------------------------------------------------------------------- 1 | name: Check PR labels 2 | 3 | on: 4 | # Run on all PRs 5 | pull_request: 6 | types: 7 | - labeled 8 | - opened 9 | - reopened 10 | - synchronize 11 | 12 | jobs: 13 | check-pr-labels: 14 | name: Check PR labels 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Check for PR category labels 18 | uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024 # ratchet:yogevbd/enforce-label-action@2.2.2 19 | with: 20 | REQUIRED_LABELS_ANY: feature,fix,chore 21 | REQUIRED_LABELS_ANY_DESCRIPTION: Please tag your PR with one of `feature`, `fix`, or `chore`. 22 | - name: Check for PR version labels 23 | uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024 # ratchet:yogevbd/enforce-label-action@2.2.2 24 | with: 25 | REQUIRED_LABELS_ANY: major,minor,patch 26 | REQUIRED_LABELS_ANY_DESCRIPTION: Please tag your PR with one of `major`, `minor`, `patch`. 27 | -------------------------------------------------------------------------------- /.github/workflows/generate-docs.yaml: -------------------------------------------------------------------------------- 1 | name: Generate docs 2 | 3 | on: 4 | # Run when a release is published 5 | release: 6 | types: 7 | - published 8 | 9 | jobs: 10 | generate-docs: 11 | name: Generate docs 12 | runs-on: ubuntu-latest 13 | permissions: 14 | pages: write 15 | id-token: write 16 | steps: 17 | - name: Check out the repository 18 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 19 | - name: Install Roc 20 | uses: hasnep/setup-roc@9866c6fdc971ee9f4b3eeba03d825dc32a5efa7f # ratchet:hasnep/setup-roc@v0.5.0 21 | with: 22 | roc-version: 0.0.0-alpha2-rolling 23 | - name: Generate docs 24 | run: roc docs src/main.roc 25 | - name: Fix absolute paths 26 | run: | 27 | find generated-docs/ -type f -name '*.html' -exec sed -i "s/\(href\|src\)=\"\//\1=\"\/${{ github.event.repository.name }}\//g" {} + 28 | - name: Upload docs artifact 29 | uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # ratchet:actions/upload-pages-artifact@v3 30 | with: 31 | path: generated-docs 32 | - name: Deploy docs 33 | uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # ratchet:actions/deploy-pages@v4 34 | -------------------------------------------------------------------------------- /.github/workflows/run-pre-commit.yaml: -------------------------------------------------------------------------------- 1 | name: Run Pre-commit 2 | 3 | on: 4 | # Run on all PRs 5 | pull_request: 6 | # Run when a PR is merged into main 7 | push: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | run-pre-commit: 13 | name: Run pre-commit 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Check out the repository 17 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 18 | - name: Install Nix 19 | uses: nixbuild/nix-quick-install-action@5bb6a3b3abe66fd09bbf250dce8ada94f856a703 # ratchet:nixbuild/nix-quick-install-action@v30 20 | - name: Cache Nix environment 21 | uses: nix-community/cache-nix-action@135667ec418502fa5a3598af6fb9eb733888ce6a # ratchet:nix-community/cache-nix-action@v6 22 | with: 23 | primary-key: nix-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/*.nix', '**/flake.lock') }} 24 | restore-prefixes-first-match: nix-${{ runner.os }}-${{ runner.arch }} 25 | - name: Use Nix dev shell for subsequent steps 26 | uses: rrbutani/use-nix-shell-action@59a52b2b9bbfe3cc0e7deb8f9059abe37a439edf # ratchet:rrbutani/use-nix-shell-action@v1 27 | with: 28 | extraNixOptions: --accept-flake-config 29 | - name: Run Pre-commit 30 | run: pre-commit run --all-files 31 | env: 32 | # Prevent this action failing when running on the main branch 33 | SKIP: no-commit-to-branch 34 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | # Run on all PRs 5 | pull_request: 6 | paths-ignore: 7 | - "**.md" 8 | # Run when a PR is merged into main 9 | push: 10 | branches: 11 | - main 12 | paths-ignore: 13 | - "**.md" 14 | 15 | jobs: 16 | test: 17 | name: Test 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: write 21 | steps: 22 | - name: Check out the repository 23 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 24 | - name: Install Roc 25 | uses: hasnep/setup-roc@9866c6fdc971ee9f4b3eeba03d825dc32a5efa7f # ratchet:hasnep/setup-roc@v0.5.0 26 | with: 27 | roc-version: 0.0.0-alpha2-rolling 28 | - name: Test the library 29 | run: roc test src/main.roc 30 | -------------------------------------------------------------------------------- /.github/workflows/update-draft-release.yaml: -------------------------------------------------------------------------------- 1 | name: Update draft release 2 | 3 | on: 4 | # Run when a PR is merged into main 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | update-draft-release: 11 | name: Update draft release 12 | permissions: 13 | # Permissions required for creating a GitHub release 14 | contents: write 15 | pull-requests: read 16 | runs-on: ubuntu-latest 17 | outputs: 18 | tag_name: ${{ steps.update-draft-release.outputs.tag_name }} 19 | steps: 20 | - name: Update the draft release 21 | id: update-draft-release 22 | uses: release-drafter/release-drafter@3f0f87098bd6b5c5b9a36d49c41d998ea58f9348 # ratchet:release-drafter/release-drafter@v6 23 | with: 24 | config-name: release-drafter.yaml 25 | disable-autolabeler: true 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | generated-docs/ 2 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | # General checks 3 | - repo: local 4 | hooks: 5 | - name: Prevent committing to main 6 | id: no-commit-to-branch 7 | language: system 8 | entry: no-commit-to-branch 9 | args: [--branch, main] 10 | pass_filenames: false 11 | - name: Make sure files end with a newline character 12 | id: end-of-file-fixer 13 | language: system 14 | entry: end-of-file-fixer 15 | types: [text] 16 | - name: Remove trailing whitespace 17 | id: trailing-whitespace-fixer 18 | language: system 19 | entry: trailing-whitespace-fixer 20 | types: [text] 21 | - name: Check for files that would conflict on case-insensitive filesystem 22 | id: check-case-conflict 23 | language: system 24 | entry: check-case-conflict 25 | - name: Check for merge conflicts 26 | id: check-merge-conflict 27 | language: system 28 | entry: check-merge-conflict 29 | - name: Check executable files have a shebang 30 | id: check-executables-have-shebangs 31 | language: system 32 | entry: check-executables-have-shebangs 33 | types: [executable] 34 | - name: Check scripts with a shebang are executable 35 | id: check-shebang-scripts-are-executable 36 | language: system 37 | entry: check-shebang-scripts-are-executable 38 | - name: Don't allow adding large files 39 | id: check-added-large-files 40 | language: system 41 | entry: check-added-large-files 42 | 43 | # Roc 44 | - repo: https://github.com/hasnep/pre-commit-roc 45 | rev: v0.1.0 46 | hooks: 47 | - name: Lint Roc files 48 | id: check 49 | args: [src/main.roc] 50 | - name: Format Roc files 51 | id: format 52 | 53 | # YAML 54 | - repo: local 55 | hooks: 56 | - name: Format YAML files 57 | id: yaml-format 58 | language: system 59 | entry: prettier --write 60 | types: [yaml] 61 | 62 | # Markdown 63 | - repo: local 64 | hooks: 65 | - name: Format markdown files 66 | id: markdown-format 67 | language: system 68 | entry: prettier --write 69 | types: [markdown] 70 | 71 | # GitHub Actions 72 | - repo: local 73 | hooks: 74 | - name: Validate GitHub Actions workflow files 75 | id: github-workflows-check 76 | language: system 77 | entry: actionlint 78 | types: [yaml] 79 | files: \.github/workflows/.*\.ya?ml$ 80 | - name: Check GitHub Actions are pinned 81 | id: github-workflows-check-pinned 82 | language: system 83 | entry: ratchet check 84 | types: [yaml] 85 | files: \.github/workflows/.*\.ya?ml$ 86 | 87 | # Nix 88 | - repo: local 89 | hooks: 90 | - name: Format Nix files 91 | id: nix-format 92 | language: system 93 | entry: nixfmt 94 | types: [nix] 95 | -------------------------------------------------------------------------------- /Justfile: -------------------------------------------------------------------------------- 1 | default: format check test examples docs 2 | 3 | format: 4 | roc format src/ 5 | roc format examples/ 6 | 7 | check: 8 | roc check src/main.roc 9 | fd --extension roc . examples/ --exec roc check 10 | 11 | test: 12 | roc test src/main.roc 13 | 14 | examples: 15 | fd --extension roc . examples/ --exec roc run 16 | 17 | docs: 18 | roc docs src/main.roc 19 | 20 | ratchet: 21 | ratchet upgrade .github/workflows/*.yaml 22 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Brian Carroll, Luke Boswell, Hannes Smit and Roc contributors 2 | 3 | The Universal Permissive License (UPL), Version 1.0 4 | 5 | Subject to the condition set forth below, permission is hereby granted to any 6 | person obtaining a copy of this software, associated documentation and/or data 7 | (collectively the "Software"), free of charge and under any and all copyright 8 | rights in the Software, and any and all patent rights owned or freely 9 | licensable by each licensor hereunder covering either (i) the unmodified 10 | Software as contributed to or provided by such licensor, or (ii) the Larger 11 | Works (as defined below), to deal in both 12 | 13 | (a) the Software, and 14 | (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 15 | one is included with the Software (each a “Larger Work” to which the Software 16 | is contributed by such licensors), 17 | 18 | without restriction, including without limitation the rights to copy, create 19 | derivative works of, display, perform, and distribute the Software and make, 20 | use, sell, offer for sale, import, export, have made, and have sold the 21 | Software and the Larger Work(s), and to sublicense the foregoing rights on 22 | either these or other terms. 23 | 24 | This license is subject to the following condition: 25 | The above copyright notice and either this complete permission notice or at 26 | a minimum a reference to the UPL must be included in all copies or 27 | substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 35 | SOFTWARE. 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Roc HTML 2 | 3 | A library to create HTML in Roc. 4 | 5 | ## Example 6 | 7 | This Roc code: 8 | 9 | ```roc 10 | Html.html [] [ 11 | Html.body [] [ 12 | Html.h1 [] [Html.text "Roc"], 13 | Html.p [] [ 14 | Html.text "You should really check out ", 15 | Html.a [Attribute.href "https://roc-lang.org/"] [Html.text "Roc"], 16 | Html.text "!", 17 | ] 18 | ] 19 | ] |> Html.render 20 | ``` 21 | 22 | Returns this HTML (give or take a little formatting): 23 | 24 | ```html 25 | 26 | 27 |
28 |You should really check out Roc!
30 | 31 | 32 | ``` 33 | 34 | ## Licence 35 | 36 | This repository is released under the [UPL licence](./LICENCE) and was mostly written by [Brian Carroll](https://github.com/brian-carroll/). Thanks, Brian! 37 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !*.roc 4 | -------------------------------------------------------------------------------- /examples/example.roc: -------------------------------------------------------------------------------- 1 | app [main!] { 2 | cli: platform "https://github.com/roc-lang/basic-cli/releases/download/0.19.0/Hj-J_zxz7V9YurCSTFcFdu6cQJie4guzsPMUi5kBYUk.tar.br", 3 | html: "../src/main.roc", 4 | } 5 | 6 | import cli.Stdout 7 | import html.Html 8 | import html.Attribute 9 | 10 | main! = |_args| 11 | page = Html.html( 12 | [], 13 | [ 14 | Html.body( 15 | [], 16 | [ 17 | Html.h1([], [Html.text("Roc")]), 18 | Html.p( 19 | [], 20 | [ 21 | Html.text("My favourite language is "), 22 | Html.a([Attribute.href("https://roc-lang.org/")], [Html.text("Roc")]), 23 | Html.text("!"), 24 | ], 25 | ), 26 | ], 27 | ), 28 | ], 29 | ) 30 | rendered_html = Html.render(page) 31 | Stdout.line!(rendered_html) 32 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-compat": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1696426674, 7 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 8 | "owner": "edolstra", 9 | "repo": "flake-compat", 10 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "edolstra", 15 | "repo": "flake-compat", 16 | "type": "github" 17 | } 18 | }, 19 | "flake-parts": { 20 | "inputs": { 21 | "nixpkgs-lib": "nixpkgs-lib" 22 | }, 23 | "locked": { 24 | "lastModified": 1736143030, 25 | "narHash": "sha256-+hu54pAoLDEZT9pjHlqL9DNzWz0NbUn8NEAHP7PQPzU=", 26 | "owner": "hercules-ci", 27 | "repo": "flake-parts", 28 | "rev": "b905f6fc23a9051a6e1b741e1438dbfc0634c6de", 29 | "type": "github" 30 | }, 31 | "original": { 32 | "owner": "hercules-ci", 33 | "repo": "flake-parts", 34 | "type": "github" 35 | } 36 | }, 37 | "flake-utils": { 38 | "inputs": { 39 | "systems": "systems" 40 | }, 41 | "locked": { 42 | "lastModified": 1726560853, 43 | "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", 44 | "owner": "numtide", 45 | "repo": "flake-utils", 46 | "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", 47 | "type": "github" 48 | }, 49 | "original": { 50 | "owner": "numtide", 51 | "repo": "flake-utils", 52 | "type": "github" 53 | } 54 | }, 55 | "nixgl": { 56 | "inputs": { 57 | "flake-utils": [ 58 | "roc", 59 | "flake-utils" 60 | ], 61 | "nixpkgs": [ 62 | "roc", 63 | "nixpkgs" 64 | ] 65 | }, 66 | "locked": { 67 | "lastModified": 1713543440, 68 | "narHash": "sha256-lnzZQYG0+EXl/6NkGpyIz+FEOc/DSEG57AP1VsdeNrM=", 69 | "owner": "guibou", 70 | "repo": "nixGL", 71 | "rev": "310f8e49a149e4c9ea52f1adf70cdc768ec53f8a", 72 | "type": "github" 73 | }, 74 | "original": { 75 | "owner": "guibou", 76 | "repo": "nixGL", 77 | "type": "github" 78 | } 79 | }, 80 | "nixpkgs": { 81 | "locked": { 82 | "lastModified": 1736701207, 83 | "narHash": "sha256-jG/+MvjVY7SlTakzZ2fJ5dC3V1PrKKrUEOEE30jrOKA=", 84 | "owner": "nixos", 85 | "repo": "nixpkgs", 86 | "rev": "ed4a395ea001367c1f13d34b1e01aa10290f67d6", 87 | "type": "github" 88 | }, 89 | "original": { 90 | "owner": "nixos", 91 | "ref": "nixos-unstable", 92 | "repo": "nixpkgs", 93 | "type": "github" 94 | } 95 | }, 96 | "nixpkgs-lib": { 97 | "locked": { 98 | "lastModified": 1735774519, 99 | "narHash": "sha256-CewEm1o2eVAnoqb6Ml+Qi9Gg/EfNAxbRx1lANGVyoLI=", 100 | "type": "tarball", 101 | "url": "https://github.com/NixOS/nixpkgs/archive/e9b51731911566bbf7e4895475a87fe06961de0b.tar.gz" 102 | }, 103 | "original": { 104 | "type": "tarball", 105 | "url": "https://github.com/NixOS/nixpkgs/archive/e9b51731911566bbf7e4895475a87fe06961de0b.tar.gz" 106 | } 107 | }, 108 | "nixpkgs_2": { 109 | "locked": { 110 | "lastModified": 1722403750, 111 | "narHash": "sha256-tRmn6UiFAPX0m9G1AVcEPjWEOc9BtGsxGcs7Bz3MpsM=", 112 | "owner": "nixos", 113 | "repo": "nixpkgs", 114 | "rev": "184957277e885c06a505db112b35dfbec7c60494", 115 | "type": "github" 116 | }, 117 | "original": { 118 | "owner": "nixos", 119 | "repo": "nixpkgs", 120 | "rev": "184957277e885c06a505db112b35dfbec7c60494", 121 | "type": "github" 122 | } 123 | }, 124 | "roc": { 125 | "inputs": { 126 | "flake-compat": "flake-compat", 127 | "flake-utils": "flake-utils", 128 | "nixgl": "nixgl", 129 | "nixpkgs": "nixpkgs_2", 130 | "rust-overlay": "rust-overlay" 131 | }, 132 | "locked": { 133 | "lastModified": 1738131757, 134 | "narHash": "sha256-cXWFnT2SlNvc8gRAhlUNakVGWii3VWZsvNijMRpX7XA=", 135 | "owner": "roc-lang", 136 | "repo": "roc", 137 | "rev": "689c58f35e0a39ca59feba549f7fcf375562a7a6", 138 | "type": "github" 139 | }, 140 | "original": { 141 | "owner": "roc-lang", 142 | "ref": "0.0.0-alpha2-rolling", 143 | "repo": "roc", 144 | "type": "github" 145 | } 146 | }, 147 | "root": { 148 | "inputs": { 149 | "flake-parts": "flake-parts", 150 | "nixpkgs": "nixpkgs", 151 | "roc": "roc" 152 | } 153 | }, 154 | "rust-overlay": { 155 | "inputs": { 156 | "nixpkgs": [ 157 | "roc", 158 | "nixpkgs" 159 | ] 160 | }, 161 | "locked": { 162 | "lastModified": 1727490462, 163 | "narHash": "sha256-OrrPiNBiikv9BR464XTT75FzOq7tKAvMbMi7YOKVIeg=", 164 | "owner": "oxalica", 165 | "repo": "rust-overlay", 166 | "rev": "11a13e50debafae4ae802f1d6b8585101516dd93", 167 | "type": "github" 168 | }, 169 | "original": { 170 | "owner": "oxalica", 171 | "repo": "rust-overlay", 172 | "type": "github" 173 | } 174 | }, 175 | "systems": { 176 | "locked": { 177 | "lastModified": 1681028828, 178 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 179 | "owner": "nix-systems", 180 | "repo": "default", 181 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 182 | "type": "github" 183 | }, 184 | "original": { 185 | "owner": "nix-systems", 186 | "repo": "default", 187 | "type": "github" 188 | } 189 | } 190 | }, 191 | "root": "root", 192 | "version": 7 193 | } 194 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 4 | flake-parts.url = "github:hercules-ci/flake-parts"; 5 | roc.url = "github:roc-lang/roc/0.0.0-alpha2-rolling"; 6 | }; 7 | 8 | nixConfig = { 9 | extra-trusted-public-keys = "roc-lang.cachix.org-1:6lZeqLP9SadjmUbskJAvcdGR2T5ViR57pDVkxJQb8R4="; 10 | extra-trusted-substituters = "https://roc-lang.cachix.org"; 11 | }; 12 | 13 | outputs = 14 | inputs@{ 15 | self, 16 | nixpkgs, 17 | flake-parts, 18 | roc, 19 | ... 20 | }: 21 | flake-parts.lib.mkFlake { inherit inputs; } { 22 | systems = [ 23 | "aarch64-darwin" 24 | "aarch64-linux" 25 | "x86_64-darwin" 26 | "x86_64-linux" 27 | ]; 28 | perSystem = 29 | { inputs', pkgs, ... }: 30 | { 31 | devShells.default = pkgs.mkShell { 32 | name = "roc-html"; 33 | packages = [ 34 | inputs'.roc.packages.cli 35 | pkgs.actionlint 36 | pkgs.check-jsonschema 37 | pkgs.fd 38 | pkgs.just 39 | pkgs.nixfmt-rfc-style 40 | pkgs.nodePackages.prettier 41 | pkgs.pre-commit 42 | pkgs.python312Packages.pre-commit-hooks 43 | pkgs.ratchet 44 | ]; 45 | shellHook = "pre-commit install --overwrite"; 46 | }; 47 | formatter = pkgs.nixfmt-rfc-style; 48 | }; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /src/Attribute.roc: -------------------------------------------------------------------------------- 1 | module [ 2 | Attribute, 3 | attribute, 4 | accept, 5 | accept_charset, 6 | accesskey, 7 | action, 8 | align, 9 | allow, 10 | alt, 11 | async, 12 | autocapitalize, 13 | autocomplete, 14 | autofocus, 15 | autoplay, 16 | background, 17 | bgcolor, 18 | border, 19 | buffered, 20 | capture, 21 | challenge, 22 | charset, 23 | checked, 24 | cite, 25 | class, 26 | code, 27 | codebase, 28 | color, 29 | cols, 30 | colspan, 31 | content, 32 | contenteditable, 33 | contextmenu, 34 | controls, 35 | coords, 36 | crossorigin, 37 | csp, 38 | data, 39 | datetime, 40 | decoding, 41 | default, 42 | defer, 43 | dir, 44 | dirname, 45 | disabled, 46 | download, 47 | draggable, 48 | enctype, 49 | enterkeyhint, 50 | for, 51 | form, 52 | formaction, 53 | formenctype, 54 | formmethod, 55 | formnovalidate, 56 | formtarget, 57 | headers, 58 | height, 59 | hidden, 60 | high, 61 | href, 62 | hreflang, 63 | http_equiv, 64 | icon, 65 | id, 66 | importance, 67 | inputmode, 68 | integrity, 69 | intrinsicsize, 70 | ismap, 71 | itemprop, 72 | keytype, 73 | kind, 74 | label, 75 | lang, 76 | language, 77 | list, 78 | loading, 79 | loop, 80 | low, 81 | manifest, 82 | max, 83 | maxlength, 84 | media, 85 | method, 86 | min, 87 | minlength, 88 | multiple, 89 | muted, 90 | name, 91 | novalidate, 92 | open, 93 | optimum, 94 | pattern, 95 | ping, 96 | placeholder, 97 | poster, 98 | preload, 99 | radiogroup, 100 | readonly, 101 | referrerpolicy, 102 | rel, 103 | required, 104 | reversed, 105 | role, 106 | rows, 107 | rowspan, 108 | sandbox, 109 | scope, 110 | scoped, 111 | selected, 112 | shape, 113 | size, 114 | sizes, 115 | slot, 116 | span, 117 | spellcheck, 118 | src, 119 | srcdoc, 120 | srclang, 121 | srcset, 122 | start, 123 | step, 124 | style, 125 | summary, 126 | tabindex, 127 | target, 128 | title, 129 | translate, 130 | type, 131 | usemap, 132 | value, 133 | width, 134 | wrap, 135 | ] 136 | 137 | Attribute : [Attribute Str Str] 138 | 139 | ## Define a non-standard attribute. 140 | ## You can use this to add attributes that are not already supported. 141 | attribute : Str -> (Str -> Attribute) 142 | attribute = |attr_name| 143 | |attr_value| Attribute(attr_name, attr_value) 144 | 145 | ## Construct a `accept` attribute. 146 | accept : Str -> Attribute 147 | accept = attribute("accept") 148 | 149 | ## Construct a `accept-charset` attribute. 150 | accept_charset : Str -> Attribute 151 | accept_charset = attribute("accept-charset") 152 | 153 | ## Construct a `accesskey` attribute. 154 | accesskey : Str -> Attribute 155 | accesskey = attribute("accesskey") 156 | 157 | ## Construct a `action` attribute. 158 | action : Str -> Attribute 159 | action = attribute("action") 160 | 161 | ## Construct a `align` attribute. 162 | align : Str -> Attribute 163 | align = attribute("align") 164 | 165 | ## Construct a `allow` attribute. 166 | allow : Str -> Attribute 167 | allow = attribute("allow") 168 | 169 | ## Construct a `alt` attribute. 170 | alt : Str -> Attribute 171 | alt = attribute("alt") 172 | 173 | ## Construct a `async` attribute. 174 | async : Str -> Attribute 175 | async = attribute("async") 176 | 177 | ## Construct a `autocapitalize` attribute. 178 | autocapitalize : Str -> Attribute 179 | autocapitalize = attribute("autocapitalize") 180 | 181 | ## Construct a `autocomplete` attribute. 182 | autocomplete : Str -> Attribute 183 | autocomplete = attribute("autocomplete") 184 | 185 | ## Construct a `autofocus` attribute. 186 | autofocus : Str -> Attribute 187 | autofocus = attribute("autofocus") 188 | 189 | ## Construct a `autoplay` attribute. 190 | autoplay : Str -> Attribute 191 | autoplay = attribute("autoplay") 192 | 193 | ## Construct a `background` attribute. 194 | background : Str -> Attribute 195 | background = attribute("background") 196 | 197 | ## Construct a `bgcolor` attribute. 198 | bgcolor : Str -> Attribute 199 | bgcolor = attribute("bgcolor") 200 | 201 | ## Construct a `border` attribute. 202 | border : Str -> Attribute 203 | border = attribute("border") 204 | 205 | ## Construct a `buffered` attribute. 206 | buffered : Str -> Attribute 207 | buffered = attribute("buffered") 208 | 209 | ## Construct a `capture` attribute. 210 | capture : Str -> Attribute 211 | capture = attribute("capture") 212 | 213 | ## Construct a `challenge` attribute. 214 | challenge : Str -> Attribute 215 | challenge = attribute("challenge") 216 | 217 | ## Construct a `charset` attribute. 218 | charset : Str -> Attribute 219 | charset = attribute("charset") 220 | 221 | ## Construct a `checked` attribute. 222 | checked : Str -> Attribute 223 | checked = attribute("checked") 224 | 225 | ## Construct a `cite` attribute. 226 | cite : Str -> Attribute 227 | cite = attribute("cite") 228 | 229 | ## Construct a `class` attribute. 230 | class : Str -> Attribute 231 | class = attribute("class") 232 | 233 | ## Construct a `code` attribute. 234 | code : Str -> Attribute 235 | code = attribute("code") 236 | 237 | ## Construct a `codebase` attribute. 238 | codebase : Str -> Attribute 239 | codebase = attribute("codebase") 240 | 241 | ## Construct a `color` attribute. 242 | color : Str -> Attribute 243 | color = attribute("color") 244 | 245 | ## Construct a `cols` attribute. 246 | cols : Str -> Attribute 247 | cols = attribute("cols") 248 | 249 | ## Construct a `colspan` attribute. 250 | colspan : Str -> Attribute 251 | colspan = attribute("colspan") 252 | 253 | ## Construct a `content` attribute. 254 | content : Str -> Attribute 255 | content = attribute("content") 256 | 257 | ## Construct a `contenteditable` attribute. 258 | contenteditable : Str -> Attribute 259 | contenteditable = attribute("contenteditable") 260 | 261 | ## Construct a `contextmenu` attribute. 262 | contextmenu : Str -> Attribute 263 | contextmenu = attribute("contextmenu") 264 | 265 | ## Construct a `controls` attribute. 266 | controls : Str -> Attribute 267 | controls = attribute("controls") 268 | 269 | ## Construct a `coords` attribute. 270 | coords : Str -> Attribute 271 | coords = attribute("coords") 272 | 273 | ## Construct a `crossorigin` attribute. 274 | crossorigin : Str -> Attribute 275 | crossorigin = attribute("crossorigin") 276 | 277 | ## Construct a `csp` attribute. 278 | csp : Str -> Attribute 279 | csp = attribute("csp") 280 | 281 | ## Construct a `data` attribute. 282 | data : Str -> Attribute 283 | data = attribute("data") 284 | 285 | ## Construct a `datetime` attribute. 286 | datetime : Str -> Attribute 287 | datetime = attribute("datetime") 288 | 289 | ## Construct a `decoding` attribute. 290 | decoding : Str -> Attribute 291 | decoding = attribute("decoding") 292 | 293 | ## Construct a `default` attribute. 294 | default : Str -> Attribute 295 | default = attribute("default") 296 | 297 | ## Construct a `defer` attribute. 298 | defer : Str -> Attribute 299 | defer = attribute("defer") 300 | 301 | ## Construct a `dir` attribute. 302 | dir : Str -> Attribute 303 | dir = attribute("dir") 304 | 305 | ## Construct a `dirname` attribute. 306 | dirname : Str -> Attribute 307 | dirname = attribute("dirname") 308 | 309 | ## Construct a `disabled` attribute. 310 | disabled : Str -> Attribute 311 | disabled = attribute("disabled") 312 | 313 | ## Construct a `download` attribute. 314 | download : Str -> Attribute 315 | download = attribute("download") 316 | 317 | ## Construct a `draggable` attribute. 318 | draggable : Str -> Attribute 319 | draggable = attribute("draggable") 320 | 321 | ## Construct a `enctype` attribute. 322 | enctype : Str -> Attribute 323 | enctype = attribute("enctype") 324 | 325 | ## Construct a `enterkeyhint` attribute. 326 | enterkeyhint : Str -> Attribute 327 | enterkeyhint = attribute("enterkeyhint") 328 | 329 | ## Construct a `for` attribute. 330 | for : Str -> Attribute 331 | for = attribute("for") 332 | 333 | ## Construct a `form` attribute. 334 | form : Str -> Attribute 335 | form = attribute("form") 336 | 337 | ## Construct a `formaction` attribute. 338 | formaction : Str -> Attribute 339 | formaction = attribute("formaction") 340 | 341 | ## Construct a `formenctype` attribute. 342 | formenctype : Str -> Attribute 343 | formenctype = attribute("formenctype") 344 | 345 | ## Construct a `formmethod` attribute. 346 | formmethod : Str -> Attribute 347 | formmethod = attribute("formmethod") 348 | 349 | ## Construct a `formnovalidate` attribute. 350 | formnovalidate : Str -> Attribute 351 | formnovalidate = attribute("formnovalidate") 352 | 353 | ## Construct a `formtarget` attribute. 354 | formtarget : Str -> Attribute 355 | formtarget = attribute("formtarget") 356 | 357 | ## Construct a `headers` attribute. 358 | headers : Str -> Attribute 359 | headers = attribute("headers") 360 | 361 | ## Construct a `height` attribute. 362 | height : Str -> Attribute 363 | height = attribute("height") 364 | 365 | ## Construct a `hidden` attribute. 366 | hidden : Str -> Attribute 367 | hidden = attribute("hidden") 368 | 369 | ## Construct a `high` attribute. 370 | high : Str -> Attribute 371 | high = attribute("high") 372 | 373 | ## Construct a `href` attribute. 374 | href : Str -> Attribute 375 | href = attribute("href") 376 | 377 | ## Construct a `hreflang` attribute. 378 | hreflang : Str -> Attribute 379 | hreflang = attribute("hreflang") 380 | 381 | ## Construct a `http-equiv` attribute. 382 | http_equiv : Str -> Attribute 383 | http_equiv = attribute("http-equiv") 384 | 385 | ## Construct a `icon` attribute. 386 | icon : Str -> Attribute 387 | icon = attribute("icon") 388 | 389 | ## Construct a `id` attribute. 390 | id : Str -> Attribute 391 | id = attribute("id") 392 | 393 | ## Construct a `importance` attribute. 394 | importance : Str -> Attribute 395 | importance = attribute("importance") 396 | 397 | ## Construct a `inputmode` attribute. 398 | inputmode : Str -> Attribute 399 | inputmode = attribute("inputmode") 400 | 401 | ## Construct a `integrity` attribute. 402 | integrity : Str -> Attribute 403 | integrity = attribute("integrity") 404 | 405 | ## Construct a `intrinsicsize` attribute. 406 | intrinsicsize : Str -> Attribute 407 | intrinsicsize = attribute("intrinsicsize") 408 | 409 | ## Construct a `ismap` attribute. 410 | ismap : Str -> Attribute 411 | ismap = attribute("ismap") 412 | 413 | ## Construct a `itemprop` attribute. 414 | itemprop : Str -> Attribute 415 | itemprop = attribute("itemprop") 416 | 417 | ## Construct a `keytype` attribute. 418 | keytype : Str -> Attribute 419 | keytype = attribute("keytype") 420 | 421 | ## Construct a `kind` attribute. 422 | kind : Str -> Attribute 423 | kind = attribute("kind") 424 | 425 | ## Construct a `label` attribute. 426 | label : Str -> Attribute 427 | label = attribute("label") 428 | 429 | ## Construct a `lang` attribute. 430 | lang : Str -> Attribute 431 | lang = attribute("lang") 432 | 433 | ## Construct a `language` attribute. 434 | language : Str -> Attribute 435 | language = attribute("language") 436 | 437 | ## Construct a `list` attribute. 438 | list : Str -> Attribute 439 | list = attribute("list") 440 | 441 | ## Construct a `loading` attribute. 442 | loading : Str -> Attribute 443 | loading = attribute("loading") 444 | 445 | ## Construct a `loop` attribute. 446 | loop : Str -> Attribute 447 | loop = attribute("loop") 448 | 449 | ## Construct a `low` attribute. 450 | low : Str -> Attribute 451 | low = attribute("low") 452 | 453 | ## Construct a `manifest` attribute. 454 | manifest : Str -> Attribute 455 | manifest = attribute("manifest") 456 | 457 | ## Construct a `max` attribute. 458 | max : Str -> Attribute 459 | max = attribute("max") 460 | 461 | ## Construct a `maxlength` attribute. 462 | maxlength : Str -> Attribute 463 | maxlength = attribute("maxlength") 464 | 465 | ## Construct a `media` attribute. 466 | media : Str -> Attribute 467 | media = attribute("media") 468 | 469 | ## Construct a `method` attribute. 470 | method : Str -> Attribute 471 | method = attribute("method") 472 | 473 | ## Construct a `min` attribute. 474 | min : Str -> Attribute 475 | min = attribute("min") 476 | 477 | ## Construct a `minlength` attribute. 478 | minlength : Str -> Attribute 479 | minlength = attribute("minlength") 480 | 481 | ## Construct a `multiple` attribute. 482 | multiple : Str -> Attribute 483 | multiple = attribute("multiple") 484 | 485 | ## Construct a `muted` attribute. 486 | muted : Str -> Attribute 487 | muted = attribute("muted") 488 | 489 | ## Construct a `name` attribute. 490 | name : Str -> Attribute 491 | name = attribute("name") 492 | 493 | ## Construct a `novalidate` attribute. 494 | novalidate : Str -> Attribute 495 | novalidate = attribute("novalidate") 496 | 497 | ## Construct a `open` attribute. 498 | open : Str -> Attribute 499 | open = attribute("open") 500 | 501 | ## Construct a `optimum` attribute. 502 | optimum : Str -> Attribute 503 | optimum = attribute("optimum") 504 | 505 | ## Construct a `pattern` attribute. 506 | pattern : Str -> Attribute 507 | pattern = attribute("pattern") 508 | 509 | ## Construct a `ping` attribute. 510 | ping : Str -> Attribute 511 | ping = attribute("ping") 512 | 513 | ## Construct a `placeholder` attribute. 514 | placeholder : Str -> Attribute 515 | placeholder = attribute("placeholder") 516 | 517 | ## Construct a `poster` attribute. 518 | poster : Str -> Attribute 519 | poster = attribute("poster") 520 | 521 | ## Construct a `preload` attribute. 522 | preload : Str -> Attribute 523 | preload = attribute("preload") 524 | 525 | ## Construct a `radiogroup` attribute. 526 | radiogroup : Str -> Attribute 527 | radiogroup = attribute("radiogroup") 528 | 529 | ## Construct a `readonly` attribute. 530 | readonly : Str -> Attribute 531 | readonly = attribute("readonly") 532 | 533 | ## Construct a `referrerpolicy` attribute. 534 | referrerpolicy : Str -> Attribute 535 | referrerpolicy = attribute("referrerpolicy") 536 | 537 | ## Construct a `rel` attribute. 538 | rel : Str -> Attribute 539 | rel = attribute("rel") 540 | 541 | ## Construct a `required` attribute. 542 | required : Str -> Attribute 543 | required = attribute("required") 544 | 545 | ## Construct a `reversed` attribute. 546 | reversed : Str -> Attribute 547 | reversed = attribute("reversed") 548 | 549 | ## Construct a `role` attribute. 550 | role : Str -> Attribute 551 | role = attribute("role") 552 | 553 | ## Construct a `rows` attribute. 554 | rows : Str -> Attribute 555 | rows = attribute("rows") 556 | 557 | ## Construct a `rowspan` attribute. 558 | rowspan : Str -> Attribute 559 | rowspan = attribute("rowspan") 560 | 561 | ## Construct a `sandbox` attribute. 562 | sandbox : Str -> Attribute 563 | sandbox = attribute("sandbox") 564 | 565 | ## Construct a `scope` attribute. 566 | scope : Str -> Attribute 567 | scope = attribute("scope") 568 | 569 | ## Construct a `scoped` attribute. 570 | scoped : Str -> Attribute 571 | scoped = attribute("scoped") 572 | 573 | ## Construct a `selected` attribute. 574 | selected : Str -> Attribute 575 | selected = attribute("selected") 576 | 577 | ## Construct a `shape` attribute. 578 | shape : Str -> Attribute 579 | shape = attribute("shape") 580 | 581 | ## Construct a `size` attribute. 582 | size : Str -> Attribute 583 | size = attribute("size") 584 | 585 | ## Construct a `sizes` attribute. 586 | sizes : Str -> Attribute 587 | sizes = attribute("sizes") 588 | 589 | ## Construct a `slot` attribute. 590 | slot : Str -> Attribute 591 | slot = attribute("slot") 592 | 593 | ## Construct a `span` attribute. 594 | span : Str -> Attribute 595 | span = attribute("span") 596 | 597 | ## Construct a `spellcheck` attribute. 598 | spellcheck : Str -> Attribute 599 | spellcheck = attribute("spellcheck") 600 | 601 | ## Construct a `src` attribute. 602 | src : Str -> Attribute 603 | src = attribute("src") 604 | 605 | ## Construct a `srcdoc` attribute. 606 | srcdoc : Str -> Attribute 607 | srcdoc = attribute("srcdoc") 608 | 609 | ## Construct a `srclang` attribute. 610 | srclang : Str -> Attribute 611 | srclang = attribute("srclang") 612 | 613 | ## Construct a `srcset` attribute. 614 | srcset : Str -> Attribute 615 | srcset = attribute("srcset") 616 | 617 | ## Construct a `start` attribute. 618 | start : Str -> Attribute 619 | start = attribute("start") 620 | 621 | ## Construct a `step` attribute. 622 | step : Str -> Attribute 623 | step = attribute("step") 624 | 625 | ## Construct a `style` attribute. 626 | style : Str -> Attribute 627 | style = attribute("style") 628 | 629 | ## Construct a `summary` attribute. 630 | summary : Str -> Attribute 631 | summary = attribute("summary") 632 | 633 | ## Construct a `tabindex` attribute. 634 | tabindex : Str -> Attribute 635 | tabindex = attribute("tabindex") 636 | 637 | ## Construct a `target` attribute. 638 | target : Str -> Attribute 639 | target = attribute("target") 640 | 641 | ## Construct a `title` attribute. 642 | title : Str -> Attribute 643 | title = attribute("title") 644 | 645 | ## Construct a `translate` attribute. 646 | translate : Str -> Attribute 647 | translate = attribute("translate") 648 | 649 | ## Construct a `type` attribute. 650 | type : Str -> Attribute 651 | type = attribute("type") 652 | 653 | ## Construct a `usemap` attribute. 654 | usemap : Str -> Attribute 655 | usemap = attribute("usemap") 656 | 657 | ## Construct a `value` attribute. 658 | value : Str -> Attribute 659 | value = attribute("value") 660 | 661 | ## Construct a `width` attribute. 662 | width : Str -> Attribute 663 | width = attribute("width") 664 | 665 | ## Construct a `wrap` attribute. 666 | wrap : Str -> Attribute 667 | wrap = attribute("wrap") 668 | -------------------------------------------------------------------------------- /src/Html.roc: -------------------------------------------------------------------------------- 1 | module [ 2 | Node, 3 | text, 4 | element, 5 | void_element, 6 | render, 7 | render_without_doc_type, 8 | dangerously_include_unescaped_html, 9 | # Content sectioning 10 | address, 11 | article, 12 | aside, 13 | footer, 14 | h1, 15 | h2, 16 | h3, 17 | h4, 18 | h5, 19 | h6, 20 | header, 21 | main, 22 | nav, 23 | section, 24 | # Demarcating edits 25 | del, 26 | ins, 27 | # Document metadata 28 | base, 29 | head, 30 | link, 31 | meta, 32 | style, 33 | title, 34 | # Embedded content 35 | embed, 36 | iframe, 37 | object, 38 | picture, 39 | portal, 40 | source, 41 | # Forms 42 | button, 43 | datalist, 44 | fieldset, 45 | form, 46 | input, 47 | label, 48 | legend, 49 | meter, 50 | optgroup, 51 | option, 52 | output, 53 | progress, 54 | select, 55 | textarea, 56 | # Image and multimedia 57 | area, 58 | audio, 59 | img, 60 | map, 61 | track, 62 | video, 63 | # Inline text semantics 64 | a, 65 | abbr, 66 | b, 67 | bdi, 68 | bdo, 69 | br, 70 | cite, 71 | code, 72 | data, 73 | dfn, 74 | em, 75 | i, 76 | kbd, 77 | mark, 78 | q, 79 | rp, 80 | rt, 81 | ruby, 82 | s, 83 | samp, 84 | small, 85 | span, 86 | strong, 87 | sub, 88 | sup, 89 | time, 90 | u, 91 | var, 92 | wbr, 93 | # Interactive elements 94 | details, 95 | dialog, 96 | summary, 97 | # Main root 98 | html, 99 | # SVG and MathML 100 | math, 101 | svg, 102 | # Scripting 103 | canvas, 104 | noscript, 105 | script, 106 | # Sectioning root 107 | body, 108 | # Table content 109 | caption, 110 | col, 111 | colgroup, 112 | table, 113 | tbody, 114 | td, 115 | tfoot, 116 | th, 117 | thead, 118 | tr, 119 | # Text content 120 | blockquote, 121 | dd, 122 | div, 123 | dl, 124 | dt, 125 | figcaption, 126 | figure, 127 | hr, 128 | li, 129 | menu, 130 | ol, 131 | p, 132 | pre, 133 | ul, 134 | # Web components 135 | slot, 136 | template, 137 | ] 138 | 139 | import Attribute exposing [Attribute, attribute] 140 | import SafeStr exposing [SafeStr, escape, dangerously_mark_safe] 141 | 142 | ## An HTML node, either an HTML element or some text inside an HTML element. 143 | Node : [Element Str U64 (List Attribute) (List Node), Text Str, UnescapedHtml Str] 144 | 145 | ## Create a `Text` node containing a string. 146 | ## 147 | ## The string will be escaped so that it's safely rendered as a text node even if the string contains HTML tags. 148 | ## 149 | ## ``` 150 | ## expect 151 | ## textNode = Html.text("") 152 | ## Html.render_without_doc_type(textNode) == "<script>alert('hi')</script>" 153 | ## ``` 154 | text : Str -> Node 155 | text = Text 156 | 157 | expect 158 | text_node = text("") 159 | render_without_doc_type(text_node) == "<script>alert('hi')</script>" 160 | 161 | ## Mark a string as safe for HTML without actually escaping it. 162 | ## 163 | ## DO NOT use this function unless you're sure the input string is safe. 164 | ## 165 | ## NEVER use this function on user input; use the `text` function instead. 166 | ## 167 | ## ``` 168 | ## expect 169 | ## htmlNode = Html.dangerously_include_unescaped_html("") 170 | ## Html.render_without_doc_type(htmlNode) == "" 171 | ## ``` 172 | dangerously_include_unescaped_html : Str -> Node 173 | dangerously_include_unescaped_html = UnescapedHtml 174 | 175 | expect 176 | html_node = dangerously_include_unescaped_html("") 177 | render_without_doc_type(html_node) == "" 178 | 179 | ## Define a non-standard HTML element. 180 | ## You can use this to add elements that are not already supported. 181 | ## 182 | ## For example, you could bring back the obsolete