Skip to content

Commit

Permalink
Pass meta to shiki transformers (#10494)
Browse files Browse the repository at this point in the history
  • Loading branch information
bluwy authored Mar 20, 2024
1 parent 17b4991 commit 19e42c3
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/stale-jeans-yawn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@astrojs/markdown-remark": patch
---

Fixes support for Shiki transformers that access the `meta` to conditionally perform transformations
3 changes: 3 additions & 0 deletions packages/integrations/markdoc/src/extensions/shiki.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
5 changes: 3 additions & 2 deletions packages/markdown/remark/src/highlight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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/;

Expand Down Expand Up @@ -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.
Expand Down
13 changes: 12 additions & 1 deletion packages/markdown/remark/src/shiki.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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) {
Expand Down
32 changes: 32 additions & 0 deletions packages/markdown/remark/test/shiki.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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\}"/);
});
});

0 comments on commit 19e42c3

Please sign in to comment.