- WordPress 6.9〜7.0 へのアップデートで、LaTeX貼り付け変換プラグインが動かなくなった原因は
window.wp.latexToMathmlの消失で、Script Module 化された@wordpress/latex-to-mathmlを dynamic import するよう修正して解決しました。 - display math を
core/htmlブロックで挿入していたため WordPress から「HTML の中の MathML 文字列」にしか見えておらず、core/mathブロックへ切り替えることで LaTeX 属性の保持と保存形式の整合が取れました。 - WordPress 7.0 での Math block の変化は「LaTeX入力欄の等幅フォント化」と「色・余白・枠線などのスタイル supports 追加」の2点で、保存形式の根本的な変更はありませんでした。
window.wp.*グローバルへの依存はバージョンアップで静かに壊れるリスクがあり、コアの実装に沿って dynamic import で解決する方針に切り替えることが今後の安全策です。
1. 問題
WordPress 6.9 で Math block が追加され、7.0 でスタイル機能が追加されました1。
LaTeX 貼り付けを MathML へ変換するプラグインをメンテしていたところ、この変化への追従で3つの問題に順番に当たりました。
修正内容と背景にある仕様を整理します。
1.1. 問題の全体像
症状はシンプルでした。
WordPress をアップデートしたら、LaTeX の貼り付け変換が動かなくなりました。$$...$$ が未変換のまま paragraph に残り、display math も inline math も通常テキストとして保存されます。
ログを確認すると、tokenizer は正常に動いており、mathCount も期待どおり増えていました。
LaTeX を検出する部分は壊れていなかった2。
変換実行の手前で詰まっていることがわかりました。
2. 問題 1:window.wp.latexToMathml が消えていた
2.1. 何が起きていたか
実機ログで次を確認しました。
coreMathRegisteredInRegistry: true
coreMathRegisteredInStore: true
latexToMathmlType: "undefined"
wpMathRelatedKeys: []Code language: HTTP (http)
core/math ブロックは正常に登録されていました。
しかし window.wp.latexToMathml は存在しませんでした3。
プラグインはこのグローバルを直接呼んでレンダラーを取得していたため、ここで静かに失敗し、LaTeX をそのままフォールバック出力していました。
2.2. なぜ消えたか
WordPress 6.9 で @wordpress/latex-to-mathml パッケージが追加された時点では、wpScript: true 付きで読み込まれており、window.wp.* グローバルに生える前提がある程度成立していました。
ところが PR #73099「MathML block: fix lazy script loading」で、Math block が存在しない場合でも常時読み込まれていた問題が修正され、Script Module 経由の遅延読み込みに切り替わりました4。
現在の packages/latex-to-mathml/package.json には wpScriptModuleExports が定義されており、wpScript は存在しません5。
コアの edit.js も import('@wordpress/latex-to-mathml') で動的 import する実装になっています。
2.3. 修正
レンダラー解決を 2 段構えにしました。
// 修正前
const latexToMathml = window.wp?.latexToMathml ?? window.wp?.latexToMathml?.default;
if (!latexToMathml) {
return fallbackOutput(latex); // ここで静かに失敗していた
}Code language: JavaScript (javascript)
// 修正後
async function resolveRenderer() {
// 1段目:グローバルが存在する環境はそのまま使う
const global = window.wp?.latexToMathml ?? window.wp?.latexToMathml?.default;
if (typeof global === 'function') return global;
// 2段目:Script Module 経由で解決する
const module = await import('@wordpress/latex-to-mathml');
return module.default ?? module.latexToMathML;
}Code language: JavaScript (javascript)
貼り付けイベントで、グローバルが即時取得できる場合は従来どおり同期処理します。
module 解決が必要な場合のみ非同期で待ち、Promise 完了後にブロック差し替えを行います。
実機では display math と inline math の両方が再び MathML へ変換されることを確認しました。
3. 問題 2:display math を core/html で挿入していた
renderer が直ったあと、今度は「変換は成功しているが、ブロックとして core/html に入っている」問題が浮かびました。
3.1. core/html と core/math の違い
core/html は任意の HTML を直接保持するブロックです。block.json では content 属性が source: "raw" で、save() は RawHTML でそのまま出力します。
数式専用の UI も LaTeX 属性も持ちません6。
core/math は数式専用のブロックです。latex 属性に入力 LaTeX を、mathML 属性に <math> 要素の内側 HTML を保持します。
スタイル supports として色・余白・枠線・フォントサイズも有効化されており、再編集時に LaTeX 入力欄が使えます7。
core/html で display math を入れていた状態では、WordPress からは「HTML block の中に MathML らしき文字列がある」にしか見えていませんでした。core/math としての扱いを受けられていませんでした。
3.2. 修正
createDisplayMathBlock() の返す block を切り替えました。
// 修正前
function createDisplayMathBlock(mathMLString) {
return wp.blocks.createBlock('core/html', {
content: mathMLString, // <math display="block">...</math> 全体
});
}Code language: JavaScript (javascript)
// 修正後
function createDisplayMathBlock(latex, mathMLString) {
// core/math の mathML 属性は <math> の内側だけを受け取る
const innerMathML = extractInnerMathML(mathMLString);
return wp.blocks.createBlock('core/math', {
latex: latex,
mathML: innerMathML,
});
}
function extractInnerMathML(mathMLString) {
const parser = new DOMParser();
const doc = parser.parseFromString(mathMLString, 'text/html');
const math = doc.querySelector('math');
return math ? math.innerHTML : '';
}Code language: JavaScript (javascript)
core/math の save() は <div><math display="block" dangerouslySetInnerHTML={{ __html: mathML }} /></div> で保存するため、mathML 属性に渡すべきなのは <math> 全体ではなく inner HTML です。
名前だけ core/math に変えて <math>...</math> 全体を渡すと、コアの保存形式とズレます8。
3.3. 実機確認
修正後の保存コードはこうなりました。
<!-- wp:math {"latex":"O(n \\cdot p + n^2 \\cdot d)"} --> <div class="wp-block-math"><math display="block">...</math></div> <!-- /wp:math -->
core/math として保存され、latex 属性がブロックコメントに残っています。
4. 問題 3:WordPress 6.9〜7.0 の Math block 仕様の変化を把握できていなかった
renderer 問題と block 種別問題が解決した後、そもそも「なぜこうなったか」を整理しました。
4.1. Math block の来歴
Math block は WordPress 6.9 で追加されました。
6.9 Field Guide では、MathML と LaTeX renderer に対応し、ブロック数式だけでなく rich text 内の inline math にも対応すると説明されています。
7.0 での変化は2点です。
1つ目は、LaTeX 入力欄が等幅フォントになったことです(PR #72557)。packages/block-library/src/math/editor.scss に次が追加されました。
.wp-block-math__textarea-control textarea {
font-family: $font-family-mono;
direction: ltr;
}Code language: SCSS (scss)
保存される MathML には影響しません。
編集ポップオーバー内の入力欄の見た目の変更です。
2つ目は、block supports が追加されたことです(PR #73544)。block.json の supports に anchor、border、color、spacing、typography.fontSize が追加され、display math のブロックから色・余白・枠線・フォントサイズをサイドバーで調整できるようになりました9。
4.2. プラグインへの影響
保存形式が全面的に変わったわけではありません。
ただし block supports が追加されたことで、保存される HTML に class や style 属性が増えることがあります。
保存後の HTML の構造を前提にした処理がある場合は再確認が必要です。
旧保存形式(deprecated.js の v1)では <math> に直接 useBlockProps.save() が付いていましたが、現行形式では <div> ラッパーの内側に <math> があります10。
wrapper 構造を前提にした検出や復元の処理は、このズレを踏む可能性があります。
5. まとめ
今回当たった問題は3つありました。
window.wp.latexToMathml が Script Module 化されたことによるレンダラー解決の失敗、display math を core/html で挿入していたことによる block 種別のズレ、そして 6.9〜7.0 の Math block 変化の把握不足です。
保存形式の根本的な変更はありませんでしたが、block supports 追加・wrapper 構造・Script Module 化という3点がプラグインの前提と静かにズレていました。
WordPress 側の仕様変化は公式 PR やコアの実装に直接当たるのが確実です。window.wp.* グローバルに依存するコードは今後も同様のズレを踏む可能性があるため、dynamic import で解決する方針に切り替えておくのが安全です。
- Math block は WordPress 6.9 に同梱された Gutenberg のバージョンで追加されました。6.9 Field Guide では、MathML と LaTeX renderer に対応したブロック数式および rich text 内の inline math に対応すると説明されています。 – WordPress 6.9 Field Guide – Make WordPress Core
- tokenizer とは、入力テキストを
$$...$$・$...$などのデリミタで区切り、数式トークンと通常テキストに分類する処理です。この段階では LaTeX の文字列としての検出だけを行っており、MathML への変換は行いません。 - WordPress では従来、
wp-scriptsビルドでwpScript: trueを持つパッケージがwindow.wp.パッケージ名というグローバル変数として読み込まれる仕組みがありました。@wordpress/blocksがwindow.wp.blocksとして使えるのも同じ仕組みです。 – @wordpress/build – Block Editor Handbook - WordPress の Script Modules API は 6.5 で導入されました。
<script type="module">タグで読み込まれ、import map によってモジュール識別子と URL が紐づけられます。従来のwp_enqueue_scriptとは独立した仕組みで、スクリプトとモジュールの間で依存関係を共有することはできません。 – Script Modules in 6.5 – Make WordPress Core wpScriptModuleExportsは Gutenberg のビルドシステムが参照する内部フィールドで、そのパッケージを WordPress の Script Module としてバンドルする対象に含めることを宣言します。サードパーティ向けの公開 API ではなく、Gutenberg 自身のビルドパイプラインが読み取ります。 – PR #66428 – WordPress/gutenbergcore/htmlでは、unfiltered_html権限を持たないユーザーが CSS や JavaScript を含む HTML を保存しようとすると、保存時にそれらが除去されます。数式目的で<math>要素のみを保持する場合はこの問題に当たりにくいですが、スクリプトを含む HTML を扱う場合は注意が必要です。 – packages/block-library/src/html/modal.js – WordPress/gutenberg- MathML は現在、Chrome・Firefox・Safari・Edge の主要ブラウザすべてでネイティブサポートされています。Chrome は 109 から MathML Core に対応しており、以前のように MathJax などの JavaScript ライブラリでの polyfill が必須という状況ではなくなっています。 – MathML in Web Browsers – Igalia
@wordpress/latex-to-mathmlの内部では、LaTeX→MathML の変換にtemmlライブラリを使っています。temml は KaTeX をフォークして HTML 出力部分を削除し、MathML 出力に特化した軽量ライブラリです。minified で約 170KB と MathJax の半分程度のサイズです。 – Temml – GitHub- PR #73544 のレビュー会話では、
typographysupport にfontSizeだけを含め、letterSpacingやtextTransformなどは除外した理由について、「数式フォントにそれらの調整は馴染まない」という判断が示されています。 – Math: enable styles options – PR #73544 - Gutenberg ブロックの
deprecated.jsは、保存形式が変わった際に旧バージョンのsave()を保持するファイルです。WordPress はブロックを読み込む際に現行のsave()と保存済み HTML を比較し、一致しない場合はdeprecatedにある旧バージョンを順番に試してブロックを復元します。旧形式のまま保存されているコンテンツも、このしくみで引き続き正しく表示されます。 – packages/block-library/src/math/deprecated.js – WordPress/gutenberg