79 lines
2 KiB
TypeScript
79 lines
2 KiB
TypeScript
|
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;
|
||
|
}
|