78 lines
2.4 KiB
TypeScript
78 lines
2.4 KiB
TypeScript
import type {NamedSSRLoadedRendererValue} from "astro"
|
|
import {AstroError} from "astro/errors"
|
|
import {AstroJSX, jsx} from "astro/jsx-runtime"
|
|
import {renderJSX} from "astro/runtime/server/index.js"
|
|
|
|
const slotName = (str: string) =>
|
|
str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase())
|
|
|
|
// NOTE: In practice, MDX components are always tagged with `__astro_tag_component__`, so the right renderer
|
|
// is used directly, and this check is not often used to return true.
|
|
export async function check(
|
|
Component: any,
|
|
props: any,
|
|
{default: children = null, ...slotted} = {},
|
|
) {
|
|
if (typeof Component !== "function") return false
|
|
const slots: Record<string, any> = {}
|
|
for (const [key, value] of Object.entries(slotted)) {
|
|
const name = slotName(key)
|
|
slots[name] = value
|
|
}
|
|
try {
|
|
const result = await Component({...props, ...slots, children})
|
|
return result[AstroJSX]
|
|
} catch (e) {
|
|
throwEnhancedErrorIfMdxComponent(e as Error, Component)
|
|
}
|
|
return false
|
|
}
|
|
|
|
export async function renderToStaticMarkup(
|
|
this: any,
|
|
Component: any,
|
|
props = {},
|
|
{default: children = null, ...slotted} = {},
|
|
) {
|
|
const slots: Record<string, any> = {}
|
|
for (const [key, value] of Object.entries(slotted)) {
|
|
const name = slotName(key)
|
|
slots[name] = value
|
|
}
|
|
|
|
const {result} = this
|
|
try {
|
|
const html = await renderJSX(
|
|
result,
|
|
jsx(Component, {...props, ...slots, children}),
|
|
)
|
|
return {html}
|
|
} catch (e) {
|
|
throwEnhancedErrorIfMdxComponent(e as Error, Component)
|
|
throw e
|
|
}
|
|
}
|
|
|
|
function throwEnhancedErrorIfMdxComponent(error: Error, Component: any) {
|
|
// if the exception is from an mdx component
|
|
// throw an error
|
|
if (Component[Symbol.for("mdx-component")]) {
|
|
// if it's an existing AstroError, we don't need to re-throw, keep the original hint
|
|
if (AstroError.is(error))
|
|
return // Mimic the fields of the internal `AstroError` class (not from `astro/errors`) to
|
|
// provide better title and hint for the error overlay
|
|
;(error as any).title = error.name
|
|
;(error as any).hint =
|
|
`This issue often occurs when your MDX component encounters runtime errors.`
|
|
throw error
|
|
}
|
|
}
|
|
|
|
const renderer: NamedSSRLoadedRendererValue = {
|
|
name: "astro:gmi",
|
|
check,
|
|
renderToStaticMarkup,
|
|
}
|
|
|
|
export default renderer
|