{"id":13484044,"url":"https://github.com/syntax-tree/hast-util-to-mdast","last_synced_at":"2025-07-12T07:38:09.938Z","repository":{"id":16599599,"uuid":"80322797","full_name":"syntax-tree/hast-util-to-mdast","owner":"syntax-tree","description":"utility to transform hast (HTML) to mdast (markdown)","archived":false,"fork":false,"pushed_at":"2025-01-28T10:04:18.000Z","size":526,"stargazers_count":41,"open_issues_count":0,"forks_count":16,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-02-28T16:56:56.243Z","etag":null,"topics":["hast","hast-util","html","markdown","mdast","mdast-util","unist"],"latest_commit_sha":null,"homepage":"https://unifiedjs.com","language":"JavaScript","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/syntax-tree.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},"funding":{"github":"unifiedjs","open_collective":"unified"}},"created_at":"2017-01-29T01:30:37.000Z","updated_at":"2025-02-01T10:13:26.000Z","dependencies_parsed_at":"2024-01-13T18:24:42.781Z","dependency_job_id":"99b1f00c-03ac-44cf-8a87-84e64e4354a2","html_url":"https://github.com/syntax-tree/hast-util-to-mdast","commit_stats":{"total_commits":267,"total_committers":12,"mean_commits":22.25,"dds":0.05992509363295884,"last_synced_commit":"08be0e15246a59a426be515111012603722c2af1"},"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/syntax-tree%2Fhast-util-to-mdast","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/syntax-tree%2Fhast-util-to-mdast/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/syntax-tree%2Fhast-util-to-mdast/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/syntax-tree%2Fhast-util-to-mdast/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/syntax-tree","download_url":"https://codeload.github.com/syntax-tree/hast-util-to-mdast/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245871766,"owners_count":20686257,"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":["hast","hast-util","html","markdown","mdast","mdast-util","unist"],"created_at":"2024-07-31T17:01:18.538Z","updated_at":"2025-03-27T15:30:59.826Z","avatar_url":"https://github.com/syntax-tree.png","language":"JavaScript","funding_links":["https://github.com/sponsors/unifiedjs","https://opencollective.com/unified"],"categories":["JavaScript","hast utilities"],"sub_categories":[],"readme":"# hast-util-to-mdast\n\n[![Build][build-badge]][build]\n[![Coverage][coverage-badge]][coverage]\n[![Downloads][downloads-badge]][downloads]\n[![Size][size-badge]][size]\n[![Sponsors][sponsors-badge]][collective]\n[![Backers][backers-badge]][collective]\n[![Chat][chat-badge]][chat]\n\n[hast][] utility to transform to [mdast][].\n\n## Contents\n\n* [What is this?](#what-is-this)\n* [When should I use this?](#when-should-i-use-this)\n* [Install](#install)\n* [Use](#use)\n* [API](#api)\n  * [`toMdast(tree[, options])`](#tomdasttree-options)\n  * [`defaultHandlers`](#defaulthandlers)\n  * [`defaultNodeHandlers`](#defaultnodehandlers)\n  * [`Handle`](#handle)\n  * [`NodeHandle`](#nodehandle)\n  * [`Options`](#options)\n  * [`State`](#state)\n* [Examples](#examples)\n  * [Example: ignoring things](#example-ignoring-things)\n  * [Example: keeping some HTML](#example-keeping-some-html)\n* [Algorithm](#algorithm)\n* [Syntax](#syntax)\n* [Syntax tree](#syntax-tree)\n* [Types](#types)\n* [Compatibility](#compatibility)\n* [Security](#security)\n* [Related](#related)\n* [Contribute](#contribute)\n* [License](#license)\n\n## What is this?\n\nThis package is a utility that takes a [hast][] (HTML) syntax tree as input and\nturns it into an [mdast][] (markdown) syntax tree.\n\n## When should I use this?\n\nThis project is useful when you want to turn HTML to markdown.\n\nThe mdast utility [`mdast-util-to-hast`][mdast-util-to-hast] does the inverse of\nthis utility.\nIt turns markdown into HTML.\n\nThe rehype plugin [`rehype-remark`][rehype-remark] wraps this utility to also\nturn HTML to markdown at a higher-level (easier) abstraction.\n\n## Install\n\nThis package is [ESM only][esm].\nIn Node.js (version 16+), install with [npm][]:\n\n```sh\nnpm install hast-util-to-mdast\n```\n\nIn Deno with [`esm.sh`][esmsh]:\n\n```js\nimport {toMdast} from 'https://esm.sh/hast-util-to-mdast@10'\n```\n\nIn browsers with [`esm.sh`][esmsh]:\n\n```html\n\u003cscript type=\"module\"\u003e\n  import {toMdast} from 'https://esm.sh/hast-util-to-mdast@10?bundle'\n\u003c/script\u003e\n```\n\n## Use\n\nSay we have the following `example.html`:\n\n```html\n\u003ch2\u003eHello \u003cstrong\u003eworld!\u003c/strong\u003e\u003c/h2\u003e\n```\n\n…and next to it a module `example.js`:\n\n```js\nimport fs from 'node:fs/promises'\nimport {fromHtml} from 'hast-util-from-html'\nimport {toMdast} from 'hast-util-to-mdast'\nimport {toMarkdown} from 'mdast-util-to-markdown'\n\nconst html = String(await fs.readFile('example.html'))\nconst hast = fromHtml(html, {fragment: true})\nconst mdast = toMdast(hast)\nconst markdown = toMarkdown(mdast)\n\nconsole.log(markdown)\n```\n\n…now running `node example.js` yields:\n\n```markdown\n## Hello **world!**\n```\n\n## API\n\nThis package exports the identifiers [`defaultHandlers`][api-default-handlers],\n[`defaultNodeHandlers`][api-default-node-handlers], and\n[`toMdast`][api-to-mdast].\nThere is no default export.\n\n### `toMdast(tree[, options])`\n\nTransform hast to mdast.\n\n###### Parameters\n\n* `tree` ([`HastNode`][hast-node])\n  — hast tree to transform\n* `options` ([`Options`][api-options], optional)\n  — configuration\n\n###### Returns\n\nmdast tree ([`MdastNode`][mdast-node]).\n\n### `defaultHandlers`\n\nDefault handlers for elements (`Record\u003cstring, Handle\u003e`).\n\nEach key is an element name, each value is a [`Handle`][api-handle].\n\n### `defaultNodeHandlers`\n\nDefault handlers for nodes (`Record\u003cstring, NodeHandle\u003e`).\n\nEach key is a node type, each value is a [`NodeHandle`][api-node-handle].\n\n### `Handle`\n\nHandle a particular element (TypeScript type).\n\n###### Parameters\n\n* `state` ([`State`][api-state])\n  — info passed around about the current state\n* `element` ([`Element`][element])\n  — element to transform\n* `parent` ([`HastParent`][hast-parent])\n  — parent of `element`\n\n###### Returns\n\nmdast node or nodes (`Array\u003cMdastNode\u003e | MdastNode | undefined`).\n\n### `NodeHandle`\n\nHandle a particular node (TypeScript type).\n\n###### Parameters\n\n* `state` ([`State`][api-state])\n  — info passed around about the current state\n* `node` (`any`)\n  — node to transform\n* `parent` ([`HastParent`][hast-parent])\n  — parent of `node`\n\n###### Returns\n\nmdast node or nodes (`Array\u003cMdastNode\u003e | MdastNode | undefined`).\n\n### `Options`\n\nConfiguration (TypeScript type).\n\n##### Fields\n\n###### `newlines`\n\nKeep line endings when collapsing whitespace (`boolean`, default: `false`).\n\nThe default collapses to a single space.\n\n###### `checked`\n\nValue to use for a checked checkbox or radio input (`string`, default: `[x]`).\n\n###### `unchecked`\n\nValue to use for an unchecked checkbox or radio input (`string`, default:\n`[ ]`).\n\n###### `quotes`\n\nList of quotes to use (`Array\u003cstring\u003e`, default: `['\"']`).\n\nEach value can be one or two characters.\nWhen two, the first character determines the opening quote and the second the\nclosing quote at that level.\nWhen one, both the opening and closing quote are that character.\n\nThe order in which the preferred quotes appear determines which quotes to use at\nwhich level of nesting.\nSo, to prefer `‘’` at the first level of nesting, and `“”` at the second, pass\n`['‘’', '“”']`.\nIf `\u003cq\u003e`s are nested deeper than the given amount of quotes, the markers wrap\naround: a third level of nesting when using `['«»', '‹›']` should have double\nguillemets, a fourth single, a fifth double again, etc.\n\n###### `document`\n\nWhether the given tree represents a complete document (`boolean`, default:\n`undefined`).\n\nApplies when the `tree` is a `root` node.\nWhen the tree represents a complete document, then things are wrapped in\nparagraphs when needed, and otherwise they’re left as-is.\nThe default checks for whether there’s mixed content: some *[phrasing][]* nodes\n*and* some non-phrasing nodes.\n\n###### `handlers`\n\nObject mapping tag names to functions handling the corresponding elements\n(`Record\u003cstring, Handle\u003e`).\n\nMerged into the defaults.\nSee [`Handle`][api-handle].\n\n###### `nodeHandlers`\n\nObject mapping node types to functions handling the corresponding nodes\n(`Record\u003cstring, NodeHandle\u003e`).\n\nMerged into the defaults.\nSee [`NodeHandle`][api-node-handle].\n\n### `State`\n\nInfo passed around about the current state (TypeScript type).\n\n###### Fields\n\n* `patch` (`(from: HastNode, to: MdastNode) =\u003e undefined`)\n  — copy a node’s positional info\n* `one` (`(node: HastNode, parent: HastParent | undefined) =\u003e Array\u003cMdastNode\u003e | MdastNode | undefined`)\n  — transform a hast node to mdast\n* `all` (`(parent: HastParent) =\u003e Array\u003cMdastContent\u003e`)\n  — transform the children of a hast parent to mdast\n* `toFlow` (`(nodes: Array\u003cMdastContent\u003e) =\u003e Array\u003cMdastFlowContent\u003e`)\n  — transform a list of mdast nodes to flow\n* `toSpecificContent` (`\u003cParentType\u003e(nodes: Array\u003cMdastContent\u003e, build: (() =\u003e ParentType)) =\u003e Array\u003cParentType\u003e`)\n  — turn arbitrary content into a list of a particular node type\n* `resolve` (`(url: string | null | undefined) =\u003e string`)\n  — resolve a URL relative to a base\n* `options` ([`Options`][api-options])\n  — user configuration\n* `elementById` (`Map\u003cstring, Element\u003e`)\n  — elements by their `id`\n* `handlers` (`Record\u003cstring, Handle\u003e`)\n  — applied element handlers (see [`Handle`][api-handle])\n* `nodeHandlers` (`Record\u003cstring, NodeHandle\u003e`)\n  — applied node handlers (see [`NodeHandle`][api-node-handle])\n* `baseFound` (`boolean`)\n  — whether a `\u003cbase\u003e` element was seen\n* `frozenBaseUrl` (`string | undefined`)\n  — `href` of `\u003cbase\u003e`, if any\n* `inTable` (`boolean`)\n  — whether we’re in a table\n* `qNesting` (`number`)\n  — how deep we’re in `\u003cq\u003e`s\n\n## Examples\n\n### Example: ignoring things\n\nIt’s possible to exclude something from within HTML when turning it into\nmarkdown, by wrapping it in an element with a `data-mdast` attribute set to\n`'ignore'`.\nFor example:\n\n```html\n\u003cp\u003e\u003cstrong\u003eStrong\u003c/strong\u003e and \u003cem data-mdast=\"ignore\"\u003eemphasis\u003c/em\u003e.\u003c/p\u003e\n```\n\nYields:\n\n```markdown\n**Strong** and .\n```\n\nIt’s also possible to pass a handler to ignore nodes.\nFor example, to ignore `em` elements, pass `handlers: {'em': function () {}}`:\n\n```html\n\u003cp\u003e\u003cstrong\u003eStrong\u003c/strong\u003e and \u003cem\u003eemphasis\u003c/em\u003e.\u003c/p\u003e\n```\n\nYields:\n\n```markdown\n**Strong** and .\n```\n\n### Example: keeping some HTML\n\nThe goal of this project is to map HTML to plain and readable markdown.\nThat means that certain elements are ignored (such as `\u003csvg\u003e`) or “downgraded”\n(such as `\u003cvideo\u003e` to links).\nYou can change this by passing handlers.\n\nSay we have the following file `example.html`:\n\n```html\n\u003cp\u003e\n  Some text with\n  \u003csvg viewBox=\"0 0 1 1\" width=\"1\" height=\"1\"\u003e\u003crect fill=\"black\" x=\"0\" y=\"0\" width=\"1\" height=\"1\" /\u003e\u003c/svg\u003e\n  a graphic… Wait is that a dead pixel?\n\u003c/p\u003e\n```\n\nThis can be achieved with `example.js` like so:\n\n```js\n/**\n * @import {Html} from 'mdast'\n */\n\nimport fs from 'node:fs/promises'\nimport {fromHtml} from 'hast-util-from-html'\nimport {toHtml} from 'hast-util-to-html'\nimport {toMdast} from 'hast-util-to-mdast'\nimport {toMarkdown} from 'mdast-util-to-markdown'\n\nconst html = String(await fs.readFile('example.html'))\nconst hast = fromHtml(html, {fragment: true})\nconst mdast = toMdast(hast, {\n  handlers: {\n    svg(state, node) {\n      /** @type {Html} */\n      const result = {type: 'html', value: toHtml(node, {space: 'svg'})}\n      state.patch(node, result)\n      return result\n    }\n  }\n})\nconst markdown = toMarkdown(mdast)\n\nconsole.log(markdown)\n```\n\nYields:\n\n```markdown\nSome text with \u003csvg viewBox=\"0 0 1 1\" width=\"1\" height=\"1\"\u003e\u003crect fill=\"black\" x=\"0\" y=\"0\" width=\"1\" height=\"1\"\u003e\u003c/rect\u003e\u003c/svg\u003e a graphic… Wait is that a dead pixel?\n```\n\n\u003c!-- Old ID of this section. --\u003e\n\n\u003ca name=\"implied-paragraphs\"\u003e\u003c/a\u003e\n\n## Algorithm\n\nThe algorithm used in this project is very powerful.\nIt supports all HTML elements, including ancient elements (`xmp`) and obscure\nones (`base`).\nIt’s particularly good at forms, media, and around implicit and explicit\nparagraphs (see [HTML Standard, A. van Kesteren; et al. WHATWG § 3.2.5.4\nParagraphs][html-paragraphs]), such as:\n\n```html\n\u003carticle\u003e\n  An implicit paragraph.\n  \u003ch1\u003eAn explicit paragraph.\u003c/h1\u003e\n\u003c/article\u003e\n```\n\nYields:\n\n```markdown\nAn implicit paragraph.\n\n# An explicit paragraph.\n```\n\n## Syntax\n\nHTML is handled according to [WHATWG HTML][html] (the living standard), which is\nalso followed by browsers such as Chrome and Firefox.\n\nThis project creates markdown according to [GFM][], which is a standard that’s\nbased on [CommonMark][] but adds the strikethrough (`~like so~`) and tables\n(`| Table header | …`) amongst some alternative syntaxes.\n\n## Syntax tree\n\nThe input syntax tree format is [hast][].\nAny HTML that can be represented in hast is accepted as input.\nThe output syntax tree format is [mdast][].\n\nWhen `\u003ctable\u003e` elements or `\u003cdel\u003e`, `\u003cs\u003e`, and `\u003cstrike\u003e` exist in the HTML,\nthen the GFM nodes `table` and `delete` are used.\nThis utility does not generate definitions or references, or syntax extensions\nsuch as footnotes, frontmatter, or math.\n\n## Types\n\nThis package is fully typed with [TypeScript][].\nIt exports the additional types [`Handle`][api-handle],\n[`NodeHandle`][api-node-handle],\n[`Options`][api-options],\nand [`State`][api-state].\n\n## Compatibility\n\nProjects maintained by the unified collective are compatible with maintained\nversions of Node.js.\n\nWhen we cut a new major release, we drop support for unmaintained versions of\nNode.\nThis means we try to keep the current release line, `hast-util-to-mdast@^10`,\ncompatible with Node.js 16.\n\n## Security\n\nUse of `hast-util-to-mdast` is safe by default.\n\n## Related\n\n* [`hast-util-to-nlcst`](https://github.com/syntax-tree/hast-util-to-nlcst)\n  — transform hast to nlcst\n* [`hast-util-to-xast`](https://github.com/syntax-tree/hast-util-to-xast)\n  — transform hast to xast\n\n## Contribute\n\nSee [`contributing.md` in `syntax-tree/.github`][health-contributing] for ways\nto get started.\nSee [`support.md`][health-support] for ways to get help.\n\nThis project has a [code of conduct][health-coc].\nBy interacting with this repository, organization, or community you agree to\nabide by its terms.\n\n## License\n\n[MIT][file-license] © [Titus Wormer][author]\n\n\u003c!-- Definitions --\u003e\n\n[api-default-handlers]: #defaulthandlers\n\n[api-default-node-handlers]: #defaultnodehandlers\n\n[api-handle]: #handle\n\n[api-node-handle]: #nodehandle\n\n[api-options]: #options\n\n[api-state]: #state\n\n[api-to-mdast]: #tomdasttree-options\n\n[author]: https://wooorm.com\n\n[backers-badge]: https://opencollective.com/unified/backers/badge.svg\n\n[build]: https://github.com/syntax-tree/hast-util-to-mdast/actions\n\n[build-badge]: https://github.com/syntax-tree/hast-util-to-mdast/workflows/main/badge.svg\n\n[chat]: https://github.com/syntax-tree/unist/discussions\n\n[chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg\n\n[collective]: https://opencollective.com/unified\n\n[commonmark]: https://commonmark.org\n\n[coverage]: https://codecov.io/github/syntax-tree/hast-util-to-mdast\n\n[coverage-badge]: https://img.shields.io/codecov/c/github/syntax-tree/hast-util-to-mdast.svg\n\n[downloads]: https://www.npmjs.com/package/hast-util-to-mdast\n\n[downloads-badge]: https://img.shields.io/npm/dm/hast-util-to-mdast.svg\n\n[element]: https://github.com/syntax-tree/hast#element\n\n[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c\n\n[esmsh]: https://esm.sh\n\n[file-license]: license\n\n[gfm]: https://github.github.com/gfm/\n\n[hast]: https://github.com/syntax-tree/hast\n\n[hast-node]: https://github.com/syntax-tree/hast#nodes\n\n[hast-parent]: https://github.com/syntax-tree/hast#parent\n\n[health-coc]: https://github.com/syntax-tree/.github/blob/main/code-of-conduct.md\n\n[health-contributing]: https://github.com/syntax-tree/.github/blob/main/contributing.md\n\n[health-support]: https://github.com/syntax-tree/.github/blob/main/support.md\n\n[html]: https://html.spec.whatwg.org/multipage/\n\n[html-paragraphs]: https://html.spec.whatwg.org/multipage/dom.html#paragraphs\n\n[mdast]: https://github.com/syntax-tree/mdast\n\n[mdast-node]: https://github.com/syntax-tree/mdast#nodes\n\n[mdast-util-to-hast]: https://github.com/syntax-tree/mdast-util-to-hast\n\n[npm]: https://docs.npmjs.com/cli/install\n\n[phrasing]: https://github.com/syntax-tree/mdast#phrasingcontent\n\n[rehype-remark]: https://github.com/rehypejs/rehype-remark\n\n[size]: https://bundlejs.com/?q=hast-util-to-mdast\n\n[size-badge]: https://img.shields.io/badge/dynamic/json?label=minzipped%20size\u0026query=$.size.compressedSize\u0026url=https://deno.bundlejs.com/?q=hast-util-to-mdast\n\n[sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg\n\n[typescript]: https://www.typescriptlang.org\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsyntax-tree%2Fhast-util-to-mdast","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsyntax-tree%2Fhast-util-to-mdast","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsyntax-tree%2Fhast-util-to-mdast/lists"}