diff options
Diffstat (limited to 'packages/integrations/markdoc/components')
4 files changed, 104 insertions, 0 deletions
diff --git a/packages/integrations/markdoc/components/RenderNode.astro b/packages/integrations/markdoc/components/RenderNode.astro new file mode 100644 index 000000000..a683cd983 --- /dev/null +++ b/packages/integrations/markdoc/components/RenderNode.astro @@ -0,0 +1,30 @@ +--- +import stringifyAttributes from 'stringify-attributes'; +import type { AstroNode } from './astroNode'; + +type Props = { + node: AstroNode; +}; + +const Node = (Astro.props as Props).node; +--- + +{ + typeof Node === 'string' ? ( + <Fragment set:text={Node} /> + ) : 'component' in Node ? ( + <Node.component {...Node.props}> + {Node.children.map((child) => ( + <Astro.self node={child} /> + ))} + </Node.component> + ) : ( + <Fragment> + <Fragment set:html={`<${Node.tag} ${stringifyAttributes(Node.attributes)}>`} /> + {Node.children.map((child) => ( + <Astro.self node={child} /> + ))} + <Fragment set:html={`</${Node.tag}>`} /> + </Fragment> + ) +} diff --git a/packages/integrations/markdoc/components/Renderer.astro b/packages/integrations/markdoc/components/Renderer.astro new file mode 100644 index 000000000..4fce72b04 --- /dev/null +++ b/packages/integrations/markdoc/components/Renderer.astro @@ -0,0 +1,21 @@ +--- +import type { RenderableTreeNode } from '@markdoc/markdoc'; +import type { AstroInstance } from 'astro'; +import { validateComponentsProp } from '../dist/utils.js'; +import { createAstroNode } from './astroNode'; +import RenderNode from './RenderNode.astro'; + +type Props = { + content: RenderableTreeNode; + components?: Record<string, AstroInstance['default']>; +}; + +const { content, components } = Astro.props as Props; + +// Will throw if components is invalid +if (components) { + validateComponentsProp(components); +} +--- + +<RenderNode node={createAstroNode(content, components)} /> diff --git a/packages/integrations/markdoc/components/astroNode.ts b/packages/integrations/markdoc/components/astroNode.ts new file mode 100644 index 000000000..12a0cd0a6 --- /dev/null +++ b/packages/integrations/markdoc/components/astroNode.ts @@ -0,0 +1,51 @@ +import type { AstroInstance } from 'astro'; +import type { RenderableTreeNode } from '@markdoc/markdoc'; +import Markdoc from '@markdoc/markdoc'; +import { MarkdocError, isCapitalized } from '../dist/utils.js'; + +export type AstroNode = + | string + | { + component: AstroInstance['default']; + props: Record<string, any>; + children: AstroNode[]; + } + | { + tag: string; + attributes: Record<string, any>; + children: AstroNode[]; + }; + +export function createAstroNode( + node: RenderableTreeNode, + components: Record<string, AstroInstance['default']> = {} +): AstroNode { + if (typeof node === 'string' || typeof node === 'number') { + return String(node); + } else if (node === null || typeof node !== 'object' || !Markdoc.Tag.isTag(node)) { + return ''; + } + + if (node.name in components) { + const component = components[node.name]; + const props = node.attributes; + const children = node.children.map((child) => createAstroNode(child, components)); + + return { + component, + props, + children, + }; + } else if (isCapitalized(node.name)) { + throw new MarkdocError({ + message: `Unable to render ${JSON.stringify(node.name)}.`, + hint: 'Did you add this to the "components" prop on your <Content /> component?', + }); + } else { + return { + tag: node.name, + attributes: node.attributes, + children: node.children.map((child) => createAstroNode(child, components)), + }; + } +} diff --git a/packages/integrations/markdoc/components/index.ts b/packages/integrations/markdoc/components/index.ts new file mode 100644 index 000000000..dc81899f1 --- /dev/null +++ b/packages/integrations/markdoc/components/index.ts @@ -0,0 +1,2 @@ +// @ts-expect-error +export { default as Renderer } from './Renderer.astro'; |