UNPKG

remark-directive-sugar

Version:

Remark plugin built on remark-directive, providing predefined directives for image captions, video embedding, styled GitHub links, badges, and more.

113 lines (112 loc) 4.34 kB
import { visit, EXIT } from 'unist-util-visit'; import { createIfNeeded, mergeProps } from '../utils.js'; const validTags = new Set([ 'figure', 'a', 'div', 'span', 'section', 'article', 'main', 'aside', 'header', 'footer', 'nav', 'fieldset', 'form', ]); /** * Handles the `image` directive. */ export function handleImageDirective(node, config, regex) { if (node.type === 'textDirective') throw new Error('Unexpected text directive. Use three colons (`:::`) for an `image` container directive.'); if (node.type === 'leafDirective') throw new Error('Unexpected leaf directive. Use three colons (`:::`) for an `image` container directive.'); const { imgProps, figureProps, figcaptionProps, elementProps, stripParagraph = true, } = config; const data = (node.data ||= {}); const attributes = node.attributes || {}; // check if it matches the valid HTML tag & get tag name let matchTag; const match = node.name.match(regex); if (match && validTags.has(match[1])) { matchTag = match[1]; } else { throw new Error('Invalid `image` directive. The directive failed to match a valid HTML tag. See https://github.com/lin-stephanie/remark-directive-sugar/blob/main/src/directives/image.ts#L14 for details.'); } // check if the image is missing & handle image props let hasImage = false; const imgProperties = createIfNeeded(imgProps, node); visit(node, 'image', (imageNode) => { if (imgProperties) { imageNode.data ||= {}; imageNode.data.hProperties = imgProperties; } hasImage = true; return EXIT; }); if (!hasImage) throw new Error('Invalid `image` directive. The image is missing.'); // remove unnecessary `paragraph` node that only contains an `image` node const newChildren = []; for (const child of node.children) { if (stripParagraph && child.type === 'paragraph' && child.children[0].type === 'image') { newChildren.push(...child.children); } else { newChildren.push(child); } } // @ts-expect-error (Type '(ContainerDirective | LeafDirective | TextDirective | Blockquote | Code | Heading | Html | ... 16 more ... | Strong)[]' is not assignable to type '(BlockContent | DefinitionContent)[]'.) node.children = newChildren; const children = node.children; if (matchTag === 'figure') { // get figcaption text (priority: `[]` of `:::image-figure[]{}`、`![]()`) let content; if (children[0].type === 'paragraph' && children[0].data?.directiveLabel && children[0].children[0].type === 'text') { content = children[0].children; children.shift(); } else if (stripParagraph && children[0].type === 'image' && children[0].alt) { content = [{ type: 'text', value: children[0].alt }]; } else if (children[0].type === 'paragraph' && children[0].children[0].type === 'image' && children[0].children[0].alt) { content = [{ type: 'text', value: children[0].children[0].alt }]; } else { throw new Error('Invalid `image` directive. The figcaption text is missing. Specify it in the `[]` of `:::image-figure[]{}` or `![]()`.'); } // handle figure & figcaption props const figureProperties = createIfNeeded(figureProps, node); const figcaptionProperties = createIfNeeded(figcaptionProps, node); // get figcaption node const figcaptionNode = { type: 'paragraph', data: { hName: 'figcaption', hProperties: mergeProps(figcaptionProperties, null, attributes), }, children: content, }; // update node data.hName = 'figure'; data.hProperties = figureProperties ?? undefined; children.push(figcaptionNode); } else { // handle element props const elementProperties = createIfNeeded(elementProps, node); // update node data.hName = matchTag; data.hProperties = mergeProps(elementProperties, null, attributes); } }