{"id":13611739,"url":"https://github.com/wooorm/markdown-rs","last_synced_at":"2025-05-13T17:04:50.024Z","repository":{"id":62315067,"uuid":"501272636","full_name":"wooorm/markdown-rs","owner":"wooorm","description":"CommonMark compliant markdown parser in Rust with ASTs and extensions","archived":false,"fork":false,"pushed_at":"2025-04-23T15:50:02.000Z","size":2433,"stargazers_count":1137,"open_issues_count":18,"forks_count":59,"subscribers_count":14,"default_branch":"main","last_synced_at":"2025-04-24T01:51:26.236Z","etag":null,"topics":["commonmark","compiler","gfm","markdown","parse","render","rust","tokenize"],"latest_commit_sha":null,"homepage":"https://docs.rs/markdown/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wooorm.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":"funding.yml","license":"license","code_of_conduct":".github/code-of-conduct.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":".github/support.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"wooorm"}},"created_at":"2022-06-08T13:53:33.000Z","updated_at":"2025-04-24T01:31:09.000Z","dependencies_parsed_at":"2022-10-30T16:00:44.701Z","dependency_job_id":"b23b619f-1a02-4618-9776-b426e2c06362","html_url":"https://github.com/wooorm/markdown-rs","commit_stats":{"total_commits":548,"total_committers":17,"mean_commits":32.23529411764706,"dds":"0.051094890510948954","last_synced_commit":"9ed45b41d5a3b8c9924da358cbd0b60c36c59759"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wooorm%2Fmarkdown-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wooorm%2Fmarkdown-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wooorm%2Fmarkdown-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wooorm%2Fmarkdown-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wooorm","download_url":"https://codeload.github.com/wooorm/markdown-rs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253990460,"owners_count":21995774,"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":["commonmark","compiler","gfm","markdown","parse","render","rust","tokenize"],"created_at":"2024-08-01T19:02:03.459Z","updated_at":"2025-05-13T17:04:50.001Z","avatar_url":"https://github.com/wooorm.png","language":"Rust","funding_links":["https://github.com/sponsors/wooorm","https://opencollective.com/unified","https://github.com/sponsors/unifiedjs"],"categories":["Rust"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cbr\u003e\n  \u003cimg width=\"192\" src=\"media/logo-chromatic.svg\" alt=\"\"\u003e\n  \u003cbr\u003e\n  \u003cbr\u003e\n  \u003cbr\u003e\n\u003c/p\u003e\n\n# markdown-rs\n\n[![Build][badge-build-image]][badge-build-url]\n[![Coverage][badge-coverage-image]][badge-coverage-url]\n\nCommonMark compliant markdown parser in Rust with ASTs and extensions.\n\n## Feature highlights\n\n* [x] **[compliant][commonmark]**\n  (100% to CommonMark)\n* [x] **[extensions][]**\n  (100% GFM, 100% MDX, frontmatter, math)\n* [x] **[safe][security]**\n  (100% safe Rust, also 100% safe HTML by default)\n* [x] **[robust][test]**\n  (2300+ tests, 100% coverage, fuzz testing)\n* [x] **[ast][mdast]**\n  (mdast)\n\n## Links\n\n* [GitHub: `wooorm/markdown-rs`][repo]\n* [`crates.io`: `markdown`][crate]\n* [`docs.rs`: `markdown`][docs]\n\n## When should I use this?\n\n* if you *just* want to turn markdown into HTML (with maybe a few extensions)\n* if you want to do *really complex things* with markdown\n\n## What is this?\n\n`markdown-rs` is an open source markdown parser written in Rust.\nIt’s implemented as a state machine (`#![no_std]` + `alloc`) that emits\nconcrete tokens,\nso that every byte is accounted for,\nwith positional info.\nThe API then exposes this information as an AST,\nwhich is easier to work with,\nor it compiles directly to HTML.\n\nWhile most markdown parsers work towards compliancy with CommonMark (or GFM),\nthis project goes further by following how the reference parsers (`cmark`,\n`cmark-gfm`) work,\nwhich is confirmed with thousands of extra tests.\n\nOther than CommonMark and GFM,\nthis project also supports common extensions to markdown such as\nMDX, math, and frontmatter.\n\nThis Rust crate has a sibling project in JavaScript:\n[`micromark`][micromark]\n(and [`mdast-util-from-markdown`][mdast-util-from-markdown] for the AST).\n\nP.S. if you want to *compile* MDX,\nuse [`mdxjs-rs`][mdxjs-rs].\n\n## Questions\n\n* to learn markdown,\n  see this [cheatsheet and tutorial][cheat]\n* for the API,\n  see the [crate docs][docs]\n* for questions,\n  see [Discussions][]\n* to help,\n  see [contribute][] or [sponsor][] below\n\n## Contents\n\n* [Install](#install)\n* [Use](#use)\n* [API](#api)\n* [Extensions](#extensions)\n* [Project](#project)\n  * [Overview](#overview)\n  * [File structure](#file-structure)\n  * [Test](#test)\n  * [Version](#version)\n  * [Security](#security)\n  * [Contribute](#contribute)\n  * [Sponsor](#sponsor)\n  * [Thanks](#thanks)\n* [Related](#related)\n* [License](#license)\n\n## Install\n\nWith [Rust][]\n(rust edition 2018+, ±version 1.56+),\ninstall with `cargo`:\n\n```sh\ncargo add markdown\n```\n\n## Use\n\n```rs\nfn main() {\n    println!(\"{}\", markdown::to_html(\"## Hi, *Saturn*! 🪐\"));\n}\n```\n\nYields:\n\n```html\n\u003ch2\u003eHi, \u003cem\u003eSaturn\u003c/em\u003e! 🪐\u003c/h2\u003e\n```\n\nExtensions (in this case GFM):\n\n```rs\nfn main() -\u003e Result\u003c(), markdown::message::Message\u003e {\n    println!(\n        \"{}\",\n        markdown::to_html_with_options(\n            \"* [x] contact ~Mercury~Venus at hi@venus.com!\",\n            \u0026markdown::Options::gfm()\n        )?\n    );\n\n    Ok(())\n}\n```\n\nYields:\n\n```html\n\u003cul\u003e\n  \u003cli\u003e\n    \u003cinput checked=\"\" disabled=\"\" type=\"checkbox\" /\u003e\n    contact \u003cdel\u003eMercury\u003c/del\u003eVenus at \u003ca href=\"mailto:hi@venus.com\"\u003ehi@venus.com\u003c/a\u003e!\n  \u003c/li\u003e\n\u003c/ul\u003e\n```\n\nSyntax tree ([mdast][]):\n\n```rs\nfn main() -\u003e Result\u003c(), markdown::message::Message\u003e {\n    println!(\n        \"{:?}\",\n        markdown::to_mdast(\"# Hi *Earth*!\", \u0026markdown::ParseOptions::default())?\n    );\n\n    Ok(())\n}\n```\n\nYields:\n\n```text\nRoot { children: [Heading { children: [Text { value: \"Hi \", position: Some(1:3-1:6 (2-5)) }, Emphasis { children: [Text { value: \"Earth\", position: Some(1:7-1:12 (6-11)) }], position: Some(1:6-1:13 (5-12)) }, Text { value: \"!\", position: Some(1:13-1:14 (12-13)) }], position: Some(1:1-1:14 (0-13)), depth: 1 }], position: Some(1:1-1:14 (0-13)) }\n```\n\n## API\n\n`markdown-rs` exposes\n[`to_html`](https://docs.rs/markdown/latest/markdown/fn.to_html.html),\n[`to_html_with_options`](https://docs.rs/markdown/latest/markdown/fn.to_html_with_options.html),\n[`to_mdast`](https://docs.rs/markdown/latest/markdown/fn.to_mdast.html),\n[`Options`](https://docs.rs/markdown/latest/markdown/struct.Options.html),\nand a few other structs and enums.\n\nSee the [crate docs][docs] for more info.\n\n## Extensions\n\n`markdown-rs` supports extensions to `CommonMark`.\nThese extensions are maintained in this project.\nThey are not enabled by default but can be turned on with options.\n\n* GFM\n  * autolink literal\n  * footnote\n  * strikethrough\n  * table\n  * tagfilter\n  * task list item\n* MDX\n  * ESM\n  * expressions\n  * JSX\n* frontmatter\n* math\n\nIt is not a goal of this project to support lots of different extensions.\nIt’s instead a goal to support very common and mostly standardized extensions.\n\n## Project\n\n`markdown-rs` is maintained as a single monolithic crate.\n\n### Overview\n\nThe process to parse markdown looks like this:\n\n```txt\n                    markdown-rs\n+-------------------------------------------------+\n|            +-------+         +---------+--html- |\n| -markdown-\u003e+ parse +-events-\u003e+ compile +        |\n|            +-------+         +---------+-mdast- |\n+-------------------------------------------------+\n```\n\n### File structure\n\nThe files in `src/` are as follows:\n\n* `construct/*.rs`\n  — CommonMark, GFM, and other extension constructs used in markdown\n* `util/*.rs`\n  — helpers often needed when parsing markdown\n* `event.rs`\n  — things with meaning happening somewhere\n* `lib.rs`\n  — public API\n* `mdast.rs`\n  — syntax tree\n* `parser.rs`\n  — turn a string of markdown into events\n* `resolve.rs`\n  — steps to process events\n* `state.rs`\n  — steps of the state machine\n* `subtokenize.rs`\n  — handle content in other content\n* `to_html.rs`\n  — turns events into a string of HTML\n* `to_mdast.rs`\n  — turns events into a syntax tree\n* `tokenizer.rs`\n  — glue the states of the state machine together\n* `unist.rs`\n  — point and position, used in mdast\n\n### Test\n\n`markdown-rs` is tested with the \\~650 CommonMark tests and more than 1k extra\ntests confirmed with CM reference parsers.\nThen there’s even more tests for GFM and other extensions.\nThese tests reach all branches in the code,\nwhich means that this project has 100% code coverage.\nFuzz testing is used to check for things that might fall through coverage.\n\nThe following bash scripts are useful when working on this project:\n\n* generate code (latest CM tests and Unicode info):\n  ```sh\n  cargo run --manifest-path generate/Cargo.toml\n  ```\n* run examples:\n  ```sh\n  RUST_BACKTRACE=1 RUST_LOG=trace cargo run --example lib --features log\n  ```\n* format:\n  ```sh\n  cargo fmt \u0026\u0026 cargo fix --all-features --all-targets --workspace\n  ```\n* lint:\n  ```sh\n  cargo fmt --check \u0026\u0026 cargo clippy --all-features --all-targets --workspace\n  ```\n* test:\n  ```sh\n  RUST_BACKTRACE=1 cargo test --all-features --workspace\n  ```\n* docs:\n  ```sh\n  cargo doc --document-private-items --examples --workspace\n  ```\n* fuzz:\n  ```sh\n  cargo install cargo-fuzz\n  cargo install honggfuzz\n  cargo +nightly fuzz run markdown_libfuzz\n  cargo hfuzz run markdown_honggfuzz\n  ```\n\n### Version\n\n`markdown-rs` follows [SemVer](https://semver.org).\n\n### Security\n\nThe typical security aspect discussed for markdown is [cross-site scripting\n(XSS)][xss] attacks.\nMarkdown itself is safe if it does not include embedded HTML or dangerous\nprotocols in links/images (such as `javascript:`).\n`markdown-rs` makes any markdown safe by default,\neven if HTML is embedded or dangerous protocols are used,\nas it encodes or drops them.\n\nTurning on the `allow_dangerous_html` or `allow_dangerous_protocol` options for\nuser-provided markdown opens you up to XSS attacks.\n\nAdditionnally,\nyou should be able to set `allow_any_img_src` safely.\nThe default is to allow only `http:`, `https:`, and relative images,\nwhich is what GitHub does.\nBut it should be safe to allow any value on `src`.\n\nThe [HTML specification][whatwg-html-image] prohibits dangerous scripts in\nimages and all modern browsers respect this and are thus safe.\nOpera 12 (from 2012) is a notable browser that did not respect this.\n\nAn aspect related to XSS for security is syntax errors:\nmarkdown itself has no syntax errors.\nSome syntax extensions\n(specifically, only MDX)\ndo include syntax errors.\nFor that reason,\n`to_html_with_options` returns `Result\u003cString, Message\u003e`,\nof which the error is a struct indicating where the problem happened,\nwhat occurred,\nand what was expected instead.\nMake sure to handle your errors when using MDX.\n\nAnother security aspect is DDoS attacks.\nFor example,\nan attacker could throw a 100mb file at `markdown-rs`,\nin which case it’s going to take a long while to finish.\nIt is also possible to crash `markdown-rs` with smaller payloads,\nnotably when thousands of\nlinks, images, emphasis, or strong\nare opened but not closed.\nIt is wise to cap the accepted size of input (500kb can hold a big book) and to\nprocess content in a different thread so that it can be stopped when needed.\n\nFor more information on markdown sanitation,\nsee\n[`improper-markup-sanitization.md`][improper] by [**@chalker**][chalker].\n\n### Contribute\n\nSee [`contributing.md`][contributing] for ways to help.\nSee [`support.md`][support] for ways to get help.\nSee [`code-of-conduct.md`][coc] for how to communicate in and around this\nproject.\n\n### Sponsor\n\nSupport this effort and give back by sponsoring:\n\n* [GitHub Sponsors](https://github.com/sponsors/wooorm)\n  (personal; monthly or one-time)\n* [OpenCollective](https://opencollective.com/unified) or\n  [GitHub Sponsors](https://github.com/sponsors/unifiedjs)\n  (unified; monthly or one-time)\n\n### Thanks\n\nSpecial thanks go out to:\n\n* [Vercel][] for funding the initial development\n* [**@Murderlon**][murderlon] for the design of the logo\n* [**@johannhof**][johannhof] for the crate name\n\n## Related\n\n* [`micromark`][micromark]\n  — same as `markdown-rs` but in JavaScript\n* [`mdxjs-rs`][mdxjs-rs]\n  — wraps `markdown-rs` to *compile* MDX to JavaScript\n\n## License\n\n[MIT][license] © [Titus Wormer][author]\n\n[badge-build-image]: https://github.com/wooorm/markdown-rs/workflows/main/badge.svg\n\n[badge-build-url]: https://github.com/wooorm/markdown-rs/actions\n\n[badge-coverage-image]: https://img.shields.io/codecov/c/github/wooorm/markdown-rs.svg\n\n[badge-coverage-url]: https://codecov.io/github/wooorm/markdown-rs\n\n[docs]: https://docs.rs/markdown/latest/markdown/\n\n[crate]: https://crates.io/crates/markdown\n\n[repo]: https://github.com/wooorm/markdown-rs\n\n[discussions]: https://github.com/wooorm/markdown-rs/discussions\n\n[commonmark]: https://spec.commonmark.org\n\n[cheat]: https://commonmark.org/help/\n\n[rust]: https://www.rust-lang.org\n\n[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting\n\n[improper]: https://github.com/ChALkeR/notes/blob/master/Improper-markup-sanitization.md\n\n[chalker]: https://github.com/ChALkeR\n\n[license]: license\n\n[author]: https://wooorm.com\n\n[mdast]: https://github.com/syntax-tree/mdast\n\n[micromark]: https://github.com/micromark/micromark\n\n[mdxjs-rs]: https://github.com/wooorm/mdxjs-rs\n\n[mdast-util-from-markdown]: https://github.com/syntax-tree/mdast-util-from-markdown\n\n[vercel]: https://vercel.com\n\n[murderlon]: https://github.com/murderlon\n\n[johannhof]: https://github.com/johannhof\n\n[contribute]: #contribute\n\n[sponsor]: #sponsor\n\n[extensions]: #extensions\n\n[security]: #security\n\n[test]: #test\n\n[contributing]: .github/contribute.md\n\n[support]: .github/support.md\n\n[coc]: .github/code-of-conduct.md\n\n[whatwg-html-image]: https://html.spec.whatwg.org/multipage/images.html#images-processing-model\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwooorm%2Fmarkdown-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwooorm%2Fmarkdown-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwooorm%2Fmarkdown-rs/lists"}