|
| 1 | +import katex from 'katex'; |
| 2 | + |
| 3 | +/** |
| 4 | + * 把 Sätteri 解析出的 math / inlineMath 节点在构建时渲染成 KaTeX 的 HTML。 |
| 5 | + * |
| 6 | + * Sätteri 的 math 特性只负责「解析」,把 $...$ 和 $$...$$ 变成 AST 节点, |
| 7 | + * 渲染需要自己接。这里返回 { raw, mdxExpressions: false }:Sätteri 文档指明 |
| 8 | + * 注入 KaTeX 这类含花括号的生成 HTML 时必须关掉 mdxExpressions, |
| 9 | + * 否则在 .mdx 文件里 {} 会被当成 JS 表达式求值。 |
| 10 | + * |
| 11 | + * 公式在构建时就变成静态 HTML + MathML,浏览器端零 JS。 |
| 12 | + * |
| 13 | + * @param {import('katex').KatexOptions} [userOptions] |
| 14 | + */ |
| 15 | +export function satteriKatex(userOptions = {}) { |
| 16 | + // 传工厂而非定义对象,这样每篇文档拿到独立的插件实例, |
| 17 | + // 文档内用 \gdef 定义的宏不会泄漏到下一篇。 |
| 18 | + return () => { |
| 19 | + /** @param {{ value?: string }} node */ |
| 20 | + const render = (node, ctx, displayMode) => { |
| 21 | + const source = node.value ?? ''; |
| 22 | + |
| 23 | + try { |
| 24 | + const html = katex.renderToString(source, { |
| 25 | + ...userOptions, |
| 26 | + displayMode, |
| 27 | + // 出错时抛出,由下面接住并降级,而不是把红色报错直接印在页面上 |
| 28 | + throwOnError: true, |
| 29 | + // 同时输出 HTML 和 MathML,屏幕阅读器能正确朗读公式 |
| 30 | + output: 'htmlAndMathml', |
| 31 | + // 迁移过来的笔记里难免有非标准写法,别为此刷屏 |
| 32 | + strict: 'ignore', |
| 33 | + }); |
| 34 | + return { raw: html, mdxExpressions: false }; |
| 35 | + } catch (error) { |
| 36 | + // 不让一条写错的公式炸掉整个构建,降级成等宽原文。 |
| 37 | + // 注意:实测 Astro 7.1.3 不会把 Sätteri 的 diagnostics 打到构建日志里, |
| 38 | + // 所以下面这条 report 目前只是留给未来(或自行调用 Sätteri 时)用的; |
| 39 | + // 真正能看见的信号是页面上那段带底色的 .math-error 原文。 |
| 40 | + ctx.report({ |
| 41 | + message: `KaTeX 渲染失败:${error instanceof Error ? error.message : String(error)}`, |
| 42 | + node, |
| 43 | + severity: 'warning', |
| 44 | + }); |
| 45 | + const escaped = source |
| 46 | + .replace(/&/g, '&') |
| 47 | + .replace(/</g, '<') |
| 48 | + .replace(/>/g, '>'); |
| 49 | + return { |
| 50 | + raw: `<code class="math-error" title="KaTeX 渲染失败">${escaped}</code>`, |
| 51 | + mdxExpressions: false, |
| 52 | + }; |
| 53 | + } |
| 54 | + }; |
| 55 | + |
| 56 | + return { |
| 57 | + name: 'satteri-katex', |
| 58 | + math: (node, ctx) => render(node, ctx, true), |
| 59 | + inlineMath: (node, ctx) => render(node, ctx, false), |
| 60 | + }; |
| 61 | + }; |
| 62 | +} |
0 commit comments