mirror of
https://github.com/supabase/supabase.git
synced 2026-05-08 09:50:33 -04:00
115 lines
3.6 KiB
JavaScript
115 lines
3.6 KiB
JavaScript
// @ts-check
|
|
|
|
import { remark } from 'remark'
|
|
import remarkGfm from 'remark-gfm'
|
|
import remarkMdx from 'remark-mdx'
|
|
import { SKIP, visit } from 'unist-util-visit'
|
|
|
|
function getJsxAttr(node, name) {
|
|
const attr = (node.attributes ?? []).find((a) => a.type === 'mdxJsxAttribute' && a.name === name)
|
|
return typeof attr?.value === 'string' ? attr.value : ''
|
|
}
|
|
|
|
function flattenChildText(nodes) {
|
|
const parts = []
|
|
for (const node of nodes) {
|
|
if (node.type === 'text' || node.type === 'inlineCode') parts.push(node.value)
|
|
else if (node.children) parts.push(flattenChildText(node.children))
|
|
}
|
|
return parts.join('')
|
|
}
|
|
|
|
const text = (value) => ({ type: 'text', value })
|
|
const para = (children) => ({ type: 'paragraph', children })
|
|
const strong = (children) => ({ type: 'strong', children })
|
|
const emph = (children) => ({ type: 'emphasis', children })
|
|
const blockquote = (children) => ({ type: 'blockquote', children })
|
|
|
|
// Unknown JSX must not become a `paragraph` (block) inside an inline parent.
|
|
function unwrap(node) {
|
|
const children = node.children ?? []
|
|
if (node.type === 'mdxJsxTextElement') return text(flattenChildText(children))
|
|
return children.length > 0 ? para(children) : text('')
|
|
}
|
|
|
|
const REWRITES = {
|
|
Img: (node) => {
|
|
const src = getJsxAttr(node, 'src')
|
|
return src ? { type: 'image', url: src, alt: getJsxAttr(node, 'alt'), title: null } : text('')
|
|
},
|
|
img: (node) => REWRITES.Img(node),
|
|
|
|
Admonition: (node) => {
|
|
const label = (getJsxAttr(node, 'type') || 'note').toUpperCase()
|
|
return blockquote([para([strong([text(label)])]), ...(node.children ?? [])])
|
|
},
|
|
|
|
Quote: (node) => {
|
|
const caption = getJsxAttr(node, 'caption')
|
|
const attribution = caption ? [para([emph([text(`— ${caption}`)])])] : []
|
|
return blockquote([...(node.children ?? []), ...attribution])
|
|
},
|
|
|
|
BlogCollapsible: (node) => {
|
|
const title = getJsxAttr(node, 'title')
|
|
const heading = title ? [para([strong([text(title)])])] : []
|
|
return blockquote([...heading, ...(node.children ?? [])])
|
|
},
|
|
|
|
Link: (node) => {
|
|
const href = getJsxAttr(node, 'href')
|
|
if (!href) return unwrap(node)
|
|
return { type: 'link', url: href, title: null, children: node.children ?? [] }
|
|
},
|
|
a: (node) => REWRITES.Link(node),
|
|
|
|
Subtitle: (node) => strong(node.children ?? []),
|
|
Badge: (node) => strong(node.children ?? []),
|
|
|
|
Avatar: (node) => (node.type === 'mdxJsxTextElement' ? text('') : para([])),
|
|
}
|
|
|
|
function rewriteJsxNode(node) {
|
|
return REWRITES[node.name]?.(node) ?? unwrap(node)
|
|
}
|
|
|
|
function transformMdxNodes() {
|
|
return (tree, file) => {
|
|
const unknownNames = new Set()
|
|
visit(tree, (node, index, parent) => {
|
|
if (parent == null || index == null) return
|
|
|
|
if (
|
|
node.type === 'mdxFlowExpression' ||
|
|
node.type === 'mdxTextExpression' ||
|
|
node.type === 'mdxjsEsm'
|
|
) {
|
|
parent.children.splice(index, 1)
|
|
return [SKIP, index]
|
|
}
|
|
|
|
if (node.type !== 'mdxJsxFlowElement' && node.type !== 'mdxJsxTextElement') return
|
|
|
|
if (node.name && !(node.name in REWRITES)) unknownNames.add(node.name)
|
|
parent.children[index] = rewriteJsxNode(node)
|
|
return [SKIP, index]
|
|
})
|
|
if (unknownNames.size > 0) {
|
|
file.message(`Unknown JSX components stripped: ${[...unknownNames].sort().join(', ')}`)
|
|
}
|
|
}
|
|
}
|
|
|
|
const processor = remark().use(remarkMdx).use(remarkGfm).use(transformMdxNodes).data('settings', {
|
|
bullet: '-',
|
|
fences: true,
|
|
incrementListMarker: false,
|
|
rule: '-',
|
|
})
|
|
|
|
export async function mdxBodyToMarkdown(rawBody) {
|
|
const file = await processor.process(rawBody)
|
|
for (const msg of file.messages) console.warn(` ⚠ ${msg.message}`)
|
|
return String(file)
|
|
}
|