#markdown-html #markdown-parser #parse-markdown #common-mark #ast #parse #render

markdown

Rust 中的 CommonMark 兼容 Markdown 解析器,带有 AST 和扩展功能

25 个版本 (3 个重大更新)

1.0.0-alpha.202024 年 8 月 13 日
1.0.0-alpha.182024 年 7 月 2 日
1.0.0-alpha.162023 年 12 月 5 日
1.0.0-alpha.152023 年 11 月 20 日
0.0.1 2015 年 1 月 25 日

#14解析器实现

Download history 7406/week @ 2024-05-03 7693/week @ 2024-05-10 7717/week @ 2024-05-17 7410/week @ 2024-05-24 7665/week @ 2024-05-31 7315/week @ 2024-06-07 7324/week @ 2024-06-14 8744/week @ 2024-06-21 8226/week @ 2024-06-28 7072/week @ 2024-07-05 8135/week @ 2024-07-12 9036/week @ 2024-07-19 9430/week @ 2024-07-26 10044/week @ 2024-08-02 12078/week @ 2024-08-09 10508/week @ 2024-08-16

43,931 每月下载量
146 包中使用 (85 直接)

MIT 许可协议

1MB
27K SLoC





markdown-rs

Build Coverage GitHub docs.rs crates.io

👉 注意:这是一个重用旧名称的新包。旧包(版本 0.3.0 及以下)存在一些问题。请确保使用新包,目前处于 alpha 版本,版本号为 1.0.0-alpha.20

Rust 中的 CommonMark 兼容 Markdown 解析器,带有 AST 和扩展功能。

功能亮点

  • 兼容 (100% 符合 CommonMark)
  • 扩展 (100% GFM,100% MDX,frontmatter,数学公式)
  • 安全 (100% 安全 Rust,默认 100% 安全 HTML)
  • 健壮 (2300+ 测试,100% 覆盖率,模糊测试)
  • AST (mdast)

何时使用此工具?

  • 如果您只想将 Markdown 转换为 HTML(可能包含一些扩展)
  • 如果您想对 Markdown 进行非常复杂的操作

这是什么?

markdown-rs 是一个用 Rust 编写的开源 Markdown 解析器。它作为一个状态机实现,生成具体的令牌,确保每个字节都被计算,并带有位置信息。API 将此信息作为 AST 暴露出来,这使其更容易处理,或者它可以直接编译为 HTML。

虽然大多数Markdown解析器都致力于符合CommonMark(或GFM),但本项目通过遵循参考解析器(cmarkcmark-gfm)的工作方式进一步发展,并通过数千次额外测试得到验证。

除了CommonMark和GFM之外,本项目还支持Markdown的常见扩展,如MDX、数学和frontmatter。

这个Rust crate有一个在JavaScript中的兄弟项目:micromark(以及mdast-util-from-markdown用于AST)。

提示:如果您想编译MDX,请使用mdxjs-rs

问题

内容

安装

使用Rust(rust edition 2018+,±版本1.56+),使用cargo安装

cargo add markdown@1.0.0-alpha.20

👉 注意:这是一个重用旧名称的新包。旧包(版本 0.3.0 及以下)存在一些问题。请确保使用新包,目前处于 alpha 版本,版本号为 1.0.0-alpha.20

使用

fn main() {
    println!("{}", markdown::to_html("## Hello, *world*!"));
}

输出

<h2>Hello, <em>world</em>!</h2>

扩展(在这种情况下是GFM)

fn main() -> Result<(), markdown::message::Message> {
    println!(
        "{}",
        markdown::to_html_with_options(
            "* [x] contact@example.com ~~strikethrough~~",
            &markdown::Options::gfm()
        )?
    );

    Ok(())
}

输出

<ul>
  <li>
    <input checked="" disabled="" type="checkbox" />
    <a href="mailto:contact@example.com">contact@example.com</a>
    <del>strikethrough</del>
  </li>
</ul>

语法树(mdast

fn main() -> Result<(), markdown::message::Message> {
    println!(
        "{:?}",
        markdown::to_mdast("# Hey, *you*!", &markdown::ParseOptions::default())?
    );

    Ok(())
}

输出

Root { children: [Heading { children: [Text { value: "Hey, ", position: Some(1:3-1:8 (2-7)) }, Emphasis { children: [Text { value: "you", position: Some(1:9-1:12 (8-11)) }], position: Some(1:8-1:13 (7-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)) }

API

markdown-rs公开了to_htmlto_html_with_optionsto_mdastOptions以及一些其他结构和枚举。

有关更多信息,请参阅crate文档

扩展

markdown-rs支持扩展到CommonMark。这些扩展由本项目维护。它们默认未启用,但可以通过选项打开。

  • frontmatter
  • GFM
    • 自动链接文本
    • 脚注
    • 删除线
    • 表格
    • 标签过滤器
    • 任务列表项
  • 数学
  • MDX
    • ESM
    • 表达式
    • JSX

本项目的目标不是支持许多不同的扩展,而是支持非常常见且基本标准化的扩展。

项目

markdown-rs作为一个单一代码库维护。

概述

解析Markdown的过程如下

                    markdown-rs
+-------------------------------------------------+
|            +-------+         +---------+--html- |
| -markdown->+ parse +-events->+ compile +        |
|            +-------+         +---------+-mdast- |
+-------------------------------------------------+

文件结构

src/中的文件如下

  • construct/*.rs — Markdown中使用的CommonMark、GFM和其他扩展结构
  • util/*.rs — 解析Markdown时经常需要的辅助工具
  • event.rs — 发生在某个地方有意义的事情
  • lib.rs — 公共API
  • mdast.rs — 语法树
  • parser.rs — 将Markdown字符串转换为事件
  • resolve.rs — 处理事件的步骤
  • state.rs — 状态机的步骤
  • subtokenize.rs — 处理其他内容中的内容
  • to_html.rs — 将事件转换为HTML字符串
  • to_mdast.rs — 将事件转换为语法树
  • tokenizer.rs — 将状态机的状态粘合在一起
  • unist.rs — 指针和位置,在mdast中使用

测试

markdown-rs 使用了 ~650 个 CommonMark 测试以及超过 1k 个与 CM 引用解析器确认的额外测试。然后还有更多针对 GFM 和其他扩展的测试。这些测试涵盖了代码的各个分支,这意味着这个项目拥有 100% 的代码覆盖率。模糊测试用于检查可能未覆盖到的部分。

以下 bash 脚本在处理此项目时很有用:

  • 生成代码(最新的 CM 测试和 Unicode 信息)
    cargo run --manifest-path generate/Cargo.toml
    
  • 运行示例
    RUST_BACKTRACE=1 RUST_LOG=trace cargo run --features log --example lib
    
  • 格式化
    cargo fmt && cargo fix --all-targets
    
  • lint
    cargo fmt --check && cargo clippy --examples --tests --benches --all-features
    
  • 测试
    RUST_BACKTRACE=1 cargo test
    
  • 文档
    cargo doc --document-private-items
    
  • 模糊测试
    cargo install cargo-fuzz
    cargo install honggfuzz
    cargo +nightly fuzz run markdown_libfuzz
    cargo hfuzz run markdown_honggfuzz
    

版本

markdown-rs 遵循 SemVer

安全

通常讨论的 markdown 安全方面是 跨站脚本(XSS)攻击。如果 markdown 不包含嵌入的 HTML 或危险的协议(如 javascript:data:)的链接/图像,那么 markdown 本身是安全的。markdown-rs 默认将任何 markdown 转换为安全版本,即使 HTML 已嵌入或使用了危险的协议,因为它会编码或丢弃它们。启用 allow_dangerous_htmlallow_dangerous_protocol 选项将使用户提供的 markdown 暴露于 XSS 攻击。

与 XSS 安全相关的一个方面是语法错误:markdown 本身没有语法错误。一些语法扩展(特别是,只有 MDX)包含语法错误。因此,to_html_with_options 返回 Result<String, Message>,其中的错误是一个结构,指示问题发生的位置、发生的情况以及期望的情况。在使用 MDX 时,请确保处理错误。

另一个安全方面是 DDoS 攻击。例如,攻击者可以向 markdown-rs 抛出一个 100mb 的文件,在这种情况下,完成它将花费很长时间。也有可能用更小的负载使 markdown-rs 崩溃,特别是当打开成千上万的链接、图像、强调或加粗而未关闭时。明智的做法是限制接受的输入大小(500kb 可以存放一本大书),并在不同的线程中处理内容,以便在需要时停止。

有关 markdown 清洗的更多信息,请参阅 improper-markup-sanitization.md,由 @chalker 提供。

贡献

有关帮助的方法,请参阅 contributing.md。有关获得帮助的方法,请参阅 support.md。有关如何在项目内部和周围进行沟通的信息,请参阅 code-of-conduct.md

赞助

通过赞助此努力并回馈以支持

感谢

特别感谢

  • micromark — 与 markdown-rs 相同,但使用 JavaScript 实现
  • mdxjs-rs — 将 markdown-rs 包装起来以将 MDX 编译成 JavaScript

许可证

MIT © Titus Wormer

依赖项