import { RenderableTreeNode, Tag, renderers, NodeType } from '@markdoc/markdoc'; import { escape } from 'html-escaper'; // TODO: expose `AstroComponentFactory` type from core type AstroComponentFactory = (props: Record) => any & { isAstroComponentFactory: true; }; export type ComponentRenderer = | AstroComponentFactory | { component: AstroComponentFactory; props?(params: { attributes: Record; getTreeNode(): Tag }): Record; }; export type AstroNode = | string | { component: AstroComponentFactory; props: Record; children: AstroNode[]; } | { tag: string; attributes: Record; children: AstroNode[]; }; export function createAstroNode( node: RenderableTreeNode, components: Record = {} ): AstroNode { if (typeof node === 'string' || typeof node === 'number') { return escape(String(node)); } else if (node === null || typeof node !== 'object' || !Tag.isTag(node)) { return ''; } if (node.name in components) { const componentRenderer = components[node.name]; const component = 'component' in componentRenderer ? componentRenderer.component : componentRenderer; const props = 'props' in componentRenderer && typeof componentRenderer.props === 'function' ? componentRenderer.props({ attributes: node.attributes, getTreeNode() { return node; }, }) : node.attributes; const children = node.children.map((child) => createAstroNode(child, components)); return { component, props, children, }; } else { return { tag: node.name, attributes: node.attributes, children: node.children.map((child) => createAstroNode(child, components)), }; } }