diff options
Diffstat (limited to 'packages/integrations/markdoc/src')
-rw-r--r-- | packages/integrations/markdoc/src/index.ts | 12 | ||||
-rw-r--r-- | packages/integrations/markdoc/src/nodes/heading.ts | 32 | ||||
-rw-r--r-- | packages/integrations/markdoc/src/nodes/index.ts | 2 | ||||
-rw-r--r-- | packages/integrations/markdoc/src/runtime.ts | 46 |
4 files changed, 65 insertions, 27 deletions
diff --git a/packages/integrations/markdoc/src/index.ts b/packages/integrations/markdoc/src/index.ts index 4360800a0..627f08c77 100644 --- a/packages/integrations/markdoc/src/index.ts +++ b/packages/integrations/markdoc/src/index.ts @@ -10,7 +10,7 @@ import { emitESMImage } from 'astro/assets'; import { bold, red, yellow } from 'kleur/colors'; import type * as rollup from 'rollup'; import { loadMarkdocConfig, type MarkdocConfigResult } from './load-config.js'; -import { applyDefaultConfig } from './runtime.js'; +import { setupConfig } from './runtime.js'; type SetupHookParams = HookParameters<'astro:config:setup'> & { // `contentEntryType` is not a public API @@ -52,7 +52,7 @@ export default function markdocIntegration(legacyConfig?: any): AstroIntegration async getRenderModule({ entry, viteId }) { const ast = Markdoc.parse(entry.body); const pluginContext = this; - const markdocConfig = applyDefaultConfig(userMarkdocConfig, entry); + const markdocConfig = setupConfig(userMarkdocConfig, entry); const validationErrors = Markdoc.validate(ast, markdocConfig).filter((e) => { return ( @@ -90,7 +90,7 @@ export default function markdocIntegration(legacyConfig?: any): AstroIntegration const res = `import { jsx as h } from 'astro/jsx-runtime'; import { Renderer } from '@astrojs/markdoc/components'; - import { collectHeadings, applyDefaultConfig, Markdoc, headingSlugger } from '@astrojs/markdoc/runtime'; + import { collectHeadings, setupConfig, Markdoc } from '@astrojs/markdoc/runtime'; import * as entry from ${JSON.stringify(viteId + '?astroContentCollectionEntry')}; ${ markdocConfigResult @@ -113,16 +113,14 @@ export function getHeadings() { instead of the Content component. Would remove double-transform and unlock variable resolution in heading slugs. */ '' } - headingSlugger.reset(); const headingConfig = userConfig.nodes?.heading; - const config = applyDefaultConfig(headingConfig ? { nodes: { heading: headingConfig } } : {}, entry); + const config = setupConfig(headingConfig ? { nodes: { heading: headingConfig } } : {}, entry); const ast = Markdoc.Ast.fromJSON(stringifiedAst); const content = Markdoc.transform(ast, config); return collectHeadings(Array.isArray(content) ? content : content.children); } export async function Content (props) { - headingSlugger.reset(); - const config = applyDefaultConfig({ + const config = setupConfig({ ...userConfig, variables: { ...userConfig.variables, ...props }, }, entry); diff --git a/packages/integrations/markdoc/src/nodes/heading.ts b/packages/integrations/markdoc/src/nodes/heading.ts index 8adf57612..19a988b63 100644 --- a/packages/integrations/markdoc/src/nodes/heading.ts +++ b/packages/integrations/markdoc/src/nodes/heading.ts @@ -1,10 +1,19 @@ -import Markdoc, { type RenderableTreeNode, type Schema } from '@markdoc/markdoc'; +import Markdoc, { type RenderableTreeNode, type Schema, type ConfigType } from '@markdoc/markdoc'; import Slugger from 'github-slugger'; import { getTextContent } from '../runtime.js'; -export const headingSlugger = new Slugger(); +type ConfigTypeWithCtx = ConfigType & { + // TODO: decide on `ctx` as a convention for config merging + ctx: { + headingSlugger: Slugger; + }; +}; -function getSlug(attributes: Record<string, any>, children: RenderableTreeNode[]): string { +function getSlug( + attributes: Record<string, any>, + children: RenderableTreeNode[], + headingSlugger: Slugger +): string { if (attributes.id && typeof attributes.id === 'string') { return attributes.id; } @@ -21,11 +30,11 @@ export const heading: Schema = { id: { type: String }, level: { type: Number, required: true, default: 1 }, }, - transform(node, config) { + transform(node, config: ConfigTypeWithCtx) { const { level, ...attributes } = node.transformAttributes(config); const children = node.transformChildren(config); - const slug = getSlug(attributes, children); + const slug = getSlug(attributes, children, config.ctx.headingSlugger); const render = config.nodes?.heading?.render ?? `h${level}`; const tagProps = @@ -39,3 +48,16 @@ export const heading: Schema = { return new Markdoc.Tag(render, tagProps, children); }, }; + +export function setupHeadingConfig(): ConfigTypeWithCtx { + const headingSlugger = new Slugger(); + + return { + ctx: { + headingSlugger, + }, + nodes: { + heading, + }, + }; +} diff --git a/packages/integrations/markdoc/src/nodes/index.ts b/packages/integrations/markdoc/src/nodes/index.ts index c25b03f27..4cd7e3667 100644 --- a/packages/integrations/markdoc/src/nodes/index.ts +++ b/packages/integrations/markdoc/src/nodes/index.ts @@ -1,4 +1,4 @@ import { heading } from './heading.js'; -export { headingSlugger } from './heading.js'; +export { setupHeadingConfig } from './heading.js'; export const nodes = { heading }; diff --git a/packages/integrations/markdoc/src/runtime.ts b/packages/integrations/markdoc/src/runtime.ts index 61b38fd02..3164cda13 100644 --- a/packages/integrations/markdoc/src/runtime.ts +++ b/packages/integrations/markdoc/src/runtime.ts @@ -4,27 +4,45 @@ import Markdoc, { type RenderableTreeNode, } from '@markdoc/markdoc'; import type { ContentEntryModule } from 'astro'; -import { nodes as astroNodes } from './nodes/index.js'; +import { setupHeadingConfig } from './nodes/index.js'; -/** Used to reset Slugger cache on each build at runtime */ +/** Used to call `Markdoc.transform()` and `Markdoc.Ast` in runtime modules */ export { default as Markdoc } from '@markdoc/markdoc'; -export { headingSlugger } from './nodes/index.js'; -export function applyDefaultConfig( - config: MarkdocConfig, - entry: ContentEntryModule -): MarkdocConfig { +/** + * Merge user config with default config and set up context (ex. heading ID slugger) + * Called on each file's individual transform + */ +export function setupConfig(userConfig: MarkdocConfig, entry: ContentEntryModule): MarkdocConfig { + const defaultConfig: MarkdocConfig = { + // `setupXConfig()` could become a "plugin" convention as well? + ...setupHeadingConfig(), + variables: { entry }, + }; + return mergeConfig(defaultConfig, userConfig); +} + +/** Merge function from `@markdoc/markdoc` internals */ +function mergeConfig(configA: MarkdocConfig, configB: MarkdocConfig): MarkdocConfig { return { - ...config, - variables: { - entry, - ...config.variables, + ...configA, + ...configB, + tags: { + ...configA.tags, + ...configB.tags, }, nodes: { - ...astroNodes, - ...config.nodes, + ...configA.nodes, + ...configB.nodes, + }, + functions: { + ...configA.functions, + ...configB.functions, + }, + variables: { + ...configA.variables, + ...configB.variables, }, - // TODO: Syntax highlighting }; } |