UNPKG

remark-docz

Version:

Remark plugin used by docz

107 lines (78 loc) 3.05 kB
import { parse } from 'url'; import generate from '@babel/generator'; import t from '@babel/types'; import visit from 'unist-util-visit'; import remove from 'unist-util-remove'; // match component name by regexp const componentName = value => { const match = value.match(/^\<\\?(\w+)/); return match && match[1]; }; // iterate in a reverse way to merge values then delete the unused node const valuesFromNodes = tree => (first, last) => { const values = []; if (first !== last) { for (let i = last; i >= first; i--) { const found = tree.children[i]; if (found.children && found.children.length > 0) { values.push(...found.children.map(child => child.value)); } if (found.value && found.value.length > 0) { values.push(found.value); } if (i !== first) remove(tree, found); } } return values; }; const mergeNodeWithoutCloseTag = (tree, node, idx) => { if (!node.value || typeof node.value !== 'string') return; // parse component name and create two regexp to check open and close tag const component = componentName(node.value); const tagOpen = new RegExp(`^\\<${component}`); const tagClose = new RegExp(`\\<\\/${component}\\>$`); const hasOpenTag = val => tagOpen.test(val); const hasCloseTag = val => tagClose.test(val); const hasJustCloseTag = val => val && !hasOpenTag(val) && hasCloseTag(val); // return default value is has open and close tag if (!component || hasOpenTag(node.value) && hasCloseTag(node.value)) { return; } // when some node has just the open tag // find node index with equivalent close tag const tagCloseIdx = tree.children.findIndex(({ value, children }) => { if (children) return children.some(c => hasJustCloseTag(c.value)); return hasJustCloseTag(value); }); if (tagCloseIdx > -1 && tagCloseIdx !== idx) { // merge all values from node open tag until node with the close tag const mergeUntilCloseTag = valuesFromNodes(tree); const values = mergeUntilCloseTag(idx, tagCloseIdx); node.value = values.reverse().join('\n'); } }; const createImgSrc = src => { const parsed = parse(src); if (parsed.protocol) { return t.stringLiteral(src); } let { pathname } = parsed; if (!/^(?:\.[./]+|@)/.test(pathname)) { pathname = `./${pathname}`; } return t.jsxExpressionContainer(t.callExpression(t.identifier('require'), [t.stringLiteral(pathname)])); }; const imageToJsx = node => generate(t.jsxOpeningElement(t.jsxIdentifier('img'), [t.jsxAttribute(t.jsxIdentifier('alt'), t.stringLiteral(node.alt)), t.jsxAttribute(t.jsxIdentifier('src'), createImgSrc(node.url))], true)).code; // turns `html` nodes into `jsx` nodes var index = (() => tree => { visit(tree, 'image', node => { // check if a node has just open tag node.type = 'jsx'; node.value = imageToJsx(node); }); visit(tree, 'jsx', (node, idx) => { // check if a node has just open tag mergeNodeWithoutCloseTag(tree, node, idx); }); }); export default index;