UNPKG

@vivliostyle/vfm

Version:

Custom Markdown syntax specialized in book authoring.

72 lines (71 loc) 2.95 kB
import { isElement as is } from 'hast-util-is-element'; import { h } from 'hastscript'; import { visit } from 'unist-util-visit'; const propertyToString = (property) => { return typeof property === 'string' || typeof property === 'number' ? String(property) // <tag prop="foo" /> || <tag prop=42 /> : Array.isArray(property) ? property.map(String).join(' ') // <tag prop="foo 42 bar" /> : ''; // <tag /> || <tag prop /> }; /** * Move ID from source element to target properties if assignIdToFigcaption is enabled. * @param source Element to move ID from (img or code). * @param targetProps Properties object to receive the ID. * @param options Figure options. */ const moveIdToFigcaption = (source, targetProps, options) => { if (options.assignIdToFigcaption && source.properties?.id) { targetProps.id = propertyToString(source.properties.id); delete source.properties.id; } }; /** * Wrap the single line `<img>` in `<figure>` and generate `<figcaption>` from the `alt` attribute. * * A single line `<img>` is a child of `<p>` with no sibling elements. Also, `<figure>` cannot be a child of `<p>`. So convert the parent `<p>` to `<figure>`. * @param img `<img>` tag. * @param parent `<p>` tag. * @param options Figure options. */ const wrapFigureImg = (img, parent, options) => { if (!(img.properties && parent.properties)) { return; } parent.tagName = 'figure'; const figcaptionProps = { 'aria-hidden': 'true' }; moveIdToFigcaption(img, figcaptionProps, options); const figcaption = h('figcaption', figcaptionProps, propertyToString(img.properties.alt)); if (options.imgFigcaptionOrder === 'figcaption-img') { parent.children.unshift(figcaption); } else { parent.children.push(figcaption); } }; export const hast = (options = {}) => (tree) => { visit(tree, 'element', (node, index, parent) => { // handle captioned code block const maybeCode = node.children?.[0]; if (is(node, 'pre') && maybeCode?.properties && maybeCode.properties.title) { const maybeTitle = maybeCode.properties.title; delete maybeCode.properties.title; if (Array.isArray(maybeCode.properties.className)) { const figcaptionProps = {}; moveIdToFigcaption(maybeCode, figcaptionProps, options); parent.children[index] = h('figure', { class: maybeCode.properties.className[0] }, h('figcaption', figcaptionProps, propertyToString(maybeTitle)), node); } return; } // handle captioned and single line (like a block) img if (is(node, 'img') && node.properties?.alt && parent && parent.tagName === 'p' && parent.children.length === 1) { wrapFigureImg(node, parent, options); } }); };