From 69ef4c62da906933e665501c6fec097f6c001e48 Mon Sep 17 00:00:00 2001 From: LadyBluenotes Date: Thu, 27 Nov 2025 09:05:22 -0800 Subject: [PATCH 1/2] markdown updates --- convex/users.ts | 17 +- src/components/Doc.tsx | 33 ++- src/components/MaintainerCard.tsx | 6 +- src/components/Markdown.tsx | 78 ++++-- src/components/MarkdownHeadingContext.tsx | 30 +++ src/components/Tabs.tsx | 82 ++++++ src/components/Toc.tsx | 4 +- src/components/TocMobile.tsx | 4 +- src/routes/_libraries/blog.$.tsx | 8 +- src/routes/_libraries/learn.tsx | 4 +- src/routes/_libraries/support.tsx | 4 +- src/routes/merch.tsx | 9 +- src/utils/markdown/index.ts | 2 + src/utils/markdown/pipeline.ts | 22 ++ src/utils/markdown/plugins.ts | 247 +++++++++++++++++++ src/utils/markdown/processor.ts | 107 ++++++++ src/utils/markdown/rehype-parse-component.ts | 108 ++++++++ 17 files changed, 709 insertions(+), 56 deletions(-) create mode 100644 src/components/MarkdownHeadingContext.tsx create mode 100644 src/components/Tabs.tsx create mode 100644 src/utils/markdown/index.ts create mode 100644 src/utils/markdown/pipeline.ts create mode 100644 src/utils/markdown/plugins.ts create mode 100644 src/utils/markdown/processor.ts create mode 100644 src/utils/markdown/rehype-parse-component.ts diff --git a/convex/users.ts b/convex/users.ts index 0cd145379..b12ae440a 100644 --- a/convex/users.ts +++ b/convex/users.ts @@ -54,7 +54,11 @@ export const listUsers = query({ nameFilter: v.optional(v.string()), capabilityFilter: v.optional( v.array( - v.union(v.literal('admin'), v.literal('disableAds'), v.literal('builder')) + v.union( + v.literal('admin'), + v.literal('disableAds'), + v.literal('builder') + ) ) ), noCapabilitiesFilter: v.optional(v.boolean()), @@ -96,10 +100,7 @@ export const listUsers = query({ // No capabilities filter if (args.noCapabilitiesFilter === true) { - if ( - !Array.isArray(user.capabilities) || - user.capabilities.length > 0 - ) { + if (!Array.isArray(user.capabilities) || user.capabilities.length > 0) { return false } } @@ -116,7 +117,11 @@ export const listUsers = query({ if (Boolean(user.adsDisabled) !== args.adsDisabledFilter) return false } if (typeof args.interestedInHidingAdsFilter === 'boolean') { - if (Boolean(user.interestedInHidingAds) !== args.interestedInHidingAdsFilter) return false + if ( + Boolean(user.interestedInHidingAds) !== + args.interestedInHidingAdsFilter + ) + return false } return true }) diff --git a/src/components/Doc.tsx b/src/components/Doc.tsx index 6c726d577..4bc6e9bae 100644 --- a/src/components/Doc.tsx +++ b/src/components/Doc.tsx @@ -1,6 +1,3 @@ -import { marked } from 'marked' -import markedAlert from 'marked-alert' -import { getHeadingList, gfmHeadingId } from 'marked-gfm-heading-id' import * as React from 'react' import { BsArrowsCollapseVertical, @@ -11,6 +8,10 @@ import { twMerge } from 'tailwind-merge' import { useWidthToggle } from '~/components/DocsLayout' import { DocTitle } from '~/components/DocTitle' import { Markdown } from '~/components/Markdown' +import { + MarkdownHeadingProvider, + useMarkdownHeadings, +} from '~/components/MarkdownHeadingContext' import { AdGate } from '~/contexts/AdsContext' import { CopyMarkdownButton } from './CopyMarkdownButton' import { GamHeader, GamLeader } from './Gam' @@ -28,7 +29,7 @@ type DocProps = { colorTo?: string } -export function Doc({ +function DocContent({ title, content, repo, @@ -38,17 +39,7 @@ export function Doc({ colorFrom, colorTo, }: DocProps) { - const { markup, headings } = React.useMemo(() => { - const markup = marked.use( - { gfm: true }, - gfmHeadingId(), - markedAlert() - )(content) as string - - const headings = getHeadingList() - - return { markup, headings } - }, [content]) + const { headings } = useMarkdownHeadings() const isTocVisible = shouldRenderToc && headings && headings.length > 1 @@ -107,7 +98,7 @@ export function Doc({ headingElements.forEach((el) => observer.observe(el)) return () => observer.disconnect() - }, []) + }, [headings]) return (
@@ -168,7 +159,7 @@ export function Doc({ 'styled-markdown-content' )} > - +
@@ -197,3 +188,11 @@ export function Doc({
) } + +export function Doc(props: DocProps) { + return ( + + + + ) +} diff --git a/src/components/MaintainerCard.tsx b/src/components/MaintainerCard.tsx index 5af1bfaf3..782a83e04 100644 --- a/src/components/MaintainerCard.tsx +++ b/src/components/MaintainerCard.tsx @@ -419,7 +419,11 @@ export function MaintainerRowCard({ ) } -export function MaintainerCard({ maintainer, libraryId, hideLibraries }: MaintainerCardProps) { +export function MaintainerCard({ + maintainer, + libraryId, + hideLibraries, +}: MaintainerCardProps) { const libraries = getPersonsMaintainerOf(maintainer) const [showAllLibraries, setShowAllLibraries] = useState(false) diff --git a/src/components/Markdown.tsx b/src/components/Markdown.tsx index 7912279f1..d247f38c4 100644 --- a/src/components/Markdown.tsx +++ b/src/components/Markdown.tsx @@ -10,12 +10,12 @@ import parse, { Element, HTMLReactParserOptions, } from 'html-react-parser' -import { marked } from 'marked' -import { gfmHeadingId } from 'marked-gfm-heading-id' -import markedAlert from 'marked-alert' import mermaid from 'mermaid' import { useToast } from '~/components/ToastProvider' import { twMerge } from 'tailwind-merge' +import { useMarkdownHeadings } from '~/components/MarkdownHeadingContext' +import { renderMarkdown } from '~/utils/markdown' +import { Tabs } from '~/components/Tabs' const CustomHeading = ({ Comp, @@ -194,7 +194,7 @@ export function CodeBlock({ return (
{ const options: HTMLReactParserOptions = { replace: (domNode) => { if (domNode instanceof Element && domNode.attribs) { + if (domNode.name === 'md-comment-component') { + const componentName = domNode.attribs['data-component'] + const rawAttributes = domNode.attribs['data-attributes'] + const attributes: Record = {} + try { + Object.assign(attributes, JSON.parse(rawAttributes)) + } catch { + // ignore JSON parse errors and fall back to empty props + } + + switch (componentName?.toLowerCase()) { + case 'tabs': { + const tabs = attributes.tabs + const panelElements = domNode.children?.filter( + (child): child is Element => + child instanceof Element && child.name === 'md-tab-panel' + ) + + const children = panelElements?.map((panel) => + domToReact(panel.children as any, options) + ) + + return + } + case 'alert': { + const variant = attributes.variant ?? 'note' + return ( + + {domToReact(domNode.children as any, options)} + + ) + } + default: + return
{domToReact(domNode.children as any, options)}
+ } + } + const replacer = markdownComponents[domNode.name] if (replacer) { return React.createElement( @@ -302,24 +339,35 @@ const options: HTMLReactParserOptions = { }, } -type MarkdownProps = { rawContent?: string; htmlMarkup?: string } +type MarkdownProps = { + rawContent?: string + htmlMarkup?: string +} export function Markdown({ rawContent, htmlMarkup }: MarkdownProps) { - return React.useMemo(() => { - if (rawContent) { - const markup = marked.use( - { gfm: true }, - gfmHeadingId(), - markedAlert() - )(rawContent) as string + const { setHeadings } = useMarkdownHeadings() - return parse(markup, options) + const rendered = React.useMemo(() => { + if (rawContent) { + return renderMarkdown(rawContent) } if (htmlMarkup) { - return parse(htmlMarkup, options) + return { markup: htmlMarkup, headings: [] } } - return null + return { markup: '', headings: [] } }, [rawContent, htmlMarkup]) + + React.useEffect(() => { + setHeadings(rendered.headings) + }, [rendered.headings, setHeadings]) + + return React.useMemo(() => { + if (!rendered.markup) { + return null + } + + return parse(rendered.markup, options) + }, [rendered.markup]) } diff --git a/src/components/MarkdownHeadingContext.tsx b/src/components/MarkdownHeadingContext.tsx new file mode 100644 index 000000000..de4a2b6a9 --- /dev/null +++ b/src/components/MarkdownHeadingContext.tsx @@ -0,0 +1,30 @@ +import * as React from 'react' +import { MarkdownHeading } from '~/utils/markdown/processor' + +const MarkdownHeadingContext = React.createContext<{ + headings: MarkdownHeading[] + setHeadings(headings: MarkdownHeading[]): void +}>({ + headings: [], + setHeadings: () => undefined, +}) + +export function MarkdownHeadingProvider({ + children, +}: { + children: React.ReactNode +}) { + const [headings, setHeadings] = React.useState([]) + + const value = React.useMemo(() => ({ headings, setHeadings }), [headings]) + + return ( + + {children} + + ) +} + +export function useMarkdownHeadings() { + return React.useContext(MarkdownHeadingContext) +} diff --git a/src/components/Tabs.tsx b/src/components/Tabs.tsx new file mode 100644 index 000000000..133c44b38 --- /dev/null +++ b/src/components/Tabs.tsx @@ -0,0 +1,82 @@ +import * as React from 'react' +import { getFrameworkOptions } from '~/libraries' + +export type TabDefinition = { + slug: string + name: string + headers?: Array +} + +export type TabsProps = { + tabs: Array + children: Array +} + +export function Tabs({ tabs, children }: TabsProps) { + const [activeSlug, setActiveSlug] = React.useState( + () => tabs[0]?.slug ?? 'tab' + ) + + return ( +
+
+ {tabs.map((tab) => { + return ( + + ) + })} +
+
+ {children.map((child, index) => { + const tab = tabs[index] + if (!tab) return null + return ( + + ) + })} +
+
+ ) +} + +const Tab = ({ + tab, + activeSlug, + setActiveSlug, +}: { + tab: TabDefinition + activeSlug: string + setActiveSlug: React.Dispatch> +}) => { + const options = React.useMemo(() => getFrameworkOptions(tab.slug), [tab.slug]) + return ( + + ) +} diff --git a/src/components/Toc.tsx b/src/components/Toc.tsx index 029e85be0..c9790d9ea 100644 --- a/src/components/Toc.tsx +++ b/src/components/Toc.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { twMerge } from 'tailwind-merge' -import { HeadingData } from 'marked-gfm-heading-id' +import { MarkdownHeading } from '~/utils/markdown/processor' const headingLevels: Record = { 1: 'pl-0.5 lg:pl-1 xl:pl-1.5 2xl:pl-2', @@ -12,7 +12,7 @@ const headingLevels: Record = { } type TocProps = { - headings: HeadingData[] + headings: MarkdownHeading[] colorFrom?: string colorTo?: string activeHeadings: Array diff --git a/src/components/TocMobile.tsx b/src/components/TocMobile.tsx index d6ca05312..3f8f4c213 100644 --- a/src/components/TocMobile.tsx +++ b/src/components/TocMobile.tsx @@ -1,10 +1,10 @@ import React from 'react' import { Link } from '@tanstack/react-router' -import type { HeadingData } from 'marked-gfm-heading-id' import { FaCaretRight, FaCaretDown } from 'react-icons/fa6' +import { MarkdownHeading } from '~/utils/markdown/processor' interface TocMobileProps { - headings: Array + headings: Array } export function TocMobile({ headings }: TocMobileProps) { diff --git a/src/routes/_libraries/blog.$.tsx b/src/routes/_libraries/blog.$.tsx index a2220d6a1..82ccac2f3 100644 --- a/src/routes/_libraries/blog.$.tsx +++ b/src/routes/_libraries/blog.$.tsx @@ -13,8 +13,6 @@ import { z } from 'zod' import { FaArrowLeft, FaEdit } from 'react-icons/fa' import { setResponseHeaders } from '@tanstack/react-start/server' import { allPosts } from 'content-collections' -import { marked } from 'marked' -import markedAlert from 'marked-alert' import * as React from 'react' import { twMerge } from 'tailwind-merge' import { DocTitle } from '~/components/DocTitle' @@ -100,10 +98,6 @@ function BlogPost() { )}._ ${content}` - const markup = React.useMemo(() => { - return marked.use({ gfm: true }, markedAlert())(blogContent) as string - }, [blogContent]) - const repo = 'tanstack/tanstack.com' const branch = 'main' @@ -164,7 +158,7 @@ ${content}` 'styled-markdown-content' )} > - +
diff --git a/src/routes/_libraries/learn.tsx b/src/routes/_libraries/learn.tsx index f27fef4d7..a80fe11cb 100644 --- a/src/routes/_libraries/learn.tsx +++ b/src/routes/_libraries/learn.tsx @@ -40,8 +40,8 @@ function LearnPage() {

Professional Workshops

- Learn directly from TanStack creators and maintainers. Remote and - in-person options available. + Learn directly from TanStack creators and maintainers. Remote + and in-person options available.

diff --git a/src/routes/_libraries/support.tsx b/src/routes/_libraries/support.tsx index d497e0aa0..dd563856d 100644 --- a/src/routes/_libraries/support.tsx +++ b/src/routes/_libraries/support.tsx @@ -79,7 +79,9 @@ function SupportComp() { NEW
-

Professional Workshops

+

+ Professional Workshops +

Learn from TanStack creators and maintainers

diff --git a/src/routes/merch.tsx b/src/routes/merch.tsx index 8d117e7fb..489799481 100644 --- a/src/routes/merch.tsx +++ b/src/routes/merch.tsx @@ -17,7 +17,8 @@ export const Route = createFileRoute('/merch')({ head: () => ({ meta: seo({ title: 'TanStack Merch', - description: 'Official TanStack merchandise including apparel and stickers.', + description: + 'Official TanStack merchandise including apparel and stickers.', }), }), }) @@ -25,7 +26,8 @@ export const Route = createFileRoute('/merch')({ const merchItems = [ { name: 'Apparel', - description: 'T-shirts, sweatshirts, hoodies, onesies, hats, totes, and phone cases featuring TanStack designs', + description: + 'T-shirts, sweatshirts, hoodies, onesies, hats, totes, and phone cases featuring TanStack designs', icons: [ { Icon: LuShirt, label: 'T-shirts' }, { Icon: PiBaseballCapBold, label: 'Hats' }, @@ -39,7 +41,8 @@ const merchItems = [ }, { name: 'Stickers & Buttons', - description: 'High-quality vinyl stickers and small buttons for your laptop, water bottle, and more', + description: + 'High-quality vinyl stickers and small buttons for your laptop, water bottle, and more', icons: [ { Icon: LuTag, label: 'Stickers' }, { Icon: LuCircle, label: 'Buttons' }, diff --git a/src/utils/markdown/index.ts b/src/utils/markdown/index.ts new file mode 100644 index 000000000..fa92cf569 --- /dev/null +++ b/src/utils/markdown/index.ts @@ -0,0 +1,2 @@ +export { renderMarkdown } from './processor' +export { rehypeParseCommentComponents } from './plugins' diff --git a/src/utils/markdown/pipeline.ts b/src/utils/markdown/pipeline.ts new file mode 100644 index 000000000..442a7e45d --- /dev/null +++ b/src/utils/markdown/pipeline.ts @@ -0,0 +1,22 @@ +import { unified } from 'unified' +import remarkParse from 'remark-parse' +import remarkRehype from 'remark-rehype' +import rehypeRaw from 'rehype-raw' +import rehypeSlug from 'rehype-slug' +import rehypeAutolinkHeadings from 'rehype-autolink-headings' +import rehypeStringify from 'rehype-stringify' + +export function createMarkdownPipeline() { + return unified() + .use(remarkParse) + .use(remarkRehype, { allowDangerousHtml: true }) + .use(rehypeRaw) + .use(rehypeSlug) + .use(rehypeAutolinkHeadings, { + behavior: 'wrap', + properties: { + className: ['anchor-heading'], + }, + }) + .use(rehypeStringify) +} diff --git a/src/utils/markdown/plugins.ts b/src/utils/markdown/plugins.ts new file mode 100644 index 000000000..e36d0fbe2 --- /dev/null +++ b/src/utils/markdown/plugins.ts @@ -0,0 +1,247 @@ +import { unified } from 'unified' +import rehypeParse from 'rehype-parse' +import { visit } from 'unist-util-visit' +import { toString } from 'hast-util-to-string' +import { isElement } from 'hast-util-is-element' + +const COMPONENT_PREFIX = '::' +const START_PREFIX = '::start:' +const END_PREFIX = '::end:' + +const componentParser = unified().use(rehypeParse, { fragment: true }) + +const normalizeComponentName = (name: string) => name.toLowerCase() + +function parseDescriptor(descriptor: string) { + const tree = componentParser.parse(`<${descriptor} />`) + const node = tree.children[0] + if (!node || node.type !== 'element') { + return null + } + + const component = node.tagName + const attributes: Record = {} + const properties = node.properties ?? {} + for (const [key, value] of Object.entries(properties)) { + if (Array.isArray(value)) { + attributes[key] = value.join(' ') + } else if (value != null) { + attributes[key] = String(value) + } + } + + return { component, attributes } +} + +const isCommentNode = (value: unknown) => + Boolean( + value && + typeof value === 'object' && + 'type' in value && + value.type === 'comment' + ) + +const slugify = (value: string, fallback: string) => { + if (!value) { + return fallback + } + return ( + value + .trim() + .toLowerCase() + .replace(/[^a-z0-9\s-]/g, '') + .replace(/\s+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, '') + .slice(0, 64) || fallback + ) +} + +export const rehypeParseCommentComponents = () => { + return (tree) => { + visit(tree, 'comment', (node, index, parent) => { + if (!isCommentNode(node) || parent == null || typeof index !== 'number') { + return + } + + const trimmed = node.value.trim() + if (!trimmed.startsWith(COMPONENT_PREFIX)) { + return + } + + const isBlock = trimmed.startsWith(START_PREFIX) + const descriptor = isBlock + ? trimmed.slice(START_PREFIX.length) + : trimmed.slice(COMPONENT_PREFIX.length) + + const parsed = parseDescriptor(descriptor) + if (!parsed) { + return + } + + const componentName = parsed.component + const element = { + type: 'element', + tagName: 'md-comment-component', + properties: { + 'data-component': componentName, + 'data-attributes': JSON.stringify(parsed.attributes ?? {}), + }, + children: [], + } + + if (!isBlock) { + parent.children.splice(index, 1, element) + return [visit.SKIP, index] + } + + let endIndex = -1 + for (let cursor = index + 1; cursor < parent.children.length; cursor++) { + const candidate = parent.children[cursor] + if ( + isCommentNode(candidate) && + candidate.value.trim().toLowerCase() === + `${END_PREFIX}${normalizeComponentName(componentName)}` + ) { + endIndex = cursor + break + } + } + + if (endIndex === -1) { + parent.children.splice(index, 1, element) + return [visit.SKIP, index] + } + + const content = parent.children.slice(index + 1, endIndex) + element.children = content + parent.children.splice(index, endIndex - index + 1, element) + return [visit.SKIP, index] + }) + } +} + +const isHeading = (node) => isElement(node) && /^h[1-6]$/.test(node.tagName) + +const headingLevel = (node) => Number(node.tagName.substring(1)) + +function extractSmartTabPanels(node) { + const children = node.children ?? [] + const headings = children.filter(isHeading) + + let sectionStarted = false + let largestHeadingLevel = Infinity + headings.forEach((heading) => { + largestHeadingLevel = Math.min(largestHeadingLevel, headingLevel(heading)) + }) + + const tabs: Array<{ + slug: string + name: string + headers: Array + }> = [] + const panels = [] + + let currentPanel = null + + children.forEach((child) => { + if (isHeading(child)) { + const level = headingLevel(child) + if (!sectionStarted) { + if (level !== largestHeadingLevel) { + return + } + sectionStarted = true + } + + if (level === largestHeadingLevel) { + if (currentPanel) { + panels.push(currentPanel) + } + const headingId = + (child.properties?.id && String(child.properties.id)) || + slugify(toString(child), `tab-${tabs.length + 1}`) + + tabs.push({ + slug: headingId, + name: toString(child), + headers: [], + }) + + currentPanel = [] + return + } + } + + if (sectionStarted) { + if (!currentPanel) { + currentPanel = [] + } + currentPanel.push(child) + } + }) + + if (currentPanel) { + panels.push(currentPanel) + } + + if (!tabs.length) { + return null + } + + panels.forEach((panelChildren, index) => { + const nestedHeadings: Array = [] + visit({ type: 'root', children: panelChildren }, 'element', (child) => { + if (isHeading(child) && typeof child.properties?.id === 'string') { + nestedHeadings.push(String(child.properties.id)) + } + }) + tabs[index]!.headers = nestedHeadings + }) + + return { tabs, panels } +} + +function transformTabsComponent(node) { + const result = extractSmartTabPanels(node) + if (!result) { + return + } + + const panelElements: Array = result.panels.map( + (panelChildren, index) => ({ + type: 'element', + tagName: 'md-tab-panel', + properties: { + 'data-tab-slug': result.tabs[index]?.slug ?? `tab-${index + 1}`, + 'data-tab-index': String(index), + }, + children: panelChildren, + }) + ) + + node.properties = { + ...node.properties, + 'data-attributes': JSON.stringify({ tabs: result.tabs }), + } + node.children = panelElements +} + +export const rehypeTransformCommentComponents = () => { + return (tree) => { + visit(tree, 'element', (node) => { + if (!isElement(node) || node.tagName !== 'md-comment-component') { + return + } + + const component = String(node.properties?.['data-component'] ?? '') + switch (normalizeComponentName(component)) { + case 'tabs': + transformTabsComponent(node) + break + default: + break + } + }) + } +} diff --git a/src/utils/markdown/processor.ts b/src/utils/markdown/processor.ts new file mode 100644 index 000000000..19a4bb055 --- /dev/null +++ b/src/utils/markdown/processor.ts @@ -0,0 +1,107 @@ +import { unified } from 'unified' +import remarkParse from 'remark-parse' +import remarkGfm from 'remark-gfm' +import remarkRehype from 'remark-rehype' +import rehypeCallouts from 'rehype-callouts' +import rehypeRaw from 'rehype-raw' +import rehypeSlug from 'rehype-slug' +import rehypeAutolinkHeadings from 'rehype-autolink-headings' +import rehypeStringify from 'rehype-stringify' +import { visit } from 'unist-util-visit' +import { toString } from 'hast-util-to-string' + +import { + rehypeParseCommentComponents, + rehypeTransformCommentComponents, +} from './plugins' + +export type MarkdownHeading = { + id: string + text: string + level: number +} + +export type MarkdownRenderResult = { + markup: string + headings: MarkdownHeading[] +} + +export function renderMarkdown(content): MarkdownRenderResult { + const headings: MarkdownHeading[] = [] + + const processor = unified() + .use(remarkParse) + .use(remarkGfm) + .use(remarkRehype, { allowDangerousHtml: true }) + .use(rehypeRaw) + .use(rehypeParseCommentComponents) + .use(rehypeCallouts, { + theme: 'github', + props: { + containerProps(node, type) { + return { + className: `markdown-alert markdown-alert-${type}`, + children: node.children, + } + }, + titleIconProps() { + return { + className: 'octicon octicon-info mr-2', + } + }, + titleProps() { + return { + className: 'markdown-alert-title', + } + }, + titleTextProps() { + return { + className: 'markdown-alert-title', + } + }, + contentProps() { + return { + className: 'markdown-alert-content', + } + }, + }, + }) + .use(rehypeSlug) + .use(rehypeTransformCommentComponents) + .use(rehypeAutolinkHeadings, { + behavior: 'wrap', + properties: { + className: ['anchor-heading'], + }, + }) + .use(() => (tree, file) => { + visit(tree, 'element', (node) => { + if (!('tagName' in node)) return + if (!/^h[1-6]$/.test(String(node.tagName))) { + return + } + + const tagName = String(node.tagName) + const id = + typeof node.properties?.id === 'string' ? node.properties.id : '' + if (!id) { + return + } + + headings.push({ + id, + level: Number(tagName.substring(1)), + text: toString(node).trim(), + }) + }) + + file.data.headings = headings + }) + + const file = processor.use(rehypeStringify).processSync(content) + + return { + markup: String(file), + headings, + } +} diff --git a/src/utils/markdown/rehype-parse-component.ts b/src/utils/markdown/rehype-parse-component.ts new file mode 100644 index 000000000..20180d10d --- /dev/null +++ b/src/utils/markdown/rehype-parse-component.ts @@ -0,0 +1,108 @@ +// Modified from: https://github.com/playfulprogramming/playfulprogramming/blob/main/src/utils/markdown/components/rehype-parse-components.ts + +import { is } from 'unist-util-is' +import * as hast from 'hast' +import { unified, Plugin } from 'unified' +import rehypeParse from 'rehype-parse' +import { VFile } from 'vfile' +import { ComponentMarkupNode, PlayfulNode, PlayfulRoot } from './components' + +const unifiedRehype = unified().use(rehypeParse, { fragment: true }) + +const COMPONENT_PREFIX = '::' +const START_PREFIX = '::start:' +const END_PREFIX = '::end:' + +const isNodeComment = (node: unknown): node is hast.Comment => + !!( + typeof node === 'object' && + node && + 'type' in node && + node.type === 'comment' + ) + +const isNodeElement = (node: unknown): node is hast.Element => + (typeof node === 'object' && + node && + 'type' in node && + node['type'] === 'element') ?? + false + +export const rehypeParseComponents: Plugin<[], PlayfulRoot> = function () { + function parseComponents( + tree: PlayfulRoot, + vfile: VFile + ): (PlayfulNode | hast.ElementContent)[] { + for (let index = 0; index < tree.children.length; index++) { + const node = tree.children[index] + + if (!isNodeComment(node)) continue + + // ` ::start:in-content-ad title="Hello world" ` + const value = String(node.value).trim() + if (!value.startsWith(COMPONENT_PREFIX)) continue + + const isRanged = value.startsWith(START_PREFIX) + // `in-content-ad title="Hello world"` + const valueContent = isRanged + ? value.substring(START_PREFIX.length) + : value.substring(COMPONENT_PREFIX.length) + + // Parse the attributes/tagNode from the start tag + const componentNode = unifiedRehype.parse(`<${valueContent}/>`) + .children[0] + if (!isNodeElement(componentNode)) { + continue + } + + // If the component is ranged, find the index of its end tag + let indexEnd = 0 + if (isRanged) { + for (let i = index + 1; i < tree.children.length; i++) { + const nodeEnd = tree.children[i] + if ( + is(nodeEnd, { + type: 'comment', + value: ` ${END_PREFIX}${componentNode.tagName} `, + }) + ) { + indexEnd = i + break + } + } + } + + // Fetch all nodes between the ranged comments (if indexEnd=0, this will be an empty array) + const componentChildren = tree.children.slice(index + 1, indexEnd) + const parsedComponentChildren = parseComponents( + { type: 'root', children: componentChildren }, + vfile + ) + + const replacement: ComponentMarkupNode = { + type: 'playful-component-markup', + position: node.position, + component: componentNode.tagName, + attributes: Object.fromEntries( + Object.entries(componentNode.properties).map(([key, value]) => [ + key, + Array.isArray(value) ? value.join(' ') : String(value), + ]) + ), + children: parsedComponentChildren, + } + + tree.children.splice( + index, + isRanged ? indexEnd - index + 1 : 1, + replacement + ) + } + + return tree.children + } + + return async (tree, vfile) => { + parseComponents(tree, vfile) + } +} From 79dd43d5e3dd4fb153798c2ce3cad05c357e5e6a Mon Sep 17 00:00:00 2001 From: LadyBluenotes Date: Thu, 27 Nov 2025 09:13:21 -0800 Subject: [PATCH 2/2] packages --- package.json | 16 +- pnpm-lock.yaml | 924 ++++++++++++++++++- src/utils/markdown/rehype-parse-component.ts | 108 --- 3 files changed, 908 insertions(+), 140 deletions(-) delete mode 100644 src/utils/markdown/rehype-parse-component.ts diff --git a/package.json b/package.json index 789af092a..f5adf5fd1 100644 --- a/package.json +++ b/package.json @@ -62,12 +62,11 @@ "downshift": "^9.0.9", "eslint-config-react-app": "^7.0.1", "gray-matter": "^4.0.3", + "hast-util-is-element": "^3.0.0", + "hast-util-to-string": "^3.0.1", "html-react-parser": "^5.1.10", "import-meta-resolve": "^4.0.0", "lru-cache": "^7.13.1", - "marked": "^13.0.2", - "marked-alert": "^2.0.1", - "marked-gfm-heading-id": "^4.0.0", "mermaid": "^11.11.0", "qss": "^3.0.0", "react": "^19.2.0", @@ -76,11 +75,22 @@ "react-icons": "^5.3.0", "react-instantsearch": "7", "react-markdown": "^6.0.3", + "rehype-autolink-headings": "^7.1.0", + "rehype-callouts": "^2.1.2", + "rehype-parse": "^9.0.1", + "rehype-raw": "^7.0.0", + "rehype-slug": "^6.0.0", + "rehype-stringify": "^10.0.1", + "remark-gfm": "^4.0.1", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.2", "remix-utils": "^8.5.0", "remove-markdown": "^0.5.0", "shiki": "^1.4.0", "tailwind-merge": "^1.14.0", "tiny-invariant": "^1.3.3", + "unified": "^11.0.5", + "unist-util-visit": "^5.0.0", "vite-bundle-analyzer": "^1.2.1", "vite-tsconfig-paths": "^5.0.1", "zod": "^4.0.17", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 69814469c..0503c426c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,6 +143,12 @@ importers: gray-matter: specifier: ^4.0.3 version: 4.0.3 + hast-util-is-element: + specifier: ^3.0.0 + version: 3.0.0 + hast-util-to-string: + specifier: ^3.0.1 + version: 3.0.1 html-react-parser: specifier: ^5.1.10 version: 5.1.10(@types/react@19.2.5)(react@19.2.0) @@ -152,15 +158,6 @@ importers: lru-cache: specifier: ^7.13.1 version: 7.18.3 - marked: - specifier: ^13.0.2 - version: 13.0.2 - marked-alert: - specifier: ^2.0.1 - version: 2.0.1(marked@13.0.2) - marked-gfm-heading-id: - specifier: ^4.0.0 - version: 4.0.0(marked@13.0.2) mermaid: specifier: ^11.11.0 version: 11.11.0 @@ -185,6 +182,33 @@ importers: react-markdown: specifier: ^6.0.3 version: 6.0.3(@types/react@19.2.5)(react@19.2.0) + rehype-autolink-headings: + specifier: ^7.1.0 + version: 7.1.0 + rehype-callouts: + specifier: ^2.1.2 + version: 2.1.2 + rehype-parse: + specifier: ^9.0.1 + version: 9.0.1 + rehype-raw: + specifier: ^7.0.0 + version: 7.0.0 + rehype-slug: + specifier: ^6.0.0 + version: 6.0.0 + rehype-stringify: + specifier: ^10.0.1 + version: 10.0.1 + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1 + remark-parse: + specifier: ^11.0.0 + version: 11.0.0 + remark-rehype: + specifier: ^11.1.2 + version: 11.1.2 remix-utils: specifier: ^8.5.0 version: 8.5.0(@oslojs/crypto@1.0.1)(@oslojs/encoding@1.1.0)(react@19.2.0)(zod@4.0.17) @@ -200,6 +224,12 @@ importers: tiny-invariant: specifier: ^1.3.3 version: 1.3.3 + unified: + specifier: ^11.0.5 + version: 11.0.5 + unist-util-visit: + specifier: ^5.0.0 + version: 5.0.0 vite-bundle-analyzer: specifier: ^1.2.1 version: 1.2.1 @@ -3759,6 +3789,9 @@ packages: '@types/d3@7.4.3': resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/dom-speech-recognition@0.0.1': resolution: {integrity: sha512-udCxb8DvjcDKfk1WTBzDsxFbLgYxmQGKrE/ricoMqHRNjSlSUCcamVTA5lIQqzY10mY5qCY0QDwBfFEwhfoDPw==} @@ -3792,6 +3825,12 @@ packages: '@types/mdast@3.0.15': resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/node@24.3.0': resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==} @@ -3830,6 +3869,9 @@ packages: '@types/unist@2.0.10': resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} @@ -4263,6 +4305,9 @@ packages: bail@1.0.5: resolution: {integrity: sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==} + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -4393,6 +4438,9 @@ packages: caniuse-lite@1.0.30001692: resolution: {integrity: sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==} + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -4405,12 +4453,21 @@ packages: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + character-entities-legacy@1.1.4: resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + character-entities@1.2.4: resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + character-reference-invalid@1.1.4: resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} @@ -4516,6 +4573,9 @@ packages: comma-separated-tokens@1.0.8: resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@10.0.1: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} @@ -4937,6 +4997,9 @@ packages: decache@4.6.2: resolution: {integrity: sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==} + decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} + dedent@1.7.0: resolution: {integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==} peerDependencies: @@ -5042,6 +5105,9 @@ packages: dettle@1.0.5: resolution: {integrity: sha512-ZVyjhAJ7sCe1PNXEGveObOH9AC8QvMga3HJIghHawtG7mE4K5pW9nz/vDGAr/U7a3LWgdOzEE7ac9MURnyfaTA==} + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + diff@8.0.2: resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} engines: {node: '>=0.3.1'} @@ -5747,6 +5813,39 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hast-util-from-html@2.0.3: + resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} + + hast-util-from-parse5@8.0.3: + resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + + hast-util-heading-rank@3.0.0: + resolution: {integrity: sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==} + + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + + hast-util-parse-selector@4.0.0: + resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-to-parse5@8.0.0: + resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} + + hast-util-to-string@3.0.1: + resolution: {integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hastscript@9.0.1: + resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + highlight.js@11.10.0: resolution: {integrity: sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==} engines: {node: '>=12.0.0'} @@ -5780,6 +5879,9 @@ packages: '@types/react': optional: true + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + htmlparser2@10.0.0: resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} @@ -6426,6 +6528,9 @@ packages: resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} engines: {node: '>= 12.0.0'} + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -6466,15 +6571,8 @@ packages: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true - marked-alert@2.0.1: - resolution: {integrity: sha512-dREhBjpPEN87b5AdlpETVoEpYiROQJ2M01sqjvQetKqpfi92BQRfSFKPIEkm90RnMpv5CFLNpWfGO6bx+tnayQ==} - peerDependencies: - marked: '>=7.0.0' - - marked-gfm-heading-id@4.0.0: - resolution: {integrity: sha512-p53gp+nBIGeryucXzUFP8w67wtpin68+jOFBPw5m9FUfqx2ngAunO/I6KU6J3Xm3fyuv2JsSDxz0Z+pYVrHrwg==} - peerDependencies: - marked: '>=13 <14' + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} marked-highlight@2.1.4: resolution: {integrity: sha512-D1GOkcdzP+1dzjoColL7umojefFrASDuLeyaHS0Zr/Uo9jkr1V6vpLRCzfi1djmEaWyK0SYMFtHnpkZ+cwFT1w==} @@ -6498,15 +6596,51 @@ packages: mdast-util-definitions@4.0.0: resolution: {integrity: sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==} + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + mdast-util-from-markdown@0.8.5: resolution: {integrity: sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==} + mdast-util-from-markdown@2.0.2: + resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + mdast-util-to-hast@10.2.0: resolution: {integrity: sha512-JoPBfJ3gBnHZ18icCwHR50orC9kNH81tiR1gs01D8Q5YpV6adHNO9nKNuFBCJQ941/32PT1a63UF/DitmS3amQ==} + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + mdast-util-to-string@2.0.0: resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==} + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + mdn-data@2.0.28: resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} @@ -6544,9 +6678,93 @@ packages: mermaid@11.11.0: resolution: {integrity: sha512-9lb/VNkZqWTRjVgCV+l1N+t4kyi94y+l5xrmBmbbxZYkfRl5hEDaTPMOcaWKCl1McG8nBEaMlWwkcAEEgjhBgg==} + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + micromark@2.11.4: resolution: {integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==} + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -7094,6 +7312,12 @@ packages: property-information@5.6.0: resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} + property-information@6.5.0: + resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -7310,12 +7534,43 @@ packages: resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} hasBin: true + rehype-autolink-headings@7.1.0: + resolution: {integrity: sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw==} + + rehype-callouts@2.1.2: + resolution: {integrity: sha512-ZZWZ6EknUHiSzr4pQ88C7db3su4DElfJRmphZJbXpDdwW3urTwlYZpHckoC9pjEvBmUEEiJAM0uuc2uxyLdTfg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + + rehype-parse@9.0.1: + resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} + + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + rehype-slug@6.0.0: + resolution: {integrity: sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==} + + rehype-stringify@10.0.1: + resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + remark-parse@9.0.0: resolution: {integrity: sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==} + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + remark-rehype@8.1.0: resolution: {integrity: sha512-EbCu9kHgAxKmW1yEYjx3QafMyGY3q8noUbNUI5xyKbaFP89wbhDrKxyIQNukNYthzjNHZu6J7hwFg7hRm1svYA==} + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + remeda@2.26.1: resolution: {integrity: sha512-hpiLfhUwkJhiMS3Z7dRrygcRdkMRZASw5qUdNdi33x1/Y9y/J5q5TyLyf8btDoVLIcsg/4fzPdaGXDTbnl+ixw==} @@ -7592,6 +7847,9 @@ packages: space-separated-tokens@1.1.5: resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} @@ -7664,6 +7922,9 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -7812,6 +8073,9 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + triple-beam@1.4.1: resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} engines: {node: '>= 14.0.0'} @@ -7819,6 +8083,9 @@ packages: trough@1.0.5: resolution: {integrity: sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==} + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + ts-api-utils@1.3.0: resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} engines: {node: '>=16'} @@ -7949,6 +8216,9 @@ packages: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + unified@9.2.2: resolution: {integrity: sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==} @@ -7961,18 +8231,33 @@ packages: unist-util-is@4.1.0: resolution: {integrity: sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==} + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + unist-util-position@3.1.0: resolution: {integrity: sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA==} + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + unist-util-stringify-position@2.0.3: resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + unist-util-visit-parents@3.1.1: resolution: {integrity: sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==} + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + unist-util-visit@2.0.3: resolution: {integrity: sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==} + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + universal-github-app-jwt@2.2.2: resolution: {integrity: sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==} @@ -8129,12 +8414,21 @@ packages: resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + vfile-location@5.0.3: + resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} + vfile-message@2.0.4: resolution: {integrity: sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==} + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + vfile@4.2.1: resolution: {integrity: sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==} + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + vite-bundle-analyzer@1.2.1: resolution: {integrity: sha512-dpJMQBnVjieMirpHILmgGsZAWU8fGyqK3ckj6O48wdy3fARlH7Mnz7BfYFUsd3ZdbuXjsYe7t5I/q2zsMOd5ig==} hasBin: true @@ -8218,6 +8512,9 @@ packages: web-encoding@1.1.5: resolution: {integrity: sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==} + web-namespaces@2.0.1: + resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} @@ -8373,6 +8670,9 @@ packages: react: optional: true + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + snapshots: '@aashutoshrathi/word-wrap@1.2.6': {} @@ -12376,6 +12676,10 @@ snapshots: '@types/d3-transition': 3.0.9 '@types/d3-zoom': 3.0.8 + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + '@types/dom-speech-recognition@0.0.1': {} '@types/estree@1.0.8': {} @@ -12390,7 +12694,7 @@ snapshots: '@types/hast@3.0.4': dependencies: - '@types/unist': 2.0.10 + '@types/unist': 3.0.3 '@types/hogan.js@3.0.5': {} @@ -12404,6 +12708,12 @@ snapshots: dependencies: '@types/unist': 2.0.10 + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + '@types/node@24.3.0': dependencies: undici-types: 7.10.0 @@ -12434,6 +12744,8 @@ snapshots: '@types/unist@2.0.10': {} + '@types/unist@3.0.3': {} + '@types/yauzl@2.10.3': dependencies: '@types/node': 24.3.0 @@ -13062,6 +13374,8 @@ snapshots: bail@1.0.5: {} + bail@2.0.2: {} + balanced-match@1.0.2: {} bare-events@2.7.0: {} @@ -13182,6 +13496,8 @@ snapshots: caniuse-lite@1.0.30001692: {} + ccount@2.0.1: {} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -13195,10 +13511,16 @@ snapshots: chalk@5.6.2: {} + character-entities-html4@2.1.0: {} + character-entities-legacy@1.1.4: {} + character-entities-legacy@3.0.0: {} + character-entities@1.2.4: {} + character-entities@2.0.2: {} + character-reference-invalid@1.1.4: {} cheerio-select@2.1.0: @@ -13335,6 +13657,8 @@ snapshots: comma-separated-tokens@1.0.8: {} + comma-separated-tokens@2.0.3: {} + commander@10.0.1: {} commander@11.1.0: {} @@ -13723,6 +14047,10 @@ snapshots: dependencies: callsite: 1.0.0 + decode-named-character-reference@1.2.0: + dependencies: + character-entities: 2.0.2 + dedent@1.7.0(babel-plugin-macros@3.1.0): optionalDependencies: babel-plugin-macros: 3.1.0 @@ -13823,6 +14151,10 @@ snapshots: dettle@1.0.5: {} + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + diff@8.0.2: {} dir-glob@3.0.1: @@ -14139,7 +14471,7 @@ snapshots: eslint: 8.57.0 eslint-plugin-flowtype: 8.0.3(@babel/plugin-syntax-flow@7.27.1(@babel/core@7.28.3))(@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.3))(eslint@8.57.0) eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0) - eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(typescript@5.9.2) + eslint-plugin-jest: 25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(typescript@5.9.2) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0) eslint-plugin-react: 7.34.0(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) @@ -14207,7 +14539,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(typescript@5.9.2): + eslint-plugin-jest@25.7.0(@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(typescript@5.9.2))(eslint@8.57.0)(typescript@5.9.2): dependencies: '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.57.0)(typescript@5.9.2) eslint: 8.57.0 @@ -14734,6 +15066,94 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-from-html@2.0.3: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + hast-util-from-parse5: 8.0.3 + parse5: 7.3.0 + vfile: 6.0.3 + vfile-message: 4.0.3 + + hast-util-from-parse5@8.0.3: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + devlop: 1.1.0 + hastscript: 9.0.1 + property-information: 7.1.0 + vfile: 6.0.3 + vfile-location: 5.0.3 + web-namespaces: 2.0.1 + + hast-util-heading-rank@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-parse-selector@4.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.2.0 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-parse5@8.0.0: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-to-string@3.0.1: + dependencies: + '@types/hast': 3.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hastscript@9.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + hast-util-parse-selector: 4.0.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + highlight.js@11.10.0: {} hogan.js@3.0.2: @@ -14768,6 +15188,8 @@ snapshots: optionalDependencies: '@types/react': 19.2.5 + html-void-elements@3.0.0: {} + htmlparser2@10.0.0: dependencies: domelementtype: 2.3.0 @@ -15396,6 +15818,8 @@ snapshots: safe-stable-stringify: 2.5.0 triple-beam: 1.4.1 + longest-streak@3.1.0: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -15438,14 +15862,7 @@ snapshots: punycode.js: 2.3.1 uc.micro: 2.1.0 - marked-alert@2.0.1(marked@13.0.2): - dependencies: - marked: 13.0.2 - - marked-gfm-heading-id@4.0.0(marked@13.0.2): - dependencies: - github-slugger: 2.0.0 - marked: 13.0.2 + markdown-table@3.0.4: {} marked-highlight@2.1.4(marked@13.0.2): dependencies: @@ -15461,6 +15878,13 @@ snapshots: dependencies: unist-util-visit: 2.0.3 + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + mdast-util-from-markdown@0.8.5: dependencies: '@types/mdast': 3.0.15 @@ -15471,6 +15895,85 @@ snapshots: transitivePeerDependencies: - supports-color + mdast-util-from-markdown@2.0.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + mdast-util-to-hast@10.2.0: dependencies: '@types/mdast': 3.0.15 @@ -15482,8 +15985,36 @@ snapshots: unist-util-position: 3.1.0 unist-util-visit: 2.0.3 + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.2.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + mdast-util-to-string@2.0.0: {} + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdn-data@2.0.28: {} mdn-data@2.12.2: {} @@ -15537,6 +16068,175 @@ snapshots: transitivePeerDependencies: - supports-color + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.2.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + micromark@2.11.4: dependencies: debug: 4.4.3 @@ -15544,6 +16244,28 @@ snapshots: transitivePeerDependencies: - supports-color + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.3 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -16088,6 +16810,10 @@ snapshots: dependencies: xtend: 4.0.2 + property-information@6.5.0: {} + + property-information@7.1.0: {} + proxy-from-env@1.1.0: {} pump@3.0.3: @@ -16340,16 +17066,93 @@ snapshots: dependencies: jsesc: 0.5.0 + rehype-autolink-headings@7.1.0: + dependencies: + '@types/hast': 3.0.4 + '@ungap/structured-clone': 1.2.0 + hast-util-heading-rank: 3.0.0 + hast-util-is-element: 3.0.0 + unified: 11.0.5 + unist-util-visit: 5.0.0 + + rehype-callouts@2.1.2: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-html: 2.0.3 + hast-util-is-element: 3.0.0 + hastscript: 9.0.1 + unist-util-visit: 5.0.0 + + rehype-parse@9.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-html: 2.0.3 + unified: 11.0.5 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-slug@6.0.0: + dependencies: + '@types/hast': 3.0.4 + github-slugger: 2.0.0 + hast-util-heading-rank: 3.0.0 + hast-util-to-string: 3.0.1 + unist-util-visit: 5.0.0 + + rehype-stringify@10.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + unified: 11.0.5 + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + remark-parse@9.0.0: dependencies: mdast-util-from-markdown: 0.8.5 transitivePeerDependencies: - supports-color + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + remark-rehype@8.1.0: dependencies: mdast-util-to-hast: 10.2.0 + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + remeda@2.26.1: dependencies: type-fest: 4.41.0 @@ -16628,6 +17431,8 @@ snapshots: space-separated-tokens@1.1.5: {} + space-separated-tokens@2.0.2: {} + spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 @@ -16727,6 +17532,11 @@ snapshots: dependencies: safe-buffer: 5.2.1 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -16872,10 +17682,14 @@ snapshots: tr46@0.0.3: {} + trim-lines@3.0.1: {} + triple-beam@1.4.1: {} trough@1.0.5: {} + trough@2.2.0: {} + ts-api-utils@1.3.0(typescript@5.9.2): dependencies: typescript: 5.9.2 @@ -16991,6 +17805,16 @@ snapshots: unicorn-magic@0.1.0: {} + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + unified@9.2.2: dependencies: '@types/unist': 2.0.10 @@ -17007,23 +17831,46 @@ snapshots: unist-util-is@4.1.0: {} + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-position@3.1.0: {} + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position@2.0.3: dependencies: '@types/unist': 2.0.10 + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit-parents@3.1.1: dependencies: '@types/unist': 2.0.10 unist-util-is: 4.1.0 + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit@2.0.3: dependencies: '@types/unist': 2.0.10 unist-util-is: 4.1.0 unist-util-visit-parents: 3.1.1 + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + universal-github-app-jwt@2.2.2: {} universal-user-agent@6.0.0: {} @@ -17129,11 +17976,21 @@ snapshots: validate-npm-package-name@5.0.1: {} + vfile-location@5.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile: 6.0.3 + vfile-message@2.0.4: dependencies: '@types/unist': 2.0.10 unist-util-stringify-position: 2.0.3 + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + vfile@4.2.1: dependencies: '@types/unist': 2.0.10 @@ -17141,6 +17998,11 @@ snapshots: unist-util-stringify-position: 2.0.3 vfile-message: 2.0.4 + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + vite-bundle-analyzer@1.2.1: {} vite-tsconfig-paths@5.0.1(typescript@5.9.2)(vite@7.1.7(@types/node@24.3.0)(jiti@2.6.0)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.5)(yaml@2.8.1)): @@ -17198,6 +18060,8 @@ snapshots: optionalDependencies: '@zxing/text-encoding': 0.9.0 + web-namespaces@2.0.1: {} + web-streams-polyfill@3.3.3: {} webidl-conversions@3.0.1: {} @@ -17367,3 +18231,5 @@ snapshots: optionalDependencies: '@types/react': 19.2.5 react: 19.2.0 + + zwitch@2.0.4: {} diff --git a/src/utils/markdown/rehype-parse-component.ts b/src/utils/markdown/rehype-parse-component.ts deleted file mode 100644 index 20180d10d..000000000 --- a/src/utils/markdown/rehype-parse-component.ts +++ /dev/null @@ -1,108 +0,0 @@ -// Modified from: https://github.com/playfulprogramming/playfulprogramming/blob/main/src/utils/markdown/components/rehype-parse-components.ts - -import { is } from 'unist-util-is' -import * as hast from 'hast' -import { unified, Plugin } from 'unified' -import rehypeParse from 'rehype-parse' -import { VFile } from 'vfile' -import { ComponentMarkupNode, PlayfulNode, PlayfulRoot } from './components' - -const unifiedRehype = unified().use(rehypeParse, { fragment: true }) - -const COMPONENT_PREFIX = '::' -const START_PREFIX = '::start:' -const END_PREFIX = '::end:' - -const isNodeComment = (node: unknown): node is hast.Comment => - !!( - typeof node === 'object' && - node && - 'type' in node && - node.type === 'comment' - ) - -const isNodeElement = (node: unknown): node is hast.Element => - (typeof node === 'object' && - node && - 'type' in node && - node['type'] === 'element') ?? - false - -export const rehypeParseComponents: Plugin<[], PlayfulRoot> = function () { - function parseComponents( - tree: PlayfulRoot, - vfile: VFile - ): (PlayfulNode | hast.ElementContent)[] { - for (let index = 0; index < tree.children.length; index++) { - const node = tree.children[index] - - if (!isNodeComment(node)) continue - - // ` ::start:in-content-ad title="Hello world" ` - const value = String(node.value).trim() - if (!value.startsWith(COMPONENT_PREFIX)) continue - - const isRanged = value.startsWith(START_PREFIX) - // `in-content-ad title="Hello world"` - const valueContent = isRanged - ? value.substring(START_PREFIX.length) - : value.substring(COMPONENT_PREFIX.length) - - // Parse the attributes/tagNode from the start tag - const componentNode = unifiedRehype.parse(`<${valueContent}/>`) - .children[0] - if (!isNodeElement(componentNode)) { - continue - } - - // If the component is ranged, find the index of its end tag - let indexEnd = 0 - if (isRanged) { - for (let i = index + 1; i < tree.children.length; i++) { - const nodeEnd = tree.children[i] - if ( - is(nodeEnd, { - type: 'comment', - value: ` ${END_PREFIX}${componentNode.tagName} `, - }) - ) { - indexEnd = i - break - } - } - } - - // Fetch all nodes between the ranged comments (if indexEnd=0, this will be an empty array) - const componentChildren = tree.children.slice(index + 1, indexEnd) - const parsedComponentChildren = parseComponents( - { type: 'root', children: componentChildren }, - vfile - ) - - const replacement: ComponentMarkupNode = { - type: 'playful-component-markup', - position: node.position, - component: componentNode.tagName, - attributes: Object.fromEntries( - Object.entries(componentNode.properties).map(([key, value]) => [ - key, - Array.isArray(value) ? value.join(' ') : String(value), - ]) - ), - children: parsedComponentChildren, - } - - tree.children.splice( - index, - isRanged ? indexEnd - index + 1 : 1, - replacement - ) - } - - return tree.children - } - - return async (tree, vfile) => { - parseComponents(tree, vfile) - } -}