今日やったこと
コンテンツ生成・校閲スクリプトを総点検して2つのバグを直した。加えてGA4の実装方法を調べたのでメモしておく。
スクリプトのバグ修正
1. JSON.parse() が try-catch なしで呼ばれていた
bulk-generate.mjs、article-generate.mjs、verify-and-fix.mjs の3本すべてで、AIレスポンスの JSON.parse() がノーガードだった。モデルが不正なJSONを返した瞬間に処理全体がクラッシュするため、数百件の一括処理中に起きると進捗がすべて消える。
// 修正前
const data = JSON.parse(jsonStr);
// 修正後
let data;
try {
data = JSON.parse(jsonStr);
} catch (e) {
throw new Error(`JSONのパースに失敗しました: ${e.message}`);
}
extractJsonRobust() という自前のJSON抽出関数がすでにあるのに、その直後がノーガードというのは惜しい実装だった。
2. --field=reading が package.json に定義されているのに未実装だった
package.json には以下のコマンドが定義されている。
"verify:reading": "node scripts/verify-and-fix.mjs --field=reading"
ところが verify-and-fix.mjs の中を探しても --field を処理するコードが存在しない。つまりこのコマンドを実行しても普通の全件処理が走るだけで、Gemini API への大量呼び出しが発生してしまう。
本来の意図は「readingフィールドのカタカナをひらがなに直すだけの軽量パス」だと判断。fix-dictionary.mjs に同様のロジックが実装済みだったのでそれを参考に、APIを呼ばずに完結する高速パスを追加した。
const FIELD_ONLY = args.find(a => a.startsWith("--field="))?.split("=")[1] ?? null;
if (FIELD_ONLY === "reading") {
if (!fm.reading) { stats.skipped++; return; }
const hasKatakana = /[\u30A1-\u30F6]/.test(fm.reading);
if (!hasKatakana) { stats.skipped++; return; } // すでにひらがなならスキップ
const fixed = fm.reading.replace(/[\u30A1-\u30F6]/g, ch =>
String.fromCharCode(ch.charCodeAt(0) - 0x60)
);
// ...保存処理
return;
}
コストゼロで動くので npm run verify:reading を気軽に実行できるようになった。
GA4 の実装方法
タグを貼るファイル
src/layouts/Layout.astro の </head> 直前に追加する。全ページがこのレイアウトを継承しているため、ここ1箇所で全ページに適用される。
コード
Astro では <script> タグをそのまま書くとバンドル処理の対象になるため、is:inline 属性が必要。
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX" is:inline></script>
<script is:inline>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
</script>
測定IDを .env で管理する場合
Astro の環境変数はクライアント側で参照するには PUBLIC_ プレフィックスが必要。
# .env
PUBLIC_GA_ID=G-XXXXXXXXXX
---
const gaId = import.meta.env.PUBLIC_GA_ID;
---
<script async src={`https://www.googletagmanager.com/gtag/js?id=${gaId}`} is:inline></script>
<script define:vars={{ gaId }} is:inline>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', gaId);
</script>
define:vars を使うことでフロントマター側の変数をインラインスクリプトに渡せる。
所感
スクリプト群は全体的によく設計されていて、content.config.ts の Zod スキーマで reading をひらがなへ自動変換する .transform() が仕込んであるなど、ビルド側の品質保証がしっかりしている。今回直したのは細かいバグだが、特に JSON.parse() のノーガードは一括生成中に静かにクラッシュするタイプの問題なので早めに直せてよかった。



コメント