diff --git a/astro.config.ts b/astro.config.ts index d56a8fbd..96f6d4bc 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -1,15 +1,14 @@ import cloudflare from "@astrojs/cloudflare"; import mdx from "@astrojs/mdx"; import react from "@astrojs/react"; -import starlightImageZoom from "starlight-image-zoom"; +import { satteri } from "@astrojs/markdown-satteri"; import starlight from "@astrojs/starlight"; import starlightLinksValidator from "starlight-links-validator"; import mermaid from "astro-mermaid"; import starlightLlmsTxt from "starlight-llms-txt"; -import { unified } from "@astrojs/markdown-remark"; import { loadEnv } from "vite"; import { readFile } from "node:fs/promises"; -import rehypeExternalLinks from "./src/plugins/rehype/external-links"; +import satteriExternalLinks from "./src/plugins/satteri/external-links"; import { defineConfig } from "astro/config"; import type { HeadUserConfig } from "node_modules/@astrojs/starlight/schemas/head"; @@ -193,14 +192,12 @@ export default defineConfig({ }), site: "https://developer.sumup.com", markdown: { - processor: unified({ - gfm: true, - rehypePlugins: [rehypeExternalLinks], - remarkRehype: { - // Use text-presentation symbol (U+21A9 + U+FE0E), not emoji. - footnoteBackContent: "\u21A9\uFE0E", + processor: satteri({ + features: { + gfm: true, + smartPunctuation: true, }, - smartypants: true, + hastPlugins: [satteriExternalLinks], }), }, @@ -250,7 +247,6 @@ export default defineConfig({ // handle well otherwise rawContent: true, }), - starlightImageZoom(), ], title: "SumUp Developer", favicon: new URL("/favicons/favicon.svg", faviconBaseURL).toString(), @@ -340,8 +336,6 @@ export default defineConfig({ }), mdx({ optimize: true, - gfm: true, - smartypants: true, }), ], server: { diff --git a/package-lock.json b/package-lock.json index 603d6e4e..28d92399 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@astrojs/check": "^0.9.6", "@astrojs/cloudflare": "^14.1.0", "@astrojs/language-server": "^2.16.10", + "@astrojs/markdown-satteri": "0.3.2", "@astrojs/react": "^6.0.0", "@astrojs/rss": "^4.0.19", "@astrojs/sitemap": "^3.7.3", @@ -47,7 +48,6 @@ "react-final-form": "^7.0.1", "rumdl": "^0.2.9", "sass": "^1.101.0", - "starlight-image-zoom": "^0.15.0", "starlight-links-validator": "^0.25.2", "starlight-llms-txt": "0.11.0", "tsx": "^4.22.4", @@ -16772,25 +16772,6 @@ "dev": true, "license": "MIT" }, - "node_modules/starlight-image-zoom": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/starlight-image-zoom/-/starlight-image-zoom-0.15.0.tgz", - "integrity": "sha512-GIZVuaiaVuFsbyYqiT7M4FYJBvkTsvdO1XoOKM/iNHt85BaVn/A7/kLpdb5XnUsAZohOMS77ahaolofnGIqeOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mdast-util-mdx-jsx": "^3.2.0", - "rehype-raw": "^7.0.0", - "unist-util-visit": "^5.1.0", - "unist-util-visit-parents": "^6.0.2" - }, - "engines": { - "node": ">=22.12.0" - }, - "peerDependencies": { - "@astrojs/starlight": ">=0.41.0" - } - }, "node_modules/starlight-links-validator": { "version": "0.25.2", "resolved": "https://registry.npmjs.org/starlight-links-validator/-/starlight-links-validator-0.25.2.tgz", diff --git a/package.json b/package.json index c0971fab..3391ca59 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@astrojs/check": "^0.9.6", "@astrojs/cloudflare": "^14.1.0", "@astrojs/language-server": "^2.16.10", + "@astrojs/markdown-satteri": "0.3.2", "@astrojs/react": "^6.0.0", "@astrojs/rss": "^4.0.19", "@astrojs/sitemap": "^3.7.3", @@ -80,7 +81,6 @@ "react-final-form": "^7.0.1", "rumdl": "^0.2.9", "sass": "^1.101.0", - "starlight-image-zoom": "^0.15.0", "starlight-links-validator": "^0.25.2", "starlight-llms-txt": "0.11.0", "tsx": "^4.22.4", diff --git a/src/overrides/MarkdownContent.astro b/src/overrides/MarkdownContent.astro index c5f0a03b..52b7cb29 100644 --- a/src/overrides/MarkdownContent.astro +++ b/src/overrides/MarkdownContent.astro @@ -1,6 +1,5 @@ --- import Default from "@astrojs/starlight/components/MarkdownContent.astro"; -import ImageZoom from "starlight-image-zoom/components/ImageZoom.astro"; --- { @@ -12,4 +11,3 @@ import ImageZoom from "starlight-image-zoom/components/ImageZoom.astro"; ) } - diff --git a/src/plugins/rehype/external-links.ts b/src/plugins/satteri/external-links.ts similarity index 56% rename from src/plugins/rehype/external-links.ts rename to src/plugins/satteri/external-links.ts index 36b0483b..e84bd16d 100644 --- a/src/plugins/rehype/external-links.ts +++ b/src/plugins/satteri/external-links.ts @@ -1,7 +1,8 @@ -import type { Element, ElementContent, Root } from "hast"; -import { visit } from "unist-util-visit"; +import { defineHastPlugin } from "satteri"; +import type { Element, ElementContent } from "hast"; const EXTERNAL_LINK_ANNOUNCEMENT = "(Opens in a new tab)"; +const FOOTNOTE_BACK_CONTENT = "\u21A9\uFE0E"; const HOST = "developer.sumup.com"; const hasImgChild = (node: Element): boolean => { @@ -71,10 +72,39 @@ const addClassName = ( return [...classNames]; }; -export default function rehypeExternalLinks() { - return (tree: Root) => { - visit(tree, "element", (node) => { - if (node.tagName !== "a") { +const isFootnoteBackref = (node: Element): boolean => { + return "data-footnote-backref" in node.properties; +}; + +const createVisuallyHiddenAnnouncement = (): ElementContent => ({ + type: "element", + tagName: "span", + properties: { + className: ["visually-hidden"], + }, + children: [{ type: "text", value: EXTERNAL_LINK_ANNOUNCEMENT }], +}); + +const createFootnoteBackContent = (): ElementContent => ({ + type: "text", + value: FOOTNOTE_BACK_CONTENT, +}); + +export default defineHastPlugin({ + name: "sumup-external-links", + element: { + filter: ["a"], + visit(node, ctx) { + if (isFootnoteBackref(node)) { + const textIndex = node.children.findIndex( + (child) => child.type === "text", + ); + + if (textIndex >= 0) { + ctx.removeChildAt(node, textIndex); + ctx.insertChildAt(node, textIndex, createFootnoteBackContent()); + } + return; } @@ -84,29 +114,17 @@ export default function rehypeExternalLinks() { return; } - node.properties = { - ...node.properties, - target: "_blank", - rel: mergeRel(node.properties?.rel), - }; + ctx.setProperty(node, "target", "_blank"); + ctx.setProperty(node, "rel", mergeRel(node.properties?.rel)); if (!hasImgChild(node)) { - node.properties.className = addClassName( - node.properties.className, - "external-link", + ctx.setProperty( + node, + "className", + addClassName(node.properties.className, "external-link"), ); - - const announcementNode: ElementContent = { - type: "element", - tagName: "span", - properties: { - className: ["visually-hidden"], - }, - children: [{ type: "text", value: EXTERNAL_LINK_ANNOUNCEMENT }], - }; - - node.children.push(announcementNode); + ctx.appendChild(node, createVisuallyHiddenAnnouncement()); } - }); - }; -} + }, + }, +});