レガシーコード解体新書:Cursorの内部アーキテクチャを完全掌握し、jQuery/Vanilla JSをReact/TypeScriptへゼロバグで完全遷移させるCI/CD連動型自動マイグレーション戦略
歴史を重ねたプロダクトの技術負債——何千行にも及ぶSpaghetti jQuery、密結合されたグローバル状態、暗黙的なDOM破壊、そして型定義が存在しないVanilla JS。これらをモダンなReact 19 + TypeScript + TailWind CSS構成へ移行するタスクは、従来の「人間による手動書き換え」ではコスト的にも品質的にも破綻します。単にChatGPTへコードを貼り付けるだけの対症療法も、コンテクストの断片化とハルシネーション(嘘のコード生成)により、サイレントバグを量産する結果に終わります。
本稿では、AI特化型エディタCursorの内部メカニズム(ベクトル検索、Merkle Treeインデックス、AST解釈力)を限界まで引き出し、大規模レガシーコードベースを完全に決定論的にモダンコードへ変換するためのエンタープライズ・マイグレーション・アーキテクチャを解説します。
—
1. Cursor Context Engineの低レイヤ解剖とマイグレーション最適化
Cursorが既存の巨大なレガシーリポジトリを正確に理解できる理由は、バックグラウンドで高速に動作するコンテクストエンジンにあります。まずはこの内部構造を最適化し、マイグレーションにおけるコンテクスト汚染を防止する必要があります。
Cursorのコンテクストインデックス構造
Cursorは開かれているワークスペースの全ファイルを走査し、以下の2段階でインデックスを構築します。
1. Merkle Treeベースのファイル変更検出: 各ファイルのハッシュツリーを構築し、差分のみを増分更新。
2. AST (Abstract Syntax Tree) 解析 + Vector Embedding: ファイルをセマンティックなブロック(関数、クラス、モジュール)に分割し、ローカルのVector Store(ChromaDBベースのカスタム実装)へ埋め込み。
レガシープロジェクトで問題となるのは、「ビルド生成物」「巨大なサードパーティライブラリ(jquery.min.jsなど)」「過剰なキャッシュ」がベクトル空間を汚染し、AIの参照精度を極端に低下させる点です。
最強のマイグレーション用 `.cursorignore` の構築
リポジトリルートに `.cursorignore` を配置し、AIのベクトル空間からノイズを徹底排除します。
`.cursorignore` – AIベクトルインデックスのノイズ排除設定
——————————————————————————
1. ビルド成果物および依存関係(AIのコンテクスト空間を圧迫する最大の原因)
——————————————————————————
node_modules/
dist/
build/
.next/
out/
——————————————————————————
2. レガシーベンダーライブラリ(AIが自作コードと誤認するのを防止)
——————————————————————————
public/js/vendor/jquery.js
public/js/vendor/bootstrap.js
/vendor/.min.js
——————————————————————————
3. ログ・キャッシュ・一時ファイル
——————————————————————————
.log
.cache/
.DS_Store
coverage/
——————————————————————————
4. マイグレーション対象外のバイナリ・アセット
——————————————————————————
.png
.jpg
.svg
.woff2
.pdf
—
2. 決定論的マイグレーションを実現する `.cursorrules` 設計
Cursorの挙動を完全に支配するためには、プロジェクトルートの `.cursorrules` に厳格な変換規約(Migration Protocol)を定義する必要があります。AIに自由度を与えすぎず、特定のデザインパターンへ強制的に収束させます。
{
“rules”: [
“あなたは超一流のフロントエンドシステムアーキテクトです。”,
“jQueryおよびVanilla JSのレガシーコードを、React 19, TypeScript (Strict Mode), Zustand, Tailwind CSSへ移行するタスクを担います。”,
“【変換の絶対原則】”,
“1. DOM直接操作 ($(‘#id’), document.querySelector) は完全に禁止。すべて React の State または Ref に置換すること。”,
“2. グローバル変数 (window.X) や暗黙的なサイドエフェクトは、Zustand スタイルまたは React Context の明示的状態に集約すること。”,
“3. JavaScript の非同期処理 ($.ajax, XMLHttpRequest, Promise チェーン) は、原則として `@tanstack/react-query` の custom hook 化すること。”,
“4. `any` 型の使用は厳禁。不明なデータ構造は、まず JSDoc または動的実行ログから推論し、厳格な interface / type を定義すること。”,
“5. 既存コードのビジネスロジック・計算ロジック(エッジケース含む)は 100% 透過的に維持すること。独自の最適化や仕様変更を混ぜてはならない。”,
“【出力コンポーネント構造】”,
“- UIコンポーネント: Presentation Component (純粋表示)”,
“- ロジック: Custom Hooks (`useXxx.ts`) に完全分離”,
“- 型定義: `types/xxx.ts` に明示的に切り出し”
]
}
—
3. 三段階「AST-to-State」プロンプト・パイプライン
jQueryなどの命令型コードをReactの宣言型コードへ移行する際、一度のプロンプトで完結させようとすると高確率で状態遷移の抜け漏れが発生します。以下の「三段階パイプライン」をCursor Composer (`Cmd+I` / `Ctrl+I`) 上で実行します。
[ Phase 1: 依存・状態抽出 ] ──> [ Phase 2: 型・インターフェース定義 ] ──> [ Phase 3: 宣言型Reactコンポーネント化 ]
【Phase 1】ロジック・状態の抽出プロンプト
@legacy/user_dashboard.js を解析してください。
DOM操作やイベントハンドラを取り除き、このコードが管理している「状態(Data State)」と「副作用(Side Effects)」、「ビジネスロジック」を以下の形式でMarkdown出力してください。コード生成は不要です。
1. State定義(初期値、型の推論含む)
2. Action定義(どのようなトリガーでStateがどう変更されるか)
3. External API Call(エンドポイント、リクエスト/レスポンス構造)
4. Edge Cases(DOMの有効/無効化、エラーハンドリング)
【Phase 2】型定義とZustandストア設計プロンプト
Phase 1の解析結果に基づき、以下のファイルを生成してください。
1. `src/types/userDashboard.ts`: 全てのデータ構造のTypeScript型定義(Strict Mode準拠)
2. `src/stores/useUserDashboardStore.ts`: 状態変更ロジックをカプセル化したZustandストア
【Phase 3】コンポーネント実装プロンプト
@legacy/user_dashboard.js と @src/stores/useUserDashboardStore.ts を参照し、
モダンなReactコンポーネント `src/components/UserDashboard.tsx` を作成してください。
- Tailwind CSSを用いてレスポンシブなUIを組むこと。
- 元のコードのデータ属性(`data-test-id` 等)はテスト互換性のために保持すること。
- `$()` によるアニメーション処理は `framer-motion` へ置換すること。
—
4. Docker + CLIによる「完全自動セルフヒーリング」マイグレーションパイプライン
大規模プロジェクトで何百ものファイルを処理する場合、GUIでの手動操作は非効率です。ここでは、Docker環境内で Headless AI API (Anthropic/OpenAI) と TypeScript Compiler (`tsc`) を連動させ、型エラーが0になるまで自動修復するスクリプトを構築します。
1. マイグレーション実行環境(`Dockerfile`)
マイグレーション専用の分離環境(ローカル環境を汚汚染せず、決定論的に実行)
FROM node:22-slim
コンパイラおよび必要なCLIツールのインストール
RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
jq \
git \
&& rm -rf /var/lib/apt/lists/
WORKDIR /workspace
TypeScriptおよび関連ツールのグローバルインストール
RUN npm install -g typescript ts-node @babel/parser @babel/traverse
パイプライン実行用スクリプトの配置
COPY migration-engine.py /usr/local/bin/migration-engine
RUN chmod +x /usr/local/bin/migration-engine
CMD [“python3”, “/usr/local/bin/migration-engine”]
2. セルフヒーリング・マイグレーションスクリプト(`migration-engine.py`)
AIが変換したTypeScriptコードを `tsc` に掛け、エラーが出力された場合はそのエラーログを即座にAIへフィードバックして再生成させるループ構造です。
!/usr/bin/env python3
“””
セルフヒーリング・自動コード変換エンジン
レガシーJSをTSXへ変換し、tsc型チェックを通過するまでAIに自動修正させる。
“””
import os
import sys
import subprocess
import json
import urllib.request
環境変数設定
ANTHROPIC_API_KEY = os.getenv(“ANTHROPIC_API_KEY”)
TARGET_FILE = os.getenv(“TARGET_FILE”) # 例: legacy/cart.js
OUTPUT_FILE = os.getenv(“OUTPUT_FILE”) # 例: src/components/Cart.tsx
def call_ai_architect(prompt: str, current_code: str = “”) -> str:
“””Claude APIを呼び出し、コード変換・修正を実行”””
payload = {
“model”: “claude-3-5-sonnet-20241022”,
“max_tokens”: 4000,
“messages”: [
{
“role”: “user”,
“content”: f”{prompt}\n\n対象コード:\n\n{current_code}\n”
}
]
}
req = urllib.request.Request(
“https://api.anthropic.com/v1/messages”,
headers={
“Content-Type”: “application/json”,
“x-api-key”: ANTHROPIC_API_KEY,
“anthropic-version”: “2023-06-01”
},
data=json.dumps(payload).encode(“utf-8”)
)
with urllib.request.urlopen(req) as response:
res = json.loads(response.read().decode(“utf-8”))
# レスポンスからコードブロック部分のみ抽出
content = res[“content”][0][“text”]
if “” in content:
return content.split(“”)[1].split(“”)[0].strip()
elif “” in content:
return content.split(“”)[1].split(“”)[0].strip()
return content
def run_typecheck(file_path: str) -> tuple[bool, str]:
“””tsc を実行して型エラーを取得”””
cmd = f”npx tsc –noEmit –strict {file_path}”
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
if result.returncode == 0:
return True, “”
return False, result.stdout + result.stderr
def main():
print(f”[] 変換開始: {TARGET_FILE} -> {OUTPUT_FILE}”)
with open(TARGET_FILE, “r”) as f:
legacy_code = f.read()
# 初回変換
initial_prompt = (
“以下のレガシーJavaScript/jQueryコードを、完全なTypeScript Reactコンポーネント(TSX)へ変換してください。”
“型定義を厳格に行い、DOM直接操作を排してください。”
)
generated_code = call_ai_architect(initial_prompt, legacy_code)
# ファイル書き出し
os.makedirs(os.path.dirname(OUTPUT_FILE), exist_ok=True)
with open(OUTPUT_FILE, “w”) as f:
f.write(generated_code)
# セルフヒーリング・ループ (最大5回試行)
max_retries = 5
for attempt in range(1, max_retries + 1):
print(f”[] 型チェック試行 [{attempt}/{max_retries}]…”)
success, errors = run_typecheck(OUTPUT_FILE)
if success:
print(f”[✔] 成功: 型チェックを完全通過しました ({OUTPUT_FILE})”)
sys.exit(0)
print(f”[!] 型エラー検出:\n{errors[:500]}…”) # ログ出力制限
# エラーフィードバックプロンプト
fix_prompt = (
f”以下のTypeScriptコードを compile しようとしましたが、型エラーが発生しました。\n”
f”エラー内容を解析し、修正したコード全体を再度出力してください。\n\n”
f”【エラーログ】:\n{errors}”
)
generated_code = call_ai_architect(fix_prompt, generated_code)
with open(OUTPUT_FILE, “w”) as f:
f.write(generated_code)
print(f”[✘] エラー: {max_retries}回の試行内に型エラーを解消できませんでした。”)
sys.exit(1)
if __name__ == “__main__”:
main()
—
5. CI/CDパイプラインとの高度な連携(GitHub Actions)
手動書き換えやローカルスクリプトのみならず、「レガシーコードへの変更が含まれるPRが作成された際、自動的にモダンコードへのリファクタPRをAIが生成する」CI/CDパイプラインを構築します。
`.github/workflows/ai-migration-guardian.yml`
name: “AI Modernization Guardian”
on:
pull_request:
paths:
- ‘public/js/legacy/’ # レガシーディレクトリ配下の変更を監視
jobs:
auto-modernize:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: リポジトリのチェックアウト
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Node.js 環境構築
uses: actions/setup-node@v4
with:
node-version: 22
cache: ‘npm’
- name: 依存関係のインストール
run: npm ci
- name: Dockerイメージのビルド
run: |
docker build -t migration-engine .
- name: 差分ファイルの特定と自動変換の実行
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
# 変更のあったレガシーJSファイルを取得
CHANGED_FILES=$(git diff –name-only ${{ github.event.before }} ${{ github.sha }} | grep ‘public/js/legacy/.\.js$’ || true)
for FILE in $CHANGED_FILES; do
# 変換先パスの決定 (例: src/modernized/xxx.tsx)
BASENAME=$(basename $FILE .js)
TARGET_TSX=”src/modernized/${BASENAME}.tsx”
echo “Processing: $FILE -> $TARGET_TSX”
docker run –rm \
-e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
-e TARGET_FILE=$FILE \
-e OUTPUT_FILE=$TARGET_TSX \
-v $(pwd):/workspace \
migration-engine
done
- name: 変換されたコードの自動コミット&PR作成
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: “refactor(ai): auto-modernize legacy JS to React TSX”
title: “🤖 [AI Auto-Migration] Legacy JavaScript Refactoring”
body: |
AI マイグレーション自動生成レポート
レガシーJSファイルの変更を検知し、自動的に React + TypeScript (Strict) 構成へ変換しました。
検証ステータス
- [x] TypeScript Strict Compile Check: PASSED
- [x] AST/State-driven Separation: APPLIED
レビューの上、モダンコードベースへのマージを検討してください。
branch: “feature/ai-modernized-${{ github.run_id }}”
—
6. Cursor内部アーキテクチャのローカルチューニングとメモリハック
大規模プロジェクト(100万行超のコードベース)でCursorをフルパワーで稼働させると、ElectronプロセスおよびローカルのVector Searchインデックス生成処理がV8エンジンのメモリ上限(通常約4GB〜8GB)に達し、クラッシュまたはクラッシュを回避するためのインデックススキップが発生します。
これを防止し、コンテクスト検索精度を常時100%に保つための低レイヤ・チューニングテクニックです。
1. CursorローカルDB(SQLite/Vector)のインデックスリセットと最適化
Cursorが内部で保持するキャッシュが破壊されたり、古いレガシーコードのメタデータが残存してハルシネーションを起こす場合、以下のパスにあるSQLiteデータベースを物理クリーンアップします。
- macOS: `~/Library/Application Support/Cursor/User/workspaceStorage/`
- Linux: `~/.config/Cursor/User/workspaceStorage/`
ローカルキャッシュのクリーンアップスクリプト
Cursorを完全に終了させた状態で実行すること
対象ワークスペースのハッシュIDディレクトリを特定して削除
cd “$HOME/Library/Application Support/Cursor/User/workspaceStorage”
ls -lt | head -n 5 # 直近で開いたワークスペースを特定
インデックスDB(state.vscdb)を直接SQLite3でVACUUMおよび不要キャッシュ削除
sqlite3
sqlite3
2. Node/V8 メモリ制限の拡張設定
Cursor内部で動くNode.jsプロセスのヒープメモリ領域を拡張し、大容量コードベースのベクトル化処理によるOOM (Out Of Memory) を回避します。
macOS/Linuxの場合、環境変数として以下を `.bashrc` や `.zshrc` に追加した状態で Cursor をターミナルから起動(`cursor .`)します。
Cursor内部のNodeプロセスに対するV8ヒープメモリ割り当てを16GBへ拡張
export NODE_OPTIONS=”–max-old-space-size=16384″
Fast-globおよびWatchプロセスのファイル監視上限をカーネルレベルで極限解放 (Linuxの場合)
sudo sysctl -w fs.inotify.max_user_watches=524288
—
7. マイグレーション前後における退行テスト(Visual & Behavior Verification)
マイグレーションにおいて最も致命的なのは「見た目は合っているが、エッジケースの挙動(イベント発火タイミングやエラーハンドリング)が破壊される」ことです。AI変換後のコンポーネントに対し、Playwrightを用いたDOMレスポンスおよび視覚的退行テスト (VRT) を自動適用します。
Playwrightによる新旧挙動・DOM等価性検証コード
// `tests/migration-verification.spec.ts`
import { test, expect } from ‘@playwright/test’;
test.describe(‘レガシーとモダンコンポーネントの挙動同等性検証’, () => {
test(‘旧jQuery画面と新React画面で同一のユーザー操作フローが完了すること’, async ({ page }) => {
// 1. 旧レガシー画面へのアクセスと操作録画
await page.goto(‘http://localhost:3000/legacy/cart.html’);
await page.fill(‘#quantity-input’, ‘3’);
await page.click(‘#add-to-cart-btn’);
// レガシーDOMの状態抽出
const legacyCartCount = await page.textContent(‘#cart-badge’);
const legacyTotalPrice = await page.textContent(‘#total-price’);
// 視覚的スナップショットの取得
await page.screenshot({ path: ‘snapshots/legacy-cart.png’, fullPage: true });
// 2. 新モダンReact画面へのアクセスと同一操作
await page.goto(‘http://localhost:3000/modern/cart’);
await page.fill(‘input[data-test-id=”quantity-input”]’, ‘3’);
await page.click(‘button[data-test-id=”add-to-cart-btn”]’);
// モダンDOMの状態抽出
const modernCartCount = await page.textContent(‘[data-test-id=”cart-badge”]’);
const modernTotalPrice = await page.textContent(‘[data-test-id=”total-price”]’);
// 視覚的スナップショットの取得
await page.screenshot({ path: ‘snapshots/modern-cart.png’, fullPage: true });
// 3. アサーション(状態の完全一致)
expect(modernCartCount?.trim()).toBe(legacyCartCount?.trim());
expect(modernTotalPrice?.trim()).toBe(legacyTotalPrice?.trim());
});
});
—
結論:AI時代のマイグレーションは「構造化された決定論」である
Cursorをはじめとする現代のAIエディタは、単なる「便利な補完ツール」ではありません。その内部メカニズム(Vector Store, AST, Context Indexing)を正しく理解し、
1. `.cursorignore` によるベクトル空間のノイズ除去
2. `.cursorrules` による決定論的コードスタイルの強制
3. 三段階プロンプトによるロジックとUIの完全分離
4. Docker + `tsc` セルフヒーリング・ループによる型安全性の自動担保
5. CI/CDパイプラインとの自動連携およびPlaywrightによる挙動保証
これらを強固に設計・構築することで、これまで数年単位のコストを要していた大規模レガシーシステムの完全モダン化を、ゼロバグかつ圧倒的スピードで達成することが可能となります。ツールに使われるのではなく、ツールのアーキテクチャを完全に掌握し、システム構成そのものを自動化することこそが、現代のDevOpsおよびフロントエンドアーキテクトに求められる真の技術力です。