ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Astro 的 Sätteri 处理器进化史:从 @astrojs/markdown-satteri 到 Astro 默认 Markdown 引擎

2026/9/8 19:13:18 拓冰建站 浏览量
Astro 的 Sätteri 处理器进化史:从 @astrojs/markdown-satteri 到 Astro 默认 Markdown 引擎 Astro 的 Sätteri 处理器进化史从 astrojs/markdown-satteri 到 Astro 默认 Markdown 引擎【免费下载链接】astroThe web framework for content-driven websites. ⭐️ Star to support our work!项目地址: https://gitcode.com/GitHub_Trending/as/astroAstro内容驱动型网站框架的 Markdown/MDX 渲染管线在本仓库中已经完成了一次重大换血基于 Rust 编写、以 WASM/JS 形式接入的 Sätteri 引擎通过astrojs/markdown-satteri包逐步演进0.2.0 → 0.4.0最终在 Astro 7.x 中成为markdown.processor的默认实现。本文以 packages/markdown/satteri/CHANGELOG.md 为时间线骨架结合该包的源码与测试完整梳理其引入动机、配置方式、语法高亮、Frontmatter 修改、MDX 编译等关键能力读完即可理解并上手这套新一代 Markdown 管线。一、它是什么把 Rust 管线 Sätteri 接入 Astro 的markdown.processorAstro 的 Markdown 渲染从设计上就是可插拔的markdown.processor配置项接收一个MarkdownProcessor对象。历史上该位置默认由astrojs/markdown-remark基于 unified/Remark 生态占据而astrojs/markdown-satteri提供了一个新的选择。根据 0.2.0 版本的发布说明该包将 Sätteri——一个用 Rust 编写的 Markdown 管线——封装为 Astro 可用的处理器。发布说明明确表述了两点动机快项目方描述它“比默认的基于 Remark 的处理器快得多”开箱即用原生支持广泛的 Markdown 特性无需再为 GFM、Smart Punctuation 等行为额外安装插件。值得注意的是该说明还写道“我们计划在未来让这成为 Astro 的默认 Markdown 处理器”。在本仓库当前状态下这一规划已经落地Astro 7.2.10 的核心配置 schema 中markdown.processor的默认工厂函数即为satteri()见 packages/astro/src/core/config/schemas/base.ts同时 packages/astro/package.json 已将astrojs/markdown-satteri列为依赖。因此这篇演进记录不只是一份第三方集成的说明更对应着当前 Astro 主版本的实际默认行为。从源码结构看整个包分为三个层次见 packages/markdown/satteri/src文件职责processor.ts定义satteri()工厂函数与选项类型暴露给用户配置入口satteri-processor.ts实现.md文件的createRenderer渲染器与内置 hast/mdast 插件mdx/create-processor.ts实现.mdx文件的createMdxRenderer编译器与布局/字符集包装二、版本里程碑速览0.2.0 → 0.4.0该 CHANGELOG 采用 0.2.0 起逐版记录时间线文件 中的关键变化可整理如下版本类型核心变化0.2.0Minor首次发布引入基于 Sätteri 的 Markdown 处理器当时不支持 Prism 高亮0.2.1Patch修复发布时缺失的 provenance 供应信息0.2.2PatchSätteri 升级至 v0.8.00.3.0Minor新增 Prism 语法高亮支持syntaxHighlight: prism0.3.1PatchSätteri 升级至 v0.9.0支持通过插件以编程方式修改 Frontmatter0.3.2Patch修复标题在页面headings元数据中被列出两次的问题0.3.4Patch修复 MDX 下自定义pre组件未作用于高亮代码块的问题0.3.7PatchSätteri 升级至 v0.10.30.3.8Patch处理器选项类型支持 Sätteri v0.10.3 全部插件条目修正smartPunctuation编辑器提示的默认值文案0.4.0Minorunified()与satteri()均可直接编译.mdx文件版本推进过程中还出现过 0.3.1-beta.x、0.3.0-alpha.0 等预发布号表明该功能在早期曾以 alpha/beta 节奏验证正式版则一路稳定到 0.4.0。每个版本都随附astrojs/internal-helpers的依赖升级说明它深度依赖 Astro 共享的内部工具层如 markdown 默认值、Shiki 高亮封装等见 package.json 依赖声明。三、安装与最小配置安装与启用方式自 0.2.0 起保持一致。在项目中安装npm install astrojs/markdown-satteri然后在astro.config.mjs中把处理器指向satteri()// astro.config.mjs import { satteri } from astrojs/markdown-satteri; export default defineConfig({ markdown: { processor: satteri(), }, });0.2.0 发布说明特别强调了一个当时的限制该处理器最初不支持 Prism 语法高亮需要保持syntaxHighlight: shiki即默认值或干脆关闭语法高亮。这一限制在 0.3.0 中被解除见下文第五节。一个容易被忽略的实现细节是Sätteri 的 Rust/WASM 二进制采用懒加载策略。在 satteri-processor.ts 的loadSatteri()中satteri模块只有在处理器真正运行时才通过动态import()加载避免在未使用 Sätteri 的构建流程中白白引入 WASM 体积。四、satteri()API选项全解与类型约束satteri()工厂函数定义在 processor.ts其选项类型SatteriProcessorOptions包含三部分选项类型作用mdastPluginsMdastPluginList在 mdastMarkdown 语法树阶段追加的插件hastPluginsHastPluginList在 hastHTML 语法树阶段追加的插件featuresSatteriFeatures开关 Markdown 语言特性gfm、smartPunctuation工厂函数返回的处理器对象带有name: satteri标识、已归一化的options字段以及两个渲染入口createRenderer(shared)—— 用于.md文件转发给createSatteriMarkdownProcessorcreateMdxRenderer(shared, mdx)—— 用于.mdx文件0.4.0 起可用转发给createSatteriMdxProcessor。其中options字段在构造时就会把未传入的插件数组折叠为[]、把features折叠为空对象{}因此集成方可以放心地写processor.options.features.gfm false而无需先判空。工厂代码注释明确了这一设计意图见 processor.ts。由于options需要保持引用同一性配置 schema 的校验注释指出用z.custom而不是z.record来避免深拷贝破坏createRenderer闭包对processor.options的读取见 base.ts实践中处理器对象常常被 Astro 配置校验流程直接复用。五、语法高亮从 Shiki-only 到 Prism 支持默认高亮是 Shiki。internal-helpers 的默认值 显示syntaxHighlightDefaults为{ type: shiki, excludeLangs: [math] }Shiki 默认主题为github-dark并默认排除math语言数学代码块由其他机制处理。0.3.0 起支持 Prism。版本说明给出了切换配置// astro.config.mjs import { satteri } from astrojs/markdown-satteri; export default defineConfig({ markdown: { processor: satteri(), syntaxHighlight: prism, }, });这一能力由 createHighlightFn 统一驱动无论 Shiki 还是 Prism最终都归结为一个把代码块转换为prehast 节点的HighlightFn。返回真实 hast 节点而不是原始 HTML 字符串的关键收益是pre仍可被 MDX 管线寻址从而保证用户自定义的components.pre覆盖在高亮块上依然生效——这正是 0.3.4 修复所保障的行为当时修复了 MDX 下自定义pre组件未生效的缺陷。Prism 分支内部复用astrojs/prism/dist/highlighter再通过 Sätteri 的htmlToHast把生成的 HTML 转回 hast。高亮阶段由createHighlightPlugin完成它只针对pre元素、向下寻找code子节点读取语言与 meta并跳过excludeLangs与默认排除列表[math]见 createHighlightPlugin。highlight.test.ts 对上述行为有完整验证默认 Shiki 高亮会产出内联background-color:样式math代码块默认不高亮syntaxHighlight: { type: shiki, excludeLangs: [mermaid] }可逐语言排除prism模式产出pre classlanguage-js>// astro.config.mjs import { satteri } from astrojs/markdown-satteri; export default defineConfig({ markdown: { processor: satteri({ features: { gfm: false, smartPunctuation: false }, }), }, });smartPunctuation还支持细粒度对象配置例如只关闭破折号替换而保留引号satteri({ features: { smartPunctuation: { dashes: false } } })markdown.test.ts 验证了 GFM 与智能标点的默认开启、gfm: false/smartypants: false时自动链接与弯引号消失、以及细粒度dashes: false时--得以保留等行为。由于这些特性现在由处理器而不是顶层配置键承担旧的markdown.gfm、markdown.smartypants、remarkPlugins、rehypePlugins、remarkRehype已被标记为弃用。校验层validate.ts的逻辑是只有当你选择的处理器是unified来自astrojs/markdown-remark时才会执行旧的 remark/rehype 插件在 Sätteri 或其他第三方处理器下使用这些旧键会触发迁移警告提示要么迁移到unified({...})要么把插件直接传给对应处理器。七、以编程方式修改 Frontmatter0.3.10.3.1 引入了一个面向文档作者与主题作者都极具价值的特性插件可以读取并修改页面 Frontmatter。此后Sätteri 插件可以访问并变更ctx.data.astro.frontmatterAstro 会以修改后的结果作为页面最终的 Frontmatter——该行为对.md与.mdx均生效。数据由SatteriAstroData承载定义于 satteri-processor.ts包含四个字段字段类型说明frontmatterRecordstring, any页面 Frontmatter插件可读写headingsMarkdownHeading[]收集到的标题元数据含depth/slug/textlocalImagePathsSetstring渲染中出现的本地图片路径remoteImagePathsSetstring通过image.domains/remotePatterns白名单校验的远程图片路径下面的示例在标题节点处向 Frontmatter 注入一个从title派生的大写关键字形似 inject 测试用例// astro.config.mjs import { satteri } from astrojs/markdown-satteri; const injectKeyword { name: inject-keyword, heading(_node, ctx) { const astro ctx.data.astro; astro.frontmatter.keyword String(astro.frontmatter.title).toUpperCase(); }, }; export default defineConfig({ markdown: { processor: satteri({ mdastPlugins: [injectKeyword] }), }, });MarkdownProcessor接口本身并不具备数据总线Sätteri 通过 TypeScript 模块增强把astro数据挂到satteri的DataMap上见 satteri-processor.ts使插件在ctx.data.astro获得类型安全的访问。渲染收尾时处理器读取的是返回包data.astro而非最初种子化的引用这样即使某个插件整体替换了ctx.data.astro也能被正确采用见 satteri-processor.ts。八、标题 ID 与headings元数据幂等与去重0.3.2标题锚点与目录元数据是内容站点的刚需。Sätteri 内置的heading-idshast 插件createHeadingIdsPlugin负责过滤h1–h6用github-slugger生成 slug若元素已带自定义id例如被其他 hast 插件先行设置则尊重该id而非重新 slug把{ depth, slug, text }压入独立的标题数组并回写到astro.headings。由于标题数组在插件工厂外部声明、slugger 在多次调用间保持状态整个插件是幂等的。0.3.2 修复的正是这一场景的边界当某个集成如 Starlight在其自身的标题 pass 中先分配了 heading ID、随后又要添加锚点链接时标题会重复出现在页面headings元数据里——修复后无论satteriHeadingIdsPlugin()被内部默认执行还是被用户再次显式加入 hastPlugins元数据都只收集一份。测试断言了重复标题的 slug 行为同一标题第二次出现得到some-text-1见 markdown.test.ts以及“用户插件先设置id: custom-id则 DOM 输出与headings元数据都使用该自定义 ID”见同文件 L97-L113。该插件的工厂satteriHeadingIdsPlugin()也被导出方便用户在自定义 hast 插件序列中显式安排其顺序。九、MDX 编译内置unified()与satteri()双双支持.mdx0.4.00.4.0 是该包迄今最重要的能力扩展unified()与satteri()两个处理器都开始自行编译.mdx文件不再依赖外部 MDX 编译链路。也就是说.md与.mdx可以走同一条处理器声明不过要真正给项目启用 MDX 支持仍需安装astrojs/mdx集成其中包含页面扩展名注册与 Vite 插件等内容。MDX 编译路径实现在 mdx/create-processor.ts 的createSatteriMdxProcessor中它调用 Sätteri 的mdxToJs编译出 JSX 代码随后在 JS 层做若干关键加工图片组件化把img转换为astro-image并在有图时注入import { Image } from astro:assets与具体资源导入同时导出__usesAstroImage标志这样用户export const components { img: ... }的覆盖仍被尊重Frontmatter 输出校验修改后的 Frontmatter 必须是合法对象否则抛出明确错误并生成export const frontmatter ...与export function getHeadings()Layout 包装若 Frontmatter 含layout则自动把默认导出改写为用astro/jsx-runtime渲染 layout并传入file、url、frontmatter、headings、children字符集兜底无 layout 的默认 MDX 页面自动在顶层包一层带charsetutf-8的 Fragment相关判断逻辑见mdx/charset.ts静态优化透传mdx.optimize含ignoreElementNames将可静态化内容编译为set:html形式的Fragment。在特性合并上MDX 处理器遵循“.mdx更具体的gfm/smartPunctuation配置优先、仅作用于布尔值时”的规则见 create-processor.ts。MDX 集成的侧翼文件如 packages/integrations/mdx/src/index.ts说明markdown.processor默认覆盖到.mdx文件extendMarkdownConfig: false时会回退到一份干净的satteri()弃用的recmaPlugins等选项只在处理器为unified时生效否则会被忽略并告警。十、mdast/hast 插件机制与内置插件导出Sätteri 处理器的强大之处在于保留了完整的 AST 插件扩展点。satteri()接受mdastPlugins与hastPlugins两套插件列表条目支持“单插件”或“[插件, 选项] 二元组”甚至可以传入条件工厂工厂接收ctx后按ctx.sourceFormat markdown等条件返回插件或null见 条件工厂测试。插件在管线中的执行顺序由渲染器编排见 createSatteriMarkdownProcessor用户 mdast 插件 → collect-images最后收集保证用户插件改写过的图片 URL 被计入 hast 阶段highlight若有高亮→ 用户 hast 插件 → image-marker → heading-ids把图片收集放在最后是有意为之——注释明确写道“最后收集以捕获用户插件对图片 URL 的重写”对应测试 markdown.test.ts L115-L129./unresolved.png被用户插件改写为./resolved.png后元数据中记录的是改写后的路径。包的公共 API见 index.ts将下列助手一并导出供集成方在自定义管线中复用导出说明satteri()处理器工厂isSatteriProcessor()按name satteri判别处理器类型satteriHeadingIdsPlugin()生成标题 ID 并收集 headingssatteriCollectImagesPlugin()收集本地/远程图片路径satteriImageMarkerPlugin()给img打__ASTRO_IMAGE_标记供后续图片处理satteriHighlightPlugin()语法高亮Shiki 与 Prism 共用旧名satteriShikiPlugin已标记弃用satteriCreateHighlightFn()构造高亮函数satteriCollectHastText()递归收集 hast 文本可解析 Frontmatter 表达式用于标题文本计算createSatteriMarkdownProcessor()底层.md渲染器工厂供测试/高级场景十一、迁移与兼容性提示如果你正从旧管线迁移validate.ts 的告警与源码注释给出了明确的边界旧的remarkPlugins/rehypePlugins/remarkRehype不再在 Sätteri 上执行。需要把它们搬到对应处理器Remark 系插件可改用unified({ remarkPlugins })来自astrojs/markdown-remark并整体作为markdown.processor或直接传入satteri({ mdastPlugins, hastPlugins })。第三方处理器无法执行 remark/rehype 插件若markdown.processor被设置为其他第三方实现使用了旧插件键会收到明确告警因为这类处理器根本没有运行 remark/rehype 的能力。astrojs/markdown-remark不再是默认依赖校验代码中的报错信息提示Sätteri 已是默认 Markdown 处理器使用被迁移的 unified 处理器时需自行npm install astrojs/markdown-remark。Frontmatter 修改有硬校验MDX 管线要求插件修改后的ctx.data.astro.frontmatter必须是合法对象若注入null/undefined将直接抛出带有指引的错误见 create-processor.ts。结语从 0.2.0 的“又一个可选处理器”到 0.4.0 的“.md/.mdx统一编译”再到本仓库中 Astro 7.2.10 将markdown.processor默认指向satteri()astrojs/markdown-satteri的 CHANGELOG 记录了一次完整的技术路线落地。对读者而言最直接的行动建议是新项目直接使用默认配置即可获得 Sätteri 管线需要 Prism 时切换syntaxHighlight: prism需要深度定制时通过satteri({ mdastPlugins, hastPlugins, features })在 AST 层面扩展或利用ctx.data.astro.frontmatter在渲染期动态改写 Frontmatter。以上所有行为均可在 源码 与 单元测试 中一一验证。【免费下载链接】astroThe web framework for content-driven websites. ⭐️ Star to support our work!项目地址: https://gitcode.com/GitHub_Trending/as/astro创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考