{"id":20311013,"url":"https://github.com/dillonkearns/elm-markdown","last_synced_at":"2025-04-04T13:10:01.298Z","repository":{"id":40329723,"uuid":"211747963","full_name":"dillonkearns/elm-markdown","owner":"dillonkearns","description":"Extensible markdown parser with custom rendering, in pure Elm.","archived":false,"fork":false,"pushed_at":"2024-11-03T16:47:13.000Z","size":2060,"stargazers_count":109,"open_issues_count":37,"forks_count":32,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-28T12:06:21.711Z","etag":null,"topics":["hacktoberfest"],"latest_commit_sha":null,"homepage":"https://package.elm-lang.org/packages/dillonkearns/elm-markdown/latest/","language":"Elm","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dillonkearns.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["dillonkearns"]}},"created_at":"2019-09-30T00:59:05.000Z","updated_at":"2025-01-23T18:21:49.000Z","dependencies_parsed_at":"2024-06-19T05:22:44.734Z","dependency_job_id":"fca3bd3d-952e-42ca-9cee-7aa5ec1b61e5","html_url":"https://github.com/dillonkearns/elm-markdown","commit_stats":{"total_commits":821,"total_committers":20,"mean_commits":41.05,"dds":"0.28989037758830694","last_synced_commit":"c086499af0cf12b77c793dfbfa9cf39fcd26d056"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dillonkearns%2Felm-markdown","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dillonkearns%2Felm-markdown/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dillonkearns%2Felm-markdown/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dillonkearns%2Felm-markdown/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dillonkearns","download_url":"https://codeload.github.com/dillonkearns/elm-markdown/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247182338,"owners_count":20897379,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hacktoberfest"],"created_at":"2024-11-14T17:35:25.466Z","updated_at":"2025-04-04T13:10:01.274Z","avatar_url":"https://github.com/dillonkearns.png","language":"Elm","funding_links":["https://github.com/sponsors/dillonkearns"],"categories":[],"sub_categories":[],"readme":"# elm-markdown\n\n[![All Contributors](https://img.shields.io/badge/all_contributors-3-orange.svg?style=flat-square)](#contributors)\n![Build Status](https://github.com/dillonkearns/elm-markdown/workflows/Elm%20CI/badge.svg) [![Elm package](https://img.shields.io/elm-package/v/dillonkearns/elm-markdown.svg)](https://package.elm-lang.org/packages/dillonkearns/elm-markdown/latest/)\n\nExtensible markdown parsing in pure elm.\n\nThis library extends the basic markdown blocks without actually adding features to the syntax.\nIt simply provides a declarative way to map certain HTML tags to your Elm view functions to render them. For example,\n\n```html\n\u003cbio\n  name=\"Dillon Kearns\"\n  photo=\"https://avatars2.githubusercontent.com/u/1384166\"\n  twitter=\"dillontkearns\"\n  github=\"dillonkearns\"\n\u003e\n  Dillon really likes building things with Elm! Here are some links -\n  [Articles](https://incrementalelm.com/articles)\n\u003c/bio\u003e\n```\n\nAnd you wire up your Elm rendering function like this\n\n```elm\nMarkdown.Html.oneOf\n  [ Markdown.Html.tag \"bio\"\n    (\\name photoUrl twitter github dribbble renderedChildren -\u003e\n      bioView renderedChildren name photoUrl twitter github dribbble\n    )\n    |\u003e Markdown.Html.withAttribute \"name\"\n    |\u003e Markdown.Html.withAttribute \"photo\"\n    |\u003e Markdown.Html.withOptionalAttribute \"twitter\"\n    |\u003e Markdown.Html.withOptionalAttribute \"github\"\n    |\u003e Markdown.Html.withOptionalAttribute \"dribbble\"\n  ]\n```\n\nNote that it gets the rendered children as an argument. This is rendering the inner contents of the HTML tag using\nyour HTML renderer, so you get all of your rendered lists, code blocks, links, etc. within your tag. You can try a [live Ellie demo of this code snippet](https://ellie-app.com/cHB3fRSKVRha1).\n\n## Live Code Demos\n\n- [Custom HTML Block Rendering (with `elm-ui`)](https://ellie-app.com/d7R3b9FsHfCa1)\n- [Extracting a table of contents from the parsed Markdown](https://ellie-app.com/cHB3fRSKVRha1)\n- [Running the built-in, standard markdown HTML renderer](https://ellie-app.com/f4FsH8bHsC6a1)\n- [Live Lisp evaluation, with values propogating through multiple Markdown HTML blocks](https://bburdette.github.io/cellme/mdcelldemo.html) - check out the source code at [github.com/bburdette/cellme/blob/master/examples/src/MdMain.elm](https://github.com/bburdette/cellme/blob/master/examples/src/MdMain.elm)\n\n## Core features\n\n### Custom Renderers\n\nYou define your own custom renderer, turning your markdown content into any data type with totally customizable logic. You can even pass back an `Err` to get custom failures (for example, broken links or validations like headings that are too long)!\n\nHere's a snippet from the default HTML renderer that comes built in to give you a sense of how you define a `Renderer`:\n\n```elm\nimport Html exposing (Html)\nimport Html.Attributes as Attr\nimport Markdown.Block as Block exposing (Block)\nimport Markdown.Html\n\ndefaultHtmlRenderer : Renderer (Html msg)\ndefaultHtmlRenderer =\n    { heading =\n        \\{ level, children } -\u003e\n            case level of\n                Block.H1 -\u003e\n                    Html.h1 [] children\n\n                Block.H2 -\u003e\n                    Html.h2 [] children\n\n                Block.H3 -\u003e\n                    Html.h3 [] children\n\n                Block.H4 -\u003e\n                    Html.h4 [] children\n\n                Block.H5 -\u003e\n                    Html.h5 [] children\n\n                Block.H6 -\u003e\n                    Html.h6 [] children\n    , paragraph = Html.p []\n    , hardLineBreak = Html.br [] []\n    , blockQuote = Html.blockquote []\n    , strong =\n        \\children -\u003e Html.strong [] children\n    , emphasis =\n        \\children -\u003e Html.em [] children\n    , codeSpan =\n        \\content -\u003e Html.code [] [ Html.text content ]\n    , link =\n        \\link content -\u003e\n            case link.title of\n                Just title -\u003e\n                    Html.a\n                        [ Attr.href link.destination\n                        , Attr.title title\n                        ]\n                        content\n\n                Nothing -\u003e\n                    Html.a [ Attr.href link.destination ] content\n    , image =\n        \\imageInfo -\u003e\n            case imageInfo.title of\n                Just title -\u003e\n                    Html.img\n                        [ Attr.src imageInfo.src\n                        , Attr.alt imageInfo.alt\n                        , Attr.title title\n                        ]\n                        []\n\n                Nothing -\u003e\n                    Html.img\n                        [ Attr.src imageInfo.src\n                        , Attr.alt imageInfo.alt\n                        ]\n                        []\n    , text =\n        Html.text\n    , unorderedList =\n        \\items -\u003e\n            Html.ul []\n                (items\n                    |\u003e List.map\n                        (\\item -\u003e\n                            case item of\n                                Block.ListItem task children -\u003e\n                                    let\n                                        checkbox =\n                                            case task of\n                                                Block.NoTask -\u003e\n                                                    Html.text \"\"\n\n                                                Block.IncompleteTask -\u003e\n                                                    Html.input\n                                                        [ Attr.disabled True\n                                                        , Attr.checked False\n                                                        , Attr.type_ \"checkbox\"\n                                                        ]\n                                                        []\n\n                                                Block.CompletedTask -\u003e\n                                                    Html.input\n                                                        [ Attr.disabled True\n                                                        , Attr.checked True\n                                                        , Attr.type_ \"checkbox\"\n                                                        ]\n                                                        []\n                                    in\n                                    Html.li [] (checkbox :: children)\n                        )\n                )\n    , orderedList =\n        \\startingIndex items -\u003e\n            Html.ol\n                (case startingIndex of\n                    1 -\u003e\n                        [ Attr.start startingIndex ]\n\n                    _ -\u003e\n                        []\n                )\n                (items\n                    |\u003e List.map\n                        (\\itemBlocks -\u003e\n                            Html.li []\n                                itemBlocks\n                        )\n                )\n    , html = Markdown.Html.oneOf []\n    , codeBlock =\n        \\block -\u003e\n            Html.pre []\n                [ Html.code []\n                    [ Html.text block.body\n                    ]\n                ]\n    , thematicBreak = Html.hr [] []\n    , table = Html.table []\n    , tableHeader = Html.thead []\n    , tableBody = Html.tbody []\n    , tableRow = Html.tr []\n    , tableHeaderCell =\n        \\maybeAlignment -\u003e\n            let\n                attrs =\n                    maybeAlignment\n                        |\u003e Maybe.map\n                            (\\alignment -\u003e\n                                case alignment of\n                                    Block.AlignLeft -\u003e\n                                        \"left\"\n\n                                    Block.AlignCenter -\u003e\n                                        \"center\"\n\n                                    Block.AlignRight -\u003e\n                                        \"right\"\n                            )\n                        |\u003e Maybe.map Attr.align\n                        |\u003e Maybe.map List.singleton\n                        |\u003e Maybe.withDefault []\n            in\n            Html.th attrs\n    , tableCell = \\_ children -\u003e Html.td [] children\n    , strikethrough = Html.span [ Attr.style \"text-decoration-line\" \"line-through\" ]\n    }\n\n```\n\n\n### Markdown Block Transformation\n\nYou get full access to the parsed markdown blocks before passing it to a renderer. That means that you can inspect it, do custom logic on it, perform validations, or even go in and transform it! It's totally customizable, and of course it's all just nice Elm custom types!\n\n[Here's a live Ellie example](https://ellie-app.com/6QtYW8pcCDna1) that transforms the AST into a table of contents and renders a `TOC` data type along with the rendered markdown.\n\n## Philosophy \u0026 Goals\n\n- Render markdown to any type (`Html`, `elm-ui` `Element`s, `String`s representing ANSI color codes for terminal output... or even a function, allowing you to inject dynamic values into your markdown view)\n- Extend markdown without adding to the syntax using custom HTML renderers, and fail explicitly for unexpected HTML tags, or missing attributes within those tags\n- Allow users to give custom parsing failures with nice error messages (for example, broken links, or custom validation like titles that are too long)\n\n### Parsing Goals\n\nThis is evolving and I would like input on the direction of parsing. My current thinking is that this library should:\n\n- Do not add any new syntax, this library has a subset of the features of Github flavored markdown.\n- Only parse the [Github-flavored markdown style](https://github.github.com/gfm/) (not CommonMark or other variants)\n- (This breaks GFM compliance in favor of explicit errors) All markdown is valid in github-flavored markdown and other variants. This library aims to give explicit errors instead of falling back and silently continuing, see example below\n- Only deviate from Github-flavored markdown rules when it helps give better error feedback for \"things you probably didn't mean to do.\" In all other cases, follow the Github-flavored markdown spec.\n\n## Current Github-flavored markdown compliance\n\nThe test suite for this library runs through all the expected outputs outlined in the GFM spec. It uses the same test suite to test these cases as highlight.js (the library that `elm-explorations/elm-markdown` uses under the hood).\n\nYou can see the latest passing and failing tests from this test suite in the `test-results` folder [(in particular, take a look at the Github-Flavored Markdown failures in in `failing/GFM`](https://github.com/dillonkearns/elm-markdown/tree/master/test-results/failing/GFM).\n\n### Examples of fallback behavior\n\nGithub flavored markdown behavior:\nLinks with missing closing parens are are rendered as raw text instead of links\n\n```markdown\n[My link](/home/ wait I forgot to close the link\n```\n\nRenders the raw string instead of a link, like so:\n\n```html\n\u003cp\u003e\n  [My link](/home/ wait I forgot to close the link\n\u003c/p\u003e\n```\n\nThis library gives an error message here, and aims to do so in similar situations.\n\n## Contributors\n\nA **huge** thanks to [Pablo Hirafuji](https://github.com/pablohirafuji/), who was kind enough to allow me to use his InlineParser in this project. It turns out that Markdown inline parsing is a very specialized algorithm, and the `elm/parser` library isn't suited to solve that particular problem.\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/stephenreddek\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/1985939?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eStephen Reddekopp\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/dillonkearns/elm-markdown/commits?author=stephenreddek\" title=\"Tests\"\u003e⚠️\u003c/a\u003e \u003ca href=\"https://github.com/dillonkearns/elm-markdown/commits?author=stephenreddek\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/thomasin\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/25238976?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ethomasin\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/dillonkearns/elm-markdown/commits?author=thomasin\" title=\"Tests\"\u003e⚠️\u003c/a\u003e \u003ca href=\"https://github.com/dillonkearns/elm-markdown/commits?author=thomasin\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://brianginsburg.com/\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/7957636?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eBrian Ginsburg\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/dillonkearns/elm-markdown/commits?author=bgins\" title=\"Tests\"\u003e⚠️\u003c/a\u003e \u003ca href=\"https://github.com/dillonkearns/elm-markdown/commits?author=bgins\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/matheus23\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/1430958?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ePhilipp Krüger\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/dillonkearns/elm-markdown/commits?author=matheus23\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/folkertdev\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/7949978?v=4\" width=\"100px;\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eFolkert de Vries\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/dillonkearns/elm-markdown/commits?author=folkertdev\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-enable --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThank you [@jinjor](https://github.com/jinjor) for your\n[`elm-xml-parser`](https://package.elm-lang.org/packages/jinjor/elm-xml-parser/latest/XmlParser) package!\n\nI needed to tweak it so I copied it into the project, but it is one of the dependencies and it worked\nwithout a hitch!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdillonkearns%2Felm-markdown","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdillonkearns%2Felm-markdown","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdillonkearns%2Felm-markdown/lists"}