Preserve code element node meta for rehype syntax highlighters (#5335)
This commit is contained in:
parent
dc00ca4648
commit
dca762cf73
7 changed files with 71 additions and 1 deletions
5
.changeset/heavy-kangaroos-sin.md
Normal file
5
.changeset/heavy-kangaroos-sin.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@astrojs/mdx': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Preserve code element node `data.meta` in `properties.metastring` for rehype syntax highlighters, like `rehype-pretty-code``
|
|
@ -39,6 +39,7 @@
|
||||||
"github-slugger": "^1.4.0",
|
"github-slugger": "^1.4.0",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"kleur": "^4.1.4",
|
"kleur": "^4.1.4",
|
||||||
|
"rehype-pretty-code": "^0.4.0",
|
||||||
"rehype-raw": "^6.1.1",
|
"rehype-raw": "^6.1.1",
|
||||||
"remark-frontmatter": "^4.0.1",
|
"remark-frontmatter": "^4.0.1",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
|
|
|
@ -11,6 +11,7 @@ import remarkSmartypants from 'remark-smartypants';
|
||||||
import type { Data, VFile } from 'vfile';
|
import type { Data, VFile } from 'vfile';
|
||||||
import { MdxOptions } from './index.js';
|
import { MdxOptions } from './index.js';
|
||||||
import rehypeCollectHeadings from './rehype-collect-headings.js';
|
import rehypeCollectHeadings from './rehype-collect-headings.js';
|
||||||
|
import rehypeMetaString from './rehype-meta-string.js';
|
||||||
import remarkPrism from './remark-prism.js';
|
import remarkPrism from './remark-prism.js';
|
||||||
import remarkShiki from './remark-shiki.js';
|
import remarkShiki from './remark-shiki.js';
|
||||||
import { jsToTreeNode } from './utils.js';
|
import { jsToTreeNode } from './utils.js';
|
||||||
|
@ -150,6 +151,8 @@ export function getRehypePlugins(
|
||||||
let rehypePlugins: PluggableList = [
|
let rehypePlugins: PluggableList = [
|
||||||
// getHeadings() is guaranteed by TS, so we can't allow user to override
|
// getHeadings() is guaranteed by TS, so we can't allow user to override
|
||||||
rehypeCollectHeadings,
|
rehypeCollectHeadings,
|
||||||
|
// ensure `data.meta` is preserved in `properties.metastring` for rehype syntax highlighters
|
||||||
|
rehypeMetaString,
|
||||||
// rehypeRaw allows custom syntax highlighters to work without added config
|
// rehypeRaw allows custom syntax highlighters to work without added config
|
||||||
[rehypeRaw, { passThrough: nodeTypes }] as any,
|
[rehypeRaw, { passThrough: nodeTypes }] as any,
|
||||||
];
|
];
|
||||||
|
|
17
packages/integrations/mdx/src/rehype-meta-string.ts
Normal file
17
packages/integrations/mdx/src/rehype-meta-string.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { visit } from 'unist-util-visit';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves `data.meta` to `properties.metastring` for the `code` element node
|
||||||
|
* as `rehype-raw` strips `data` from all nodes, which may contain useful information.
|
||||||
|
* e.g. ```js {1:3} => metastring: "{1:3}"
|
||||||
|
*/
|
||||||
|
export default function rehypeMetaString() {
|
||||||
|
return function (tree: any) {
|
||||||
|
visit(tree, (node) => {
|
||||||
|
if (node.type === 'element' && node.tagName === 'code' && node.data?.meta) {
|
||||||
|
node.properties ??= {};
|
||||||
|
node.properties.metastring = node.data.meta;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
# Syntax highlighting
|
# Syntax highlighting
|
||||||
|
|
||||||
```astro
|
```astro {2}
|
||||||
---
|
---
|
||||||
const handlesAstroSyntax = true
|
const handlesAstroSyntax = true
|
||||||
---
|
---
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { expect } from 'chai';
|
||||||
import { parseHTML } from 'linkedom';
|
import { parseHTML } from 'linkedom';
|
||||||
import { loadFixture } from '../../../astro/test/test-utils.js';
|
import { loadFixture } from '../../../astro/test/test-utils.js';
|
||||||
import shikiTwoslash from 'remark-shiki-twoslash';
|
import shikiTwoslash from 'remark-shiki-twoslash';
|
||||||
|
import rehypePrettyCode from 'rehype-pretty-code';
|
||||||
|
|
||||||
const FIXTURE_ROOT = new URL('./fixtures/mdx-syntax-hightlighting/', import.meta.url);
|
const FIXTURE_ROOT = new URL('./fixtures/mdx-syntax-hightlighting/', import.meta.url);
|
||||||
|
|
||||||
|
@ -88,4 +89,31 @@ describe('MDX syntax highlighting', () => {
|
||||||
const twoslashCodeBlock = document.querySelector('pre.shiki');
|
const twoslashCodeBlock = document.querySelector('pre.shiki');
|
||||||
expect(twoslashCodeBlock).to.not.be.null;
|
expect(twoslashCodeBlock).to.not.be.null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('supports custom highlighter - rehype-pretty-code', async () => {
|
||||||
|
const fixture = await loadFixture({
|
||||||
|
root: FIXTURE_ROOT,
|
||||||
|
markdown: {
|
||||||
|
syntaxHighlight: false,
|
||||||
|
},
|
||||||
|
integrations: [
|
||||||
|
mdx({
|
||||||
|
rehypePlugins: [
|
||||||
|
[
|
||||||
|
rehypePrettyCode,
|
||||||
|
{
|
||||||
|
onVisitHighlightedLine(node) {
|
||||||
|
node.properties.style = 'background-color:#000000';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await fixture.build();
|
||||||
|
|
||||||
|
const html = await fixture.readFile('/index.html');
|
||||||
|
expect(html).to.include('style="background-color:#000000"')
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -2782,6 +2782,7 @@ importers:
|
||||||
mdast-util-to-string: ^3.1.0
|
mdast-util-to-string: ^3.1.0
|
||||||
mocha: ^9.2.2
|
mocha: ^9.2.2
|
||||||
reading-time: ^1.5.0
|
reading-time: ^1.5.0
|
||||||
|
rehype-pretty-code: ^0.4.0
|
||||||
rehype-raw: ^6.1.1
|
rehype-raw: ^6.1.1
|
||||||
remark-frontmatter: ^4.0.1
|
remark-frontmatter: ^4.0.1
|
||||||
remark-gfm: ^3.0.1
|
remark-gfm: ^3.0.1
|
||||||
|
@ -2802,6 +2803,7 @@ importers:
|
||||||
github-slugger: 1.5.0
|
github-slugger: 1.5.0
|
||||||
gray-matter: 4.0.3
|
gray-matter: 4.0.3
|
||||||
kleur: 4.1.5
|
kleur: 4.1.5
|
||||||
|
rehype-pretty-code: 0.4.0_shiki@0.11.1
|
||||||
rehype-raw: 6.1.1
|
rehype-raw: 6.1.1
|
||||||
remark-frontmatter: 4.0.1
|
remark-frontmatter: 4.0.1
|
||||||
remark-gfm: 3.0.1
|
remark-gfm: 3.0.1
|
||||||
|
@ -15280,6 +15282,10 @@ packages:
|
||||||
unist-util-visit-children: 1.1.4
|
unist-util-visit-children: 1.1.4
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/parse-numeric-range/1.3.0:
|
||||||
|
resolution: {integrity: sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/parse-package-name/1.0.0:
|
/parse-package-name/1.0.0:
|
||||||
resolution: {integrity: sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==}
|
resolution: {integrity: sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -16210,6 +16216,16 @@ packages:
|
||||||
unified: 10.1.2
|
unified: 10.1.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/rehype-pretty-code/0.4.0_shiki@0.11.1:
|
||||||
|
resolution: {integrity: sha512-Bp91nfo4blpgCXlvGP1hsG+kRFfjqBVU09o1RFcnNA62u+iIzJiJRGzpfBj4FaItq7CEQL5ASGB7vLxN5xCvyA==}
|
||||||
|
engines: {node: ^12.16.0 || >=13.2.0}
|
||||||
|
peerDependencies:
|
||||||
|
shiki: '*'
|
||||||
|
dependencies:
|
||||||
|
parse-numeric-range: 1.3.0
|
||||||
|
shiki: 0.11.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/rehype-raw/6.1.1:
|
/rehype-raw/6.1.1:
|
||||||
resolution: {integrity: sha512-d6AKtisSRtDRX4aSPsJGTfnzrX2ZkHQLE5kiUuGOeEoLpbEulFF4hj0mLPbsa+7vmguDKOVVEQdHKDSwoaIDsQ==}
|
resolution: {integrity: sha512-d6AKtisSRtDRX4aSPsJGTfnzrX2ZkHQLE5kiUuGOeEoLpbEulFF4hj0mLPbsa+7vmguDKOVVEQdHKDSwoaIDsQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
Loading…
Reference in a new issue