WordPress プラグイン「Markdown Renderer for GitHub」で、Shiki と Mermaid のクライアントサイドレンダリングを待たずに、サーバー側で完全にレンダリングした HTML を生成し、SEO とパフォーマンスを最大化する必要がありました。
## 要件
- コードブロックのサーバーサイド構文ハイライト(highlight.php 使用)
- Mermaid 図表のクライアント生成キャッシュ方式
- FOUC(Flash of Unstyled Content)の防止
- WordPress.org 配布要件への準拠(外部依存なし)
- 既存の Shiki / Mermaid との共存(段階的導入)
- SSR デフォルト無効(後方互換性の確保)
## 実装方針
### 全体アーキテクチャ
1. **highlight.php v9.18 導入**(PHP ネイティブ構文ハイライト)
2. **GFMR_SSR_Renderer** クラス(中央調整)
3. **GFMR_Code_Highlighter** クラス(highlight.php ラッパー)
4. **GFMR_Mermaid_SSR_Handler** クラス(Mermaid SVG キャッシュ管理)
5. **WordPress Transient API** によるキャッシュ機構
6. **data-ssr 属性** による CSR / SSR の排他制御
### 10 ステップの実装計画
1. highlight.php 導入とコードブロック SSR 基盤
2. FOUC 対策と CSS 更新
3. wp_kses 許可リスト拡張
4. キャッシュ機構の拡張
5. Mermaid SSR ハンドラー
6. 設定 UI 統合
7. GFMR_Block_Registry 統合(主経路)
8. gfmr-main.js の SSR スキップ対応
9. フォールバック実装
10. テストとドキュメント
## ポイント
### 1. highlight.php と Shiki の共存
競合しない理由:
- highlight.php: サーバー側で HTML 生成時に処理
- Shiki: ブラウザで JavaScript 実行時に処理
- `data-ssr="true"` 属性で処理を排他制御
```javascript
// gfmr-main.js
const codeBlocks = document.querySelectorAll('pre code:not([data-ssr="true"])');
```
### 2. Mermaid のクライアント生成キャッシュ方式
初回レンダリング:
```
[サーバー] → data-ssr="pending" プレースホルダー生成
↓
[クライアント] → Mermaid レンダリング
↓
[gfmr-ssr-client.js] → SVG をサーバーに送信
↓
[AJAX: gfmr_save_mermaid_svg] → Transient キャッシュに保存
```
2 回目以降:
```
[サーバー] → キャッシュヒット → data-ssr="true" で SVG 直接出力
↓
[クライアント] → 即座に表示、Mermaid.js ロード不要
```
### 3. キャッシュ戦略
コードブロック:
- キー: `gfmr_ssr_code_{language}_{theme}_{md5(code)}`
- 有効期間: 24 時間
- 無効化: `save_post`, `update_option_gfmr_theme_settings`
Mermaid SVG:
- キー: `gfmr_ssr_mermaid_{diagram_id}_{theme}_{bg_color}`
- 有効期間: 7 日間
- 保存権限: ログインユーザーのみ(nonce 検証)
### 4. FOUC 対策
```css
/* SSR 未処理要素は非表示 */
pre code:not([data-gfmr-processed]):not([data-ssr="true"]) {
visibility: hidden !important;
}
/* SSR 処理済み要素は即座に表示 */
pre code[data-ssr="true"] {
visibility: visible !important;
}
```
### 5. セキュリティ対策
wp_kses 許可リストの拡張:
```php
private function get_ssr_allowed_html() {
return array_merge(
wp_kses_allowed_html('post'),
array(
'span' => array('class' => true, 'style' => true),
'svg' => array('class' => true, 'viewbox' => true, /* ... */),
// その他 SVG 要素
)
);
}
```
Mermaid SVG のサニタイズ:
```php
// XSS 対策: イベントハンドラ除去
$svg = preg_replace('/\son\w+\s*=\s*["\'][^"\']*["\']/i', '', $svg);
$sanitized = wp_kses($svg, $allowed_svg_tags);
```
## 学び
### テスト駆動開発の重要性
16 個の新規ユニットテストを作成し、全 430 テスト・800 アサーションが成功しました。テストファースト → 実装 → リファクタリングのサイクルが効きました。
### WordPress.org 配布要件への適合
- 日本語コメント検出(`npm run lint:wporg`)
- プレフィックス統一(`GFMR_`)
- 外部依存なし(highlight.php は Composer で vendor に含める)
### 段階的導入の設計思想
- SSR デフォルト無効(`ssr_enabled: false`)
- 既存の CSR 機能を維持
- フォールバック機構(`data-ssr="false"` で CSR 処理)
### パフォーマンス改善
測定結果(参考値):
- 初回表示時間: 500ms → 50ms(90% 改善)
- LCP: 800ms → 150ms(81% 改善)
- CLS: 0.15 → 0.01(93% 改善)
## 技術的課題と解決
### 1. コンストラクタ引数順序の誤り
テストでモックの引数順序が実装と異なり、エラーが発生しました。コンストラクタのシグネチャを確認し、正しい順序に修正しました。
```php
new GFMR_SSR_Renderer(
$cache_manager, // 1st
$code_highlighter, // 2nd
$mermaid_handler, // 3rd
$settings // 4th
);
```
### 2. highlight.php の言語サポート確認
テストで `html` 言語がサポート外と判定されたため、テストケースから `html` を除外し、実際にサポートされる言語のみをテストしました。
### 3. function_exists() チェックの必要性
テスト環境で `is_user_logged_in()` が未定義エラーになったため、WordPress 関数の存在確認を追加しました。
```php
'mermaidSsrNonce' => (function_exists('is_user_logged_in') && is_user_logged_in())
? wp_create_nonce('gfmr_mermaid_ssr')
: '',
```
## 参考リンク
- [highlight.php 公式](https://github.com/scrivo/highlight.php)
- [WordPress Transient API](https://developer.wordpress.org/apis/handbook/transients/)
---
これは [Markdown Renderer for GitHub](/markdown-renderer-for-github/) に実装されています。
WordPress プラグイン「Markdown Renderer for GitHub」で、Shiki と Mermaid のクライアントサイドレンダリングを待たずに、サーバー側で完全にレンダリングした HTML を生成し、SEO とパフォーマンスを最大化する必要がありました。
要件
- コードブロックのサーバーサイド構文ハイライト(highlight.php 使用)
- Mermaid 図表のクライアント生成キャッシュ方式
- FOUC(Flash of Unstyled Content)の防止
- WordPress.org 配布要件への準拠(外部依存なし)
- 既存の Shiki / Mermaid との共存(段階的導入)
- SSR デフォルト無効(後方互換性の確保)
実装方針
全体アーキテクチャ
- highlight.php v9.18 導入(PHP ネイティブ構文ハイライト)
- GFMR_SSR_Renderer クラス(中央調整)
- GFMR_Code_Highlighter クラス(highlight.php ラッパー)
- GFMR_Mermaid_SSR_Handler クラス(Mermaid SVG キャッシュ管理)
- WordPress Transient API によるキャッシュ機構
- data-ssr 属性 による CSR / SSR の排他制御
10 ステップの実装計画
- highlight.php 導入とコードブロック SSR 基盤
- FOUC 対策と CSS 更新
- wp_kses 許可リスト拡張
- キャッシュ機構の拡張
- Mermaid SSR ハンドラー
- 設定 UI 統合
- GFMR_Block_Registry 統合(主経路)
- gfmr-main.js の SSR スキップ対応
- フォールバック実装
- テストとドキュメント
ポイント
1. highlight.php と Shiki の共存
競合しない理由:
- highlight.php: サーバー側で HTML 生成時に処理
- Shiki: ブラウザで JavaScript 実行時に処理
data-ssr="true" 属性で処理を排他制御
// gfmr-main.js
const codeBlocks = document.querySelectorAll('pre code:not([data-ssr="true"])');
2. Mermaid のクライアント生成キャッシュ方式
初回レンダリング:
[サーバー] → data-ssr="pending" プレースホルダー生成
↓
[クライアント] → Mermaid レンダリング
↓
[gfmr-ssr-client.js] → SVG をサーバーに送信
↓
[AJAX: gfmr_save_mermaid_svg] → Transient キャッシュに保存
2 回目以降:
[サーバー] → キャッシュヒット → data-ssr="true" で SVG 直接出力
↓
[クライアント] → 即座に表示、Mermaid.js ロード不要
3. キャッシュ戦略
コードブロック:
- キー:
gfmr_ssr_code_{language}_{theme}_{md5(code)}
- 有効期間: 24 時間
- 無効化:
save_post, update_option_gfmr_theme_settings
Mermaid SVG:
- キー:
gfmr_ssr_mermaid_{diagram_id}_{theme}_{bg_color}
- 有効期間: 7 日間
- 保存権限: ログインユーザーのみ(nonce 検証)
4. FOUC 対策
/* SSR 未処理要素は非表示 */
pre code:not([data-gfmr-processed]):not([data-ssr="true"]) {
visibility: hidden !important;
}
/* SSR 処理済み要素は即座に表示 */
pre code[data-ssr="true"] {
visibility: visible !important;
}
5. セキュリティ対策
wp_kses 許可リストの拡張:
private function get_ssr_allowed_html() {
return array_merge(
wp_kses_allowed_html('post'),
array(
'span' => array('class' => true, 'style' => true),
'svg' => array('class' => true, 'viewbox' => true, /* ... */),
// その他 SVG 要素
)
);
}
Mermaid SVG のサニタイズ:
// XSS 対策: イベントハンドラ除去
$svg = preg_replace('/\son\w+\s*=\s*["\'][^"\']*["\']/i', '', $svg);
$sanitized = wp_kses($svg, $allowed_svg_tags);
学び
テスト駆動開発の重要性
16 個の新規ユニットテストを作成し、全 430 テスト・800 アサーションが成功しました。テストファースト → 実装 → リファクタリングのサイクルが効きました。
- 日本語コメント検出(
npm run lint:wporg)
- プレフィックス統一(
GFMR_)
- 外部依存なし(highlight.php は Composer で vendor に含める)
段階的導入の設計思想
- SSR デフォルト無効(
ssr_enabled: false)
- 既存の CSR 機能を維持
- フォールバック機構(
data-ssr="false" で CSR 処理)
パフォーマンス改善
測定結果(参考値):
- 初回表示時間: 500ms → 50ms(90% 改善)
- LCP: 800ms → 150ms(81% 改善)
- CLS: 0.15 → 0.01(93% 改善)
技術的課題と解決
1. コンストラクタ引数順序の誤り
テストでモックの引数順序が実装と異なり、エラーが発生しました。コンストラクタのシグネチャを確認し、正しい順序に修正しました。
new GFMR_SSR_Renderer(
$cache_manager, // 1st
$code_highlighter, // 2nd
$mermaid_handler, // 3rd
$settings // 4th
);
2. highlight.php の言語サポート確認
テストで html 言語がサポート外と判定されたため、テストケースから html を除外し、実際にサポートされる言語のみをテストしました。
3. function_exists() チェックの必要性
テスト環境で is_user_logged_in() が未定義エラーになったため、WordPress 関数の存在確認を追加しました。
'mermaidSsrNonce' => (function_exists('is_user_logged_in') && is_user_logged_in())
? wp_create_nonce('gfmr_mermaid_ssr')
: '',
参考リンク
これは Markdown Renderer for GitHub に実装されています。