diff options
Diffstat (limited to 'src/renderer.ts')
| -rw-r--r-- | src/renderer.ts | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/src/renderer.ts b/src/renderer.ts new file mode 100644 index 0000000..677c234 --- /dev/null +++ b/src/renderer.ts @@ -0,0 +1,77 @@ +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:jsx", + check, + renderToStaticMarkup, +} + +export default renderer |
