diff options
-rw-r--r-- | .changeset/stale-jeans-yawn.md | 5 | ||||
-rw-r--r-- | packages/integrations/markdoc/src/extensions/shiki.ts | 3 | ||||
-rw-r--r-- | packages/markdown/remark/src/highlight.ts | 5 | ||||
-rw-r--r-- | packages/markdown/remark/src/shiki.ts | 13 | ||||
-rw-r--r-- | packages/markdown/remark/test/shiki.test.js | 32 |
5 files changed, 55 insertions, 3 deletions
diff --git a/.changeset/stale-jeans-yawn.md b/.changeset/stale-jeans-yawn.md new file mode 100644 index 000000000..1cf616cae --- /dev/null +++ b/.changeset/stale-jeans-yawn.md @@ -0,0 +1,5 @@ +--- +"@astrojs/markdown-remark": patch +--- + +Fixes support for Shiki transformers that access the `meta` to conditionally perform transformations diff --git a/packages/integrations/markdoc/src/extensions/shiki.ts b/packages/integrations/markdoc/src/extensions/shiki.ts index 3026d8080..a39eb69a9 100644 --- a/packages/integrations/markdoc/src/extensions/shiki.ts +++ b/packages/integrations/markdoc/src/extensions/shiki.ts @@ -12,6 +12,9 @@ export default async function shiki(config?: ShikiConfig): Promise<AstroMarkdocC fence: { attributes: Markdoc.nodes.fence.attributes!, transform({ attributes }) { + // NOTE: The `meta` from fence code, e.g. ```js {1,3-4}, isn't quite supported by Markdoc. + // Only the `js` part is parsed as `attributes.language` and the rest is ignored. This means + // some Shiki transformers may not work correctly as it relies on the `meta`. const lang = typeof attributes.language === 'string' ? attributes.language : 'plaintext'; const html = highlighter.highlight(attributes.content, lang); diff --git a/packages/markdown/remark/src/highlight.ts b/packages/markdown/remark/src/highlight.ts index eaf4c9bdf..31f11119f 100644 --- a/packages/markdown/remark/src/highlight.ts +++ b/packages/markdown/remark/src/highlight.ts @@ -4,7 +4,7 @@ import { toText } from 'hast-util-to-text'; import { removePosition } from 'unist-util-remove-position'; import { visitParents } from 'unist-util-visit-parents'; -type Highlighter = (code: string, language: string) => string; +type Highlighter = (code: string, language: string, options?: { meta?: string }) => string; const languagePattern = /\blanguage-(\S+)\b/; @@ -55,8 +55,9 @@ export function highlightCodeBlocks(tree: Root, highlighter: Highlighter) { return; } + const meta = (node.data as any)?.meta ?? node.properties.metastring ?? undefined; const code = toText(node, { whitespace: 'pre' }); - const html = highlighter(code, languageMatch?.[1] || 'plaintext'); + const html = highlighter(code, languageMatch?.[1] || 'plaintext', { meta }); // The replacement returns a root node with 1 child, the `<pr>` element replacement. const replacement = fromHtml(html, { fragment: true }).children[0] as Element; // We just generated this node, so any positional information is invalid. diff --git a/packages/markdown/remark/src/shiki.ts b/packages/markdown/remark/src/shiki.ts index bd9280f8c..ff1589dac 100644 --- a/packages/markdown/remark/src/shiki.ts +++ b/packages/markdown/remark/src/shiki.ts @@ -7,7 +7,14 @@ export interface ShikiHighlighter { highlight( code: string, lang?: string, - options?: { inline?: boolean; attributes?: Record<string, string> } + options?: { + inline?: boolean; + attributes?: Record<string, string>; + /** + * Raw `meta` information to be used by Shiki transformers + */ + meta?: string; + } ): string; } @@ -56,6 +63,10 @@ export async function createShikiHighlighter({ return highlighter.codeToHtml(code, { ...themeOptions, lang, + // NOTE: while we can spread `options.attributes` here so that Shiki can auto-serialize this as rendered + // attributes on the top-level tag, it's not clear whether it is fine to pass all attributes as meta, as + // they're technically not meta, nor parsed from Shiki's `parseMetaString` API. + meta: options?.meta ? { __raw: options?.meta } : undefined, transformers: [ { pre(node) { diff --git a/packages/markdown/remark/test/shiki.test.js b/packages/markdown/remark/test/shiki.test.js index c6044b019..601b7fabf 100644 --- a/packages/markdown/remark/test/shiki.test.js +++ b/packages/markdown/remark/test/shiki.test.js @@ -53,4 +53,36 @@ describe('shiki syntax highlighting', () => { assert.match(html, />-<\/span>/); assert.match(html, />+<\/span>/); }); + + it('renders attributes', async () => { + const highlighter = await createShikiHighlighter(); + + const html = highlighter.highlight(`foo`, 'js', { + attributes: { 'data-foo': 'bar', autofocus: true }, + }); + + assert.match(html, /data-foo="bar"/); + assert.match(html, /autofocus(?!=)/); + }); + + it('supports transformers that reads meta', async () => { + const highlighter = await createShikiHighlighter({ + transformers: [ + { + pre(node) { + const meta = this.options.meta?.__raw; + if (meta) { + node.properties['data-test'] = meta; + } + }, + }, + ], + }); + + const html = highlighter.highlight(`foo`, 'js', { + meta: '{1,3-4}', + }); + + assert.match(html, /data-test="\{1,3-4\}"/); + }); }); |