{"id":26579629,"url":"https://github.com/nberlette/comrak-wasm","last_synced_at":"2026-04-09T01:31:13.713Z","repository":{"id":283916369,"uuid":"953280952","full_name":"nberlette/comrak-wasm","owner":"nberlette","description":"TypeScript and WebAssembly bindings for Comrak, the Markdown-to-HTML renderer written in Rust.","archived":false,"fork":false,"pushed_at":"2025-12-14T09:04:21.000Z","size":2574,"stargazers_count":6,"open_issues_count":8,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-16T13:17:06.878Z","etag":null,"topics":["bun","comrak","deno","jsr","markdown","markdown-parsing-rendering","markdown-renderer","markdown-to-html","node","rust","typescript","wasm","webassembly"],"latest_commit_sha":null,"homepage":"https://jsr.io/@nick/comrak","language":"TypeScript","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/nberlette.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING","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":null,"dco":null,"cla":null},"funding":{"ko_fi":"nberlette"}},"created_at":"2025-03-23T01:26:56.000Z","updated_at":"2025-12-14T09:04:24.000Z","dependencies_parsed_at":"2025-03-23T02:28:07.630Z","dependency_job_id":"e51025dc-514b-46ba-a3b9-3d01d9958ecc","html_url":"https://github.com/nberlette/comrak-wasm","commit_stats":null,"previous_names":["nberlette/comrak-wasm"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/nberlette/comrak-wasm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nberlette%2Fcomrak-wasm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nberlette%2Fcomrak-wasm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nberlette%2Fcomrak-wasm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nberlette%2Fcomrak-wasm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nberlette","download_url":"https://codeload.github.com/nberlette/comrak-wasm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nberlette%2Fcomrak-wasm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31581864,"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":["bun","comrak","deno","jsr","markdown","markdown-parsing-rendering","markdown-renderer","markdown-to-html","node","rust","typescript","wasm","webassembly"],"created_at":"2025-03-23T06:18:16.201Z","updated_at":"2026-04-09T01:31:13.689Z","avatar_url":"https://github.com/nberlette.png","language":"TypeScript","funding_links":["https://ko-fi.com/nberlette"],"categories":[],"sub_categories":[],"readme":"# [`@nick/comrak`]\n\n\u003cbig\u003eHigh-performance Markdown to HTML converter powered by WebAssembly.\u003c/big\u003e\n\n---\n\n## Overview\n\n`@nick/comrak` is a fast and efficient Markdown to HTML converter written in\nRust, compiled to WebAssembly, and wrapped with a high-level TypeScript API. It\nrenders HTML, CommonMark, and CommonMark XML, and mirrors the configurability of\nthe upstream `comrak` crate.\n\n## Usage\n\nConvert Markdown to HTML with a single function call:\n\n```ts\nimport assert from \"node:assert\";\nimport { markdownToHTML } from \"@nick/comrak\";\n\nconst markdown = \"# Hello, **world**!\";\nconst html = markdownToHTML(markdown);\n\nassert.strictEqual(html, \"\u003ch1\u003eHello, \u003cstrong\u003eworld\u003c/strong\u003e!\u003c/h1\u003e\\n\");\n```\n\nRender any format you need:\n\n```ts\nimport assert from \"node:assert\";\nimport {\n  markdownToCommonMark,\n  markdownToHTML,\n  markdownToXML,\n} from \"@nick/comrak\";\n\nconst md = \"# Hello, **world**!\";\n\nassert.strictEqual(\n  markdownToHTML(md),\n  \"\u003ch1\u003eHello, \u003cstrong\u003eworld\u003c/strong\u003e!\u003c/h1\u003e\\n\",\n);\nassert.strictEqual(\n  markdownToXML(md),\n  '\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\\n' +\n    '\u003c!DOCTYPE document SYSTEM \"CommonMark.dtd\"\u003e\\n' +\n    '\u003cdocument xmlns=\"http://commonmark.org/xml/1.0\"\u003e\\n' +\n    '  \u003cheading level=\"1\"\u003e\\n' +\n    '    \u003ctext xml:space=\"preserve\"\u003eHello, \u003c/text\u003e\\n' +\n    \"    \u003cstrong\u003e\\n\" +\n    '      \u003ctext xml:space=\"preserve\"\u003eworld\u003c/text\u003e\\n' +\n    \"    \u003c/strong\u003e\\n\" +\n    '    \u003ctext xml:space=\"preserve\"\u003e!\u003c/text\u003e\\n' +\n    \"  \u003c/heading\u003e\\n\" +\n    \"\u003c/document\u003e\\n\",\n);\nassert.strictEqual(markdownToCommonMark(md), \"# Hello, **world**\\\\!\\n\");\n```\n\nParse once and render anywhere:\n\n```ts\nimport assert from \"node:assert\";\nimport {\n  type Options,\n  parseMarkdown,\n  renderCommonMark,\n  renderHTML,\n  renderXML,\n} from \"@nick/comrak\";\n\nconst options = { extension: { tasklist: true } } satisfies Options;\nconst ast = parseMarkdown(\"# Hello, **world**!\\n\\n- [x] Done\\n\", options);\n\nassert.strictEqual(\n  renderHTML(ast, options),\n  \"\u003ch1\u003eHello, \u003cstrong\u003eworld\u003c/strong\u003e!\u003c/h1\u003e\\n\" +\n    '\u003cul\u003e\\n\u003cli\u003e\u003cinput type=\"checkbox\" checked=\"\" disabled=\"\" /\u003e Done\u003c/li\u003e\\n\u003c/ul\u003e\\n',\n);\nassert.strictEqual(\n  renderXML(ast, options),\n  '\u003c?xml version=\"1.0\" encoding=\"UTF-8\"?\u003e\\n' +\n    '\u003c!DOCTYPE document SYSTEM \"CommonMark.dtd\"\u003e\\n' +\n    '\u003cdocument xmlns=\"http://commonmark.org/xml/1.0\"\u003e\\n' +\n    '  \u003cheading level=\"1\"\u003e\\n' +\n    '    \u003ctext xml:space=\"preserve\"\u003eHello, \u003c/text\u003e\\n' +\n    \"    \u003cstrong\u003e\\n\" +\n    '      \u003ctext xml:space=\"preserve\"\u003eworld\u003c/text\u003e\\n' +\n    \"    \u003c/strong\u003e\\n\" +\n    '    \u003ctext xml:space=\"preserve\"\u003e!\u003c/text\u003e\\n' +\n    \"  \u003c/heading\u003e\\n\" +\n    '  \u003clist type=\"bullet\" tasklist=\"true\" tight=\"true\"\u003e\\n' +\n    '    \u003ctaskitem completed=\"true\"\u003e\\n' +\n    \"      \u003cparagraph\u003e\\n\" +\n    '        \u003ctext xml:space=\"preserve\"\u003eDone\u003c/text\u003e\\n' +\n    \"      \u003c/paragraph\u003e\\n\" +\n    \"    \u003c/taskitem\u003e\\n\" +\n    \"  \u003c/list\u003e\\n\" +\n    \"\u003c/document\u003e\\n\",\n);\nassert.strictEqual(\n  renderCommonMark(ast, options),\n  \"# Hello, **world**\\\\!\\n\\n- [x] Done\\n\",\n);\n```\n\nAdd plugins for custom rendering:\n\n````ts\nimport assert from \"node:assert\";\nimport { markdownToHTML, Options } from \"@nick/comrak\";\n\nconst options = Options.default();\noptions.plugins.render.codefenceSyntaxHighlighter = {\n  highlight: (code, lang) =\u003e `highlighted:${lang ?? \"none\"}:${code.trim()}`,\n  pre: () =\u003e '\u003cpre class=\"custom-pre\"\u003e',\n  code: () =\u003e '\u003ccode class=\"custom-code\"\u003e',\n};\n\nconst html = markdownToHTML(\"```ts\\nlet x = 1;\\n```\\n\", options);\nassert.strictEqual(\n  html,\n  '\u003cpre class=\"custom-pre\"\u003e\u003ccode class=\"custom-code\"\u003ehighlighted:ts:let x = 1;\u003c/code\u003e\u003c/pre\u003e\\n',\n);\n````\n\n```ts\nimport assert from \"node:assert\";\nimport { markdownToHTML, Options } from \"@nick/comrak\";\n\nconst options = Options.default();\noptions.render.sourcepos = true;\noptions.plugins.render.headingAdapter = {\n  enter: ({ level, content }, sourcepos) =\u003e {\n    const attrs = [`data-level=\"${level}\"`, `data-text=\"${content}\"`];\n    if (sourcepos) {\n      const { start, end } = sourcepos;\n      attrs.push(\n        `data-sourcepos=\"${start.line}:${start.column}-${end.line}:${end.column}\"`,\n      );\n    }\n    return `\u003ch${level} ${attrs.join(\" \")}\u003e`;\n  },\n  exit: ({ level }) =\u003e `\u003c/h${level}\u003e`,\n};\n\nconst html = markdownToHTML(\"# Hello!\\n\\n## Subheading\\n\", options);\nassert.strictEqual(\n  html,\n  '\u003ch1 data-level=\"1\" data-text=\"Hello!\" data-sourcepos=\"1:1-1:8\"\u003eHello!\u003c/h1\u003e\\n' +\n    '\u003ch2 data-level=\"2\" data-text=\"Subheading\" data-sourcepos=\"3:1-3:13\"\u003eSubheading\u003c/h2\u003e',\n);\n```\n\n---\n\n## Install\n\nInstall via your preferred package manager:\n\n```sh\ndeno add jsr:@nick/comrak\n```\n\n```sh\npnpm add jsr:@nick/comrak\n```\n\n```sh\nyarn add jsr:@nick/comrak\n```\n\n```sh\nbunx jsr add @nick/comrak\n```\n\n```sh\nnpx jsr add @nick/comrak\n```\n\n\u003e [!IMPORTANT]\n\u003e\n\u003e Support for the `jsr:` protocol is available in PNPM v10.2+ and Yarn v4.2+. If\n\u003e you're using an older version of these package managers, you can use the `dlx`\n\u003e command instead.\n\n```sh\npnpm dlx jsr add @nick/comrak\n```\n\n```sh\nyarn dlx jsr add @nick/comrak\n```\n\n### [npm]\n\nThis package is also distributed on [npm] as [`comrak`][npm].\n\n```sh\ndeno add npm:comrak\n```\n\n```sh\npnpm add comrak\n```\n\n```sh\nyarn add comrak\n```\n\n```sh\nbun add comrak\n```\n\n```sh\nnpm i comrak\n```\n\n---\n\n## API\n\n- `markdownToHTML(markdown, options?)` Render Markdown to HTML.\n- `markdownToXML(markdown, options?)` Render Markdown to CommonMark XML.\n- `markdownToCommonMark(markdown, options?)` Render Markdown back to CommonMark.\n- `parseMarkdown(markdown, options?)` Parse Markdown into an AST.\n- `renderHTML(ast, options?)` Render an AST to HTML.\n- `renderXML(ast, options?)` Render an AST to CommonMark XML.\n- `renderCommonMark(ast, options?)` Render an AST to CommonMark text.\n- `Options.default()` Get a fresh, fully-populated options object.\n- `HeadingAdapter` / `SyntaxHighlighterAdapter` Plug custom heading rendering or\n  code fence highlighting into Comrak.\n\n---\n\n## Options\n\nThe `Options` interface mirrors the Rust crate's configuration surface, spanning\n[extensions], [parsing], [rendering], and [plugins].\n\n[extensions]: #extensionoptions\n[parsing]: #parseoptions\n[rendering]: #renderoptions\n[plugins]: #plugins\n\n### `ExtensionOptions`\n\n- **`autolink?: boolean`** Enables the autolink extension (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"Hello www.github.com.\\n\", {\n    extension: { autolink: true },\n  });\n\n  assert.strictEqual(\n    html,\n    '\u003cp\u003eHello \u003ca href=\"http://www.github.com\"\u003ewww.github.com\u003c/a\u003e.\u003c/p\u003e\\n',\n  );\n  ```\n\n- **`descriptionLists?: boolean`** Enables description lists (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"Term\\n\\n: Definition\", {\n    extension: { descriptionLists: true },\n  });\n\n  assert.strictEqual(\n    html,\n    \"\u003cdl\u003e\\n\u003cdt\u003eTerm\u003c/dt\u003e\\n\u003cdd\u003e\\n\u003cp\u003eDefinition\u003c/p\u003e\\n\u003c/dd\u003e\\n\u003c/dl\u003e\\n\",\n  );\n  ```\n\n- **`footnotes?: boolean`** Enables footnotes support (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"Hi[^x].\\n\\n[^x]: A greeting.\\n\", {\n    extension: { footnotes: true },\n  });\n\n  assert.strictEqual(\n    html,\n    '\u003cp\u003eHi\u003csup class=\"footnote-ref\"\u003e\u003ca href=\"#fn-x\" id=\"fnref-x\" data-footnote-ref\u003e1\u003c/a\u003e\u003c/sup\u003e.\u003c/p\u003e\\n' +\n      '\u003csection class=\"footnotes\" data-footnotes\u003e\\n\u003col\u003e\\n\u003cli id=\"fn-x\"\u003e\\n\u003cp\u003eA greeting. \u003ca href=\"#fnref-x\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\"\u003e↩\u003c/a\u003e\u003c/p\u003e\\n\u003c/li\u003e\\n\u003c/ol\u003e\\n\u003c/section\u003e\\n',\n  );\n  ```\n\n- **`inlineFootnotes?: boolean`** Enables inline footnotes (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML, Options } from \"@nick/comrak\";\n\n  const options = Options.default();\n  options.extension.footnotes = true;\n  options.extension.inlineFootnotes = true;\n\n  const html = markdownToHTML(\"Hi^[An inline note].\\n\", options);\n  assert.strictEqual(\n    html,\n    '\u003cp\u003eHi\u003csup class=\"footnote-ref\"\u003e\u003ca href=\"#fn-__inline_1\" id=\"fnref-__inline_1\" data-footnote-ref\u003e1\u003c/a\u003e\u003c/sup\u003e.\u003c/p\u003e\\n' +\n      '\u003csection class=\"footnotes\" data-footnotes\u003e\\n\u003col\u003e\\n\u003cli id=\"fn-__inline_1\"\u003e\\n\u003cp\u003eAn inline note \u003ca href=\"#fnref-__inline_1\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"1\" aria-label=\"Back to reference 1\"\u003e↩\u003c/a\u003e\u003c/p\u003e\\n\u003c/li\u003e\\n\u003c/ol\u003e\\n\u003c/section\u003e\\n',\n  );\n  ```\n\n- **`frontMatterDelimiter?: string | null`** Processes front matter. Defaults to\n  `\"---\"`.\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"---\\nlayout: post\\n---\\nText\\n\", {\n    extension: { frontMatterDelimiter: \"---\" },\n  });\n\n  assert.strictEqual(html, \"\u003cp\u003eText\u003c/p\u003e\\n\");\n  ```\n\n- **`headerIDs?: string | null`** Generates header IDs with an optional prefix\n  (default: `\"\"`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"# README\\n\", {\n    extension: { headerIDs: \"user-content-\" },\n  });\n\n  assert.strictEqual(\n    html,\n    '\u003ch1\u003e\u003ca href=\"#readme\" aria-hidden=\"true\" class=\"anchor\" id=\"user-content-readme\"\u003e\u003c/a\u003eREADME\u003c/h1\u003e\\n',\n  );\n  ```\n\n- **`table?: boolean`** Enables table support (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"| a | b |\\n|---|---|\\n| c | d |\\n\", {\n    extension: { table: true },\n  });\n\n  assert.strictEqual(\n    html,\n    \"\u003ctable\u003e\\n\u003cthead\u003e\\n\u003ctr\u003e\\n\u003cth\u003ea\u003c/th\u003e\\n\u003cth\u003eb\u003c/th\u003e\\n\u003c/tr\u003e\\n\u003c/thead\u003e\\n\u003ctbody\u003e\\n\u003ctr\u003e\\n\u003ctd\u003ec\u003c/td\u003e\\n\u003ctd\u003ed\u003c/td\u003e\\n\u003c/tr\u003e\\n\u003c/tbody\u003e\\n\u003c/table\u003e\\n\",\n  );\n  ```\n\n- **`tagfilter?: boolean`** Filters disallowed raw HTML tags (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"Hello \u003cxmp\u003e.\\n\\n\u003cxmp\u003e\", {\n    extension: { tagfilter: true },\n    render: { unsafe: true },\n  });\n\n  assert.strictEqual(html, \"\u003cp\u003eHello \u0026lt;xmp\u003e.\u003c/p\u003e\\n\u0026lt;xmp\u003e\\n\");\n  ```\n\n- **`tasklist?: boolean`** Enables task list items (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"* [x] Done\\n* [ ] Not done\\n\", {\n    extension: { tasklist: true },\n  });\n\n  assert.strictEqual(\n    html,\n    '\u003cul\u003e\\n\u003cli\u003e\u003cinput type=\"checkbox\" checked=\"\" disabled=\"\" /\u003e Done\u003c/li\u003e\\n\u003cli\u003e\u003cinput type=\"checkbox\" disabled=\"\" /\u003e Not done\u003c/li\u003e\\n\u003c/ul\u003e\\n',\n  );\n  ```\n\n- **`multilineBlockQuotes?: boolean`** Enables multiline block quotes (default:\n  `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"\u003e\u003e\u003e\\nparagraph\\n\u003e\u003e\u003e\", {\n    extension: { multilineBlockQuotes: true },\n  });\n\n  assert.strictEqual(html, \"\u003cblockquote\u003e\\n\u003cp\u003eparagraph\u003c/p\u003e\\n\u003c/blockquote\u003e\\n\");\n  ```\n\n- **`alerts?: boolean`** Enables GitHub-style alerts (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"\u003e [!note]\\n\u003e Something of note\", {\n    extension: { alerts: true },\n  });\n\n  assert.strictEqual(\n    html,\n    '\u003cdiv class=\"markdown-alert markdown-alert-note\"\u003e\\n\u003cp class=\"markdown-alert-title\"\u003eNote\u003c/p\u003e\\n\u003cp\u003eSomething of note\u003c/p\u003e\\n\u003c/div\u003e\\n',\n  );\n  ```\n\n- **`mathDollars?: boolean`** Enables math using dollar syntax (default:\n  `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"$1 + 2$ and $$x = y$$\", {\n    extension: { mathDollars: true },\n  });\n\n  assert.strictEqual(\n    html,\n    '\u003cp\u003e\u003cspan data-math-style=\"inline\"\u003e1 + 2\u003c/span\u003e and \u003cspan data-math-style=\"display\"\u003ex = y\u003c/span\u003e\u003c/p\u003e\\n',\n  );\n  ```\n\n- **`mathCode?: boolean`** Enables math using code syntax (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"$`1 + 2`$\", {\n    extension: { mathCode: true },\n  });\n\n  assert.strictEqual(\n    html,\n    '\u003cp\u003e\u003ccode data-math-style=\"inline\"\u003e1 + 2\u003c/code\u003e\u003c/p\u003e\\n',\n  );\n  ```\n\n- **`wikilinksTitleBeforePipe?: boolean`** Enables wikilinks where the title is\n  before the pipe (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"[[link label|url]]\", {\n    extension: { wikilinksTitleBeforePipe: true },\n  });\n\n  assert.strictEqual(\n    html,\n    '\u003cp\u003e\u003ca href=\"url\" data-wikilink=\"true\"\u003elink label\u003c/a\u003e\u003c/p\u003e\\n',\n  );\n  ```\n\n- **`wikilinksTitleAfterPipe?: boolean`** Enables wikilinks where the title is\n  after the pipe (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"[[url|link label]]\", {\n    extension: { wikilinksTitleAfterPipe: true },\n  });\n\n  assert.strictEqual(\n    html,\n    '\u003cp\u003e\u003ca href=\"url\" data-wikilink=\"true\"\u003elink label\u003c/a\u003e\u003c/p\u003e\\n',\n  );\n  ```\n\n- **`underline?: boolean`** Enables underlines using double underscores\n  (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"__underlined text__\", {\n    extension: { underline: true },\n  });\n\n  assert.strictEqual(html, \"\u003cp\u003e\u003cu\u003eunderlined text\u003c/u\u003e\u003c/p\u003e\\n\");\n  ```\n\n- **`strikethrough?: boolean`** Enables strikethrough formatting (default:\n  `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"Hello ~world~ there.\\n\", {\n    extension: { strikethrough: true },\n  });\n\n  assert.strictEqual(html, \"\u003cp\u003eHello \u003cdel\u003eworld\u003c/del\u003e there.\u003c/p\u003e\\n\");\n  ```\n\n- **`superscript?: boolean`** Enables superscript formatting (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"e = mc^2^.\\n\", {\n    extension: { superscript: true },\n  });\n\n  assert.strictEqual(html, \"\u003cp\u003ee = mc\u003csup\u003e2\u003c/sup\u003e.\u003c/p\u003e\\n\");\n  ```\n\n- **`subscript?: boolean`** Enables subscript text using single tildes (default:\n  `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"H~2~O\", {\n    extension: { subscript: true },\n  });\n\n  assert.strictEqual(html, \"\u003cp\u003eH\u003csub\u003e2\u003c/sub\u003eO\u003c/p\u003e\\n\");\n  ```\n\n- **`spoiler?: boolean`** Enables spoilers using double vertical bars (default:\n  `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"Darth Vader is ||Luke's father||\", {\n    extension: { spoiler: true },\n  });\n\n  assert.strictEqual(\n    html,\n    '\u003cp\u003eDarth Vader is \u003cspan class=\"spoiler\"\u003eLuke\\'s father\u003c/span\u003e\u003c/p\u003e\\n',\n  );\n  ```\n\n- **`greentext?: boolean`** Requires a space after `\u003e` for blockquotes (default:\n  `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"\u003eimplying implications\", {\n    extension: { greentext: true },\n  });\n\n  assert.strictEqual(html, \"\u003cp\u003e\u0026gt;implying implications\u003c/p\u003e\\n\");\n  ```\n\n- **`shortcodes?: boolean`** Replaces `:emoji:` shortcodes (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"Happy Friday! :smile:\", {\n    extension: { shortcodes: true },\n  });\n\n  assert.strictEqual(html, \"\u003cp\u003eHappy Friday! 😄\u003c/p\u003e\\n\");\n  ```\n\n- **`imageURLRewriter?: URLRewriter | null`** Rewrites image URLs (default:\n  `null`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML, Options } from \"@nick/comrak\";\n\n  const options = Options.default();\n  options.extension.imageURLRewriter = (url: string) =\u003e\n    `https://cdn.example.com/images/${encodeURIComponent(url)}`;\n\n  const html = markdownToHTML(\"![alt text](image.png)\", options);\n  assert.strictEqual(\n    html,\n    '\u003cp\u003e\u003cimg src=\"https://cdn.example.com/images/image.png\" alt=\"alt text\" /\u003e\u003c/p\u003e\\n',\n  );\n  ```\n\n- **`linkURLRewriter?: URLRewriter | null`** Rewrites link URLs (default:\n  `null`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML, Options } from \"@nick/comrak\";\n\n  const options = Options.default();\n  options.extension.linkURLRewriter = (url: string) =\u003e\n    `https://safe.example.com/norefer?url=${encodeURIComponent(url)}`;\n\n  const html = markdownToHTML(\n    \"[my link](http://unsafe.example.com/bad)\",\n    options,\n  );\n  assert.strictEqual(\n    html,\n    '\u003cp\u003e\u003ca href=\"https://safe.example.com/norefer?url=http%3A%2F%2Funsafe.example.com%2Fbad\"\u003emy link\u003c/a\u003e\u003c/p\u003e\\n',\n  );\n  ```\n\n- **`cjkFriendlyEmphasis?: boolean`** Recognizes emphasis in CJK contexts\n  (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"**この文は重要です。**但这句话并不重要。\", {\n    extension: { cjkFriendlyEmphasis: true },\n  });\n\n  assert.strictEqual(\n    html,\n    \"\u003cp\u003e\u003cstrong\u003eこの文は重要です。\u003c/strong\u003e但这句话并不重要。\u003c/p\u003e\\n\",\n  );\n  ```\n\n- **`subtext?: boolean`** Enables block-scoped subtext (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML, Options } from \"@nick/comrak\";\n\n  const options = Options.default();\n  options.extension.subtext = true;\n\n  const html = markdownToHTML(\"-# subtext\", options);\n  assert.strictEqual(html, \"\u003cp\u003e\u003csub\u003esubtext\u003c/sub\u003e\u003c/p\u003e\\n\");\n  ```\n\n- **`highlight?: boolean`** Enables highlighting using `==` (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML, Options } from \"@nick/comrak\";\n\n  const options = Options.default();\n  options.extension.highlight = true;\n\n  const html = markdownToHTML(\"Hey, ==this is important==!\", options);\n  assert.strictEqual(html, \"\u003cp\u003eHey, \u003cmark\u003ethis is important\u003c/mark\u003e!\u003c/p\u003e\\n\");\n  ```\n\n### `ParseOptions`\n\n- **`defaultInfoString?: string | null`** Default info string for fenced code\n  blocks (default: `null`).\n\n  ````ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  assert.strictEqual(\n    markdownToHTML(\"```\\nfn hello();\\n```\\n\"),\n    \"\u003cpre\u003e\u003ccode\u003efn hello();\\n\u003c/code\u003e\u003c/pre\u003e\\n\",\n  );\n\n  assert.strictEqual(\n    markdownToHTML(\"```\\nfn hello();\\n```\\n\", {\n      parse: { defaultInfoString: \"rust\" },\n    }),\n    '\u003cpre\u003e\u003ccode class=\"language-rust\"\u003efn hello();\\n\u003c/code\u003e\u003c/pre\u003e\\n',\n  );\n  ````\n\n- **`smart?: boolean`** Enable smart punctuation conversion (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  assert.strictEqual(\n    markdownToHTML(\"'Hello,' \\\"world\\\" ...\"),\n    \"\u003cp\u003e'Hello,' \u0026quot;world\u0026quot; ...\u003c/p\u003e\\n\",\n  );\n\n  assert.strictEqual(\n    markdownToHTML(\"'Hello,' \\\"world\\\" ...\", { parse: { smart: true } }),\n    \"\u003cp\u003e‘Hello,’ “world” …\u003c/p\u003e\\n\",\n  );\n  ```\n\n- **`relaxedTasklistMatching?: boolean`** Allow symbols beyond `x`/`X` for\n  tasklists (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const markdown =\n    \"* [x] Done\\n* [ ] Not done\\n* [-] Also done\\n* [ ] Also not done\\n\";\n\n  const relaxed = markdownToHTML(markdown, {\n    extension: { tasklist: true },\n    parse: { relaxedTasklistMatching: true },\n  });\n  assert.strictEqual(\n    relaxed,\n    '\u003cul\u003e\\n\u003cli\u003e\u003cinput type=\"checkbox\" checked=\"\" disabled=\"\" /\u003e Done\u003c/li\u003e\\n\u003cli\u003e\u003cinput type=\"checkbox\" disabled=\"\" /\u003e Not done\u003c/li\u003e\\n\u003cli\u003e\u003cinput type=\"checkbox\" checked=\"\" disabled=\"\" /\u003e Also done\u003c/li\u003e\\n\u003cli\u003e\u003cinput type=\"checkbox\" disabled=\"\" /\u003e Also not done\u003c/li\u003e\\n\u003c/ul\u003e\\n',\n  );\n\n  const strict = markdownToHTML(markdown, {\n    extension: { tasklist: true },\n    parse: { relaxedTasklistMatching: false },\n  });\n  assert.strictEqual(\n    strict,\n    '\u003cul\u003e\\n\u003cli\u003e\u003cinput type=\"checkbox\" checked=\"\" disabled=\"\" /\u003e Done\u003c/li\u003e\\n\u003cli\u003e\u003cinput type=\"checkbox\" disabled=\"\" /\u003e Not done\u003c/li\u003e\\n\u003cli\u003e[-] Also done\u003c/li\u003e\\n\u003cli\u003e\u003cinput type=\"checkbox\" disabled=\"\" /\u003e Also not done\u003c/li\u003e\\n\u003c/ul\u003e\\n',\n  );\n  ```\n\n- **`tasklistInTable?: boolean`** Parse tasklist items inside tables (default:\n  `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML, Options } from \"@nick/comrak\";\n\n  const options = Options.default();\n  options.extension.table = true;\n  options.extension.tasklist = true;\n\n  const markdown = \"| val |\\n| - |\\n| [ ] |\\n\";\n  assert.strictEqual(\n    markdownToHTML(markdown, options),\n    \"\u003ctable\u003e\\n\u003cthead\u003e\\n\u003ctr\u003e\\n\u003cth\u003eval\u003c/th\u003e\\n\u003c/tr\u003e\\n\u003c/thead\u003e\\n\u003ctbody\u003e\\n\u003ctr\u003e\\n\u003ctd\u003e[ ]\u003c/td\u003e\\n\u003c/tr\u003e\\n\u003c/tbody\u003e\\n\u003c/table\u003e\\n\",\n  );\n\n  options.parse.tasklistInTable = true;\n  assert.strictEqual(\n    markdownToHTML(markdown, options),\n    '\u003ctable\u003e\\n\u003cthead\u003e\\n\u003ctr\u003e\\n\u003cth\u003eval\u003c/th\u003e\\n\u003c/tr\u003e\\n\u003c/thead\u003e\\n\u003ctbody\u003e\\n\u003ctr\u003e\\n\u003ctd\u003e\\n\u003cinput type=\"checkbox\" disabled=\"\" /\u003e \u003c/td\u003e\\n\u003c/tr\u003e\\n\u003c/tbody\u003e\\n\u003c/table\u003e\\n',\n  );\n  ```\n\n- **`relaxedAutolinks?: boolean`** Detect links inside brackets and allow all\n  URL schemes (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"[https://foo.com]\", {\n    extension: { autolink: true },\n    parse: { relaxedAutolinks: true },\n  });\n\n  assert.strictEqual(\n    html,\n    '\u003cp\u003e[\u003ca href=\"https://foo.com\"\u003ehttps://foo.com\u003c/a\u003e]\u003c/p\u003e\\n',\n  );\n  ```\n\n- **`ignoreSetext?: boolean`** Ignore setext headings in input (default:\n  `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"setext heading\\n---\", {\n    parse: { ignoreSetext: true },\n  });\n\n  assert.strictEqual(html, \"\u003cp\u003esetext heading\u003c/p\u003e\\n\u003chr /\u003e\\n\");\n  ```\n\n- **`brokenLinkCallback?: BrokenLinkCallback | null`** Handle undefined link\n  references (default: `null`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML, Options } from \"@nick/comrak\";\n\n  const options = Options.default();\n  options.parse.brokenLinkCallback = (ref) =\u003e ({\n    url: \"https://img.shields.io/badge/placeholder-lightgrey.svg\",\n    title: `Placeholder Badge (original: ${ref.original})`,\n  });\n\n  const html = markdownToHTML(\"![Build Status][undefined-badge]\\n\", options);\n  assert.strictEqual(\n    html,\n    '\u003cp\u003e\u003cimg src=\"https://img.shields.io/badge/placeholder-lightgrey.svg\" alt=\"Build Status\" title=\"Placeholder Badge (original: undefined-badge)\" /\u003e\u003c/p\u003e\\n',\n  );\n  ```\n\n- **`leaveFootnoteDefinitions?: boolean`** Keep footnote definitions in place\n  within the AST (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { Options, parseMarkdown, renderCommonMark } from \"@nick/comrak\";\n\n  const options = Options.default();\n  options.extension.footnotes = true;\n  options.parse.leaveFootnoteDefinitions = true;\n\n  const ast = parseMarkdown(\"Hi[^x].\\n\\n[^x]: A greeting.\\n\", options);\n  const cm = renderCommonMark(ast, options);\n\n  assert.strictEqual(cm, \"Hi[^x].\\n\\n[^x]:\\n    A greeting.\\n\");\n  ```\n\n- **`escapedCharSpans?: boolean`** Keep escaped characters as spans in the AST\n  (default: `false`). Enabling `render.escapedCharSpans` also enables this.\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"Notify user \\\\@example\", {\n    render: { escapedCharSpans: true },\n  });\n\n  assert.strictEqual(\n    html,\n    \"\u003cp\u003eNotify user \u003cspan data-escaped-char\u003e@\u003c/span\u003eexample\u003c/p\u003e\\n\",\n  );\n  ```\n\n### `RenderOptions`\n\n- **`escape?: boolean`** Escape raw HTML instead of clobbering it (default:\n  `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  assert.strictEqual(\n    markdownToHTML(\"\u003ci\u003eitalic text\u003c/i\u003e\"),\n    \"\u003cp\u003e\u003c!-- raw HTML omitted --\u003eitalic text\u003c!-- raw HTML omitted --\u003e\u003c/p\u003e\\n\",\n  );\n\n  assert.strictEqual(\n    markdownToHTML(\"\u003ci\u003eitalic text\u003c/i\u003e\", { render: { escape: true } }),\n    \"\u003cp\u003e\u0026lt;i\u0026gt;italic text\u0026lt;/i\u0026gt;\u003c/p\u003e\\n\",\n  );\n  ```\n\n- **`githubPreLang?: boolean`** Use GitHub-style `\u003cpre lang=\"xyz\"\u003e` for fenced\n  code blocks (default: `false`).\n\n  ````ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  assert.strictEqual(\n    markdownToHTML(\"```rust\\nfn hello();\\n```\\n\", {\n      render: { githubPreLang: true },\n    }),\n    '\u003cpre lang=\"rust\"\u003e\u003ccode\u003efn hello();\\n\u003c/code\u003e\u003c/pre\u003e\\n',\n  );\n  ````\n\n- **`hardbreaks?: boolean`** Convert soft line breaks to hard breaks (default:\n  `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  assert.strictEqual(\n    markdownToHTML(\"Hello.\\nWorld.\\n\"),\n    \"\u003cp\u003eHello.\\nWorld.\u003c/p\u003e\\n\",\n  );\n\n  assert.strictEqual(\n    markdownToHTML(\"Hello.\\nWorld.\\n\", { render: { hardbreaks: true } }),\n    \"\u003cp\u003eHello.\u003cbr /\u003e\\nWorld.\u003c/p\u003e\\n\",\n  );\n  ```\n\n- **`unsafe?: boolean`** Allow rendering of raw HTML and potentially dangerous\n  links (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const markdown = \"\u003cscript\u003e\\nalert('xyz');\\n\u003c/script\u003e\\n\\n\" +\n    \"Possibly \u003cmarquee\u003eannoying\u003c/marquee\u003e.\\n\\n\" +\n    \"[Dangerous](javascript:alert(document.cookie)).\\n\\n\" +\n    \"[Safe](http://commonmark.org).\";\n\n  assert.strictEqual(\n    markdownToHTML(markdown),\n    \"\u003c!-- raw HTML omitted --\u003e\\n\" +\n      \"\u003cp\u003ePossibly \u003c!-- raw HTML omitted --\u003eannoying\u003c!-- raw HTML omitted --\u003e.\u003c/p\u003e\\n\" +\n      '\u003cp\u003e\u003ca href=\"\"\u003eDangerous\u003c/a\u003e.\u003c/p\u003e\\n' +\n      '\u003cp\u003e\u003ca href=\"http://commonmark.org\"\u003eSafe\u003c/a\u003e.\u003c/p\u003e\\n',\n  );\n\n  assert.strictEqual(\n    markdownToHTML(markdown, { render: { unsafe: true } }),\n    \"\u003cscript\u003e\\nalert('xyz');\\n\u003c/script\u003e\\n\" +\n      \"\u003cp\u003ePossibly \u003cmarquee\u003eannoying\u003c/marquee\u003e.\u003c/p\u003e\\n\" +\n      '\u003cp\u003e\u003ca href=\"javascript:alert(document.cookie)\"\u003eDangerous\u003c/a\u003e.\u003c/p\u003e\\n' +\n      '\u003cp\u003e\u003ca href=\"http://commonmark.org\"\u003eSafe\u003c/a\u003e.\u003c/p\u003e\\n',\n  );\n  ```\n\n- **`width?: number`** Wrap column for CommonMark output (default: `0`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToCommonMark } from \"@nick/comrak\";\n\n  const markdown =\n    \"Hello, **world**!\\n\\nNew line of text that should wrap when width is set.\\n\";\n\n  assert.strictEqual(\n    markdownToCommonMark(markdown),\n    \"Hello, **world**\\\\!\\n\\nNew line of text that should wrap when width is set.\\n\",\n  );\n\n  assert.strictEqual(\n    markdownToCommonMark(markdown, { render: { width: 20 } }),\n    \"Hello, **world**\\\\!\\n\\nNew line of text\\nthat should wrap\\nwhen width is set.\\n\",\n  );\n  ```\n\n- **`fullInfoString?: boolean`** Use the full info string for fenced code blocks\n  (default: `false`).\n\n  ````ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"```rust extra info\\nfn hello();\\n```\\n\", {\n    render: { fullInfoString: true },\n  });\n\n  assert.strictEqual(\n    html,\n    '\u003cpre\u003e\u003ccode data-meta=\"extra info\" class=\"language-rust\"\u003efn hello();\\n\u003c/code\u003e\u003c/pre\u003e\\n',\n  );\n  ````\n\n- **`listStyle?: \"dash\" | \"plus\" | \"star\"`** List marker style for CommonMark\n  output (default: `\"dash\"`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToCommonMark } from \"@nick/comrak\";\n\n  assert.strictEqual(\n    markdownToCommonMark(\"* Item\\n* Item\\n\", { render: { listStyle: \"star\" } }),\n    \"* Item\\n* Item\\n\",\n  );\n\n  assert.strictEqual(\n    markdownToCommonMark(\"* Item\\n* Item\\n\", { render: { listStyle: \"dash\" } }),\n    \"- Item\\n- Item\\n\",\n  );\n  ```\n\n- **`sourcepos?: boolean`** Include source position attributes (default:\n  `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"Hello *world*!\", {\n    render: { sourcepos: true },\n  });\n\n  assert.strictEqual(\n    html,\n    '\u003cp data-sourcepos=\"1:1-1:14\"\u003eHello \u003cem data-sourcepos=\"1:7-1:13\"\u003eworld\u003c/em\u003e!\u003c/p\u003e\\n',\n  );\n  ```\n\n- **`escapedCharSpans?: boolean`** Wrap escaped characters in `\u003cspan\u003e` tags\n  (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"Notify user \\\\@example\", {\n    render: { escapedCharSpans: true },\n  });\n\n  assert.strictEqual(\n    html,\n    \"\u003cp\u003eNotify user \u003cspan data-escaped-char\u003e@\u003c/span\u003eexample\u003c/p\u003e\\n\",\n  );\n  ```\n\n- **`ignoreEmptyLinks?: boolean`** Ignore empty links in input (default:\n  `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"[]()\", {\n    render: { ignoreEmptyLinks: true },\n  });\n\n  assert.strictEqual(html, \"\u003cp\u003e[]()\u003c/p\u003e\\n\");\n  ```\n\n- **`gfmQuirks?: boolean`** Enable GFM quirks in HTML output (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"****abcd****\", {\n    render: { gfmQuirks: true },\n  });\n\n  assert.strictEqual(html, \"\u003cp\u003e\u003cstrong\u003eabcd\u003c/strong\u003e\u003c/p\u003e\\n\");\n  ```\n\n- **`preferFenced?: boolean`** Prefer fenced code blocks in CommonMark output\n  (default: `false`).\n\n  ````ts\n  import assert from \"node:assert\";\n  import { markdownToCommonMark } from \"@nick/comrak\";\n\n  assert.strictEqual(\n    markdownToCommonMark(\"    indented code\\n\"),\n    \"    indented code\\n\",\n  );\n\n  assert.strictEqual(\n    markdownToCommonMark(\"    indented code\\n\", {\n      render: { preferFenced: true },\n    }),\n    \"```\\nindented code\\n```\\n\",\n  );\n  ````\n\n- **`figureWithCaption?: boolean`** Render images as `\u003cfigure\u003e` elements with\n  captions (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\n    '![image](https://example.com/image.png \"this is an image\")',\n    { render: { figureWithCaption: true } },\n  );\n\n  assert.strictEqual(\n    html,\n    '\u003cp\u003e\u003cfigure\u003e\u003cimg src=\"https://example.com/image.png\" alt=\"image\" title=\"this is an image\" /\u003e\u003cfigcaption\u003ethis is an image\u003c/figcaption\u003e\u003c/figure\u003e\u003c/p\u003e\\n',\n  );\n  ```\n\n- **`tasklistClasses?: boolean`** Add task list CSS classes (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const html = markdownToHTML(\"- [ ] Foo\", {\n    extension: { tasklist: true },\n    render: { tasklistClasses: true },\n  });\n\n  assert.strictEqual(\n    html,\n    '\u003cul class=\"contains-task-list\"\u003e\\n\u003cli class=\"task-list-item\"\u003e\u003cinput type=\"checkbox\" class=\"task-list-item-checkbox\" disabled=\"\" /\u003e Foo\u003c/li\u003e\\n\u003c/ul\u003e\\n',\n  );\n  ```\n\n- **`olWidth?: number`** Minimum marker width when rendering ordered lists\n  (default: `0`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToHTML } from \"@nick/comrak\";\n\n  const markdown = \"1. one\\n10. ten\\n\";\n\n  assert.strictEqual(\n    markdownToHTML(markdown, { render: { olWidth: 0 } }),\n    \"\u003col\u003e\\n\u003cli\u003eone\u003c/li\u003e\\n\u003cli\u003eten\u003c/li\u003e\\n\u003c/ol\u003e\\n\",\n  );\n\n  assert.strictEqual(\n    markdownToHTML(markdown, { render: { olWidth: 3 } }),\n    \"\u003col\u003e\\n\u003cli\u003eone\u003c/li\u003e\\n\u003cli\u003eten\u003c/li\u003e\\n\u003c/ol\u003e\\n\",\n  );\n  ```\n\n- **`experimentalMinimizeCommonmark?: boolean`** Minimize escapes in CommonMark\n  output (default: `false`).\n\n  ```ts\n  import assert from \"node:assert\";\n  import { markdownToCommonMark } from \"@nick/comrak\";\n\n  assert.strictEqual(\n    markdownToCommonMark(\"Hello, **world**!\\n\"),\n    \"Hello, **world**\\\\!\\n\",\n  );\n\n  assert.strictEqual(\n    markdownToCommonMark(\"Hello, **world**!\\n\", {\n      render: { experimentalMinimizeCommonmark: true },\n    }),\n    \"Hello, **world**!\\n\",\n  );\n  ```\n\n### Plugins\n\n- **`render.codefenceSyntaxHighlighter`** Plug in custom syntax highlighting.\n  See the [plugin example](#usage) above for a minimal adapter that injects\n  custom `\u003cpre\u003e`/`\u003ccode\u003e` tags and HTML.\n- **`render.headingAdapter`** Customize heading rendering. The heading adapter\n  example in the [usage section](#usage) demonstrates adding data attributes and\n  propagating source positions.\n\n---\n\n## Further Reading\n\n### Compatibility\n\nThis package is designed to work seamlessly across multiple JavaScript runtimes.\nWhile [the original `comrak` module] required the Deno runtime, this fork boasts\nwide compatibility that's been verified to run on Deno, Node.js, Bun, Cloudflare\nWorkers, and even web browsers. Theoretically, it should be fully compatible by\nany JavaScript runtime that supports WebAssembly.\n\n### Under the Hood\n\n`@nick/comrak` is built using a combination of Rust, WebAssembly, and\nTypeScript. Internally, this project wraps the [`comrak`] crate by\n[Talya Connor] with the necessary Rust to generate a WebAssembly module.\n\n#### Compilation\n\nThe WebAssembly is built using [`@deno/wasmbuild`], and inlined into a\nJavaScript file for portability and the best compatibility. The [API] that is\nexposed as a thin TypeScript wrapper around the generated JavaScript bindings,\nconsisting mostly of type definitions.\n\n#### Compression\n\nThe [brotli] compression algorithm is used to compress the WebAssembly binary\nduring the build step, making it ~75% smaller and a **lot** quicker to load.\nDecompression is performed immediately on import with [`debrotli`].\n\n#### Performance Gains\n\nWhen all is said and done, this results in a **_very_** snappy experience for\nusers, with minimal overhead during initial load and fast Markdown rendering\ntimes. It also reduces the amount of data transferred over the network, making\nit ideal for use in web applications and serverless environments.\n\n### Motivation\n\nThe original `comrak` module was published on the Deno registry, which cannot be\nused as a dependency in other JavaScript runtimes. JSR has forbidden the use of\nHTTPS-based dependencies in all of its packages (yup, including deno.land),\nthereby rendering the original `comrak` module incompatible with JSR. And thus,\n`@nick/comrak` was born, on an overcast Saturday evening in late March of 2025.\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n**[MIT] · Made with ❤️ by [Nicholas Berlette]**\n\n\u003csmall\u003e\n\n[github] · [issues] · [jsr] · [npm] · [docs]\n\n\u003c/small\u003e\n\u003c/div\u003e\n\n---\n\n[MIT]: https://nick.mit-license.org \"MIT License. Copyright (c) Nicholas Berlette. All rights reserved.\"\n[API]: https://jsr.io/@nick/comrak/doc \"View the API documentation for @nick/comrak\"\n[docs]: https://jsr.io/@nick/comrak/doc \"View the API documentation for @nick/comrak\"\n[GitHub]: https://github.com/nberlette/comrak-wasm#readme \"Give this project a star on GitHub! ⭐\"\n[issues]: https://github.com/nberlette/comrak-wasm/issues \"Report an issue or suggest a feature on GitHub\"\n[JSR]: https://jsr.io/@nick/comrak \"View the @nick/comrak package on JSR: The JavaScript Registry\"\n[npm]: https://npmjs.com/package/comrak \"View the comrak package on npm\"\n[Nicholas Berlette]: https://github.com/nberlette \"Follow @nberlette on GitHub for more cool projects!\"\n[Talya Connor]: https://kivikakk.ee \"View Talya Connor's Personal Website\"\n[`@nick/comrak`]: https://jsr.io/@nick/comrak \"View the @nick/comrak package on the JavaScript Registry\"\n[`comrak`]: https://github.com/kivikakk/comrak \"View the comrak GitHub repository\"\n[the original `comrak` module]: https://deno.land/x/comrak \"View the original comrak module on Deno\"\n[`@deno/wasmbuild`]: https://jsr.io/@deno/wasmbuild \"View the @deno/wasmbuild GitHub repository\"\n[`debrotli`]: https://npmjs.com/package/debrotli \"View the debrotli package on npm\"\n[brotli]: https://github.com/google/brotli \"View the Brotli GitHub repository\"\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnberlette%2Fcomrak-wasm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnberlette%2Fcomrak-wasm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnberlette%2Fcomrak-wasm/lists"}