implement version badges rehype plugin

This commit is contained in:
Hölting, Moritz (Intern) 2025-08-06 09:50:49 +02:00
parent 2e8261241f
commit 398318cae2
5 changed files with 180 additions and 36 deletions

View File

@ -6,6 +6,7 @@ import starlightUtils from "@lorenzo_lewis/starlight-utils";
import wasm from "vite-plugin-wasm"; import wasm from "vite-plugin-wasm";
import shikiConfig from "./src/utils/shiki"; import shikiConfig from "./src/utils/shiki";
import remarkVersionBadges from "./src/remark-plugins/version-badges";
const playgroundSidebarEntry = { const playgroundSidebarEntry = {
label: 'Playground', label: 'Playground',
@ -67,6 +68,9 @@ export default defineConfig({
Pagination: "./src/components/override/Pagination.astro", Pagination: "./src/components/override/Pagination.astro",
SocialIcons: './src/components/override/SocialIcons.astro', SocialIcons: './src/components/override/SocialIcons.astro',
}, },
customCss: [
'./src/remark-plugins/version-badges.css',
],
sidebar: [ sidebar: [
{ {
label: "leadingNavLinks", label: "leadingNavLinks",
@ -131,5 +135,10 @@ export default defineConfig({
plugins: [ plugins: [
wasm(), wasm(),
], ],
} },
markdown: {
remarkPlugins: [
remarkVersionBadges,
],
},
}); });

View File

@ -21,8 +21,10 @@
"@mui/icons-material": "^6.1.1", "@mui/icons-material": "^6.1.1",
"@mui/material": "^6.1.1", "@mui/material": "^6.1.1",
"@shikijs/monaco": "^1.7.0", "@shikijs/monaco": "^1.7.0",
"@types/mdast": "^4.0.4",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"@types/unist": "^3.0.3",
"astro": "^4.15.9", "astro": "^4.15.9",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
@ -32,6 +34,8 @@
"starlight-links-validator": "^0.12.1", "starlight-links-validator": "^0.12.1",
"tm-themes": "^1.4.3", "tm-themes": "^1.4.3",
"typescript": "^5.4.5", "typescript": "^5.4.5",
"unified": "^11.0.5",
"unist-util-visit": "^5.0.0",
"use-immer": "^0.10.0", "use-immer": "^0.10.0",
"vite-plugin-wasm": "^3.3.0" "vite-plugin-wasm": "^3.3.0"
}, },

View File

@ -38,12 +38,18 @@ importers:
'@shikijs/monaco': '@shikijs/monaco':
specifier: ^1.7.0 specifier: ^1.7.0
version: 1.7.0 version: 1.7.0
'@types/mdast':
specifier: ^4.0.4
version: 4.0.4
'@types/react': '@types/react':
specifier: ^18.3.3 specifier: ^18.3.3
version: 18.3.3 version: 18.3.3
'@types/react-dom': '@types/react-dom':
specifier: ^18.3.0 specifier: ^18.3.0
version: 18.3.0 version: 18.3.0
'@types/unist':
specifier: ^3.0.3
version: 3.0.3
astro: astro:
specifier: ^4.15.9 specifier: ^4.15.9
version: 4.15.9(rollup@4.22.5)(sass@1.77.6)(typescript@5.4.5) version: 4.15.9(rollup@4.22.5)(sass@1.77.6)(typescript@5.4.5)
@ -71,6 +77,12 @@ importers:
typescript: typescript:
specifier: ^5.4.5 specifier: ^5.4.5
version: 5.4.5 version: 5.4.5
unified:
specifier: ^11.0.5
version: 11.0.5
unist-util-visit:
specifier: ^5.0.0
version: 5.0.0
use-immer: use-immer:
specifier: ^0.10.0 specifier: ^0.10.0
version: 0.10.0(immer@10.1.1)(react@18.3.1) version: 0.10.0(immer@10.1.1)(react@18.3.1)
@ -1014,8 +1026,8 @@ packages:
'@types/unist@2.0.10': '@types/unist@2.0.10':
resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
'@types/unist@3.0.2': '@types/unist@3.0.3':
resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==} resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
'@ungap/structured-clone@1.2.0': '@ungap/structured-clone@1.2.0':
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
@ -3754,11 +3766,11 @@ snapshots:
'@types/hast@3.0.4': '@types/hast@3.0.4':
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
'@types/mdast@4.0.4': '@types/mdast@4.0.4':
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
'@types/mdx@2.0.13': {} '@types/mdx@2.0.13': {}
@ -3766,7 +3778,7 @@ snapshots:
'@types/nlcst@2.0.3': '@types/nlcst@2.0.3':
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
'@types/node@17.0.45': {} '@types/node@17.0.45': {}
@ -3795,7 +3807,7 @@ snapshots:
'@types/unist@2.0.10': {} '@types/unist@2.0.10': {}
'@types/unist@3.0.2': {} '@types/unist@3.0.3': {}
'@ungap/structured-clone@1.2.0': {} '@ungap/structured-clone@1.2.0': {}
@ -4277,7 +4289,7 @@ snapshots:
estree-util-visit@2.0.0: estree-util-visit@2.0.0:
dependencies: dependencies:
'@types/estree-jsx': 1.0.5 '@types/estree-jsx': 1.0.5
'@types/unist': 3.0.2 '@types/unist': 3.0.3
estree-walker@2.0.2: {} estree-walker@2.0.2: {}
@ -4387,7 +4399,7 @@ snapshots:
hast-util-from-parse5@8.0.1: hast-util-from-parse5@8.0.1:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
'@types/unist': 3.0.2 '@types/unist': 3.0.3
devlop: 1.1.0 devlop: 1.1.0
hastscript: 8.0.0 hastscript: 8.0.0
property-information: 6.5.0 property-information: 6.5.0
@ -4422,7 +4434,7 @@ snapshots:
hast-util-raw@9.0.3: hast-util-raw@9.0.3:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
'@types/unist': 3.0.2 '@types/unist': 3.0.3
'@ungap/structured-clone': 1.2.0 '@ungap/structured-clone': 1.2.0
hast-util-from-parse5: 8.0.1 hast-util-from-parse5: 8.0.1
hast-util-to-parse5: 8.0.0 hast-util-to-parse5: 8.0.0
@ -4438,7 +4450,7 @@ snapshots:
hast-util-select@6.0.2: hast-util-select@6.0.2:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
'@types/unist': 3.0.2 '@types/unist': 3.0.3
bcp-47-match: 2.0.3 bcp-47-match: 2.0.3
comma-separated-tokens: 2.0.3 comma-separated-tokens: 2.0.3
css-selector-parser: 3.0.5 css-selector-parser: 3.0.5
@ -4478,7 +4490,7 @@ snapshots:
hast-util-to-html@9.0.1: hast-util-to-html@9.0.1:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
'@types/unist': 3.0.2 '@types/unist': 3.0.3
ccount: 2.0.1 ccount: 2.0.1
comma-separated-tokens: 2.0.3 comma-separated-tokens: 2.0.3
hast-util-raw: 9.0.3 hast-util-raw: 9.0.3
@ -4493,7 +4505,7 @@ snapshots:
hast-util-to-html@9.0.3: hast-util-to-html@9.0.3:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
'@types/unist': 3.0.2 '@types/unist': 3.0.3
ccount: 2.0.1 ccount: 2.0.1
comma-separated-tokens: 2.0.3 comma-separated-tokens: 2.0.3
hast-util-whitespace: 3.0.0 hast-util-whitespace: 3.0.0
@ -4508,7 +4520,7 @@ snapshots:
dependencies: dependencies:
'@types/estree': 1.0.5 '@types/estree': 1.0.5
'@types/hast': 3.0.4 '@types/hast': 3.0.4
'@types/unist': 3.0.2 '@types/unist': 3.0.3
comma-separated-tokens: 2.0.3 comma-separated-tokens: 2.0.3
devlop: 1.1.0 devlop: 1.1.0
estree-util-is-identifier-name: 3.0.0 estree-util-is-identifier-name: 3.0.0
@ -4541,7 +4553,7 @@ snapshots:
hast-util-to-text@4.0.2: hast-util-to-text@4.0.2:
dependencies: dependencies:
'@types/hast': 3.0.4 '@types/hast': 3.0.4
'@types/unist': 3.0.2 '@types/unist': 3.0.3
hast-util-is-element: 3.0.0 hast-util-is-element: 3.0.0
unist-util-find-after: 5.0.0 unist-util-find-after: 5.0.0
@ -4731,13 +4743,13 @@ snapshots:
mdast-util-definitions@6.0.0: mdast-util-definitions@6.0.0:
dependencies: dependencies:
'@types/mdast': 4.0.4 '@types/mdast': 4.0.4
'@types/unist': 3.0.2 '@types/unist': 3.0.3
unist-util-visit: 5.0.0 unist-util-visit: 5.0.0
mdast-util-directive@3.0.0: mdast-util-directive@3.0.0:
dependencies: dependencies:
'@types/mdast': 4.0.4 '@types/mdast': 4.0.4
'@types/unist': 3.0.2 '@types/unist': 3.0.3
devlop: 1.1.0 devlop: 1.1.0
mdast-util-from-markdown: 2.0.1 mdast-util-from-markdown: 2.0.1
mdast-util-to-markdown: 2.1.0 mdast-util-to-markdown: 2.1.0
@ -4757,7 +4769,7 @@ snapshots:
mdast-util-from-markdown@2.0.1: mdast-util-from-markdown@2.0.1:
dependencies: dependencies:
'@types/mdast': 4.0.4 '@types/mdast': 4.0.4
'@types/unist': 3.0.2 '@types/unist': 3.0.3
decode-named-character-reference: 1.0.2 decode-named-character-reference: 1.0.2
devlop: 1.1.0 devlop: 1.1.0
mdast-util-to-string: 4.0.0 mdast-util-to-string: 4.0.0
@ -4844,7 +4856,7 @@ snapshots:
'@types/estree-jsx': 1.0.5 '@types/estree-jsx': 1.0.5
'@types/hast': 3.0.4 '@types/hast': 3.0.4
'@types/mdast': 4.0.4 '@types/mdast': 4.0.4
'@types/unist': 3.0.2 '@types/unist': 3.0.3
ccount: 2.0.1 ccount: 2.0.1
devlop: 1.1.0 devlop: 1.1.0
mdast-util-from-markdown: 2.0.1 mdast-util-from-markdown: 2.0.1
@ -4898,7 +4910,7 @@ snapshots:
mdast-util-to-markdown@2.1.0: mdast-util-to-markdown@2.1.0:
dependencies: dependencies:
'@types/mdast': 4.0.4 '@types/mdast': 4.0.4
'@types/unist': 3.0.2 '@types/unist': 3.0.3
longest-streak: 3.1.0 longest-streak: 3.1.0
mdast-util-phrasing: 4.1.0 mdast-util-phrasing: 4.1.0
mdast-util-to-string: 4.0.0 mdast-util-to-string: 4.0.0
@ -5130,7 +5142,7 @@ snapshots:
dependencies: dependencies:
'@types/acorn': 4.0.6 '@types/acorn': 4.0.6
'@types/estree': 1.0.5 '@types/estree': 1.0.5
'@types/unist': 3.0.2 '@types/unist': 3.0.3
devlop: 1.1.0 devlop: 1.1.0
estree-util-visit: 2.0.0 estree-util-visit: 2.0.0
micromark-util-symbol: 2.0.0 micromark-util-symbol: 2.0.0
@ -5302,7 +5314,7 @@ snapshots:
parse-latin@7.0.0: parse-latin@7.0.0:
dependencies: dependencies:
'@types/nlcst': 2.0.3 '@types/nlcst': 2.0.3
'@types/unist': 3.0.2 '@types/unist': 3.0.3
nlcst-to-string: 4.0.0 nlcst-to-string: 4.0.0
unist-util-modify-children: 4.0.0 unist-util-modify-children: 4.0.0
unist-util-visit-children: 3.0.0 unist-util-visit-children: 3.0.0
@ -5812,7 +5824,7 @@ snapshots:
unified@11.0.5: unified@11.0.5:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
bail: 2.0.2 bail: 2.0.2
devlop: 1.1.0 devlop: 1.1.0
extend: 3.0.2 extend: 3.0.2
@ -5822,47 +5834,47 @@ snapshots:
unist-util-find-after@5.0.0: unist-util-find-after@5.0.0:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
unist-util-is: 6.0.0 unist-util-is: 6.0.0
unist-util-is@6.0.0: unist-util-is@6.0.0:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
unist-util-modify-children@4.0.0: unist-util-modify-children@4.0.0:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
array-iterate: 2.0.1 array-iterate: 2.0.1
unist-util-position-from-estree@2.0.0: unist-util-position-from-estree@2.0.0:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
unist-util-position@5.0.0: unist-util-position@5.0.0:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
unist-util-remove-position@5.0.0: unist-util-remove-position@5.0.0:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
unist-util-visit: 5.0.0 unist-util-visit: 5.0.0
unist-util-stringify-position@4.0.0: unist-util-stringify-position@4.0.0:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
unist-util-visit-children@3.0.0: unist-util-visit-children@3.0.0:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
unist-util-visit-parents@6.0.1: unist-util-visit-parents@6.0.1:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
unist-util-is: 6.0.0 unist-util-is: 6.0.0
unist-util-visit@5.0.0: unist-util-visit@5.0.0:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
unist-util-is: 6.0.0 unist-util-is: 6.0.0
unist-util-visit-parents: 6.0.1 unist-util-visit-parents: 6.0.1
@ -5881,17 +5893,17 @@ snapshots:
vfile-location@5.0.2: vfile-location@5.0.2:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
vfile: 6.0.3 vfile: 6.0.3
vfile-message@4.0.2: vfile-message@4.0.2:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
unist-util-stringify-position: 4.0.0 unist-util-stringify-position: 4.0.0
vfile@6.0.3: vfile@6.0.3:
dependencies: dependencies:
'@types/unist': 3.0.2 '@types/unist': 3.0.3
vfile-message: 4.0.2 vfile-message: 4.0.2
vite-plugin-wasm@3.3.0(vite@5.4.8(sass@1.77.6)): vite-plugin-wasm@3.3.0(vite@5.4.8(sass@1.77.6)):

View File

@ -0,0 +1,55 @@
.version-badges {
display: flex;
gap: 0.5rem;
margin: 0.5em 0 !important;
margin-bottom: 0 !important;
flex-wrap: wrap;
}
.version-badges + p {
margin-top: 0.5rem !important;
}
.version-badges span {
font-size: 0.75rem;
padding: 2px 6px;
border-radius: 4px;
white-space: nowrap;
border: 1px solid transparent;
}
html[data-theme='light'] .version-badges .since-badge {
background-color: #e0f7fa;
color: #00796b;
border-color: #b2ebf2;
}
html[data-theme='light'] .version-badges .changed-badge {
background-color: #fff8e1;
color: #795548;
border-color: #ffe082;
}
html[data-theme='light'] .version-badges .deprecated-badge {
background-color: #ffebee;
color: #b71c1c;
border-color: #ffcdd2;
}
html[data-theme='dark'] .version-badges .since-badge {
background-color: #004d40;
color: #b2dfdb;
border-color: #00695c;
}
html[data-theme='dark'] .version-badges .changed-badge {
background-color: #3e2723;
color: #ffe0b2;
border-color: #6d5037;
}
html[data-theme='dark'] .version-badges .deprecated-badge {
background-color: #4a0000;
color: #ffcdd2;
border-color: #b71c1c;
}

View File

@ -0,0 +1,64 @@
import { visit } from 'unist-util-visit';
import type { Plugin } from 'unified';
import type { Parent } from 'unist';
import type { Paragraph, Text } from 'mdast';
const INLINE_REGEX = /!(since|changed|deprecated)\[([^\]]+)\]/g;
const remarkVersionBadges: Plugin = () => {
return (tree) => {
visit(tree, 'paragraph', (node, index, parent) => {
const paragraph = node as Paragraph;
const parentNode = parent as Parent;
if (!paragraph.children || paragraph.children.length !== 1) return;
const child = paragraph.children[0];
if(child.type !== 'text') return;
const value = child.value;
const matches = [...value.matchAll(INLINE_REGEX)];
if(matches.length === 0) return;
const isOnlyBadges = matches.reduce((allMatched, match) => {
return allMatched && match[0] === value.slice(match.index!, match.index! + match[0].length)
}, true);
if (!isOnlyBadges) return;
const badges: string[] = matches.map(([_, type, version]) => {
let label = '';
let className = '';
let title = '';
switch(type) {
case 'since':
label = `Since v${version}`;
className = 'since-badge';
title = `This feature was added in v${version}`;
break;
case 'changed':
label = `Changed in v${version}`;
className = 'changed-badge';
title = `This feature was changed in v${version}`;
break;
case 'deprecated':
label = `Deprecated since v${version}`;
className = 'deprecated-badge'
title = `This feature is deprecated since v${version}`;
break;
}
return `<span class="${className}" title="${title}">${label}</span>`;
});
const htmlNode = {
type: 'html',
value: `<div class="version-badges">${badges.join('')}</div>`,
} as const;
parentNode.children.splice(index!, 1, htmlNode);
});
}
}
export default remarkVersionBadges;