Socket
Socket
Sign inDemoInstall

mdast-util-to-hast

Package Overview
Dependencies
Maintainers
2
Versions
59
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mdast-util-to-hast - npm Package Compare versions

Comparing version 12.2.6 to 12.3.0

lib/handlers/table-cell.d.ts

20

complex-types.d.ts

@@ -1,18 +0,4 @@

import type {Literal} from 'hast'
// To do: next major: remove this file.
export type {Raw} from './index.js'
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
export interface Raw extends Literal {
type: 'raw'
}
declare module 'hast' {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface RootContentMap {
raw: Raw
}
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface ElementContentMap {
raw: Raw
}
}
/// <reference types="./index.js" />

@@ -1,7 +0,48 @@

export type Options = import('./lib/index.js').Options
export type Handler = import('./lib/index.js').Handler
export type Handlers = import('./lib/index.js').Handlers
export type H = import('./lib/index.js').H
export type Raw = import('./complex-types.js').Raw
export {one, all} from './lib/traverse.js'
export {defaultHandlers, toHast} from './lib/index.js'
import type {Literal} from 'hast'
import type {State} from './lib/state.js'
// Expose types.
export type {State, Handler, Handlers, Options} from './lib/state.js'
// To do: next major: remove.
/**
* Deprecated: use `State`.
*/
export type H = State
// Expose JS API.
export {handlers as defaultHandlers} from './lib/handlers/index.js'
// To do: next major: remove.
export {one, all} from './lib/state.js'
export {toHast} from './lib/index.js'
// Expose node type.
/**
* Raw string of HTML embedded into HTML AST.
*/
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
export interface Raw extends Literal {
/**
* Node type.
*/
type: 'raw'
}
// Register nodes in content.
declare module 'hast' {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface RootContentMap {
/**
* Raw string of HTML embedded into HTML AST.
*/
raw: Raw
}
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface ElementContentMap {
/**
* Raw string of HTML embedded into HTML AST.
*/
raw: Raw
}
}

@@ -1,10 +0,5 @@

/**
* @typedef {import('./lib/index.js').Options} Options
* @typedef {import('./lib/index.js').Handler} Handler
* @typedef {import('./lib/index.js').Handlers} Handlers
* @typedef {import('./lib/index.js').H} H
* @typedef {import('./complex-types.js').Raw} Raw
*/
export {one, all} from './lib/traverse.js'
export {defaultHandlers, toHast} from './lib/index.js'
// Note: types exposed from `index.d.ts`.
export {handlers as defaultHandlers} from './lib/handlers/index.js'
// To do: next major: remove.
export {one, all} from './lib/state.js'
export {toHast} from './lib/index.js'
/**
* @param {H} h
* @returns {Element|null}
* Generate a hast footer for called footnote definitions.
*
* @param {State} state
* Info passed around.
* @returns {Element | undefined}
* `section` element or `undefined`.
*/
export function footer(h: H): Element | null
export function footer(state: State): Element | undefined
export type Element = import('hast').Element
export type ElementContent = import('hast').ElementContent
export type H = import('./index.js').H
export type State = import('./state.js').State
/**
* @typedef {import('hast').Element} Element
* @typedef {import('hast').ElementContent} ElementContent
* @typedef {import('./index.js').H} H
*
* @typedef {import('./state.js').State} State
*/
import {normalizeUri} from 'micromark-util-sanitize-uri'
import {u} from 'unist-builder'
import {all} from './traverse.js'
import {wrap} from './wrap.js'
/**
* @param {H} h
* @returns {Element|null}
* Generate a hast footer for called footnote definitions.
*
* @param {State} state
* Info passed around.
* @returns {Element | undefined}
* `section` element or `undefined`.
*/
export function footer(h) {
let index = -1
export function footer(state) {
/** @type {Array<ElementContent>} */
const listItems = []
let index = -1
while (++index < h.footnoteOrder.length) {
const def = h.footnoteById[h.footnoteOrder[index].toUpperCase()]
while (++index < state.footnoteOrder.length) {
const def = state.footnoteById[state.footnoteOrder[index]]

@@ -28,4 +30,4 @@ if (!def) {

const content = all(h, def)
const id = String(def.identifier)
const content = state.all(def)
const id = String(def.identifier).toUpperCase()
const safeId = normalizeUri(id.toLowerCase())

@@ -36,3 +38,3 @@ let referenceIndex = 0

while (++referenceIndex <= h.footnoteCounts[id]) {
while (++referenceIndex <= state.footnoteCounts[id]) {
/** @type {Element} */

@@ -45,3 +47,3 @@ const backReference = {

'#' +
h.clobberPrefix +
state.clobberPrefix +
'fnref-' +

@@ -52,3 +54,3 @@ safeId +

className: ['data-footnote-backref'],
ariaLabel: h.footnoteBackLabel
ariaLabel: state.footnoteBackLabel
},

@@ -92,9 +94,7 @@ children: [{type: 'text', value: '↩'}]

tagName: 'li',
properties: {id: h.clobberPrefix + 'fn-' + safeId},
children: wrap(content, true)
properties: {id: state.clobberPrefix + 'fn-' + safeId},
children: state.wrap(content, true)
}
if (def.position) {
listItem.position = def.position
}
state.patch(def, listItem)

@@ -105,3 +105,3 @@ listItems.push(listItem)

if (listItems.length === 0) {
return null
return
}

@@ -116,8 +116,9 @@

type: 'element',
tagName: h.footnoteLabelTagName,
tagName: state.footnoteLabelTagName,
properties: {
...JSON.parse(JSON.stringify(h.footnoteLabelProperties)),
// To do: use structured clone.
...JSON.parse(JSON.stringify(state.footnoteLabelProperties)),
id: 'footnote-label'
},
children: [u('text', h.footnoteLabel)]
children: [{type: 'text', value: state.footnoteLabel}]
},

@@ -129,3 +130,3 @@ {type: 'text', value: '\n'},

properties: {},
children: wrap(listItems, true)
children: state.wrap(listItems, true)
},

@@ -132,0 +133,0 @@ {type: 'text', value: '\n'}

/**
* @param {H} h
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').Blockquote} Blockquote
* @typedef {import('../state.js').State} State
*/
/**
* Turn an mdast `blockquote` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Blockquote} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function blockquote(h: H, node: Blockquote): import('hast').Element
export function blockquote(state: State, node: Blockquote): Element
export type Element = import('hast').Element
export type Blockquote = import('mdast').Blockquote
export type H = import('../index.js').H
export type State = import('../state.js').State
/**
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').Blockquote} Blockquote
* @typedef {import('../index.js').H} H
* @typedef {import('../state.js').State} State
*/
import {wrap} from '../wrap.js'
import {all} from '../traverse.js'
/**
* @param {H} h
* Turn an mdast `blockquote` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Blockquote} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function blockquote(h, node) {
return h(node, 'blockquote', wrap(all(h, node), true))
export function blockquote(state, node) {
/** @type {Element} */
const result = {
type: 'element',
tagName: 'blockquote',
properties: {},
children: state.wrap(state.all(node), true)
}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* @param {H} h
* @typedef {import('hast').Element} Element
* @typedef {import('hast').Text} Text
* @typedef {import('mdast').Break} Break
* @typedef {import('../state.js').State} State
*/
/**
* Turn an mdast `break` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Break} node
* @returns {Array<Element|Text>}
* mdast node.
* @returns {Array<Element | Text>}
* hast element content.
*/
export function hardBreak(h: H, node: Break): Array<Element | Text>
export function hardBreak(state: State, node: Break): Array<Element | Text>
export type Element = import('hast').Element
export type Text = import('hast').Text
export type Break = import('mdast').Break
export type H = import('../index.js').H
export type State = import('../state.js').State

@@ -5,14 +5,20 @@ /**

* @typedef {import('mdast').Break} Break
* @typedef {import('../index.js').H} H
* @typedef {import('../state.js').State} State
*/
import {u} from 'unist-builder'
/**
* @param {H} h
* Turn an mdast `break` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Break} node
* @returns {Array<Element|Text>}
* mdast node.
* @returns {Array<Element | Text>}
* hast element content.
*/
export function hardBreak(h, node) {
return [h(node, 'br'), u('text', '\n')]
export function hardBreak(state, node) {
/** @type {Element} */
const result = {type: 'element', tagName: 'br', properties: {}, children: []}
state.patch(node, result)
return [state.applyData(node, result), {type: 'text', value: '\n'}]
}
/**
* @param {H} h
* @typedef {import('hast').Element} Element
* @typedef {import('hast').Properties} Properties
* @typedef {import('mdast').Code} Code
* @typedef {import('../state.js').State} State
*/
/**
* Turn an mdast `code` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Code} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function code(h: H, node: Code): import('hast').Element
export function code(state: State, node: Code): Element
export type Element = import('hast').Element
export type Properties = import('hast').Properties
export type Code = import('mdast').Code
export type Properties = import('hast').Properties
export type H = import('../index.js').H
export type State = import('../state.js').State
/**
* @typedef {import('hast').Element} Element
* @typedef {import('hast').Properties} Properties
* @typedef {import('mdast').Code} Code
* @typedef {import('hast').Properties} Properties
* @typedef {import('../index.js').H} H
* @typedef {import('../state.js').State} State
*/
import {u} from 'unist-builder'
/**
* @param {H} h
* Turn an mdast `code` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Code} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function code(h, node) {
export function code(state, node) {
const value = node.value ? node.value + '\n' : ''
// To do: next major, use `node.lang` w/o regex, the splitting’s been going
// on for years in remark now.
const lang = node.lang && node.lang.match(/^[^ \t]+(?=[ \t]|$)/)
const lang = node.lang ? node.lang.match(/^[^ \t]+(?=[ \t]|$)/) : null
/** @type {Properties} */
const props = {}
const properties = {}
if (lang) {
props.className = ['language-' + lang]
properties.className = ['language-' + lang]
}
const code = h(node, 'code', props, [u('text', value)])
// Create `<code>`.
/** @type {Element} */
let result = {
type: 'element',
tagName: 'code',
properties,
children: [{type: 'text', value}]
}
if (node.meta) {
code.data = {meta: node.meta}
result.data = {meta: node.meta}
}
return h(node.position, 'pre', [code])
state.patch(node, result)
result = state.applyData(node, result)
// Create `<pre>`.
result = {type: 'element', tagName: 'pre', properties: {}, children: [result]}
state.patch(node, result)
return result
}
/**
* @param {H} h
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').Delete} Delete
* @typedef {import('../state.js').State} State
*/
/**
* Turn an mdast `delete` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Delete} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function strikethrough(h: H, node: Delete): import('hast').Element
export function strikethrough(state: State, node: Delete): Element
export type Element = import('hast').Element
export type Delete = import('mdast').Delete
export type H = import('../index.js').H
export type State = import('../state.js').State
/**
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').Delete} Delete
* @typedef {import('../index.js').H} H
* @typedef {import('../state.js').State} State
*/
import {all} from '../traverse.js'
/**
* @param {H} h
* Turn an mdast `delete` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Delete} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function strikethrough(h, node) {
return h(node, 'del', all(h, node))
export function strikethrough(state, node) {
/** @type {Element} */
const result = {
type: 'element',
tagName: 'del',
properties: {},
children: state.all(node)
}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* @param {H} h
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').Emphasis} Emphasis
* @typedef {import('../state.js').State} State
*/
/**
* Turn an mdast `emphasis` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Emphasis} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function emphasis(h: H, node: Emphasis): import('hast').Element
export function emphasis(state: State, node: Emphasis): Element
export type Element = import('hast').Element
export type Emphasis = import('mdast').Emphasis
export type H = import('../index.js').H
export type State = import('../state.js').State
/**
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').Emphasis} Emphasis
* @typedef {import('../index.js').H} H
* @typedef {import('../state.js').State} State
*/
import {all} from '../traverse.js'
/**
* @param {H} h
* Turn an mdast `emphasis` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Emphasis} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function emphasis(h, node) {
return h(node, 'em', all(h, node))
export function emphasis(state, node) {
/** @type {Element} */
const result = {
type: 'element',
tagName: 'em',
properties: {},
children: state.all(node)
}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* @param {H} h
* Turn an mdast `footnoteReference` node into hast.
*
* @param {State} state
* Info passed around.
* @param {FootnoteReference} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function footnoteReference(
h: H,
state: State,
node: FootnoteReference
): import('hast').Element
): Element
export type FootnoteReference = import('mdast').FootnoteReference
export type H = import('../index.js').H
export type Element = import('hast').Element
export type State = import('../state.js').State
/**
* @typedef {import('mdast').FootnoteReference} FootnoteReference
* @typedef {import('../index.js').H} H
* @typedef {import('hast').Element} Element
* @typedef {import('../state.js').State} State
*/
import {normalizeUri} from 'micromark-util-sanitize-uri'
import {u} from 'unist-builder'
/**
* @param {H} h
* Turn an mdast `footnoteReference` node into hast.
*
* @param {State} state
* Info passed around.
* @param {FootnoteReference} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function footnoteReference(h, node) {
const id = String(node.identifier)
export function footnoteReference(state, node) {
const id = String(node.identifier).toUpperCase()
const safeId = normalizeUri(id.toLowerCase())
const index = h.footnoteOrder.indexOf(id)
const index = state.footnoteOrder.indexOf(id)
/** @type {number} */

@@ -21,29 +27,39 @@ let counter

if (index === -1) {
h.footnoteOrder.push(id)
h.footnoteCounts[id] = 1
counter = h.footnoteOrder.length
state.footnoteOrder.push(id)
state.footnoteCounts[id] = 1
counter = state.footnoteOrder.length
} else {
h.footnoteCounts[id]++
state.footnoteCounts[id]++
counter = index + 1
}
const reuseCounter = h.footnoteCounts[id]
const reuseCounter = state.footnoteCounts[id]
return h(node, 'sup', [
h(
node.position,
'a',
{
href: '#' + h.clobberPrefix + 'fn-' + safeId,
id:
h.clobberPrefix +
'fnref-' +
safeId +
(reuseCounter > 1 ? '-' + reuseCounter : ''),
dataFootnoteRef: true,
ariaDescribedBy: 'footnote-label'
},
[u('text', String(counter))]
)
])
/** @type {Element} */
const link = {
type: 'element',
tagName: 'a',
properties: {
href: '#' + state.clobberPrefix + 'fn-' + safeId,
id:
state.clobberPrefix +
'fnref-' +
safeId +
(reuseCounter > 1 ? '-' + reuseCounter : ''),
dataFootnoteRef: true,
ariaDescribedBy: ['footnote-label']
},
children: [{type: 'text', value: String(counter)}]
}
state.patch(node, link)
/** @type {Element} */
const sup = {
type: 'element',
tagName: 'sup',
properties: {},
children: [link]
}
state.patch(node, sup)
return state.applyData(node, sup)
}
/**
* @param {H} h
* Turn an mdast `footnote` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Footnote} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function footnote(h: H, node: Footnote): import('hast').Element
export function footnote(state: State, node: Footnote): Element
export type Element = import('hast').Element
export type Footnote = import('mdast').Footnote
export type H = import('../index.js').H
export type State = import('../state.js').State
/**
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').Footnote} Footnote
* @typedef {import('../index.js').H} H
*
* @todo
* `footnote` (or “inline note”) are a pandoc footnotes feature (`^[a note]`)
* that does not exist in GFM.
* We still have support for it, so that things remain working with
* `micromark-extension-footnote` and `mdast-util-footnote`, but in the future
* we might be able to remove it?
* @typedef {import('../state.js').State} State
*/

@@ -15,8 +9,20 @@

// To do: when both:
// * <https://github.com/micromark/micromark-extension-footnote>
// * <https://github.com/syntax-tree/mdast-util-footnote>
// …are archived, remove this (also from mdast).
// These inline notes are not used in GFM.
/**
* @param {H} h
* Turn an mdast `footnote` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Footnote} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function footnote(h, node) {
const footnoteById = h.footnoteById
export function footnote(state, node) {
const footnoteById = state.footnoteById
let no = 1

@@ -35,3 +41,3 @@

return footnoteReference(h, {
return footnoteReference(state, {
type: 'footnoteReference',

@@ -38,0 +44,0 @@ identifier,

/**
* @param {H} h
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').Heading} Heading
* @typedef {import('../state.js').State} State
*/
/**
* Turn an mdast `heading` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Heading} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function heading(h: H, node: Heading): import('hast').Element
export function heading(state: State, node: Heading): Element
export type Element = import('hast').Element
export type Heading = import('mdast').Heading
export type H = import('../index.js').H
export type State = import('../state.js').State
/**
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').Heading} Heading
* @typedef {import('../index.js').H} H
* @typedef {import('../state.js').State} State
*/
import {all} from '../traverse.js'
/**
* @param {H} h
* Turn an mdast `heading` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Heading} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function heading(h, node) {
return h(node, 'h' + node.depth, all(h, node))
export function heading(state, node) {
/** @type {Element} */
const result = {
type: 'element',
tagName: 'h' + node.depth,
properties: {},
children: state.all(node)
}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* Return either a `raw` node in dangerous mode, otherwise nothing.
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').HTML} Html
* @typedef {import('../state.js').State} State
* @typedef {import('../../index.js').Raw} Raw
*/
/**
* Turn an mdast `html` node into hast (`raw` node in dangerous mode, otherwise
* nothing).
*
* @param {H} h
* @param {HTML} node
* @param {State} state
* Info passed around.
* @param {Html} node
* mdast node.
* @returns {Raw | Element | null}
* hast node.
*/
export function html(h: H, node: HTML): import('hast').ElementContent | null
export type HTML = import('mdast').HTML
export type H = import('../index.js').H
export function html(state: State, node: Html): Raw | Element | null
export type Element = import('hast').Element
export type Html = import('mdast').HTML
export type State = import('../state.js').State
export type Raw = import('../../index.js').Raw
/**
* @typedef {import('mdast').HTML} HTML
* @typedef {import('../index.js').H} H
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').HTML} Html
* @typedef {import('../state.js').State} State
* @typedef {import('../../index.js').Raw} Raw
*/
import {u} from 'unist-builder'
/**
* Return either a `raw` node in dangerous mode, otherwise nothing.
* Turn an mdast `html` node into hast (`raw` node in dangerous mode, otherwise
* nothing).
*
* @param {H} h
* @param {HTML} node
* @param {State} state
* Info passed around.
* @param {Html} node
* mdast node.
* @returns {Raw | Element | null}
* hast node.
*/
export function html(h, node) {
return h.dangerous ? h.augment(node, u('raw', node.value)) : null
export function html(state, node) {
if (state.dangerous) {
/** @type {Raw} */
const result = {type: 'raw', value: node.value}
state.patch(node, result)
return state.applyData(node, result)
}
// To do: next major: return `undefined`.
return null
}
/**
* @param {H} h
* Turn an mdast `imageReference` node into hast.
*
* @param {State} state
* Info passed around.
* @param {ImageReference} node
* mdast node.
* @returns {ElementContent | Array<ElementContent>}
* hast node.
*/
export function imageReference(
h: H,
state: State,
node: ImageReference
): import('hast').ElementContent | import('hast').ElementContent[]
): ElementContent | Array<ElementContent>
export type ElementContent = import('hast').ElementContent
export type Element = import('hast').Element
export type Properties = import('hast').Properties
export type ImageReference = import('mdast').ImageReference
export type Parent = import('mdast').Parent
export type Properties = import('hast').Properties
export type H = import('../index.js').H
export type State = import('../state.js').State
/**
* @typedef {import('hast').ElementContent} ElementContent
* @typedef {import('hast').Element} Element
* @typedef {import('hast').Properties} Properties
* @typedef {import('mdast').ImageReference} ImageReference
* @typedef {import('mdast').Parent} Parent
* @typedef {import('hast').Properties} Properties
* @typedef {import('../index.js').H} H
* @typedef {import('../state.js').State} State
*/

@@ -12,20 +13,29 @@

/**
* @param {H} h
* Turn an mdast `imageReference` node into hast.
*
* @param {State} state
* Info passed around.
* @param {ImageReference} node
* mdast node.
* @returns {ElementContent | Array<ElementContent>}
* hast node.
*/
export function imageReference(h, node) {
const def = h.definition(node.identifier)
export function imageReference(state, node) {
const def = state.definition(node.identifier)
if (!def) {
return revert(h, node)
return revert(state, node)
}
/** @type {Properties} */
const props = {src: normalizeUri(def.url || ''), alt: node.alt}
const properties = {src: normalizeUri(def.url || ''), alt: node.alt}
if (def.title !== null && def.title !== undefined) {
props.title = def.title
properties.title = def.title
}
return h(node, 'img', props)
/** @type {Element} */
const result = {type: 'element', tagName: 'img', properties, children: []}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* @param {H} h
* Turn an mdast `image` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Image} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function image(h: H, node: Image): import('hast').Element
export function image(state: State, node: Image): Element
export type Element = import('hast').Element
export type Properties = import('hast').Properties
export type Image = import('mdast').Image
export type Properties = import('hast').Properties
export type H = import('../index.js').H
export type State = import('../state.js').State
/**
* @typedef {import('hast').Element} Element
* @typedef {import('hast').Properties} Properties
* @typedef {import('mdast').Image} Image
* @typedef {import('hast').Properties} Properties
* @typedef {import('../index.js').H} H
* @typedef {import('../state.js').State} State
*/

@@ -10,14 +11,27 @@

/**
* @param {H} h
* Turn an mdast `image` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Image} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function image(h, node) {
export function image(state, node) {
/** @type {Properties} */
const props = {src: normalizeUri(node.url), alt: node.alt}
const properties = {src: normalizeUri(node.url)}
if (node.alt !== null && node.alt !== undefined) {
properties.alt = node.alt
}
if (node.title !== null && node.title !== undefined) {
props.title = node.title
properties.title = node.title
}
return h(node, 'img', props)
/** @type {Element} */
const result = {type: 'element', tagName: 'img', properties, children: []}
state.patch(node, result)
return state.applyData(node, result)
}

@@ -22,2 +22,4 @@ export namespace handlers {

export {table}
export {tableCell}
export {tableRow}
export {text}

@@ -50,2 +52,4 @@ export {thematicBreak}

import {table} from './table.js'
import {tableCell} from './table-cell.js'
import {tableRow} from './table-row.js'
import {text} from './text.js'

@@ -52,0 +56,0 @@ import {thematicBreak} from './thematic-break.js'

@@ -21,5 +21,10 @@ import {blockquote} from './blockquote.js'

import {table} from './table.js'
import {tableRow} from './table-row.js'
import {tableCell} from './table-cell.js'
import {text} from './text.js'
import {thematicBreak} from './thematic-break.js'
/**
* Default handlers for nodes.
*/
export const handlers = {

@@ -46,2 +51,4 @@ blockquote,

table,
tableCell,
tableRow,
text,

@@ -57,3 +64,4 @@ thematicBreak,

function ignore() {
// To do: next major: return `undefined`.
return null
}
/**
* @param {H} h
* @typedef {import('hast').Element} Element
* @typedef {import('hast').Text} Text
* @typedef {import('mdast').InlineCode} InlineCode
* @typedef {import('../state.js').State} State
*/
/**
* Turn an mdast `inlineCode` node into hast.
*
* @param {State} state
* Info passed around.
* @param {InlineCode} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function inlineCode(h: H, node: InlineCode): import('hast').Element
export function inlineCode(state: State, node: InlineCode): Element
export type Element = import('hast').Element
export type Text = import('hast').Text
export type InlineCode = import('mdast').InlineCode
export type H = import('../index.js').H
export type State = import('../state.js').State
/**
* @typedef {import('hast').Element} Element
* @typedef {import('hast').Text} Text
* @typedef {import('mdast').InlineCode} InlineCode
* @typedef {import('../index.js').H} H
* @typedef {import('../state.js').State} State
*/
import {u} from 'unist-builder'
/**
* @param {H} h
* Turn an mdast `inlineCode` node into hast.
*
* @param {State} state
* Info passed around.
* @param {InlineCode} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function inlineCode(h, node) {
return h(node, 'code', [u('text', node.value.replace(/\r?\n|\r/g, ' '))])
export function inlineCode(state, node) {
/** @type {Text} */
const text = {type: 'text', value: node.value.replace(/\r?\n|\r/g, ' ')}
state.patch(node, text)
/** @type {Element} */
const result = {
type: 'element',
tagName: 'code',
properties: {},
children: [text]
}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* @param {H} h
* Turn an mdast `linkReference` node into hast.
*
* @param {State} state
* Info passed around.
* @param {LinkReference} node
* mdast node.
* @returns {ElementContent | Array<ElementContent>}
* hast node.
*/
export function linkReference(
h: H,
state: State,
node: LinkReference
): import('hast').ElementContent | import('hast').ElementContent[]
): ElementContent | Array<ElementContent>
export type Element = import('hast').Element
export type ElementContent = import('hast').ElementContent
export type Properties = import('hast').Properties
export type LinkReference = import('mdast').LinkReference
export type Properties = import('hast').Properties
export type H = import('../index.js').H
export type Parent = import('mdast').Parent
export type State = import('../state.js').State
/**
* @typedef {import('hast').Element} Element
* @typedef {import('hast').ElementContent} ElementContent
* @typedef {import('hast').Properties} Properties
* @typedef {import('mdast').LinkReference} LinkReference
* @typedef {import('hast').Properties} Properties
* @typedef {import('../index.js').H} H
* @typedef {import('mdast').Parent} Parent
* @typedef {import('../state.js').State} State
*/

@@ -10,23 +11,36 @@

import {revert} from '../revert.js'
import {all} from '../traverse.js'
/**
* @param {H} h
* Turn an mdast `linkReference` node into hast.
*
* @param {State} state
* Info passed around.
* @param {LinkReference} node
* mdast node.
* @returns {ElementContent | Array<ElementContent>}
* hast node.
*/
export function linkReference(h, node) {
const def = h.definition(node.identifier)
export function linkReference(state, node) {
const def = state.definition(node.identifier)
if (!def) {
return revert(h, node)
return revert(state, node)
}
/** @type {Properties} */
const props = {href: normalizeUri(def.url || '')}
const properties = {href: normalizeUri(def.url || '')}
if (def.title !== null && def.title !== undefined) {
props.title = def.title
properties.title = def.title
}
return h(node, 'a', props, all(h, node))
/** @type {Element} */
const result = {
type: 'element',
tagName: 'a',
properties,
children: state.all(node)
}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* @param {H} h
* Turn an mdast `link` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Link} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function link(h: H, node: Link): import('hast').Element
export function link(state: State, node: Link): Element
export type Element = import('hast').Element
export type Properties = import('hast').Properties
export type Link = import('mdast').Link
export type Properties = import('hast').Properties
export type H = import('../index.js').H
export type State = import('../state.js').State
/**
* @typedef {import('hast').Element} Element
* @typedef {import('hast').Properties} Properties
* @typedef {import('mdast').Link} Link
* @typedef {import('hast').Properties} Properties
* @typedef {import('../index.js').H} H
* @typedef {import('../state.js').State} State
*/
import {normalizeUri} from 'micromark-util-sanitize-uri'
import {all} from '../traverse.js'
/**
* @param {H} h
* Turn an mdast `link` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Link} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function link(h, node) {
export function link(state, node) {
/** @type {Properties} */
const props = {href: normalizeUri(node.url)}
const properties = {href: normalizeUri(node.url)}
if (node.title !== null && node.title !== undefined) {
props.title = node.title
properties.title = node.title
}
return h(node, 'a', props, all(h, node))
/** @type {Element} */
const result = {
type: 'element',
tagName: 'a',
properties,
children: state.all(node)
}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* @param {H} h
* @typedef {import('hast').Element} Element
* @typedef {import('hast').ElementContent} ElementContent
* @typedef {import('hast').Properties} Properties
* @typedef {import('mdast').Content} Content
* @typedef {import('mdast').ListItem} ListItem
* @typedef {import('mdast').Parent} Parent
* @typedef {import('mdast').Root} Root
* @typedef {import('../state.js').State} State
*/
/**
* @typedef {Root | Content} Nodes
* @typedef {Extract<Nodes, Parent>} Parents
*/
/**
* Turn an mdast `listItem` node into hast.
*
* @param {State} state
* Info passed around.
* @param {ListItem} node
* @param {List} parent
* mdast node.
* @param {Parents | null | undefined} parent
* Parent of `node`.
* @returns {Element}
* hast node.
*/
export function listItem(
h: H,
state: State,
node: ListItem,
parent: List
): import('hast').Element
parent: Parents | null | undefined
): Element
export type Element = import('hast').Element
export type ElementContent = import('hast').ElementContent
export type Properties = import('hast').Properties
export type Content = import('mdast').Content
export type ListItem = import('mdast').ListItem
export type List = import('mdast').List
export type Properties = import('hast').Properties
export type Element = import('hast').Element
export type H = import('../index.js').H
export type Content = import('../index.js').Content
export type Parent = import('mdast').Parent
export type Root = import('mdast').Root
export type State = import('../state.js').State
export type Nodes = Root | Content
export type Parents = Extract<Nodes, Parent>
/**
* @typedef {import('hast').Element} Element
* @typedef {import('hast').ElementContent} ElementContent
* @typedef {import('hast').Properties} Properties
* @typedef {import('mdast').Content} Content
* @typedef {import('mdast').ListItem} ListItem
* @typedef {import('mdast').List} List
* @typedef {import('hast').Properties} Properties
* @typedef {import('hast').Element} Element
* @typedef {import('../index.js').H} H
* @typedef {import('../index.js').Content} Content
* @typedef {import('mdast').Parent} Parent
* @typedef {import('mdast').Root} Root
* @typedef {import('../state.js').State} State
*/
import {u} from 'unist-builder'
import {all} from '../traverse.js'
/**
* @typedef {Root | Content} Nodes
* @typedef {Extract<Nodes, Parent>} Parents
*/
/**
* @param {H} h
* Turn an mdast `listItem` node into hast.
*
* @param {State} state
* Info passed around.
* @param {ListItem} node
* @param {List} parent
* mdast node.
* @param {Parents | null | undefined} parent
* Parent of `node`.
* @returns {Element}
* hast node.
*/
export function listItem(h, node, parent) {
const result = all(h, node)
export function listItem(state, node, parent) {
const results = state.all(node)
const loose = parent ? listLoose(parent) : listItemLoose(node)
/** @type {Properties} */
const props = {}
/** @type {Array<Content>} */
const wrapped = []
const properties = {}
/** @type {Array<ElementContent>} */
const children = []
if (typeof node.checked === 'boolean') {
const head = results[0]
/** @type {Element} */
let paragraph
if (
result[0] &&
result[0].type === 'element' &&
result[0].tagName === 'p'
) {
paragraph = result[0]
if (head && head.type === 'element' && head.tagName === 'p') {
paragraph = head
} else {
paragraph = h(null, 'p', [])
result.unshift(paragraph)
paragraph = {type: 'element', tagName: 'p', properties: {}, children: []}
results.unshift(paragraph)
}
if (paragraph.children.length > 0) {
paragraph.children.unshift(u('text', ' '))
paragraph.children.unshift({type: 'text', value: ' '})
}
paragraph.children.unshift(
h(null, 'input', {
type: 'checkbox',
checked: node.checked,
disabled: true
})
)
paragraph.children.unshift({
type: 'element',
tagName: 'input',
properties: {type: 'checkbox', checked: node.checked, disabled: true},
children: []
})
// According to github-markdown-css, this class hides bullet.
// See: <https://github.com/sindresorhus/github-markdown-css>.
props.className = ['task-list-item']
properties.className = ['task-list-item']
}

@@ -60,4 +67,4 @@

while (++index < result.length) {
const child = result[index]
while (++index < results.length) {
const child = results[index]

@@ -71,36 +78,42 @@ // Add eols before nodes, except if this is a loose, first paragraph.

) {
wrapped.push(u('text', '\n'))
children.push({type: 'text', value: '\n'})
}
if (child.type === 'element' && child.tagName === 'p' && !loose) {
wrapped.push(...child.children)
children.push(...child.children)
} else {
wrapped.push(child)
children.push(child)
}
}
const tail = result[result.length - 1]
const tail = results[results.length - 1]
// Add a final eol.
if (tail && (loose || !('tagName' in tail) || tail.tagName !== 'p')) {
wrapped.push(u('text', '\n'))
if (tail && (loose || tail.type !== 'element' || tail.tagName !== 'p')) {
children.push({type: 'text', value: '\n'})
}
return h(node, 'li', props, wrapped)
/** @type {Element} */
const result = {type: 'element', tagName: 'li', properties, children}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* @param {List} node
* @param {Parents} node
* @return {Boolean}
*/
function listLoose(node) {
let loose = node.spread
const children = node.children
let index = -1
let loose = false
if (node.type === 'list') {
loose = node.spread || false
const children = node.children
let index = -1
while (!loose && ++index < children.length) {
loose = listItemLoose(children[index])
while (!loose && ++index < children.length) {
loose = listItemLoose(children[index])
}
}
return Boolean(loose)
return loose
}

@@ -107,0 +120,0 @@

/**
* @param {H} h
* @typedef {import('hast').Element} Element
* @typedef {import('hast').Properties} Properties
* @typedef {import('mdast').List} List
* @typedef {import('../state.js').State} State
*/
/**
* Turn an mdast `list` node into hast.
*
* @param {State} state
* Info passed around.
* @param {List} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function list(h: H, node: List): import('hast').Element
export type List = import('mdast').List
export function list(state: State, node: List): Element
export type Element = import('hast').Element
export type Properties = import('hast').Properties
export type H = import('../index.js').H
export type List = import('mdast').List
export type State = import('../state.js').State
/**
* @typedef {import('mdast').List} List
* @typedef {import('hast').Element} Element
* @typedef {import('hast').Properties} Properties
* @typedef {import('../index.js').H} H
* @typedef {import('mdast').List} List
* @typedef {import('../state.js').State} State
*/
import {wrap} from '../wrap.js'
import {all} from '../traverse.js'
/**
* @param {H} h
* Turn an mdast `list` node into hast.
*
* @param {State} state
* Info passed around.
* @param {List} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function list(h, node) {
export function list(state, node) {
/** @type {Properties} */
const props = {}
const name = node.ordered ? 'ol' : 'ul'
const items = all(h, node)
const properties = {}
const results = state.all(node)
let index = -1
if (typeof node.start === 'number' && node.start !== 1) {
props.start = node.start
properties.start = node.start
}
// Like GitHub, add a class for custom styling.
while (++index < items.length) {
const item = items[index]
while (++index < results.length) {
const child = results[index]
if (
item.type === 'element' &&
item.tagName === 'li' &&
item.properties &&
Array.isArray(item.properties.className) &&
item.properties.className.includes('task-list-item')
child.type === 'element' &&
child.tagName === 'li' &&
child.properties &&
Array.isArray(child.properties.className) &&
child.properties.className.includes('task-list-item')
) {
props.className = ['contains-task-list']
properties.className = ['contains-task-list']
break

@@ -42,3 +44,11 @@ }

return h(node, name, props, wrap(items, true))
/** @type {Element} */
const result = {
type: 'element',
tagName: node.ordered ? 'ol' : 'ul',
properties,
children: state.wrap(results, true)
}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* @param {H} h
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').Paragraph} Paragraph
* @typedef {import('../state.js').State} State
*/
/**
* Turn an mdast `paragraph` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Paragraph} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function paragraph(h: H, node: Paragraph): import('hast').Element
export function paragraph(state: State, node: Paragraph): Element
export type Element = import('hast').Element
export type Paragraph = import('mdast').Paragraph
export type H = import('../index.js').H
export type State = import('../state.js').State
/**
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').Paragraph} Paragraph
* @typedef {import('../index.js').H} H
* @typedef {import('../state.js').State} State
*/
import {all} from '../traverse.js'
/**
* @param {H} h
* Turn an mdast `paragraph` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Paragraph} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function paragraph(h, node) {
return h(node, 'p', all(h, node))
export function paragraph(state, node) {
/** @type {Element} */
const result = {
type: 'element',
tagName: 'p',
properties: {},
children: state.all(node)
}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* @param {H} h
* @param {Root} node
* @typedef {import('hast').Root} HastRoot
* @typedef {import('hast').Element} HastElement
* @typedef {import('mdast').Root} MdastRoot
* @typedef {import('../state.js').State} State
*/
export function root(h: H, node: Root): import('hast').ElementContent
export type Root = import('mdast').Root
export type H = import('../index.js').H
/**
* Turn an mdast `root` node into hast.
*
* @param {State} state
* Info passed around.
* @param {MdastRoot} node
* mdast node.
* @returns {HastRoot | HastElement}
* hast node.
*/
export function root(state: State, node: MdastRoot): HastRoot | HastElement
export type HastRoot = import('hast').Root
export type HastElement = import('hast').Element
export type MdastRoot = import('mdast').Root
export type State = import('../state.js').State
/**
* @typedef {import('mdast').Root} Root
* @typedef {import('../index.js').H} H
* @typedef {import('hast').Root} HastRoot
* @typedef {import('hast').Element} HastElement
* @typedef {import('mdast').Root} MdastRoot
* @typedef {import('../state.js').State} State
*/
import {u} from 'unist-builder'
import {all} from '../traverse.js'
import {wrap} from '../wrap.js'
/**
* @param {H} h
* @param {Root} node
* Turn an mdast `root` node into hast.
*
* @param {State} state
* Info passed around.
* @param {MdastRoot} node
* mdast node.
* @returns {HastRoot | HastElement}
* hast node.
*/
export function root(h, node) {
// @ts-expect-error `root`s are also fine.
return h.augment(node, u('root', wrap(all(h, node))))
export function root(state, node) {
/** @type {HastRoot} */
const result = {type: 'root', children: state.wrap(state.all(node))}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* @param {H} h
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').Strong} Strong
* @typedef {import('../state.js').State} State
*/
/**
* Turn an mdast `strong` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Strong} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function strong(h: H, node: Strong): import('hast').Element
export function strong(state: State, node: Strong): Element
export type Element = import('hast').Element
export type Strong = import('mdast').Strong
export type H = import('../index.js').H
export type State = import('../state.js').State
/**
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').Strong} Strong
* @typedef {import('../index.js').H} H
* @typedef {import('../state.js').State} State
*/
import {all} from '../traverse.js'
/**
* @param {H} h
* Turn an mdast `strong` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Strong} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function strong(h, node) {
return h(node, 'strong', all(h, node))
export function strong(state, node) {
/** @type {Element} */
const result = {
type: 'element',
tagName: 'strong',
properties: {},
children: state.all(node)
}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* @param {H} h
* Turn an mdast `table` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Table} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function table(h: H, node: Table): import('hast').Element
export function table(state: State, node: Table): Element
export type Element = import('hast').Element
export type Table = import('mdast').Table
export type Element = import('hast').Element
export type H = import('../index.js').H
export type Content = import('../index.js').Content
export type State = import('../state.js').State
/**
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').Table} Table
* @typedef {import('hast').Element} Element
* @typedef {import('../index.js').H} H
* @typedef {import('../index.js').Content} Content
* @typedef {import('../state.js').State} State
*/
import {pointStart, pointEnd} from 'unist-util-position'
import {wrap} from '../wrap.js'
import {all} from '../traverse.js'
/**
* @param {H} h
* Turn an mdast `table` node into hast.
*
* @param {State} state
* Info passed around.
* @param {Table} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function table(h, node) {
const rows = node.children
let index = -1
const align = node.align || []
export function table(state, node) {
const rows = state.all(node)
const firstRow = rows.shift()
/** @type {Array<Element>} */
const result = []
const tableContent = []
while (++index < rows.length) {
const row = rows[index].children
const name = index === 0 ? 'th' : 'td'
/** @type {Array<Content>} */
const out = []
let cellIndex = -1
const length = node.align ? align.length : row.length
if (firstRow) {
/** @type {Element} */
const head = {
type: 'element',
tagName: 'thead',
properties: {},
children: state.wrap([firstRow], true)
}
state.patch(node.children[0], head)
tableContent.push(head)
}
while (++cellIndex < length) {
const cell = row[cellIndex]
out.push(
h(cell, name, {align: align[cellIndex]}, cell ? all(h, cell) : [])
)
if (rows.length > 0) {
/** @type {Element} */
const body = {
type: 'element',
tagName: 'tbody',
properties: {},
children: state.wrap(rows, true)
}
result[index] = h(rows[index], 'tr', wrap(out, true))
const start = pointStart(node.children[1])
const end = pointEnd(node.children[node.children.length - 1])
if (start.line && end.line) body.position = {start, end}
tableContent.push(body)
}
return h(
node,
'table',
wrap(
[h(result[0].position, 'thead', wrap([result[0]], true))].concat(
result[1]
? h(
{
start: pointStart(result[1]),
end: pointEnd(result[result.length - 1])
},
'tbody',
wrap(result.slice(1), true)
)
: []
),
true
)
)
/** @type {Element} */
const result = {
type: 'element',
tagName: 'table',
properties: {},
children: state.wrap(tableContent, true)
}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* @param {H} h
* @param {Text} node
* Turn an mdast `text` node into hast.
*
* @param {State} state
* Info passed around.
* @param {MdastText} node
* mdast node.
* @returns {HastText | HastElement}
* hast node.
*/
export function text(h: H, node: Text): import('hast').ElementContent
export type Text = import('mdast').Text
export type H = import('../index.js').H
export function text(state: State, node: MdastText): HastText | HastElement
export type HastElement = import('hast').Element
export type HastText = import('hast').Text
export type MdastText = import('mdast').Text
export type State = import('../state.js').State
/**
* @typedef {import('mdast').Text} Text
* @typedef {import('../index.js').H} H
* @typedef {import('hast').Element} HastElement
* @typedef {import('hast').Text} HastText
* @typedef {import('mdast').Text} MdastText
* @typedef {import('../state.js').State} State
*/
import {trimLines} from 'trim-lines'
import {u} from 'unist-builder'
/**
* @param {H} h
* @param {Text} node
* Turn an mdast `text` node into hast.
*
* @param {State} state
* Info passed around.
* @param {MdastText} node
* mdast node.
* @returns {HastText | HastElement}
* hast node.
*/
export function text(h, node) {
return h.augment(node, u('text', trimLines(String(node.value))))
export function text(state, node) {
/** @type {HastText} */
const result = {type: 'text', value: trimLines(String(node.value))}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').ThematicBreak} ThematicBreak
* @typedef {import('hast').Element} Element
* @typedef {import('../index.js').H} H
* @typedef {import('../state.js').State} State
*/
/**
* @param {H} h
* @param {ThematicBreak} [node]
* Turn an mdast `thematicBreak` node into hast.
*
* @param {State} state
* Info passed around.
* @param {ThematicBreak} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function thematicBreak(
h: H,
node?: import('mdast').ThematicBreak | undefined
): import('hast').Element
export function thematicBreak(state: State, node: ThematicBreak): Element
export type Element = import('hast').Element
export type ThematicBreak = import('mdast').ThematicBreak
export type Element = import('hast').Element
export type H = import('../index.js').H
export type State = import('../state.js').State
/**
* @typedef {import('hast').Element} Element
* @typedef {import('mdast').ThematicBreak} ThematicBreak
* @typedef {import('hast').Element} Element
* @typedef {import('../index.js').H} H
* @typedef {import('../state.js').State} State
*/
/**
* @param {H} h
* @param {ThematicBreak} [node]
* Turn an mdast `thematicBreak` node into hast.
*
* @param {State} state
* Info passed around.
* @param {ThematicBreak} node
* mdast node.
* @returns {Element}
* hast node.
*/
export function thematicBreak(h, node) {
return h(node, 'hr')
export function thematicBreak(state, node) {
/** @type {Element} */
const result = {
type: 'element',
tagName: 'hr',
properties: {},
children: []
}
state.patch(node, result)
return state.applyData(node, result)
}
/**
* Transform `tree` (an mdast node) to a hast node.
* Transform mdast to hast.
*
* @param {MdastNode} tree mdast node
* @param {Options} [options] Configuration
* @returns {HastNode|null|undefined} hast node
* ##### Notes
*
* ###### HTML
*
* Raw HTML is available in mdast as `html` nodes and can be embedded in hast
* as semistandard `raw` nodes.
* Most utilities ignore `raw` nodes but two notable ones don’t:
*
* * `hast-util-to-html` also has an option `allowDangerousHtml` which will
* output the raw HTML.
* This is typically discouraged as noted by the option name but is useful
* if you completely trust authors
* * `hast-util-raw` can handle the raw embedded HTML strings by parsing them
* into standard hast nodes (`element`, `text`, etc).
* This is a heavy task as it needs a full HTML parser, but it is the only
* way to support untrusted content
*
* ###### Footnotes
*
* Many options supported here relate to footnotes.
* Footnotes are not specified by CommonMark, which we follow by default.
* They are supported by GitHub, so footnotes can be enabled in markdown with
* `mdast-util-gfm`.
*
* The options `footnoteBackLabel` and `footnoteLabel` define natural language
* that explains footnotes, which is hidden for sighted users but shown to
* assistive technology.
* When your page is not in English, you must define translated values.
*
* Back references use ARIA attributes, but the section label itself uses a
* heading that is hidden with an `sr-only` class.
* To show it to sighted users, define different attributes in
* `footnoteLabelProperties`.
*
* ###### Clobbering
*
* Footnotes introduces a problem, as it links footnote calls to footnote
* definitions on the page through `id` attributes generated from user content,
* which results in DOM clobbering.
*
* DOM clobbering is this:
*
* ```html
* <p id=x></p>
* <script>alert(x) // `x` now refers to the DOM `p#x` element</script>
* ```
*
* Elements by their ID are made available by browsers on the `window` object,
* which is a security risk.
* Using a prefix solves this problem.
*
* More information on how to handle clobbering and the prefix is explained in
* Example: headings (DOM clobbering) in `rehype-sanitize`.
*
* ###### Unknown nodes
*
* Unknown nodes are nodes with a type that isn’t in `handlers` or `passThrough`.
* The default behavior for unknown nodes is:
*
* * when the node has a `value` (and doesn’t have `data.hName`,
* `data.hProperties`, or `data.hChildren`, see later), create a hast `text`
* node
* * otherwise, create a `<div>` element (which could be changed with
* `data.hName`), with its children mapped from mdast to hast as well
*
* This behavior can be changed by passing an `unknownHandler`.
*
* @param {MdastNodes} tree
* mdast tree.
* @param {Options | null | undefined} [options]
* Configuration.
* @returns {HastNodes | null | undefined}
* hast tree.
*/
export function toHast(
tree: MdastNode,
options?: Options | undefined
): HastNode | null | undefined
export {handlers as defaultHandlers} from './handlers/index.js'
export type MdastNode =
| import('mdast').Root
| import('mdast').Parent['children'][number]
export type HastNode =
| import('hast').Root
| import('hast').Parent['children'][number]
export type Parent = import('mdast').Parent
export type Definition = import('mdast').Definition
export type FootnoteDefinition = import('mdast').FootnoteDefinition
export type Properties = import('hast').Properties
export type Element = import('hast').Element
export type Content = import('hast').ElementContent
export type EmbeddedHastFields = {
/**
* Defines the tag name of an element.
*/
hName?: string | undefined
/**
* Defines the properties of an element.
*/
hProperties?: import('hast').Properties | undefined
/**
* Defines the (hast) children of an element.
*/
hChildren?: import('hast').ElementContent[] | undefined
}
/**
* unist data with embedded hast fields.
*/
export type Data = Record<string, unknown> & EmbeddedHastFields
/**
* unist node with embedded hast data.
*/
export type NodeWithData = MdastNode & {
data?: Data
}
export type PositionLike = {
start?: PointLike | null | undefined
end?: PointLike | null | undefined
}
export type PointLike = {
line?: number | null | undefined
column?: number | null | undefined
offset?: number | null | undefined
}
/**
* Handle a node.
*/
export type Handler = (
h: H,
node: any,
parent: Parent | null
) => Content | Array<Content> | null | undefined
export type HFunctionProps = (
node: MdastNode | PositionLike | null | undefined,
tagName: string,
props: Properties,
children?: import('hast').ElementContent[] | null | undefined
) => Element
export type HFunctionNoProps = (
node: MdastNode | PositionLike | null | undefined,
tagName: string,
children?: import('hast').ElementContent[] | null | undefined
) => Element
export type HFields = {
/**
* Whether HTML is allowed.
*/
dangerous: boolean
/**
* Prefix to use to prevent DOM clobbering.
*/
clobberPrefix: string
/**
* Label to use to introduce the footnote section.
*/
footnoteLabel: string
/**
* HTML used for the footnote label.
*/
footnoteLabelTagName: string
/**
* Properties on the HTML tag used for the footnote label.
*/
footnoteLabelProperties: Properties
/**
* Label to use to go back to a footnote call from the footnote section.
*/
footnoteBackLabel: string
/**
* Definition cache.
*/
definition: (identifier: string) => Definition | null
/**
* Footnote cache.
*/
footnoteById: Record<string, FootnoteDefinition>
/**
* Order in which footnotes occur.
*/
footnoteOrder: Array<string>
/**
* Counts the same footnote was used.
*/
footnoteCounts: Record<string, number>
/**
* Applied handlers.
*/
handlers: Handlers
/**
* Handler for any none not in `passThrough` or otherwise handled.
*/
unknownHandler: Handler
/**
* Like `h` but lower-level and usable on non-elements.
*/
augment: (
left: NodeWithData | PositionLike | null | undefined,
right: Content
) => Content
/**
* List of node types to pass through untouched (except for their children).
*/
passThrough: Array<string>
}
/**
* Configuration (optional).
*/
export type Options = {
/**
* Whether to allow `html` nodes and inject them as `raw` HTML.
*/
allowDangerousHtml?: boolean | undefined
/**
* Prefix to use before the `id` attribute to prevent it from *clobbering*.
* attributes.
* DOM clobbering is this:
*
* ```html
* <p id=x></p>
* <script>alert(x)</script>
* ```
*
* Elements by their ID are made available in browsers on the `window` object.
* Using a prefix prevents this from being a problem.
*/
clobberPrefix?: string | undefined
/**
* Label to use for the footnotes section.
* Affects screen reader users.
* Change it if you’re authoring in a different language.
*/
footnoteLabel?: string | undefined
/**
* HTML tag to use for the footnote label.
* Can be changed to match your document structure and play well with your choice of css.
*/
footnoteLabelTagName?: string | undefined
/**
* Properties to use on the footnote label.
* A 'sr-only' class is added by default to hide this from sighted users.
* Change it to make the label visible, or add classes for other purposes.
*/
footnoteLabelProperties?: import('hast').Properties | undefined
/**
* Label to use from backreferences back to their footnote call.
* Affects screen reader users.
* Change it if you’re authoring in a different language.
*/
footnoteBackLabel?: string | undefined
/**
* Object mapping mdast nodes to functions handling them
*/
handlers?: Handlers | undefined
/**
* List of custom mdast node types to pass through (keep) in hast
*/
passThrough?: string[] | undefined
/**
* Handler for all unknown nodes.
*/
unknownHandler?: Handler | undefined
}
/**
* Map of node types to handlers
*/
export type Handlers = Record<string, Handler>
/**
* Handle context
*/
export type H = HFunctionProps & HFunctionNoProps & HFields
tree: MdastNodes,
options?: Options | null | undefined
): HastNodes | null | undefined
export type HastContent = import('hast').Content
export type HastRoot = import('hast').Root
export type MdastContent = import('mdast').Content
export type MdastRoot = import('mdast').Root
export type Options = import('./state.js').Options
export type HastNodes = HastRoot | HastContent
export type MdastNodes = MdastRoot | MdastContent
/**
* @typedef {import('mdast').Root|import('mdast').Parent['children'][number]} MdastNode
* @typedef {import('hast').Root|import('hast').Parent['children'][number]} HastNode
* @typedef {import('mdast').Parent} Parent
* @typedef {import('mdast').Definition} Definition
* @typedef {import('mdast').FootnoteDefinition} FootnoteDefinition
* @typedef {import('hast').Properties} Properties
* @typedef {import('hast').Element} Element
* @typedef {import('hast').ElementContent} Content
* @typedef {import('hast').Content} HastContent
* @typedef {import('hast').Root} HastRoot
*
* @typedef EmbeddedHastFields
* @property {string} [hName]
* Defines the tag name of an element.
* @property {Properties} [hProperties]
* Defines the properties of an element.
* @property {Array<Content>} [hChildren]
* Defines the (hast) children of an element.
* @typedef {import('mdast').Content} MdastContent
* @typedef {import('mdast').Root} MdastRoot
*
* @typedef {Record<string, unknown> & EmbeddedHastFields} Data
* unist data with embedded hast fields.
* @typedef {import('./state.js').Options} Options
*/
/**
* @typedef {HastRoot | HastContent} HastNodes
* @typedef {MdastRoot | MdastContent} MdastNodes
*/
import {footer} from './footer.js'
import {createState} from './state.js'
/**
* Transform mdast to hast.
*
* @typedef {MdastNode & {data?: Data}} NodeWithData
* unist node with embedded hast data.
* ##### Notes
*
* @typedef PositionLike
* @property {PointLike | null | undefined} [start]
* @property {PointLike | null | undefined} [end]
* ###### HTML
*
* @typedef PointLike
* @property {number | null | undefined} [line]
* @property {number | null | undefined} [column]
* @property {number | null | undefined} [offset]
* Raw HTML is available in mdast as `html` nodes and can be embedded in hast
* as semistandard `raw` nodes.
* Most utilities ignore `raw` nodes but two notable ones don’t:
*
* @callback Handler
* Handle a node.
* @param {H} h
* Handle context.
* @param {any} node
* mdast node to handle.
* @param {Parent|null} parent
* Parent of `node`.
* @returns {Content|Array<Content>|null|undefined}
* hast node.
* * `hast-util-to-html` also has an option `allowDangerousHtml` which will
* output the raw HTML.
* This is typically discouraged as noted by the option name but is useful
* if you completely trust authors
* * `hast-util-raw` can handle the raw embedded HTML strings by parsing them
* into standard hast nodes (`element`, `text`, etc).
* This is a heavy task as it needs a full HTML parser, but it is the only
* way to support untrusted content
*
* @callback HFunctionProps
* @param {MdastNode|PositionLike|null|undefined} node
* mdast node or unist position.
* @param {string} tagName
* HTML tag name.
* @param {Properties} props
* Properties.
* @param {Array<Content>?} [children]
* hast content.
* @returns {Element}
* Compiled element.
* ###### Footnotes
*
* @callback HFunctionNoProps
* @param {MdastNode|PositionLike|null|undefined} node
* mdast node or unist position.
* @param {string} tagName
* HTML tag name.
* @param {Array<Content>?} [children]
* hast content
* @returns {Element}
* Compiled element.
* Many options supported here relate to footnotes.
* Footnotes are not specified by CommonMark, which we follow by default.
* They are supported by GitHub, so footnotes can be enabled in markdown with
* `mdast-util-gfm`.
*
* @typedef HFields
* @property {boolean} dangerous
* Whether HTML is allowed.
* @property {string} clobberPrefix
* Prefix to use to prevent DOM clobbering.
* @property {string} footnoteLabel
* Label to use to introduce the footnote section.
* @property {string} footnoteLabelTagName
* HTML used for the footnote label.
* @property {Properties} footnoteLabelProperties
* Properties on the HTML tag used for the footnote label.
* @property {string} footnoteBackLabel
* Label to use to go back to a footnote call from the footnote section.
* @property {(identifier: string) => Definition|null} definition
* Definition cache.
* @property {Record<string, FootnoteDefinition>} footnoteById
* Footnote cache.
* @property {Array<string>} footnoteOrder
* Order in which footnotes occur.
* @property {Record<string, number>} footnoteCounts
* Counts the same footnote was used.
* @property {Handlers} handlers
* Applied handlers.
* @property {Handler} unknownHandler
* Handler for any none not in `passThrough` or otherwise handled.
* @property {(left: NodeWithData|PositionLike|null|undefined, right: Content) => Content} augment
* Like `h` but lower-level and usable on non-elements.
* @property {Array<string>} passThrough
* List of node types to pass through untouched (except for their children).
* The options `footnoteBackLabel` and `footnoteLabel` define natural language
* that explains footnotes, which is hidden for sighted users but shown to
* assistive technology.
* When your page is not in English, you must define translated values.
*
* @typedef Options
* Configuration (optional).
* @property {boolean} [allowDangerousHtml=false]
* Whether to allow `html` nodes and inject them as `raw` HTML.
* @property {string} [clobberPrefix='user-content-']
* Prefix to use before the `id` attribute to prevent it from *clobbering*.
* attributes.
* DOM clobbering is this:
* Back references use ARIA attributes, but the section label itself uses a
* heading that is hidden with an `sr-only` class.
* To show it to sighted users, define different attributes in
* `footnoteLabelProperties`.
*
* ```html
* <p id=x></p>
* <script>alert(x)</script>
* ```
* ###### Clobbering
*
* Elements by their ID are made available in browsers on the `window` object.
* Using a prefix prevents this from being a problem.
* @property {string} [footnoteLabel='Footnotes']
* Label to use for the footnotes section.
* Affects screen reader users.
* Change it if you’re authoring in a different language.
* @property {string} [footnoteLabelTagName='h2']
* HTML tag to use for the footnote label.
* Can be changed to match your document structure and play well with your choice of css.
* @property {Properties} [footnoteLabelProperties={className: ['sr-only']}]
* Properties to use on the footnote label.
* A 'sr-only' class is added by default to hide this from sighted users.
* Change it to make the label visible, or add classes for other purposes.
* @property {string} [footnoteBackLabel='Back to content']
* Label to use from backreferences back to their footnote call.
* Affects screen reader users.
* Change it if you’re authoring in a different language.
* @property {Handlers} [handlers]
* Object mapping mdast nodes to functions handling them
* @property {Array<string>} [passThrough]
* List of custom mdast node types to pass through (keep) in hast
* @property {Handler} [unknownHandler]
* Handler for all unknown nodes.
* Footnotes introduces a problem, as it links footnote calls to footnote
* definitions on the page through `id` attributes generated from user content,
* which results in DOM clobbering.
*
* @typedef {Record<string, Handler>} Handlers
* Map of node types to handlers
* @typedef {HFunctionProps & HFunctionNoProps & HFields} H
* Handle context
*/
import {u} from 'unist-builder'
import {visit} from 'unist-util-visit'
import {pointStart, pointEnd} from 'unist-util-position'
import {generated} from 'unist-util-generated'
import {definitions} from 'mdast-util-definitions'
import {one} from './traverse.js'
import {footer} from './footer.js'
import {handlers} from './handlers/index.js'
const own = {}.hasOwnProperty
/**
* Turn mdast into hast.
* DOM clobbering is this:
*
* @param {MdastNode} tree
* mdast node.
* @param {Options} [options]
* Configuration (optional).
* @returns {H}
* `h` function.
*/
function factory(tree, options) {
const settings = options || {}
const dangerous = settings.allowDangerousHtml || false
/** @type {Record<string, FootnoteDefinition>} */
const footnoteById = {}
h.dangerous = dangerous
h.clobberPrefix =
settings.clobberPrefix === undefined || settings.clobberPrefix === null
? 'user-content-'
: settings.clobberPrefix
h.footnoteLabel = settings.footnoteLabel || 'Footnotes'
h.footnoteLabelTagName = settings.footnoteLabelTagName || 'h2'
h.footnoteLabelProperties = settings.footnoteLabelProperties || {
className: ['sr-only']
}
h.footnoteBackLabel = settings.footnoteBackLabel || 'Back to content'
h.definition = definitions(tree)
h.footnoteById = footnoteById
/** @type {Array<string>} */
h.footnoteOrder = []
/** @type {Record<string, number>} */
h.footnoteCounts = {}
h.augment = augment
h.handlers = {...handlers, ...settings.handlers}
h.unknownHandler = settings.unknownHandler
h.passThrough = settings.passThrough
visit(tree, 'footnoteDefinition', (definition) => {
const id = String(definition.identifier).toUpperCase()
// Mimick CM behavior of link definitions.
// See: <https://github.com/syntax-tree/mdast-util-definitions/blob/8290999/index.js#L26>.
if (!own.call(footnoteById, id)) {
footnoteById[id] = definition
}
})
// @ts-expect-error Hush, it’s fine!
return h
/**
* Finalise the created `right`, a hast node, from `left`, an mdast node.
*
* @param {(NodeWithData|PositionLike)?} left
* @param {Content} right
* @returns {Content}
*/
function augment(left, right) {
// Handle `data.hName`, `data.hProperties, `data.hChildren`.
if (left && 'data' in left && left.data) {
/** @type {Data} */
const data = left.data
if (data.hName) {
if (right.type !== 'element') {
right = {
type: 'element',
tagName: '',
properties: {},
children: []
}
}
right.tagName = data.hName
}
if (right.type === 'element' && data.hProperties) {
right.properties = {...right.properties, ...data.hProperties}
}
if ('children' in right && right.children && data.hChildren) {
right.children = data.hChildren
}
}
if (left) {
const ctx = 'type' in left ? left : {position: left}
if (!generated(ctx)) {
// @ts-expect-error: fine.
right.position = {start: pointStart(ctx), end: pointEnd(ctx)}
}
}
return right
}
/**
* Create an element for `node`.
*
* @type {HFunctionProps}
*/
function h(node, tagName, props, children) {
if (Array.isArray(props)) {
children = props
props = {}
}
// @ts-expect-error augmenting an element yields an element.
return augment(node, {
type: 'element',
tagName,
properties: props || {},
children: children || []
})
}
}
/**
* Transform `tree` (an mdast node) to a hast node.
* ```html
* <p id=x></p>
* <script>alert(x) // `x` now refers to the DOM `p#x` element</script>
* ```
*
* @param {MdastNode} tree mdast node
* @param {Options} [options] Configuration
* @returns {HastNode|null|undefined} hast node
* Elements by their ID are made available by browsers on the `window` object,
* which is a security risk.
* Using a prefix solves this problem.
*
* More information on how to handle clobbering and the prefix is explained in
* Example: headings (DOM clobbering) in `rehype-sanitize`.
*
* ###### Unknown nodes
*
* Unknown nodes are nodes with a type that isn’t in `handlers` or `passThrough`.
* The default behavior for unknown nodes is:
*
* * when the node has a `value` (and doesn’t have `data.hName`,
* `data.hProperties`, or `data.hChildren`, see later), create a hast `text`
* node
* * otherwise, create a `<div>` element (which could be changed with
* `data.hName`), with its children mapped from mdast to hast as well
*
* This behavior can be changed by passing an `unknownHandler`.
*
* @param {MdastNodes} tree
* mdast tree.
* @param {Options | null | undefined} [options]
* Configuration.
* @returns {HastNodes | null | undefined}
* hast tree.
*/
// To do: next major: always return a single `root`.
export function toHast(tree, options) {
const h = factory(tree, options)
const node = one(h, tree, null)
const foot = footer(h)
const state = createState(tree, options)
const node = state.one(tree, null)
const foot = footer(state)

@@ -287,8 +106,7 @@ if (foot) {

// So assume `node` is a parent node.
node.children.push(u('text', '\n'), foot)
node.children.push({type: 'text', value: '\n'}, foot)
}
// To do: next major: always return root?
return Array.isArray(node) ? {type: 'root', children: node} : node
}
export {handlers as defaultHandlers} from './handlers/index.js'
/**
* @typedef {import('hast').ElementContent} ElementContent
*
* @typedef {import('mdast').Content} Content
* @typedef {import('mdast').Reference} Reference
* @typedef {import('mdast').Root} Root
*
* @typedef {import('./state.js').State} State
*/
/**
* @typedef {Root | Content} Nodes
* @typedef {Extract<Nodes, Reference>} References
*/
/**
* Return the content of a reference without definition as plain text.
*
* @param {H} h
* @param {ImageReference|LinkReference} node
* @returns {Content|Array<Content>}
* @param {State} state
* Info passed around.
* @param {References} node
* Reference node (image, link).
* @returns {ElementContent | Array<ElementContent>}
* hast content.
*/
export function revert(
h: H,
node: ImageReference | LinkReference
): Content | Array<Content>
export type LinkReference = import('mdast').LinkReference
export type ImageReference = import('mdast').ImageReference
export type H = import('./index.js').H
export type Content = import('./index.js').Content
state: State,
node: References
): ElementContent | Array<ElementContent>
export type ElementContent = import('hast').ElementContent
export type Content = import('mdast').Content
export type Reference = import('mdast').Reference
export type Root = import('mdast').Root
export type State = import('./state.js').State
export type Nodes = Root | Content
export type References = Extract<Nodes, Reference>
/**
* @typedef {import('mdast').LinkReference} LinkReference
* @typedef {import('mdast').ImageReference} ImageReference
* @typedef {import('./index.js').H} H
* @typedef {import('./index.js').Content} Content
* @typedef {import('hast').ElementContent} ElementContent
*
* @typedef {import('mdast').Content} Content
* @typedef {import('mdast').Reference} Reference
* @typedef {import('mdast').Root} Root
*
* @typedef {import('./state.js').State} State
*/
import {u} from 'unist-builder'
import {all} from './traverse.js'
/**
* @typedef {Root | Content} Nodes
* @typedef {Extract<Nodes, Reference>} References
*/
// To do: next major: always return array.
/**
* Return the content of a reference without definition as plain text.
*
* @param {H} h
* @param {ImageReference|LinkReference} node
* @returns {Content|Array<Content>}
* @param {State} state
* Info passed around.
* @param {References} node
* Reference node (image, link).
* @returns {ElementContent | Array<ElementContent>}
* hast content.
*/
export function revert(h, node) {
export function revert(state, node) {
const subtype = node.referenceType

@@ -29,6 +39,6 @@ let suffix = ']'

if (node.type === 'imageReference') {
return u('text', '![' + node.alt + suffix)
return {type: 'text', value: '![' + node.alt + suffix}
}
const contents = all(h, node)
const contents = state.all(node)
const head = contents[0]

@@ -39,3 +49,3 @@

} else {
contents.unshift(u('text', '['))
contents.unshift({type: 'text', value: '['})
}

@@ -48,3 +58,3 @@

} else {
contents.push(u('text', suffix))
contents.push({type: 'text', value: suffix})
}

@@ -51,0 +61,0 @@

/**
* @param {H} h
* @param {MdastNode} node
* @param {MdastParent | null} parent
* Transform an mdast node into a hast node.
*
* @param {State} state
* Info passed around.
* @param {MdastNodes} node
* mdast node.
* @param {MdastParents | null | undefined} [parent]
* Parent of `node`.
* @returns {HastElementContent | Array<HastElementContent> | null | undefined}
* Resulting hast node.
*/
export function one(
h: H,
node: MdastNode,
parent: MdastParent | null
):
| import('hast').ElementContent
| import('hast').ElementContent[]
| null
| undefined
state: State,
node: MdastNodes,
parent?: MdastParents | null | undefined
): HastElementContent | Array<HastElementContent> | null | undefined
/**
* @param {H} h
* @param {MdastNode} parent
* Transform the children of an mdast node into hast nodes.
*
* @param {State} state
* Info passed around.
* @param {MdastNodes} parent
* mdast node to compile
* @returns {Array<HastElementContent>}
* Resulting hast nodes.
*/
export function all(h: H, parent: MdastNode): import('hast').ElementContent[]
export type MdastNode = import('mdast').Root | import('mdast').Content
export type MdastParent = Extract<MdastNode, import('mdast').Parent>
export type Handler = import('./index.js').Handler
export type H = import('./index.js').H
export type Content = import('./index.js').Content
export function all(state: State, parent: MdastNodes): Array<HastElementContent>
export type HastElementContent = import('hast').ElementContent
export type HastElement = import('hast').Element
export type HastText = import('hast').Text
export type MdastContent = import('mdast').Content
export type MdastParent = import('mdast').Parent
export type MdastRoot = import('mdast').Root
export type State = import('./state.js').State
export type MdastNodes = MdastRoot | MdastContent
export type MdastParents = Extract<MdastNodes, MdastParent>
/**
* Wrap `nodes` with line feeds between each entry.
* Optionally adds line feeds at the start and end.
* @typedef {import('hast').Content} Content
* @typedef {import('hast').Text} Text
*/
/**
* Wrap `nodes` with line endings between each node.
*
* @param {Array<Content>} nodes
* @param {boolean} [loose=false]
* @returns {Array<Content>}
* @template {Content} Type
* Node type.
* @param {Array<Type>} nodes
* List of nodes to wrap.
* @param {boolean | null | undefined} [loose=false]
* Whether to add line endings at start and end.
* @returns {Array<Type | Text>}
* Wrapped nodes.
*/
export function wrap(
nodes: Array<Content>,
loose?: boolean | undefined
): Array<Content>
export type Content = import('./index.js').Content
export function wrap<Type extends import('hast').Content>(
nodes: Type[],
loose?: boolean | null | undefined
): (import('hast').Text | Type)[]
export type Content = import('hast').Content
export type Text = import('hast').Text
{
"name": "mdast-util-to-hast",
"version": "12.2.6",
"version": "12.3.0",
"description": "mdast utility to transform to hast",

@@ -43,3 +43,2 @@ "license": "MIT",

"trim-lines": "^3.0.0",
"unist-builder": "^3.0.0",
"unist-util-generated": "^2.0.0",

@@ -50,5 +49,6 @@ "unist-util-position": "^4.0.0",

"devDependencies": {
"@types/tape": "^4.0.0",
"@types/node": "^18.0.0",
"c8": "^7.0.0",
"hast-util-to-html": "^8.0.0",
"hast-util-to-html": "^8.0.4",
"hastscript": "^7.0.0",
"mdast-util-from-markdown": "^1.0.0",

@@ -60,4 +60,2 @@ "mdast-util-gfm": "^2.0.0",

"remark-preset-wooorm": "^9.0.0",
"rimraf": "^3.0.0",
"tape": "^5.0.0",
"type-coverage": "^2.0.0",

@@ -69,6 +67,6 @@ "typescript": "^4.0.0",

"prepack": "npm run build && npm run format",
"build": "rimraf \"{lib/**/**,test/**}*.d.ts\" \"index.d.ts\" && tsc && type-coverage",
"format": "remark . -qfo && prettier -w . --loglevel warn && xo --fix",
"test-api": "node test/index.js",
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node test/index.js",
"build": "tsc --build --clean && tsc --build && type-coverage",
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
"test-api": "node --conditions development test/index.js",
"test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api",
"test": "npm run build && npm run format && npm run test-coverage"

@@ -106,5 +104,5 @@ },

"ignoreFiles": [
"lib/index.d.ts"
"lib/state.d.ts"
]
}
}

@@ -20,6 +20,9 @@ # mdast-util-to-hast

* [API](#api)
* [`toHast(node[, options])`](#tohastnode-options)
* [`toHast(tree[, options])`](#tohasttree-options)
* [`defaultHandlers`](#defaulthandlers)
* [`all(h, parent)`](#allh-parent)
* [`one(h, node, parent)`](#oneh-node-parent)
* [`Handler`](#handler)
* [`Handlers`](#handlers)
* [`Options`](#options)
* [`Raw`](#raw)
* [`State`](#state)
* [Examples](#examples)

@@ -31,2 +34,4 @@ * [Example: supporting HTML in markdown naïvely](#example-supporting-html-in-markdown-naïvely)

* [Algorithm](#algorithm)
* [Default handling](#default-handling)
* [Fields on nodes](#fields-on-nodes)
* [CSS](#css)

@@ -62,3 +67,3 @@ * [Syntax tree](#syntax-tree)

This package is [ESM only][esm].
In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]:
In Node.js (version 14.14+ and 16.0+), install with [npm][]:

@@ -72,3 +77,3 @@ ```sh

```js
import {toHast} from "https://esm.sh/mdast-util-to-hast@12"
import {toHast} from 'https://esm.sh/mdast-util-to-hast@12'
```

@@ -80,3 +85,3 @@

<script type="module">
import {toHast} from "https://esm.sh/mdast-util-to-hast@12?bundle"
import {toHast} from 'https://esm.sh/mdast-util-to-hast@12?bundle'
</script>

@@ -96,3 +101,3 @@ ```

```js
import {promises as fs} from 'node:fs'
import {fs} from 'node:fs/promises'
import {fromMarkdown} from 'mdast-util-from-markdown'

@@ -118,18 +123,25 @@ import {toHast} from 'mdast-util-to-hast'

This package exports the identifiers `toHast`, `defaultHandlers`, `all`, and
`one`.
This package exports the identifiers [`defaultHandlers`][api-default-handlers]
and [`toHast`][api-to-hast].
There is no default export.
### `toHast(node[, options])`
### `toHast(tree[, options])`
[mdast][] utility to transform to [hast][].
Transform mdast to hast.
##### `options`
###### Parameters
Configuration (optional).
* `tree` ([`MdastNode`][mdast-node])
— mdast tree
* `options` ([`Options`][api-options], optional)
— configuration
###### `options.allowDangerousHtml`
###### Returns
Whether to persist raw HTML in markdown in the hast tree (`boolean`, default:
`false`).
hast tree ([`HastNode | null | undefined`][hast-node]).
##### Notes
###### HTML
Raw HTML is available in mdast as [`html`][mdast-html] nodes and can be embedded

@@ -148,6 +160,25 @@ in hast as semistandard `raw` nodes.

###### `options.clobberPrefix`
###### Footnotes
Prefix to use before the `id` attribute on footnotes to prevent it from
*clobbering* (`string`, default: `'user-content-'`).
Many options supported here relate to footnotes.
Footnotes are not specified by CommonMark, which we follow by default.
They are supported by GitHub, so footnotes can be enabled in markdown with
[`mdast-util-gfm`][mdast-util-gfm].
The options `footnoteBackLabel` and `footnoteLabel` define natural language
that explains footnotes, which is hidden for sighted users but shown to
assistive technology.
When your page is not in English, you must define translated values.
Back references use ARIA attributes, but the section label itself uses a
heading that is hidden with an `sr-only` class.
To show it to sighted users, define different attributes in
`footnoteLabelProperties`.
###### Clobbering
Footnotes introduces a problem, as it links footnote calls to footnote
definitions on the page through `id` attributes generated from user content,
which results in DOM clobbering.
DOM clobbering is this:

@@ -167,116 +198,119 @@

> 👉 **Note**: this option affects footnotes.
> Footnotes are not specified by CommonMark.
> They are supported by GitHub, so they can be enabled by using the utility
> [`mdast-util-gfm`][mdast-util-gfm].
###### Unknown nodes
###### `options.footnoteLabel`
Unknown nodes are nodes with a type that isn’t in `handlers` or `passThrough`.
The default behavior for unknown nodes is:
Label to use for the footnotes section (`string`, default: `'Footnotes'`).
Affects screen readers.
Change it when the markdown is not in English.
* when the node has a `value` (and doesn’t have `data.hName`,
`data.hProperties`, or `data.hChildren`, see later), create a hast `text`
node
* otherwise, create a `<div>` element (which could be changed with
`data.hName`), with its children mapped from mdast to hast as well
> 👉 **Note**: this option affects footnotes.
> Footnotes are not specified by CommonMark.
> They are supported by GitHub, so they can be enabled by using the utility
> [`mdast-util-gfm`][mdast-util-gfm].
This behavior can be changed by passing an `unknownHandler`.
###### `options.footnoteLabelTagName`
### `defaultHandlers`
HTML tag to use for the footnote label (`string`, default: `h2`).
Can be changed to match your document structure and play well with your CSS.
Default handlers for nodes ([`Handlers`][api-handlers]).
> 👉 **Note**: this option affects footnotes.
> Footnotes are not specified by CommonMark.
> They are supported by GitHub, so they can be enabled by using the utility
> [`mdast-util-gfm`][mdast-util-gfm].
### `Handler`
###### `options.footnoteLabelProperties`
Handle a node (TypeScript).
Properties to use on the footnote label (`object`, default:
`{className: ['sr-only']}`).
Importantly, `id: 'footnote-label'` is always added, because footnote calls use
it with `aria-describedby` to provide an accessible label.
A `sr-only` class is added by default to hide this from sighted users.
Change it to make the label visible, or add classes for other purposes.
###### Parameters
> 👉 **Note**: this option affects footnotes.
> Footnotes are not specified by CommonMark.
> They are supported by GitHub, so they can be enabled by using the utility
> [`mdast-util-gfm`][mdast-util-gfm].
* `state` ([`State`][api-state])
— info passed around
* `node` ([`MdastNode`][mdast-node])
— node to handle
* `parent` ([`MdastNode | null | undefined`][mdast-node])
— parent of `node`
###### `options.footnoteBackLabel`
###### Returns
Label to use from backreferences back to their footnote call (`string`, default:
`'Back to content'`).
Affects screen readers.
Change it when the markdown is not in English.
Result ([`HastNode | Array<HastNode> | null | undefined`][mdast-node]).
> 👉 **Note**: this option affects footnotes.
> Footnotes are not specified by CommonMark.
> They are supported by GitHub, so they can be enabled by using the utility
> [`mdast-util-gfm`][mdast-util-gfm].
### `Handlers`
###### `options.handlers`
Handle nodes (TypeScript).
Object mapping node types to functions handling the corresponding nodes.
See [`lib/handlers/`][handlers] for examples.
###### Type
In a handler, you have access to `h`, which should be used to create hast nodes
from mdast nodes.
On `h`, there are several fields that may be of interest.
```ts
type Handlers = Record<string, Handler>
```
###### `options.passThrough`
### `Options`
List of mdast node types to pass through (keep) in hast (`Array<string>`,
default: `[]`).
If the passed through nodes have children, those children are expected to be
mdast and will be handled.
Configuration (TypeScript).
Similar functionality can be achieved with a custom handler.
A `passThrough` of `['customNode']` is equivalent to:
###### Fields
```js
toHast(/* … */, {
handlers: {
customNode(h, node) {
return 'children' in node ? {...node, children: all(h, node)} : node
}
}
})
```
* `allowDangerousHtml` (`boolean`, default: `false`)
— whether to persist raw HTML in markdown in the hast tree
* `clobberPrefix` (`string`, default: `'user-content-'`)
— prefix to use before the `id` attribute on footnotes to prevent it from
*clobbering*
* `footnoteBackLabel` (`string`, default: `'Back to content'`)
— label to use from backreferences back to their footnote call (affects
screen readers)
* `footnoteLabel` (`string`, default: `'Footnotes'`)
— label to use for the footnotes section (affects screen readers)
* `footnoteLabelProperties`
([`Properties`][properties], default: `{className: ['sr-only']}`)
— properties to use on the footnote label
(note that `id: 'footnote-label'` is always added as footnote calls use it
with `aria-describedby` to provide an accessible label)
* `footnoteLabelTagName` (`string`, default: `h2`)
— tag name to use for the footnote label
* `handlers` ([`Handlers`][api-handlers], optional)
— extra handlers for nodes
* `passThrough` (`Array<string>`, optional)
— list of custom mdast node types to pass through (keep) in hast (note that
the node itself is passed, but eventual children are transformed)
* `unknownHandler` ([`Handler`][api-handler], optional)
— handle all unknown nodes
###### `options.unknownHandler`
### `Raw`
Handler for unknown nodes (`Handler?`).
Unknown nodes are nodes with a type that isn’t in `handlers` or `passThrough`.
The default behavior for unknown nodes is:
Raw string of HTML embedded into HTML AST (TypeScript).
* when the node has a `value` (and doesn’t have `data.hName`,
`data.hProperties`, or `data.hChildren`, see later), create a hast `text`
node
* otherwise, create a `<div>` element (which could be changed with
`data.hName`), with its children mapped from mdast to hast as well
###### Type
##### Returns
```ts
import type {Literal} from 'hast'
[`HastNode`][hast-node].
interface Raw extends Literal {
type: 'raw'
}
```
### `defaultHandlers`
### `State`
Object mapping mdast node types to functions that can handle them.
See [`lib/handlers/index.js`][default-handlers].
Info passed around about the current state (TypeScript type).
### `all(h, parent)`
###### Fields
Helper function for writing custom handlers passed to `options.handlers`.
Pass it `h` and a parent node (mdast) and it will turn the node’s children into
an array of transformed nodes (hast).
<!-- To do: add `options`, alternative to `definition`. -->
### `one(h, node, parent)`
* `patch` (`(from: MdastNode, to: HastNode) => void`)
— copy a node’s positional info
* `applyData` (`<Type extends HastNode>(from: MdastNode, to: Type) => Type | HastElement`)
— honor the `data` of `from` and maybe generate an element instead of `to`
* `one` (`(node: MdastNode, parent: MdastNode | undefined) => HastNode | Array<HastNode> | undefined`)
— transform an mdast node to hast
* `all` (`(node: MdastNode) => Array<HastNode>`)
— transform the children of an mdast parent to hast
* `wrap` (`<Type extends HastNode>(nodes: Array<Type>, loose?: boolean) => Array<Type | HastText>`)
— wrap `nodes` with line endings between each node, adds initial/final line
endings when `loose`
* `handlers` ([`Handlers`][api-handlers])
— applied node handlers
* `footnoteById` (`Record<string, MdastFootnoteDefinition>`)
— footnote definitions by their uppercased identifier
* `footnoteOrder` (`Array<string>`)
— identifiers of order when footnote calls first appear in tree order
* `footnoteCounts` (`Record<string, number>`)
— counts for how often the same footnote was called
Helper function for writing custom handlers passed to `options.handlers`.
Pass it `h`, a `node`, and its `parent` (mdast) and it will turn `node` into
hast content.
## Examples

@@ -435,3 +469,3 @@

```js
import {toHast, all} from 'mdast-util-to-hast'
import {toHast} from 'mdast-util-to-hast'
import {toHtml} from 'hast-util-to-html'

@@ -446,4 +480,9 @@

handlers: {
mark(h, node) {
return h(node, 'mark', all(h, node))
mark(state, node) {
return {
type: 'element',
tagName: 'mark',
properties: {},
children: state.all(node)
}
}

@@ -485,3 +524,3 @@ }

##### Default handling
### Default handling

@@ -1123,3 +1162,3 @@ The following table gives insight into what input turns into what output:

##### Fields on nodes
### Fields on nodes

@@ -1141,5 +1180,5 @@ A frequent problem arises when having to turn one syntax tree into another.

* `node.data.hName` configures the element’s tag name
* `node.data.hProperties` is mixed into the element’s properties
* `node.data.hChildren` configures the element’s children
* `node.data.hName` — define the element’s tag name
* `node.data.hProperties` — define extra properties to use
* `node.data.hChildren` — define hast children to use

@@ -1180,3 +1219,2 @@ ###### `hName`

alt: 'Big red circle on a black background',
title: null,
data: {hProperties: {className: ['responsive']}}

@@ -1255,3 +1293,3 @@ }

Assuming you know how to use (semantic) HTML and CSS, then it should generally
be straight forward to style the HTML produced by this plugin.
be straightforward to style the HTML produced by this plugin.
With CSS, you can get creative and style the results as you please.

@@ -1309,3 +1347,3 @@

interface Raw <: Literal {
type: "raw"
type: 'raw'
}

@@ -1322,10 +1360,15 @@ ```

This package is fully typed with [TypeScript][].
It also exports `Options`, `Handler`, `Handlers`, `H`, and `Raw` types.
It also exports [`Handler`][api-handler], [`Handlers`][api-handlers],
[`Options`][api-options], [`Raw`][api-raw], and [`State`][api-state] types.
If you’re working with raw nodes in the hast syntax tree (which are added when
It also registers the `Raw` node type with `@types/mdast`.
If you’re working with the syntax tree (and you pass
`allowDangerousHtml: true`), make sure to import this utility somewhere in your
types, as that registers the new node types in the tree.
types, as that registers the new node type in the tree.
```js
/** @typedef {import('mdast-util-to-hast')} */
/**
* @typedef {import('mdast-util-to-hast')}
*/
import {visit} from 'unist-util-visit'

@@ -1345,3 +1388,3 @@

versions of Node.js.
As of now, that is Node.js 12.20+, 14.14+, and 16.0+.
As of now, that is Node.js 14.14+ and 16.0+.
Our projects sometimes work with older versions, but this is not guaranteed.

@@ -1481,13 +1524,15 @@

[contributing]: https://github.com/syntax-tree/.github/blob/HEAD/contributing.md
[contributing]: https://github.com/syntax-tree/.github/blob/main/contributing.md
[support]: https://github.com/syntax-tree/.github/blob/HEAD/support.md
[support]: https://github.com/syntax-tree/.github/blob/main/support.md
[coc]: https://github.com/syntax-tree/.github/blob/HEAD/code-of-conduct.md
[coc]: https://github.com/syntax-tree/.github/blob/main/code-of-conduct.md
[mdast]: https://github.com/syntax-tree/mdast
[mdast-node]: https://github.com/syntax-tree/mdast#nodes
[mdast-html]: https://github.com/syntax-tree/mdast#html
[hast-util-table-cell-style]: https://github.com/mapbox/hast-util-table-cell-style
[mdast-util-gfm]: https://github.com/syntax-tree/mdast-util-gfm

@@ -1498,10 +1543,6 @@ [hast]: https://github.com/syntax-tree/hast

[remark-rehype]: https://github.com/remarkjs/remark-rehype
[properties]: https://github.com/syntax-tree/hast#properties
[handlers]: lib/handlers
[hast-util-table-cell-style]: https://github.com/mapbox/hast-util-table-cell-style
[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting
[mdast-util-gfm]: https://github.com/syntax-tree/mdast-util-gfm
[hast-util-to-mdast]: https://github.com/syntax-tree/hast-util-to-mdast

@@ -1515,8 +1556,24 @@

[remark-rehype]: https://github.com/remarkjs/remark-rehype
[clobber-example]: https://github.com/rehypejs/rehype-sanitize#example-headings-dom-clobbering
[default-handlers]: lib/handlers/index.js
[github-markdown-css]: https://github.com/sindresorhus/github-markdown-css
[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting
[dfn-literal]: https://github.com/syntax-tree/hast#literal
[api-default-handlers]: #defaulthandlers
[api-to-hast]: #tohasttree-options
[api-handler]: #handler
[api-handlers]: #handlers
[api-options]: #options
[api-raw]: #raw
[api-state]: #state
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc