diff options
author | 2025-01-09 22:57:02 +0800 | |
---|---|---|
committer | 2025-01-09 22:57:02 +0800 | |
commit | a20a4d7b8ffe3ae941b5c510b319ac6f9783aabe (patch) | |
tree | 2692fd883e94c4e1dea15c09f28da0432680ca78 /packages/integrations/markdoc/components | |
parent | ce842c9b7e27b8ab1c5d43bafd1a64a7bfa29194 (diff) | |
download | astro-a20a4d7b8ffe3ae941b5c510b319ac6f9783aabe.tar.gz astro-a20a4d7b8ffe3ae941b5c510b319ac6f9783aabe.tar.zst astro-a20a4d7b8ffe3ae941b5c510b319ac6f9783aabe.zip |
Fix markdoc render code block in if tag (#12930)
Diffstat (limited to 'packages/integrations/markdoc/components')
-rw-r--r-- | packages/integrations/markdoc/components/Renderer.astro | 17 | ||||
-rw-r--r-- | packages/integrations/markdoc/components/TreeNode.ts | 146 |
2 files changed, 88 insertions, 75 deletions
diff --git a/packages/integrations/markdoc/components/Renderer.astro b/packages/integrations/markdoc/components/Renderer.astro index c26d92ad7..270604091 100644 --- a/packages/integrations/markdoc/components/Renderer.astro +++ b/packages/integrations/markdoc/components/Renderer.astro @@ -1,6 +1,6 @@ --- //! astro-head-inject -import type { Config } from '@markdoc/markdoc'; +import type { Config, RenderableTreeNodes } from '@markdoc/markdoc'; import Markdoc from '@markdoc/markdoc'; import { ComponentNode, createTreeNode } from './TreeNode.js'; @@ -12,13 +12,12 @@ type Props = { const { stringifiedAst, config } = Astro.props as Props; const ast = Markdoc.Ast.fromJSON(stringifiedAst); -const content = await Markdoc.transform(ast, config); +// The AST may be an array, and `transform` has overloads for arrays and non-array cases, +// However TypeScript seems to struggle to combine both overloads into a single signature. +// Also, `transform` returns a promise here but the types don't reflect that. +// @ts-expect-error +const content = (await Markdoc.transform(ast, config)) as RenderableTreeNodes; +const treeNode = await createTreeNode(content); --- -{ - Array.isArray(content) ? ( - content.map(async (c) => <ComponentNode treeNode={await createTreeNode(c)} />) - ) : ( - <ComponentNode treeNode={await createTreeNode(content)} /> - ) -} +<ComponentNode treeNode={treeNode} /> 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) }; |