summaryrefslogtreecommitdiff
path: root/src/renderer.ts
diff options
context:
space:
mode:
authoraspizu <aspizu@protonmail.com>2025-03-10 02:58:09 +0530
committeraspizu <aspizu@protonmail.com>2025-03-10 02:58:09 +0530
commit8137b02b1ea25836f80e6d61fae7592ddb6837f2 (patch)
tree41c14a3a75bec0dc2a83c6de36b56f88aa1b4404 /src/renderer.ts
initial commit
Diffstat (limited to 'src/renderer.ts')
-rw-r--r--src/renderer.ts77
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