diff options
Diffstat (limited to 'packages/integrations/markdoc/components/TreeNode.ts')
-rw-r--r-- | packages/integrations/markdoc/components/TreeNode.ts | 146 |
1 files changed, 80 insertions, 66 deletions
diff --git a/packages/integrations/markdoc/components/TreeNode.ts b/packages/integrations/markdoc/components/TreeNode.ts index cbcf15329..d63ca8c1c 100644 --- a/packages/integrations/markdoc/components/TreeNode.ts +++ b/packages/integrations/markdoc/components/TreeNode.ts @@ -1,6 +1,6 @@ -import type { RenderableTreeNode } from '@markdoc/markdoc'; +import type { RenderableTreeNodes } from '@markdoc/markdoc'; import Markdoc from '@markdoc/markdoc'; -import type { AstroInstance } from 'astro'; +import type { AstroInstance, SSRResult } from 'astro'; import type { HTMLString } from 'astro/runtime/server/index.js'; import { createComponent, @@ -15,6 +15,9 @@ import { } from 'astro/runtime/server/index.js'; export type TreeNode = + // Markdoc `if` tag often returns an array of nodes in the AST, which gets translated + // here as an array of `TreeNode`s, which we'll render all without a wrapper. + | TreeNode[] | { type: 'text'; content: string | HTMLString; @@ -35,75 +38,86 @@ export type TreeNode = children: TreeNode[]; }; -export const ComponentNode = createComponent({ - factory(result: any, { treeNode }: { treeNode: TreeNode }) { - if (treeNode.type === 'text') return render`${treeNode.content}`; - - const slots = { - default: () => - render`${treeNode.children.map((child) => - renderComponent(result, 'ComponentNode', ComponentNode, { treeNode: child }), - )}`, - }; - if (treeNode.type === 'component') { - let styles = '', - links = '', - scripts = ''; - if (Array.isArray(treeNode.collectedStyles)) { - styles = treeNode.collectedStyles - .map((style: any) => - renderUniqueStylesheet(result, { - type: 'inline', - content: style, - }), - ) - .join(''); - } - if (Array.isArray(treeNode.collectedLinks)) { - links = treeNode.collectedLinks - .map((link: any) => { - return renderUniqueStylesheet(result, { - type: 'external', - src: link[0] === '/' ? link : '/' + link, - }); - }) - .join(''); - } - if (Array.isArray(treeNode.collectedScripts)) { - scripts = treeNode.collectedScripts - .map((script: any) => renderScriptElement(script)) - .join(''); - } - - const head = unescapeHTML(styles + links + scripts); - - let headAndContent = createHeadAndContent( - head, - renderTemplate`${renderComponent( - result, - treeNode.component.name, - treeNode.component, - treeNode.props, - slots, - )}`, - ); - - // Let the runtime know that this component is being used. - result._metadata.propagators.add({ - init() { - return headAndContent; - }, - }); - - return headAndContent; +function renderTreeNodeToFactoryResult(result: SSRResult, treeNode: TreeNode) { + if (Array.isArray(treeNode)) { + return Promise.all(treeNode.map((node) => renderTreeNodeToFactoryResult(result, node))); + } + + if (treeNode.type === 'text') return render`${treeNode.content}`; + + const slots = { + default: () => + render`${treeNode.children.map((child) => + renderComponent(result, 'ComponentNode', ComponentNode, { treeNode: child }), + )}`, + }; + if (treeNode.type === 'component') { + let styles = '', + links = '', + scripts = ''; + if (Array.isArray(treeNode.collectedStyles)) { + styles = treeNode.collectedStyles + .map((style: any) => + renderUniqueStylesheet(result, { + type: 'inline', + content: style, + }), + ) + .join(''); + } + if (Array.isArray(treeNode.collectedLinks)) { + links = treeNode.collectedLinks + .map((link: any) => { + return renderUniqueStylesheet(result, { + type: 'external', + src: link[0] === '/' ? link : '/' + link, + }); + }) + .join(''); } - return renderComponent(result, treeNode.tag, treeNode.tag, treeNode.attributes, slots); + if (Array.isArray(treeNode.collectedScripts)) { + scripts = treeNode.collectedScripts + .map((script: any) => renderScriptElement(script)) + .join(''); + } + + const head = unescapeHTML(styles + links + scripts); + + let headAndContent = createHeadAndContent( + head, + renderTemplate`${renderComponent( + result, + treeNode.component.name, + treeNode.component, + treeNode.props, + slots, + )}`, + ); + + // Let the runtime know that this component is being used. + // @ts-expect-error Astro only uses `init()` so specify it only (plus `_metadata` is internal) + result._metadata.propagators.add({ + init() { + return headAndContent; + }, + }); + + return headAndContent; + } + return renderComponent(result, treeNode.tag, treeNode.tag, treeNode.attributes, slots); +} + +export const ComponentNode = createComponent({ + factory(result: SSRResult, { treeNode }: { treeNode: TreeNode | TreeNode[] }) { + return renderTreeNodeToFactoryResult(result, treeNode); }, propagation: 'self', }); -export async function createTreeNode(node: RenderableTreeNode): Promise<TreeNode> { - if (isHTMLString(node)) { +export async function createTreeNode(node: RenderableTreeNodes): Promise<TreeNode> { + if (Array.isArray(node)) { + return Promise.all(node.map((child) => createTreeNode(child))); + } else if (isHTMLString(node)) { return { type: 'text', content: node as HTMLString }; } else if (typeof node === 'string' || typeof node === 'number') { return { type: 'text', content: String(node) }; |