mirror of
https://github.com/explosion/spaCy.git
synced 2025-03-26 12:54:12 +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'
|
import remarkWrapSections from './remarkWrapSections.mjs'
|
||||||
|
|
||||||
const remarkPlugins = [
|
const remarkPlugins = [
|
||||||
|
remarkCustomAttrs,
|
||||||
remarkWrapSections,
|
remarkWrapSections,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,55 +1,71 @@
|
||||||
/**
|
const parseAttribute = (expression) => {
|
||||||
* Simplified implementation of remark-attr that allows custom attributes on
|
if (expression.type !== 'AssignmentExpression' || !expression.left || !expression.right) {
|
||||||
* nodes *inline* via the following syntax: {#some-id key="value"}. Extracting
|
return
|
||||||
* 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
|
|
||||||
}
|
}
|
||||||
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