From 26e873b9f63eb78a4097fd941490c49d12fe70df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=B6=E4=B9=8B=E5=88=83?= <1+timecrackbot@noreply.192.168.1.87> Date: Thu, 17 Sep 2026 17:38:37 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20Markdown=20=E7=AE=A1=E7=BA=BF?= =?UTF-8?q?=E8=BF=81=E7=A7=BB=E5=88=B0=20S=C3=A4tteri=EF=BC=88Phase=201=20?= =?UTF-8?q?+=20Phase=202=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 把 markdown.processor 从 unified() 换成 Astro 7 的默认管线 satteri(), 并把原来 12 个 remark/rehype 插件逐一移植到 Sätteri 的 MDAST/HAST 插件模型。 内置能力直接替代(零代码) - remark-math → features.math - remark-directive → 未启用(见下) - rehype-slug → satteriHeadingIdsPlugin() 新增 src/plugins/satteri/(6 个插件,约 200 行) - reading-time.mjs after 钩子写 ctx.data.astro.frontmatter.minutes/words - excerpt.mjs 取第一段写 frontmatter.excerpt - shift-headings.mjs 工厂按 fileURL 判断,只对 posts 下的文章生效 - sectionize.mjs remark-sectionize 的等价实现(递归分组 + data.hName) - autolink-headings.mjs 标题锚点,输出与 rehype-autolink-headings 逐字一致 - image-lightbox.mjs 上一版 rehype 图片灯箱的 HAST 版 删除(已迁移或本就无效) - remark-reading-time / remark-excerpt / remark-shift-headings / remark-directive-rehype / rehype-component-admonition / rehype-image-lightbox —— 共 6 个旧插件文件 - 未启用 features.directive:内容里的 `::: tip`(冒号后有空格)和 `> [!tip] Tip` 在迁移前后都渲染成普通段落/引用,属于死语法; 保持现状以免升级顺手把它激活。 行为对齐 - smartPunctuation: { dashes: false } —— Astro 的 unified 管线会把直引号 排成弯引号但不转换 ---,Sätteri 默认会转,显式关掉 dashes 才能逐字一致。 验证(与 unified() 构建逐页对比,41 页) - 正文 HTML 完全一致 35 页;把 HTML 实体和表格对齐写法归一化后, 只剩 3 页有差异:1 页是 KaTeX 尚未迁移(见下),2 页是中文/emoji 旁的 引号方向(Sätteri 的结果更正确)。 - meta description 仍由 excerpt 正确生成;阅读时长与基线一致。 - astro check:1 error / 0 warnings,与迁移前完全相同(原有 footer.astro 报错)。 - pnpm build 41 页通过;dev server 正常。 尚未迁移 - KaTeX:Sätteri 的 math 只负责解析,不渲染。数学公式目前落成 ,是可见的回归,Phase 3 处理。 - package.json 里 remark-*/rehype-* 依赖暂未清理,等 Phase 3 确定后再收。 --- astro.config.mjs | 93 ++++++--------------- package.json | 2 + pnpm-lock.yaml | 6 ++ src/components/ImageLightbox.astro | 2 +- src/plugins/rehype-component-admonition.mjs | 34 -------- src/plugins/rehype-image-lightbox.mjs | 45 ---------- src/plugins/remark-directive-rehype.js | 33 -------- src/plugins/remark-excerpt.js | 18 ---- src/plugins/remark-reading-time.mjs | 16 ---- src/plugins/remark-shift-headings.mjs | 21 ----- src/plugins/satteri/autolink-headings.mjs | 50 +++++++++++ src/plugins/satteri/excerpt.mjs | 19 +++++ src/plugins/satteri/image-lightbox.mjs | 34 ++++++++ src/plugins/satteri/reading-time.mjs | 20 +++++ src/plugins/satteri/sectionize.mjs | 65 ++++++++++++++ src/plugins/satteri/shift-headings.mjs | 27 ++++++ 16 files changed, 248 insertions(+), 237 deletions(-) delete mode 100644 src/plugins/rehype-component-admonition.mjs delete mode 100644 src/plugins/rehype-image-lightbox.mjs delete mode 100644 src/plugins/remark-directive-rehype.js delete mode 100644 src/plugins/remark-excerpt.js delete mode 100644 src/plugins/remark-reading-time.mjs delete mode 100644 src/plugins/remark-shift-headings.mjs create mode 100644 src/plugins/satteri/autolink-headings.mjs create mode 100644 src/plugins/satteri/excerpt.mjs create mode 100644 src/plugins/satteri/image-lightbox.mjs create mode 100644 src/plugins/satteri/reading-time.mjs create mode 100644 src/plugins/satteri/sectionize.mjs create mode 100644 src/plugins/satteri/shift-headings.mjs diff --git a/astro.config.mjs b/astro.config.mjs index ba28ceb..4155157 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -5,30 +5,17 @@ import mdx from "@astrojs/mdx" import { pluginCollapsibleSections } from "@expressive-code/plugin-collapsible-sections" import { pluginLineNumbers } from "@expressive-code/plugin-line-numbers" import expressiveCode from "astro-expressive-code" -import rehypeAutolinkHeadings from "rehype-autolink-headings" -import rehypeComponents from "rehype-components" -import rehypeKatex from "rehype-katex" -import rehypeSlug from "rehype-slug" -import remarkDirective from "remark-directive" -import remarkGithubAdmonitionsToDirectives from "remark-github-admonitions-to-directives" -import remarkMath from "remark-math" -import remarkSectionize from "remark-sectionize" -import { remarkShiftHeadings } from "./src/plugins/remark-shift-headings.mjs" -import { AdmonitionComponent } from "./src/plugins/rehype-component-admonition.mjs" -import { parseDirectiveNode } from "./src/plugins/remark-directive-rehype.js" -import { remarkExcerpt } from "./src/plugins/remark-excerpt.js" -import { remarkReadingTime } from "./src/plugins/remark-reading-time.mjs" import { pluginLanguageBadge } from "./src/plugins/expressive-code/language-badge" import { pluginCustomCopyButton } from "./src/plugins/expressive-code/custom-copy-button" -import { unified } from "@astrojs/markdown-remark" -import { rehypeImageLightbox } from "./src/plugins/rehype-image-lightbox.mjs" +import { satteri, satteriHeadingIdsPlugin } from "@astrojs/markdown-satteri" +import { excerpt } from "./src/plugins/satteri/excerpt.mjs" +import { readingTime } from "./src/plugins/satteri/reading-time.mjs" +import { sectionize } from "./src/plugins/satteri/sectionize.mjs" +import { shiftHeadings } from "./src/plugins/satteri/shift-headings.mjs" +import { autolinkHeadings } from "./src/plugins/satteri/autolink-headings.mjs" +import { imageLightbox } from "./src/plugins/satteri/image-lightbox.mjs" import { site } from "./src/data/site" -const createAdmonitionComponent = (type) => (properties = {}, children = []) => { - const normalizedChildren = Array.isArray(children) ? children : [] - return AdmonitionComponent(properties, normalizedChildren, type) -} - export default defineConfig({ srcDir: "./src", output: "static", @@ -88,56 +75,24 @@ export default defineConfig({ ], compressHTML: true, markdown: { - processor: unified({ - remarkPlugins: [ - remarkMath, - remarkReadingTime, - remarkExcerpt, - remarkShiftHeadings, - remarkGithubAdmonitionsToDirectives, - remarkDirective, - remarkSectionize, - parseDirectiveNode, + // Astro 7 的 Sätteri 管线。以下插件是 unified 时期 remark/rehype 插件的对应移植, + // 见 src/plugins/satteri/。 + // + // 注意:math 只负责「解析」,渲染成 KaTeX 需要额外的 HAST 插件 —— 尚未迁移。 + processor: satteri({ + // dashes: false 是为了和迁移前的输出逐字一致:Astro 的 unified 管线 + // 会把直引号排成弯引号,但不转换 --- (Sätteri 默认会转成破折号)。 + features: { math: true, smartPunctuation: { dashes: false } }, + mdastPlugins: [ + readingTime, + excerpt, + shiftHeadings(), + sectionize(), ], - rehypePlugins: [ - rehypeKatex, - rehypeSlug, - [ - rehypeComponents, - { - components: { - note: createAdmonitionComponent("note"), - tip: createAdmonitionComponent("tip"), - important: createAdmonitionComponent("important"), - caution: createAdmonitionComponent("caution"), - warning: createAdmonitionComponent("warning"), - }, - }, - ], - [ - rehypeAutolinkHeadings, - { - behavior: "append", - properties: { - className: ["anchor"], - }, - content: { - type: "element", - tagName: "span", - properties: { - className: ["anchor-icon"], - "data-pagefind-ignore": true, - }, - children: [ - { - type: "text", - value: "#", - }, - ], - }, - }, - ], - rehypeImageLightbox, + hastPlugins: [ + satteriHeadingIdsPlugin(), + autolinkHeadings, + imageLightbox, ], }), }, diff --git a/package.json b/package.json index 3d20272..ffd0597 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "@astrojs/markdown-remark": "^7.3.1", + "@astrojs/markdown-satteri": "^0.4.1", "@astrojs/mdx": "^8.0.1", "@astrojs/react": "^6.0.6", "@astrojs/rss": "^4.0.19", @@ -86,6 +87,7 @@ "remark-math": "^6.0.0", "remark-sectionize": "^2.1.0", "sanitize-html": "^2.17.0", + "satteri": "^0.10.5", "sonner": "^1.7.4", "tailwind-merge": "^2.5.5", "tailwindcss-animate": "^1.0.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 28d1e7c..ffcd4fe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@astrojs/markdown-remark': specifier: ^7.3.1 version: 7.3.1 + '@astrojs/markdown-satteri': + specifier: ^0.4.1 + version: 0.4.1 '@astrojs/mdx': specifier: ^8.0.1 version: 8.0.1(@astrojs/markdown-remark@7.3.1)(@astrojs/markdown-satteri@0.4.1)(astro@7.3.3(@astrojs/markdown-remark@7.3.1)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.3)(@types/node@22.19.2)(jiti@2.7.0)(terser@5.51.2)(yaml@2.9.1)) @@ -224,6 +227,9 @@ importers: sanitize-html: specifier: ^2.17.0 version: 2.17.7 + satteri: + specifier: ^0.10.5 + version: 0.10.5 sonner: specifier: ^1.7.4 version: 1.7.4(react-dom@19.2.0(react@19.2.0))(react@19.2.0) diff --git a/src/components/ImageLightbox.astro b/src/components/ImageLightbox.astro index ef6a162..68c3112 100644 --- a/src/components/ImageLightbox.astro +++ b/src/components/ImageLightbox.astro @@ -4,7 +4,7 @@ * * 任何带 `data-lightbox-src` 的元素点击即可放大: * - 相册页的 `