import remarkFrontmatter from 'remark-frontmatter'
import { collapseWhiteSpace } from 'collapse-white-space'
import { visit } from 'unist-util-visit'
import { Root, RootContent } from 'mdast'
import { remark } from 'remark'
import remarkGfm from 'remark-gfm'
import remarkMdx from 'remark-mdx'
import { parseHtmlToMdxAst, remarkMdxJsxNormalize } from './html/html-to-mdx-ast.js'
export { parseHtmlToMdxAst, remarkMdxJsxNormalize }
export function mdxParse(code: string) {
const file = mdxProcessor.processSync(code)
return file.data.ast as Root
}
/**
* https://github.com/mdx-js/mdx/blob/b3351fadcb6f78833a72757b7135dcfb8ab646fe/packages/mdx/lib/plugin/remark-mark-and-unravel.js
* A tiny plugin that unravels `
x
` but also
* `
` (so it has no knowledge of "HTML").
*
* It also marks JSX as being explicitly JSX, so when a user passes a `h1`
* component, it is used for `# heading` but not for `heading
`.
*
*/
export function remarkMarkAndUnravel() {
return function (tree: Root) {
visit(tree, function (node, index, parent) {
let offset = -1
let all = true
let oneOrMore = false
if (
parent &&
typeof index === 'number' &&
node.type === 'paragraph'
) {
const children = node.children
while (++offset < children.length) {
const child = children[offset]
if (
child.type === 'mdxJsxTextElement' ||
child.type === 'mdxTextExpression'
) {
oneOrMore = true
} else if (
child.type === 'text' &&
collapseWhiteSpace(child.value, {
style: 'html',
trim: true,
}) === ''
) {
// Empty.
} else {
all = false
break
}
}
if (all && oneOrMore) {
offset = -1
const newChildren: RootContent[] = []
while (++offset < children.length) {
const child = children[offset]
if (child.type === 'mdxJsxTextElement') {
// @ts-expect-error: mutate because it is faster; content model is fine.
child.type = 'mdxJsxFlowElement'
}
if (child.type === 'mdxTextExpression') {
// @ts-expect-error: mutate because it is faster; content model is fine.
child.type = 'mdxFlowExpression'
}
if (
child.type === 'text' &&
/^[\t\r\n ]+$/.test(String(child.value))
) {
// Empty.
} else {
newChildren.push(child)
}
}
parent.children.splice(index, 1, ...newChildren)
return index
}
}
})
}
}
const mdxProcessor = remark()
.use(remarkMdx)
.use(remarkFrontmatter, ['yaml', 'toml'])
.use(remarkGfm)
.use(remarkMarkAndUnravel)
.use(() => {
return (tree, file) => {
file.data.ast = tree
}
})