summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/stale-jeans-yawn.md5
-rw-r--r--packages/integrations/markdoc/src/extensions/shiki.ts3
-rw-r--r--packages/markdown/remark/src/highlight.ts5
-rw-r--r--packages/markdown/remark/src/shiki.ts13
-rw-r--r--packages/markdown/remark/test/shiki.test.js32
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\}"/);
+ });
});