From 19e42c368184013fc30d1e46753b9e9383bb2bdf Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Wed, 20 Mar 2024 19:15:07 +0800 Subject: [PATCH] Pass meta to shiki transformers (#10494) --- .changeset/stale-jeans-yawn.md | 5 +++ .../markdoc/src/extensions/shiki.ts | 3 ++ packages/markdown/remark/src/highlight.ts | 5 +-- packages/markdown/remark/src/shiki.ts | 13 +++++++- packages/markdown/remark/test/shiki.test.js | 32 +++++++++++++++++++ 5 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 .changeset/stale-jeans-yawn.md diff --git a/.changeset/stale-jeans-yawn.md b/.changeset/stale-jeans-yawn.md new file mode 100644 index 0000000000..1cf616cae8 --- /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 3026d80806..a39eb69a9f 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 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 `` 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 bd9280f8c5..ff1589dac1 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 } + options?: { + inline?: boolean; + attributes?: Record; + /** + * 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 c6044b019d..601b7fabf6 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\}"/); + }); });