{"id":45831135,"url":"https://github.com/ph1p/ironmark","last_synced_at":"2026-04-12T14:14:22.627Z","repository":{"id":340380114,"uuid":"1165662386","full_name":"ph1p/ironmark","owner":"ph1p","description":"A blazing fast markdown to HTML/AST parser","archived":false,"fork":false,"pushed_at":"2026-04-02T11:50:16.000Z","size":633,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-02T23:51:44.949Z","etag":null,"topics":["javascript","markdown","rust","typescript","wasm"],"latest_commit_sha":null,"homepage":"https://ph1p.js.org/ironmark/","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/ph1p.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-24T12:08:56.000Z","updated_at":"2026-04-02T11:50:20.000Z","dependencies_parsed_at":"2026-03-03T02:00:50.040Z","dependency_job_id":null,"html_url":"https://github.com/ph1p/ironmark","commit_stats":null,"previous_names":["ph1p/oximark"],"tags_count":35,"template":false,"template_full_name":null,"purl":"pkg:github/ph1p/ironmark","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ph1p%2Fironmark","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ph1p%2Fironmark/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ph1p%2Fironmark/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ph1p%2Fironmark/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ph1p","download_url":"https://codeload.github.com/ph1p/ironmark/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ph1p%2Fironmark/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31577486,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["javascript","markdown","rust","typescript","wasm"],"created_at":"2026-02-26T22:10:46.783Z","updated_at":"2026-04-09T00:08:54.654Z","avatar_url":"https://github.com/ph1p.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ironmark\n\n[![CI](https://github.com/ph1p/ironmark/actions/workflows/ci.yml/badge.svg)](https://github.com/ph1p/ironmark/actions/workflows/ci.yml) [![npm](https://img.shields.io/npm/v/ironmark)](https://www.npmjs.com/package/ironmark) [![crates.io](https://img.shields.io/crates/v/ironmark)](https://crates.io/crates/ironmark)\n\nFast Markdown to HTML/AST parser written in Rust with **zero third-party** parsing dependencies. Fully compliant with [CommonMark 0.31.2](https://spec.commonmark.org/0.31.2/) (652/652 spec tests pass). Available as a Rust crate and as an npm package via WebAssembly, with both HTML and AST output APIs.\n\n## Options\n\n### Extensions (default `true`)\n\n| Option        | JS (`camelCase`)           | Rust (`snake_case`)           | Description                    |\n| ------------- | -------------------------- | ----------------------------- | ------------------------------ |\n| Hard breaks   | `hardBreaks`               | `hard_breaks`                 | Every newline becomes `\u003cbr /\u003e` |\n| Highlight     | `enableHighlight`          | `enable_highlight`            | `==text==` → `\u003cmark\u003e`          |\n| Strikethrough | `enableStrikethrough`      | `enable_strikethrough`        | `~~text~~` → `\u003cdel\u003e`           |\n| Underline     | `enableUnderline`          | `enable_underline`            | `++text++` → `\u003cu\u003e`             |\n| Tables        | `enableTables`             | `enable_tables`               | Pipe table syntax              |\n| Autolink      | `enableAutolink`           | `enable_autolink`             | Bare URLs \u0026 emails → `\u003ca\u003e`     |\n| Task lists    | `enableTaskLists`          | `enable_task_lists`           | `- [ ]` / `- [x]` checkboxes   |\n| Indented code | `enableIndentedCodeBlocks` | `enable_indented_code_blocks` | 4-space indent → `\u003cpre\u003e\u003ccode\u003e` |\n\n### Extensions (default `false`)\n\n| Option              | JS (`camelCase`)       | Rust (`snake_case`)      | Description                                            |\n| ------------------- | ---------------------- | ------------------------ | ------------------------------------------------------ |\n| Wiki links          | `enableWikiLinks`      | `enable_wiki_links`      | `[[page]]` → `\u003ca href=\"page\"\u003e`                         |\n| LaTeX math          | `enableLatexMath`      | `enable_latex_math`      | `$inline$` and `$$display$$` → `\u003cspan class=\"math-…\"\u003e` |\n| Heading IDs         | `enableHeadingIds`     | `enable_heading_ids`     | Auto `id=` on headings from slugified text             |\n| Heading anchors     | `enableHeadingAnchors` | `enable_heading_anchors` | `\u003ca class=\"anchor\"\u003e` inside each heading (implies IDs) |\n| Permissive headings | `permissiveAtxHeaders` | `permissive_atx_headers` | Allow `#Heading` without space after `#`               |\n\n### Security\n\n| Option           | JS (`camelCase`) | Rust (`snake_case`) | Default        | Description                                                        |\n| ---------------- | ---------------- | ------------------- | -------------- | ------------------------------------------------------------------ |\n| Disable raw HTML | `disableRawHtml` | `disable_raw_html`  | `false`        | Escape **all** HTML blocks and inline HTML                         |\n| No HTML blocks   | `noHtmlBlocks`   | `no_html_blocks`    | `false`        | Escape block-level HTML only (more granular than `disableRawHtml`) |\n| No HTML spans    | `noHtmlSpans`    | `no_html_spans`     | `false`        | Escape inline HTML only                                            |\n| Tag filter       | `tagFilter`      | `tag_filter`        | `false`        | GFM tag filter: escape `\u003cscript\u003e`, `\u003ciframe\u003e`, etc.                |\n| Max nesting      | —                | `max_nesting_depth` | `128`          | Limit blockquote/list nesting depth (DoS prevention)               |\n| Max input size   | —                | `max_input_size`    | `0` (no limit) | Truncate input beyond this byte count                              |\n\n\u003e In the WASM build, `max_nesting_depth` is fixed at `128` and `max_input_size` at `10 MB`.\n\nDangerous URI schemes (`javascript:`, `vbscript:`, `data:` except `data:image/…`) are **always** stripped from link and image destinations, regardless of options.\n\n### Other options\n\n| Option              | JS (`camelCase`)     | Rust (`snake_case`)   | Default | Description                                       |\n| ------------------- | -------------------- | --------------------- | ------- | ------------------------------------------------- |\n| Collapse whitespace | `collapseWhitespace` | `collapse_whitespace` | `false` | Collapse runs of spaces/tabs in text to one space |\n\n## JavaScript / TypeScript\n\n```bash\nnpm install ironmark\n# or\npnpm add ironmark\n```\n\n### Node.js\n\nWASM is embedded and loaded synchronously — no `init()` needed:\n\n```ts\nimport { parse } from \"ironmark\";\n\nconst html = parse(\"# Hello\\n\\nThis is **fast**.\");\n\n// safe mode for untrusted input\nconst safe = parse(userInput, { disableRawHtml: true });\n```\n\n### AST Output\n\nUse `parseToAst()` when you need the block-level document structure instead of rendered HTML.\n\n```ts\nimport { parseToAst } from \"ironmark\";\n\nconst astJson = parseToAst(\"# Hello\\n\\n- [x] done\");\nconst ast = JSON.parse(astJson);\n```\n\n`parseToAst()` returns a JSON string for portability across JS runtimes and WASM boundaries.\n\n### Browser / Bundler\n\nCall `init()` once before using `parse()`. It's idempotent and optionally accepts a custom `.wasm` URL.\n\n```ts\nimport { init, parse } from \"ironmark\";\n\nawait init();\n\nconst html = parse(\"# Hello\\n\\nThis is **fast**.\");\n```\n\n#### Vite\n\n```ts\nimport { init, parse } from \"ironmark\";\nimport wasmUrl from \"ironmark/ironmark.wasm?url\";\n\nawait init(wasmUrl);\n\nconst html = parse(\"# Hello\\n\\nThis is **fast**.\");\n```\n\n## Rust\n\n```bash\ncargo add ironmark\n```\n\n```rust\nuse ironmark::{parse, ParseOptions};\n\nfn main() {\n    // with defaults\n    let html = parse(\"# Hello\\n\\nThis is **fast**.\", \u0026ParseOptions::default());\n\n    // with custom options\n    let html = parse(\"line one\\nline two\", \u0026ParseOptions {\n        hard_breaks: false,\n        enable_strikethrough: false,\n        ..Default::default()\n    });\n\n    // safe mode for untrusted input\n    let html = parse(\"\u003cscript\u003ealert(1)\u003c/script\u003e\", \u0026ParseOptions {\n        disable_raw_html: true,\n        max_input_size: 1_000_000, // 1 MB\n        ..Default::default()\n    });\n}\n```\n\n### AST Output\n\n`parse_to_ast()` returns the typed Rust AST (`Block`) directly:\n\n```rust\nuse ironmark::{Block, ParseOptions, parse_to_ast};\n\nfn main() {\n    let ast = parse_to_ast(\"# Hello\", \u0026ParseOptions::default());\n\n    match ast {\n        Block::Document { children } =\u003e {\n            println!(\"top-level blocks: {}\", children.len());\n        }\n        _ =\u003e unreachable!(\"root nodes always Document\"),\n    }\n}\n```\n\nExported AST types:\n\n- `Block`\n- `ListKind`\n- `TableData`\n- `TableAlignment`\n\n## C / C++\n\nThe crate compiles to a static library (`libironmark.a`) that exposes two C functions. A header is provided at `include/ironmark.h`.\n\n### Build the library\n\n```bash\ncargo build --release\n# output: target/release/libironmark.a\n```\n\n### Link\n\n```sh\n# Linux\ncc -o example example.c -L target/release -l ironmark -lpthread -ldl\n\n# macOS\ncc -o example example.c -L target/release -l ironmark \\\n   -framework CoreFoundation -framework Security\n```\n\n### Usage\n\n```c\n#include \"include/ironmark.h\"\n#include \u003cstdio.h\u003e\n\nint main(void) {\n    char *html = ironmark_parse(\"# Hello\\n\\nThis is **fast**.\");\n    if (html) {\n        printf(\"%s\\n\", html);\n        ironmark_free(html);\n    }\n    return 0;\n}\n```\n\n**Memory contract**: `ironmark_parse` returns a heap-allocated string. You **must** free it with `ironmark_free`. Passing any other pointer to `ironmark_free` is undefined behaviour. Both functions are null-safe: `ironmark_parse(NULL)` returns `NULL`; `ironmark_free(NULL)` is a no-op.\n\nParsing always uses the default `ParseOptions` (all extensions enabled, `disable_raw_html` off). Options are not yet configurable through the C API.\n\n## Benchmarks\n\nCompares ironmark against pulldown-cmark, comrak, markdown-it, and markdown-rs. Results are available at [ph1p.js.org/ironmark/#benchmark](https://ph1p.js.org/ironmark/#benchmark).\n\n```bash\ncargo bench                          # run all benchmarks\ncargo bench --features bench-md4c   # include md4c (requires: brew install md4c)\npnpm bench                          # run + update playground data\n```\n\n## Development\n\nThis project uses [pnpm](https://pnpm.io/) for package management.\n\n### Build from source\n\n```bash\npnpm setup:wasm\npnpm build\n```\n\n| Command           | Description            |\n| ----------------- | ---------------------- |\n| `pnpm setup:wasm` | Install prerequisites  |\n| `pnpm build`      | Release WASM build     |\n| `pnpm build:dev`  | Debug WASM build       |\n| `pnpm test`       | Run Rust tests         |\n| `pnpm check`      | Format check           |\n| `pnpm clean`      | Remove build artifacts |\n\n### Troubleshooting\n\n**`wasm32-unknown-unknown target not found`** or **`wasm-bindgen not found`** — run `pnpm setup:wasm` to install all prerequisites.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fph1p%2Fironmark","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fph1p%2Fironmark","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fph1p%2Fironmark/lists"}