fix(offlinedocs): render GitHub markdown alerts (#28822)

> 🤖 This PR was modified by Coder Agents on behalf of Jake Howell.

Ports the GitHub Markdown alert transformer and styling from
`coder/coder.com` into the Next.js `offlinedocs` app so note, tip,
important, warning, and caution blocks render consistently.

Moves HTML sanitization into the rehype pipeline so Markdown blockquotes
and supported raw HTML are parsed before sanitization.
This commit is contained in:
Jake Howell
2026-09-04 11:30:03 +05:00
committed by GitHub
parent 07f9018c61
commit 0ca6e5bf78
4 changed files with 315 additions and 171 deletions
+215
View File
@@ -0,0 +1,215 @@
/*
* Adapted from rehype-github-alert.
*
* The MIT License
*
* Copyright (c) Titus Wormer <tituswormer@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* @see {@link https://github.com/rehypejs/rehype-github/tree/main/packages/alert}
*/
import type { Root } from "hast";
import { whitespace } from "hast-util-whitespace";
import { visit } from "unist-util-visit";
interface Icon {
d: string;
name: string;
}
const icons: Map<string, Icon> = new Map();
const TABS_CLASSNAME = "tabs";
icons.set("caution", {
d: "M4.47.22A.749.749 0 0 1 5 0h6c.199 0 .389.079.53.22l4.25 4.25c.141.14.22.331.22.53v6a.749.749 0 0 1-.22.53l-4.25 4.25A.749.749 0 0 1 11 16H5a.749.749 0 0 1-.53-.22L.22 11.53A.749.749 0 0 1 0 11V5c0-.199.079-.389.22-.53Zm.84 1.28L1.5 5.31v5.38l3.81 3.81h5.38l3.81-3.81V5.31L10.69 1.5ZM8 4a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4Zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z",
name: "octicon-stop",
});
icons.set("important", {
d: "M0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v9.5A1.75 1.75 0 0 1 14.25 13H8.06l-2.573 2.573A1.458 1.458 0 0 1 3 14.543V13H1.75A1.75 1.75 0 0 1 0 11.25Zm1.75-.25a.25.25 0 0 0-.25.25v9.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h6.5a.25.25 0 0 0 .25-.25v-9.5a.25.25 0 0 0-.25-.25Zm7 2.25v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 9a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z",
name: "octicon-report",
});
icons.set("note", {
d: "M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z",
name: "octicon-info",
});
icons.set("tip", {
d: "M8 1.5c-2.363 0-4 1.69-4 3.75 0 .984.424 1.625.984 2.304l.214.253c.223.264.47.556.673.848.284.411.537.896.621 1.49a.75.75 0 0 1-1.484.211c-.04-.282-.163-.547-.37-.847a8.456 8.456 0 0 0-.542-.68c-.084-.1-.173-.205-.268-.32C3.201 7.75 2.5 6.766 2.5 5.25 2.5 2.31 4.863 0 8 0s5.5 2.31 5.5 5.25c0 1.516-.701 2.5-1.328 3.259-.095.115-.184.22-.268.319-.207.245-.383.453-.541.681-.208.3-.33.565-.37.847a.751.751 0 0 1-1.485-.212c.084-.593.337-1.078.621-1.489.203-.292.45-.584.673-.848.075-.088.147-.173.213-.253.561-.679.985-1.32.985-2.304 0-2.06-1.637-3.75-4-3.75ZM5.75 12h4.5a.75.75 0 0 1 0 1.5h-4.5a.75.75 0 0 1 0-1.5ZM6 15.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 0 1.5h-2.5a.75.75 0 0 1-.75-.75Z",
name: "octicon-light-bulb",
});
icons.set("warning", {
d: "M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z",
name: "octicon-alert",
});
/**
* Plugin to enhance alerts.
*
* @returns
* Transform.
*/
export default function coderGithubAlert() {
return (tree: Root) => {
visit(tree, "element", (node, index, parent) => {
if (
node.tagName !== "blockquote" ||
typeof index !== "number" ||
!parent
) {
return;
}
const parentClassName =
parent.type === "element" ? parent.properties.className : "";
const isInTabs = Array.isArray(parentClassName)
? parentClassName.includes(TABS_CLASSNAME)
: parentClassName === TABS_CLASSNAME;
const isInAllowedParent =
parent.type === "root" ||
parent.tagName === "li" ||
parent.tagName === "details" ||
isInTabs;
// Must be a `blockquote` element that is a direct child of the root,
// a list item, a details element, or a tabs container.
if (!isInAllowedParent) {
return;
}
// Must start with a `<p>`.
let headIndex = 0;
while (
headIndex < node.children.length &&
whitespace(node.children[headIndex])
) {
headIndex++;
}
const head = node.children[headIndex];
// Must start with a `p`.
if (!head || head.type !== "element" || head.tagName !== "p") return;
// Must start with `[!`.
const text = head.children[0];
if (!text || text.type !== "text" || !text.value.startsWith("[!")) return;
// Must have `]`.
const end = text.value.indexOf("]");
if (end === -1) return;
// Must be a known name between
const name = text.value.slice(2, end).toLowerCase();
const icon = icons.get(name);
if (!icon) return;
if (end + 1 === text.value.length) {
// Has to be a `<br>`.
const next = head.children[1];
if (next) {
if (next.type !== "element" || next.tagName !== "br") return;
// No next sibling? Exit too.
if (!head.children[2]) return;
// Drop the entire `[!note]` text *and* the `<br>`.
head.children = head.children.slice(2);
// Drop the `\n` that typically (in markdown) follows `<br>`.
const node = head.children[0];
if (node && node.type === "text" && node.value.charAt(0) === "\n") {
node.value = node.value.slice(1);
}
} else {
let skipped = false;
// Skip past whitespace.
while (
headIndex + 1 < node.children.length &&
whitespace(node.children[headIndex + 1])
) {
skipped = true;
headIndex++;
}
// Exit if there’s no following element.
if (
headIndex + 1 === node.children.length ||
node.children[headIndex + 1].type !== "element"
) {
return;
}
// Without whitespace,
// we still want to skip the paragraph to drop the `[!note]`.
if (!skipped) headIndex++;
}
} else if (
text.value.charAt(end + 1) === "\n" &&
(end + 2 === text.value.length ||
!whitespace(text.value.slice(end + 2)))
) {
// Drop the `[!note]` from the `text`.
text.value = text.value.slice(end + 2);
} else {
return;
}
const displayName = name.charAt(0).toUpperCase() + name.slice(1);
parent.children[index] = {
type: "element",
tagName: "div",
properties: { className: ["markdown-alert", `markdown-alert-${name}`] },
children: [
{
type: "element",
tagName: "p",
properties: { className: ["markdown-alert-title"] },
children: [
{
type: "element",
tagName: "svg",
properties: {
className: ["octicon", icon.name],
viewBox: "0 0 16 16",
version: "1.1",
width: "16",
height: "16",
ariaHidden: "true",
},
children: [
{
type: "element",
tagName: "path",
properties: { d: icon.d },
children: [],
},
],
},
{ type: "text", value: displayName },
],
},
...node.children.slice(headIndex),
],
};
});
};
}
+4 -2
View File
@@ -19,6 +19,7 @@
"archiver": "8.0.0",
"framer-motion": "^10.18.0",
"front-matter": "4.0.2",
"hast-util-whitespace": "3.0.0",
"lodash": "4.18.1",
"next": "15.5.23",
"react": "18.3.1",
@@ -26,16 +27,17 @@
"react-icons": "5.7.0",
"react-markdown": "10.1.0",
"rehype-raw": "7.0.0",
"rehype-sanitize": "6.0.0",
"remark-gfm": "4.0.1",
"sanitize-html": "2.17.7"
"unist-util-visit": "5.0.0"
},
"devDependencies": {
"@biomejs/biome": "2.5.10",
"@types/hast": "3.0.4",
"@types/lodash": "4.17.25",
"@types/node": "22.20.1",
"@types/react": "18.3.12",
"@types/react-dom": "18.3.1",
"@types/sanitize-html": "2.16.1",
"eslint": "8.57.1",
"eslint-config-next": "14.2.35",
"typescript": "6.0.3"
+66 -7
View File
@@ -37,8 +37,12 @@ import { ReactNode } from "react";
import { MdMenu } from "react-icons/md";
import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw";
import rehypeSanitize, {
defaultSchema,
type Options as RehypeSanitizeOptions,
} from "rehype-sanitize";
import remarkGfm from "remark-gfm";
import sanitizeHtml from "sanitize-html";
import coderGithubAlert from "../lib/coderGithubAlert";
type FilePath = string;
type UrlPath = string;
@@ -52,6 +56,17 @@ type Manifest = { versions: string[]; routes: Route[] };
type NavItem = { title: string; path: UrlPath; children?: NavItem[] };
type Nav = NavItem[];
const markdownSanitizeSchema: RehypeSanitizeOptions = {
...defaultSchema,
tagNames: [...(defaultSchema.tagNames ?? []), "small", "video"],
attributes: {
...defaultSchema.attributes,
"*": [...(defaultSchema.attributes?.["*"] ?? []), "className"],
source: [...(defaultSchema.attributes?.source ?? []), "src", "type"],
video: ["autoPlay", "loop", "playsInline"],
},
};
const readContentFile = (filePath: string) => {
const baseDir = process.cwd();
const docsPath = path.join(baseDir, "..", "docs");
@@ -219,8 +234,7 @@ export const getStaticProps: GetStaticProps = (context) => {
const { attributes, body } = fm<{ title?: string }>(
readContentFile(route.path),
);
// Serialize MDX to support custom components
const content = sanitizeHtml(body);
const content = body;
const navigation = getNavigation(manifest);
const version = manifest.versions[0];
@@ -414,7 +428,47 @@ const DocsPage: NextPage<{
overflowY="auto"
>
<Box maxW="872">
<Box lineHeight="tall">
<Box
lineHeight="tall"
sx={{
"& .markdown-alert": {
my: 4,
p: 5,
borderWidth: 1,
borderStyle: "solid",
borderColor: "var(--markdown-alert-color)",
borderRadius: "md",
},
"& .markdown-alert-title": {
display: "flex",
alignItems: "center",
gap: 1,
pt: 0,
fontWeight: 600,
color: "var(--markdown-alert-color)",
},
"& .markdown-alert > :last-child": { pb: 0 },
"& .markdown-alert svg": {
fill: "currentColor",
flexShrink: 0,
},
"& .markdown-alert-note": {
"--markdown-alert-color": "#0969da",
},
"& .markdown-alert-tip": {
"--markdown-alert-color": "#1a7f37",
},
"& .markdown-alert-important": {
"--markdown-alert-color": "#8250df",
},
"& .markdown-alert-warning": {
"--markdown-alert-color": "#9a6700",
},
"& .markdown-alert-caution": {
"--markdown-alert-color": "#cf222e",
},
}}
>
{/* Some docs don't have the title */}
<Heading
as="h1"
@@ -428,7 +482,12 @@ const DocsPage: NextPage<{
</Heading>
<ReactMarkdown
rehypePlugins={[rehypeRaw]}
rehypePlugins={[
rehypeRaw,
// Sanitize author HTML before adding trusted alert markup.
[rehypeSanitize, markdownSanitizeSchema],
coderGithubAlert,
]}
remarkPlugins={[remarkGfm]}
urlTransform={transformLinkUriSource(route.path)}
components={{
@@ -477,8 +536,8 @@ const DocsPage: NextPage<{
height="auto"
/>
),
p: ({ children }) => (
<Text pt={2} pb={2}>
p: ({ children, className }) => (
<Text className={className} pt={2} pb={2}>
{children}
</Text>
),
+30 -162
View File
@@ -37,6 +37,9 @@ importers:
front-matter:
specifier: 4.0.2
version: 4.0.2
hast-util-whitespace:
specifier: 3.0.0
version: 3.0.0
lodash:
specifier: 4.18.1
version: 4.18.1
@@ -58,16 +61,22 @@ importers:
rehype-raw:
specifier: 7.0.0
version: 7.0.0
rehype-sanitize:
specifier: 6.0.0
version: 6.0.0
remark-gfm:
specifier: 4.0.1
version: 4.0.1
sanitize-html:
specifier: 2.17.7
version: 2.17.7
unist-util-visit:
specifier: 5.0.0
version: 5.0.0
devDependencies:
'@biomejs/biome':
specifier: 2.5.10
version: 2.5.10
'@types/hast':
specifier: 3.0.4
version: 3.0.4
'@types/lodash':
specifier: 4.17.25
version: 4.17.25
@@ -80,9 +89,6 @@ importers:
'@types/react-dom':
specifier: 18.3.1
version: 18.3.1
'@types/sanitize-html':
specifier: 2.16.1
version: 2.16.1
eslint:
specifier: 8.57.1
version: 8.57.1
@@ -621,9 +627,6 @@ packages:
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/hast@3.0.3':
resolution: {integrity: sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ==}
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
@@ -657,9 +660,6 @@ packages:
'@types/react@18.3.12':
resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==}
'@types/sanitize-html@2.16.1':
resolution: {integrity: sha512-n9wjs8bCOTyN/ynwD8s/nTcTreIHB1vf31vhLMGqUPNHaweKC4/fAl4Dj+hUlCTKYgm4P3k83fmiFfzkZ6sgMA==}
'@types/unist@2.0.11':
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
@@ -1091,9 +1091,6 @@ packages:
resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
engines: {node: '>= 0.4'}
dayjs@1.11.23:
resolution: {integrity: sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==}
debug@3.2.7:
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies:
@@ -1126,10 +1123,6 @@ packages:
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
deepmerge@4.3.1:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
define-data-property@1.1.4:
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
engines: {node: '>= 0.4'}
@@ -1160,35 +1153,6 @@ packages:
resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
engines: {node: '>=6.0.0'}
dom-serializer@2.0.0:
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
dom-serializer@3.1.1:
resolution: {integrity: sha512-4MEa38/QexBob6gFNwu+EGdWvhJ1OKuNwdYY3Y3NyeWDQfnGeDYQUDfIRzWu5B5gsv03so2Uxd28YC6zrsx3Lw==}
engines: {node: '>=20.19.0'}
domelementtype@2.3.0:
resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
domelementtype@3.0.0:
resolution: {integrity: sha512-umCQid3jKbDmVjx8jGaW7uUykm4DEUeyV21hPxNMo2nV955DhUThwqyOIDtreepP31hl84X7G5U9ZfsWvIB3Pg==}
engines: {node: '>=20.19.0'}
domhandler@5.0.3:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'}
domhandler@6.0.1:
resolution: {integrity: sha512-gYzvtM72ZtxQO0T048kd6HWSbbGCNOUwcnfQ01cqIJ4X2IYKFFHZ5mKvrQETcFXxsRObZulDaKmy//R7TPtsBg==}
engines: {node: '>=20.19.0'}
domutils@3.2.2:
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
domutils@4.0.2:
resolution: {integrity: sha512-qI4JLRKnSzqFqr7hAlS5xQDusBCjKSEG4t4+7aNrIQMHBcsC2TGEhuyABJdYkgSewL57PNLYEiibY2iPKhKpaA==}
engines: {node: '>=20.19.0'}
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
@@ -1206,14 +1170,6 @@ packages:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
entities@7.0.1:
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
engines: {node: '>=0.12'}
entities@8.0.0:
resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==}
engines: {node: '>=20.19.0'}
error-ex@1.3.4:
resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
@@ -1559,6 +1515,9 @@ packages:
hast-util-raw@9.0.1:
resolution: {integrity: sha512-5m1gmba658Q+lO5uqL5YNGQWeh1MYWZbZmWrM5lncdcuiXuo5E2HT/CIOp0rLF8ksfSwiCVJ3twlgVRyTGThGA==}
hast-util-sanitize@5.0.2:
resolution: {integrity: sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==}
hast-util-to-jsx-runtime@2.3.6:
resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
@@ -1580,13 +1539,6 @@ packages:
html-void-elements@3.0.0:
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
htmlparser2@10.1.0:
resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==}
htmlparser2@12.0.0:
resolution: {integrity: sha512-Tz7u1i95/g2x2jz81+x0FBVhBhY5aRTvD3tXXdFaljuNdzDLJ8UGNRrTcj2cgQvAg3iW/h77Fz15nLW0L0CrZw==}
engines: {node: '>=20.19.0'}
ieee754@1.2.1:
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
@@ -1714,10 +1666,6 @@ packages:
resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
engines: {node: '>=12'}
is-plain-object@5.1.0:
resolution: {integrity: sha512-bUi/yjmtKYcRVUtWRGr0UA6xEFh2I6zWUwMrUXB3s7bmYCaZ8a+0ZsTRkrawh/mzlSD1Y0Ph8bp/U+TvBpWDNw==}
engines: {node: '>=0.10.0'}
is-regex@1.2.1:
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
engines: {node: '>= 0.4'}
@@ -1816,9 +1764,6 @@ packages:
resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
engines: {node: '>=0.10'}
launder@1.7.1:
resolution: {integrity: sha512-mU6WRz5EusL9ZZuiZ5SO4Y6C0P9PAUR9iwdb6bzj4KDihm28DiHFw+/yk9DBH4f+Pv1wuzQ4e2jV3oQ7mkIqvw==}
lazystream@1.0.1:
resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
engines: {node: '>= 0.6.3'}
@@ -2113,9 +2058,6 @@ packages:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
parse-srcset@1.0.2:
resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==}
parse5@7.1.2:
resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
@@ -2282,6 +2224,9 @@ packages:
rehype-raw@7.0.0:
resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==}
rehype-sanitize@6.0.0:
resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==}
remark-gfm@4.0.1:
resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
@@ -2341,10 +2286,6 @@ packages:
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
engines: {node: '>= 0.4'}
sanitize-html@2.17.7:
resolution: {integrity: sha512-PGtEkc9cbnedU3s9TmzDbpsZ8w086g/0Q8k8/oIO1NLNU3i5k9yn835CrjJSajp1KMmkisbO1qPXxNKO3welAg==}
engines: {node: '>=22.12.0'}
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
@@ -3205,10 +3146,6 @@ snapshots:
'@types/estree@1.0.8': {}
'@types/hast@3.0.3':
dependencies:
'@types/unist': 3.0.2
'@types/hast@3.0.4':
dependencies:
'@types/unist': 3.0.3
@@ -3244,10 +3181,6 @@ snapshots:
'@types/prop-types': 15.7.13
csstype: 3.1.3
'@types/sanitize-html@2.16.1':
dependencies:
htmlparser2: 10.1.0
'@types/unist@2.0.11': {}
'@types/unist@3.0.2': {}
@@ -3692,8 +3625,6 @@ snapshots:
es-errors: 1.3.0
is-data-view: 1.0.2
dayjs@1.11.23: {}
debug@3.2.7:
dependencies:
ms: 2.1.3
@@ -3712,8 +3643,6 @@ snapshots:
deep-is@0.1.4: {}
deepmerge@4.3.1: {}
define-data-property@1.1.4:
dependencies:
es-define-property: 1.0.1
@@ -3745,42 +3674,6 @@ snapshots:
dependencies:
esutils: 2.0.3
dom-serializer@2.0.0:
dependencies:
domelementtype: 2.3.0
domhandler: 5.0.3
entities: 4.5.0
dom-serializer@3.1.1:
dependencies:
domelementtype: 3.0.0
domhandler: 6.0.1
entities: 8.0.0
domelementtype@2.3.0: {}
domelementtype@3.0.0: {}
domhandler@5.0.3:
dependencies:
domelementtype: 2.3.0
domhandler@6.0.1:
dependencies:
domelementtype: 3.0.0
domutils@3.2.2:
dependencies:
dom-serializer: 2.0.0
domelementtype: 2.3.0
domhandler: 5.0.3
domutils@4.0.2:
dependencies:
dom-serializer: 3.1.1
domelementtype: 3.0.0
domhandler: 6.0.1
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -3795,10 +3688,6 @@ snapshots:
entities@4.5.0: {}
entities@7.0.1: {}
entities@8.0.0: {}
error-ex@1.3.4:
dependencies:
is-arrayish: 0.2.1
@@ -4315,6 +4204,12 @@ snapshots:
web-namespaces: 2.0.1
zwitch: 2.0.4
hast-util-sanitize@5.0.2:
dependencies:
'@types/hast': 3.0.4
'@ungap/structured-clone': 1.3.0
unist-util-position: 5.0.0
hast-util-to-jsx-runtime@2.3.6:
dependencies:
'@types/estree': 1.0.8
@@ -4365,20 +4260,6 @@ snapshots:
html-void-elements@3.0.0: {}
htmlparser2@10.1.0:
dependencies:
domelementtype: 2.3.0
domhandler: 5.0.3
domutils: 3.2.2
entities: 7.0.1
htmlparser2@12.0.0:
dependencies:
domelementtype: 3.0.0
domhandler: 6.0.1
domutils: 4.0.2
entities: 8.0.0
ieee754@1.2.1: {}
ignore@5.3.2: {}
@@ -4502,8 +4383,6 @@ snapshots:
is-plain-obj@4.1.0: {}
is-plain-object@5.1.0: {}
is-regex@1.2.1:
dependencies:
call-bound: 1.0.4
@@ -4604,10 +4483,6 @@ snapshots:
dependencies:
language-subtag-registry: 0.3.23
launder@1.7.1:
dependencies:
dayjs: 1.11.23
lazystream@1.0.1:
dependencies:
readable-stream: 2.3.8
@@ -5127,8 +5002,6 @@ snapshots:
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
parse-srcset@1.0.2: {}
parse5@7.1.2:
dependencies:
entities: 4.5.0
@@ -5308,10 +5181,15 @@ snapshots:
rehype-raw@7.0.0:
dependencies:
'@types/hast': 3.0.3
'@types/hast': 3.0.4
hast-util-raw: 9.0.1
vfile: 6.0.1
rehype-sanitize@6.0.0:
dependencies:
'@types/hast': 3.0.4
hast-util-sanitize: 5.0.2
remark-gfm@4.0.1:
dependencies:
'@types/mdast': 4.0.4
@@ -5399,16 +5277,6 @@ snapshots:
es-errors: 1.3.0
is-regex: 1.2.1
sanitize-html@2.17.7:
dependencies:
deepmerge: 4.3.1
escape-string-regexp: 4.0.0
htmlparser2: 12.0.0
is-plain-object: 5.1.0
launder: 1.7.1
parse-srcset: 1.0.2
postcss: 8.5.10
scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0