Technical Docs
JSON Formatter の設計
JSONの整形・バリデーション処理の設計思想と、ブラウザ差異を吸収したエラー位置特定アルゴリズムを解説します。
1. 設計思想 — なぜこのツールが必要か
フロントエンド開発では、APIレスポンスやconfigファイルのJSONを頻繁に扱います。 ネットワーク転送のために1行に圧縮されたJSONは、そのままでは人間には読みにくく、 デバッグ時間を無駄に消費します。
さらに重要なのがエラー箇所の特定です。 「このJSONは無効です」と告げるだけのツールは多いですが、 「3行目15列目に問題があります」まで示すことで、デバッグ効率が大幅に向上します。 JSON Formatter はこの2点を解決することを最優先の目標としました。
2. 技術アーキテクチャ
本ツールはプロジェクト全体の3層アーキテクチャに従って実装されています。
- Layer 1 — 純粋関数(
src/lib/tools/json-formatter.ts):formatJson(input, indent)がビジネスロジックのすべてを担います。 副作用なし、外部依存なし。JsonFormatOutput(Success | Error の Union型)を返します。 - Layer 2 — Client Component(
_components/JsonFormatterClient.tsx):useStateでフォームの状態を管理し、入力変更のたびに Layer 1 を呼び出します。 - Layer 3 — Server Component(
app/tools/json-formatter/page.tsx):export const metadataでSEO情報を定義し、ToolLayoutで共通ヘッダーを適用します。
エラーを throw せず Union型で返す設計により、 Layer 2 は try-catch なしでエラー状態を扱えます。 TypeScriptの型絞り込み(type narrowing)が自然に機能するため、 コードの可読性と安全性が向上します。
3. ロジック解説
formatJson() の中核は JSON.parse() とJSON.stringify() の組み合わせです。JSON.parse() が成功すれば JSON.stringify(parsed, null, indent)で整形済み文字列を返します。
構文エラーの場合、JSON.parse() は SyntaxError をthrowします。 このエラーメッセージからエラー位置を抽出するのがextractErrorPosition() の役割です。 ブラウザごとにメッセージ形式が異なるため、2パターンの正規表現で対応しています。
- Chrome / V8:
"... at position N"形式 → 絶対位置 N をpositionToLineColumn()で行・列に変換します。input.slice(0, N).split('\n')で分割した配列の長さが行数、 最終要素の文字数+1が列番号です。 - Firefox / Safari:
"... at line N column N"形式 → 正規表現で直接行・列を抽出できます。
どちらのパターンにも一致しない場合は、行・列なしのエラーメッセージのみを返します。 これにより将来のブラウザエンジンの変更にも安全に対応できます。
4. 品質管理
src/lib/tools/__tests__/json-formatter.test.ts に Vitest による単体テストを実装しています。
- 正常系: オブジェクト・配列・ネストしたJSONの整形、 インデント幅2/4の切り替え確認。
- 異常系: 構文エラー時のエラーメッセージ確認、 行・列番号の正確性検証、空文字列入力のハンドリング。
純粋関数として実装されているため、DOMや外部APIへの依存なしに テスト環境(jsdom)で高速に実行できます。