summaryrefslogtreecommitdiff
path: root/packages/integrations/markdoc/src/runtime.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/markdoc/src/runtime.ts')
-rw-r--r--packages/integrations/markdoc/src/runtime.ts78
1 files changed, 78 insertions, 0 deletions
diff --git a/packages/integrations/markdoc/src/runtime.ts b/packages/integrations/markdoc/src/runtime.ts
new file mode 100644
index 000000000..dadb73cd6
--- /dev/null
+++ b/packages/integrations/markdoc/src/runtime.ts
@@ -0,0 +1,78 @@
+import type { MarkdownHeading } from '@astrojs/markdown-remark';
+import Markdoc, {
+ type RenderableTreeNode,
+ type ConfigType as MarkdocConfig,
+} from '@markdoc/markdoc';
+import type { ContentEntryModule } from 'astro';
+import { nodes as astroNodes } from './nodes/index.js';
+
+/** Used to reset Slugger cache on each build at runtime */
+export { headingSlugger } from './nodes/index.js';
+export { default as Markdoc } from '@markdoc/markdoc';
+
+export function applyDefaultConfig(
+ config: MarkdocConfig,
+ entry: ContentEntryModule
+): MarkdocConfig {
+ return {
+ ...config,
+ variables: {
+ entry,
+ ...config.variables,
+ },
+ nodes: {
+ ...astroNodes,
+ ...config.nodes,
+ },
+ // TODO: Syntax highlighting
+ };
+}
+
+/**
+ * Get text content as a string from a Markdoc transform AST
+ */
+export function getTextContent(childNodes: RenderableTreeNode[]): string {
+ let text = '';
+ for (const node of childNodes) {
+ if (typeof node === 'string' || typeof node === 'number') {
+ text += node;
+ } else if (typeof node === 'object' && Markdoc.Tag.isTag(node)) {
+ text += getTextContent(node.children);
+ }
+ }
+ return text;
+}
+
+const headingLevels = [1, 2, 3, 4, 5, 6] as const;
+
+/**
+ * Collect headings from Markdoc transform AST
+ * for `headings` result on `render()` return value
+ */
+export function collectHeadings(children: RenderableTreeNode[]): MarkdownHeading[] {
+ let collectedHeadings: MarkdownHeading[] = [];
+ for (const node of children) {
+ if (typeof node !== 'object' || !Markdoc.Tag.isTag(node)) continue;
+
+ if (node.attributes.__collectHeading === true && typeof node.attributes.level === 'number') {
+ collectedHeadings.push({
+ slug: node.attributes.id,
+ depth: node.attributes.level,
+ text: getTextContent(node.children),
+ });
+ continue;
+ }
+
+ for (const level of headingLevels) {
+ if (node.name === 'h' + level) {
+ collectedHeadings.push({
+ slug: node.attributes.id,
+ depth: level,
+ text: getTextContent(node.children),
+ });
+ }
+ }
+ collectedHeadings.concat(collectHeadings(node.children));
+ }
+ return collectedHeadings;
+}