mirror of
				https://github.com/explosion/spaCy.git
				synced 2025-11-04 01:48:04 +03:00 
			
		
		
		
	Add plugin for custom attributes on Markdown elements
This commit is contained in:
		
							parent
							
								
									343c0054f2
								
							
						
					
					
						commit
						4340a9a248
					
				| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
import remarkCustomAttrs from './remarkCustomAttrs.mjs'
 | 
			
		||||
import remarkWrapSections from './remarkWrapSections.mjs'
 | 
			
		||||
 | 
			
		||||
const remarkPlugins = [
 | 
			
		||||
    remarkCustomAttrs,
 | 
			
		||||
    remarkWrapSections,
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,55 +1,71 @@
 | 
			
		|||
/**
 | 
			
		||||
 * Simplified implementation of remark-attr that allows custom attributes on
 | 
			
		||||
 * nodes *inline* via the following syntax: {#some-id key="value"}. Extracting
 | 
			
		||||
 * them inline is slightly hackier (at least in this implementation), but it
 | 
			
		||||
 * makes the resulting markup valid and compatible with formatters like
 | 
			
		||||
 * Prettier, which do not allow additional content right below headlines.
 | 
			
		||||
 * Based on: https://github.com/arobase-che/remark-attr
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const visit = require('unist-util-visit')
 | 
			
		||||
const parseAttr = require('md-attr-parser')
 | 
			
		||||
 | 
			
		||||
const defaultOptions = {
 | 
			
		||||
    elements: ['heading', 'link'],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function remarkCustomAttrs(userOptions = {}) {
 | 
			
		||||
    const options = Object.assign({}, defaultOptions, userOptions)
 | 
			
		||||
    function transformer(tree) {
 | 
			
		||||
        visit(tree, null, (node) => {
 | 
			
		||||
            if (options.elements.includes(node.type)) {
 | 
			
		||||
                if (
 | 
			
		||||
                    node.children &&
 | 
			
		||||
                    node.children.length &&
 | 
			
		||||
                    node.children[0].type === 'text' &&
 | 
			
		||||
                    node.children[0].value
 | 
			
		||||
                ) {
 | 
			
		||||
                    if (
 | 
			
		||||
                        node.children.length > 1 &&
 | 
			
		||||
                        node.children.every((el) => el.type === 'text')
 | 
			
		||||
                    ) {
 | 
			
		||||
                        // If headlines contain escaped characters, e.g.
 | 
			
		||||
                        // Doc.\_\_init\_\_, it will be split into several nodes
 | 
			
		||||
                        const mergedText = node.children.map((el) => el.value).join('')
 | 
			
		||||
                        node.children[0].value = mergedText
 | 
			
		||||
                        node.children = [node.children[0]]
 | 
			
		||||
                    }
 | 
			
		||||
                    const parsed = node.children[0].value.split(/\{(.*?)\}/)
 | 
			
		||||
                    if (parsed.length >= 2 && parsed[1]) {
 | 
			
		||||
                        const text = parsed[0].trim()
 | 
			
		||||
                        const { prop } = parseAttr(parsed[1])
 | 
			
		||||
                        const data = node.data || (node.data = {})
 | 
			
		||||
                        const hProps = data.hProperties || (data.hProperties = {})
 | 
			
		||||
                        node.data.hProperties = Object.assign({}, hProps, prop)
 | 
			
		||||
                        node.children[0].value = text
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
        return tree
 | 
			
		||||
const parseAttribute = (expression) => {
 | 
			
		||||
    if (expression.type !== 'AssignmentExpression' || !expression.left || !expression.right) {
 | 
			
		||||
        return
 | 
			
		||||
    }
 | 
			
		||||
    return transformer
 | 
			
		||||
 | 
			
		||||
    const { left, right } = expression
 | 
			
		||||
 | 
			
		||||
    if (left.type !== 'Identifier' || right.type !== 'Literal' || !left.name || !right.value) {
 | 
			
		||||
        return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return { type: 'mdxJsxAttribute', name: left.name, value: right.value }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = remarkCustomAttrs
 | 
			
		||||
const handleNode = (node) => {
 | 
			
		||||
    if (node.type === 'section' && node.children) {
 | 
			
		||||
        return {
 | 
			
		||||
            ...node,
 | 
			
		||||
            children: node.children.map(handleNode),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (node.type !== 'heading' || !node.children || node.children < 2) {
 | 
			
		||||
        return node
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const indexLast = node.children.length - 1
 | 
			
		||||
    const lastNode = node.children[indexLast]
 | 
			
		||||
 | 
			
		||||
    if (lastNode.type !== 'mdxTextExpression' || !lastNode.data || !lastNode.data.estree) {
 | 
			
		||||
        return node
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const { estree } = lastNode.data
 | 
			
		||||
 | 
			
		||||
    if (estree.type !== 'Program' || !estree.body || estree.body.length <= 0 || !estree.body[0]) {
 | 
			
		||||
        return node
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const estreeBodyFirstNode = estree.body[0]
 | 
			
		||||
 | 
			
		||||
    if (estreeBodyFirstNode.type !== 'ExpressionStatement' || !estreeBodyFirstNode.expression) {
 | 
			
		||||
        return node
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const statement = estreeBodyFirstNode.expression
 | 
			
		||||
 | 
			
		||||
    const attributeExpressions = [
 | 
			
		||||
        ...(statement.type === 'SequenceExpression' && statement.expressions
 | 
			
		||||
            ? statement.expressions
 | 
			
		||||
            : []),
 | 
			
		||||
        ...(statement.type === 'AssignmentExpression' ? [statement] : []),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    // This replaces the markdown heading with a JSX element
 | 
			
		||||
    return {
 | 
			
		||||
        type: 'mdxJsxFlowElement',
 | 
			
		||||
        name: `h${node.depth}`,
 | 
			
		||||
        attributes: attributeExpressions.map(parseAttribute),
 | 
			
		||||
        children: [node.children[0]],
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const parseAstTree = (markdownAST) => ({
 | 
			
		||||
    ...markdownAST,
 | 
			
		||||
    children: markdownAST.children.map(handleNode),
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const remarkCustomAttrs = () => parseAstTree
 | 
			
		||||
 | 
			
		||||
export default remarkCustomAttrs
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user