mirror of
https://github.com/withastro/astro.git
synced 2024-12-16 21:46:22 -05:00
Pass meta to shiki transformers (#10494)
This commit is contained in:
parent
17b4991cff
commit
19e42c3681
5 changed files with 55 additions and 3 deletions
5
.changeset/stale-jeans-yawn.md
Normal file
5
.changeset/stale-jeans-yawn.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@astrojs/markdown-remark": patch
|
||||
---
|
||||
|
||||
Fixes support for Shiki transformers that access the `meta` to conditionally perform transformations
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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\}"/);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue