diff --git a/.changeset/tidy-markdoc-dashed-keys.md b/.changeset/tidy-markdoc-dashed-keys.md new file mode 100644 index 000000000000..159333f090e0 --- /dev/null +++ b/.changeset/tidy-markdoc-dashed-keys.md @@ -0,0 +1,5 @@ +--- +'@astrojs/markdoc': patch +--- + +Fixes custom `transform` functions being incorrectly dropped for tags and nodes whose names require bracket access (e.g. `side-note`). The check that detects whether a transform respects a custom `render` component now recognizes bracket notation, optional chaining and whitespace, not only dot notation. diff --git a/packages/integrations/markdoc/src/runtime.ts b/packages/integrations/markdoc/src/runtime.ts index efbcc03e22bf..17636a3a2104 100644 --- a/packages/integrations/markdoc/src/runtime.ts +++ b/packages/integrations/markdoc/src/runtime.ts @@ -147,11 +147,15 @@ function syncTagNodeAttributes(config: MergedConfig): void { */ function transformRespectsRender(transform: { toString(): string }, configKey: string): boolean { const source = transform.toString(); - // Astro's transforms check config.nodes?.X?.render or config.tags?.X?.render - return ( - source.includes(`config.nodes?.${configKey}?.render`) || - source.includes(`config.tags?.${configKey}?.render`) + // `configKey` may not be a valid identifier (e.g. `side-note`), so a render-respecting + // transform can read `render` via dot or bracket access. Escape the key and match either. + const key = configKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const member = (prop: string) => + `(?:\\??\\.\\s*${prop}|\\??\\.?\\s*\\[\\s*['"\`]${prop}['"\`]\\s*\\])`; + const pattern = new RegExp( + `config\\s*\\??\\.\\s*(?:nodes|tags)\\s*${member(key)}\\s*${member('render')}`, ); + return pattern.test(source); } export function resolveComponentImports( diff --git a/packages/integrations/markdoc/test/resolve-component-imports.test.ts b/packages/integrations/markdoc/test/resolve-component-imports.test.ts new file mode 100644 index 000000000000..b98b55da757f --- /dev/null +++ b/packages/integrations/markdoc/test/resolve-component-imports.test.ts @@ -0,0 +1,72 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import type { AstroInstance } from 'astro'; +import { resolveComponentImports } from '../dist/runtime.js'; + +type MarkdocConfig = Parameters[0]; +type NodeComponentMap = Parameters[2]; + +// Minimal stand-in for an Astro component; only referential identity matters here. +const Component = (() => null) as unknown as AstroInstance['default']; +const noNodeComponents = {} as NodeComponentMap; + +/** + * Regression test for issue #17118: tag/node names containing characters that + * require bracket access (e.g. `side-note`) were always treated as not respecting + * `render`, because the detection only matched dot notation. + */ +describe('Markdoc - resolveComponentImports', () => { + it('keeps a render-respecting transform for tag names that need bracket access (e.g. dashes)', () => { + const markdocConfig: MarkdocConfig = { + tags: { + 'side-note': { + // A dashed key can only read `render` through bracket notation. + transform(node, config) { + if (config.tags?.['side-note']?.render) return []; + return node.transformChildren(config); + }, + }, + }, + nodes: {}, + }; + + const resolved = resolveComponentImports( + markdocConfig, + { 'side-note': Component }, + noNodeComponents, + ); + + assert.equal(resolved.tags['side-note'].render, Component); + assert.equal( + typeof resolved.tags['side-note'].transform, + 'function', + 'a render-respecting transform should be preserved for dashed tag names', + ); + }); + + it('removes a transform that does not respect render so the custom component wins', () => { + const markdocConfig: MarkdocConfig = { + tags: { + 'side-note': { + transform(node, config) { + return node.transformChildren(config); + }, + }, + }, + nodes: {}, + }; + + const resolved = resolveComponentImports( + markdocConfig, + { 'side-note': Component }, + noNodeComponents, + ); + + assert.equal(resolved.tags['side-note'].render, Component); + assert.equal( + resolved.tags['side-note'].transform, + undefined, + 'a non-render-respecting transform should be removed so `render` wins', + ); + }); +});