migrate-lovable について
Lovableやv0で生成したアプリを、Domoのクライアント側Reactと静的ファイルの要件に合わせるためのガイドです。開発者がサーバー側コードを特定して手動で移行する手順を示し、自動変換は行いません。
主な機能
- 01SSRコードとAPIルートの特定
- 02バックエンド取得をDomo APIに置換
- 03ルーティングとVite設定の変更
- 04Lovable固有の依存関係の削除
- 05マニフェスト、サムネイル、公開ファイルの準備
活用シーン
- 01生成アプリのDomo非互換箇所の確認
- 02プロトタイプのDomoクライアント側構成への移植
- 03Domo公開用ビルド成果物の準備
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ stahura/ domo-ai-vibe-rules --skill migrate-lovableインストール後、Claude Code で/migrate-lovableを入力して読み込まれたか確認します。
詳細
Lovable から Domo への移行
生成されたアプリ(Lovable/v0)を Domo に変換する
多くの開発者は、Lovable、v0、その他同様の LLM ベースの生成ツールなどの AI ツールを使って、アプリのプロトタイプを作成しています。しかし、これらのツールは通常、Domo のクライアントサイドのみのアーキテクチャと互換性のないサーバーサイドレンダリング(SSR)を使用するアプリを生成します。
変換における課題
生成されたアプリには、次のようなものが含まれていることがよくあります。
- サーバーサイドレンダリング(Next.js、Remix、SvelteKit、Nuxt)
- API ルート(
pages/api/またはapp/api/ディレクトリ) - サーバーサイドでのデータ取得(
getServerSideProps、ローダーなど) - フレームワーク固有のルーティング(Next.js App Router、Remix routes)
Domo アプリは、次の要件を満たす必要があります。
- 純粋なクライアントサイドの React — サーバーでのレンダリングは行いません
- 静的ファイルベース — Domo は静的ファイルのみを提供します
- Domo API との統合 — バックエンドエンドポイントの代わりに、
domo.get()、Query、AppDBClientなどを使用します
推奨される変換パターン
DA CLI は変換ツールではありません。アプリを自動的に変換することはできません。ただし、参照用の構成や出発点として利用できます。
ステップ 1:参照用の構成を生成する
# Create a fresh Domo app to use as reference
da new my-converted-app
cd my-converted-app
# This gives you the correct structure to compare against
ステップ 2:SSR コードを検出して削除する
SSR の兆候を確認します。
getServerSideProps、getStaticProps(Next.js)loader、action関数(Remix)+page.server.js、+server.js(SvelteKit)pages/api/またはapp/api/ディレクトリ- サーバーサイドでの
process.envの使用 - コンポーネント内のデータベース接続
対応: サーバーサイドのコードをすべて削除します。Domo アプリは完全にブラウザー上で動作します。
ステップ 3:データ取得を置き換える
変更前(Next.js の例):
// ❌ Server-side data fetching
export async function getServerSideProps() {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
return { props: { data } };
}
// ❌ API route
// pages/api/users.ts
export default async function handler(req, res) {
const users = await db.users.findMany();
res.json(users);
}
変更後(Domo):
// ✅ Client-side with Domo APIs
import domo from 'ryuu.js';
import Query from '@domoinc/query';
import { AppDBClient } from '@domoinc/toolkit';
// Fetch from Domo dataset
const data = await domo.get('/data/v1/sales');
// Or use Query API for filtered/aggregated data
const summary = await new Query()
.select(['region', 'sales'])
.groupBy('region', { sales: 'sum' })
.fetch('sales-dataset');
// Or use AppDB for document storage
const tasksClient = new AppDBClient.DocumentsClient('Tasks');
const tasks = await tasksClient.get();
ステップ 4:ルーティングを更新する
変更前(Next.js):
// ❌ Next.js routing
import Link from 'next/link';
<Link href="/dashboard">Dashboard</Link>
変更後(Domo):
// ✅ HashRouter for client-side routing
import { HashRouter, Routes, Route } from 'react-router-dom';
import domo from 'ryuu.js';
// Use HashRouter (works without server rewrites)
<HashRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</HashRouter>
// For Domo navigation, use domo.navigate()
domo.navigate('/page/123456789');
ステップ 5:ビルド設定を修正する
変更前(Next.js):
// next.config.js
module.exports = {
// Next.js config
}
変更後(Domo — Vite):
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
base: './', // CRITICAL: Relative paths for Domo
plugins: [react()],
});
lovable-tagger などの Lovable 固有の Vite プラグインを削除します。
// ❌ Remove this
import { componentTagger } from "lovable-tagger";
plugins: [react(), mode === "development" && componentTagger()].filter(Boolean),
// ✅ Replace with
plugins: [react()],
ステップ 6:Lovable 固有の依存関係を削除する
Lovable/v0 アプリには、削除が必要なツールが含まれています。
削除する依存関係:
next-themes— SSR のテーマプロバイダーです。useTheme()を、ハードコードしたテーマ文字列(例:"light")に置き換えますlovable-tagger— Lovable の開発時専用コンポーネントタグ付けツールです
削除するファイル:
playwright.config.ts—lovable-agent-playwright-configを使用しますplaywright-fixture.ts— Lovable のテスト用フィクスチャを再エクスポートしますbun.lock/bun.lockb— Lovable は Bun を使用します。npm に統一してくださいApp.css— 生成されたアプリでは使用されていないことがよくあります(先に確認してください)
Sonner トースターでの next-themes の使用を修正する:
// ❌ Before (depends on next-themes)
import { useTheme } from "next-themes";
const { theme = "system" } = useTheme();
<Sonner theme={theme as ToasterProps["theme"]} />
// ✅ After (hardcoded, no SSR dependency)
<Sonner theme="light" />
ステップ 7:Domo マニフェストを作成する
Domo では、公開ディレクトリに manifest.json が必要です。
{
"name": "My App Name",
"version": "1.0.0",
"size": {
"width": 10,
"height": 10
},
"mapping": [],
"fileName": "index.html",
"id": "",
"proxyId": ""
}
name— Domo での表示名size— カードのサイズ(Domo ダッシュボードのグリッド上の列数 x 行数)mapping— データセットのマッピング(モックデータまたは AppDB を使用する場合は空の配列)fileName— エントリー HTML ファイル(常にindex.html)id— 空のままにします。初回の公開時にdomo publishが値を設定します
ステップ 8:サムネイルを作成する
必須 — これがないと domo publish は失敗します。
プロジェクトのルートに、名前を thumbnail.png とした 300x300 PNG を配置します。これは Domo Appstore とモバイルアプリで使用されます。
# Generate a simple thumbnail programmatically (Python + Pillow)
python3 -c "
from PIL import Image, ImageDraw, ImageFont
img = Image.new('RGB', (300, 300), color=(15, 23, 42))
draw = ImageDraw.Draw(img)
draw.rectangle([0, 0, 300, 6], fill=(99, 102, 241))
try:
font = ImageFont.truetype('/System/Library/Fonts/Helvetica.ttc', 72)
except: font = ImageFont.load_default()
bbox = draw.textbbox((0, 0), 'EF', font=font)
draw.text(((300-(bbox[2]-bbox[0]))/2, 110), 'EF', fill='white', font=font)
img.save('thumbnail.png')
"
または、任意の 300x300 PNG 画像を使用します。
ステップ 9:dist/ ディレクトリから公開する
重要 — プロジェクトのルートから domo publish を実行すると、CLI が node_modules、ソースファイル、設定ファイルをアップロードしようとして、ペイロードが大きくなりすぎるため、400: Unable to parse form content エラーが発生します。
解決策: manifest.json と thumbnail.png を dist/ にコピーして、そこから公開します。
# Manual publish
npm run build
cp manifest.json thumbnail.png dist/
cd dist && domo publish
package.json のスクリプトを更新して自動化します。
{
"scripts": {
"build": "vite build && cp manifest.json thumbnail.png dist/",
"publish": "npm run build && cd dist && domo publish"
}
}
これにより、ビルド済みアセット、マニフェスト、サムネイルのみがアップロードされます。
ステップ 10:コンポーネントを移植する
DA CLI を使用して適切な構成の新しいコンポーネントを生成してから、ロジックを移植します。
# Generate component structure
da generate component SalesChart
# Copy component logic from generated app
# Replace data fetching with Domo APIs
# Update imports and dependencies
DA CLIでできること
✅ 参照用の構成 - Domo アプリの正しい構成を示します
✅ コンポーネントの生成 - 適切な構成のコンポーネントを作成します
✅ パターンの例 - Domo の規約を紹介します
✅ 新規作成 - 変換が複雑すぎる場合は、新しく作成してロジックを移植できます
DA CLIでできないこと
❌ 自動変換 - SSR をクライアントサイドに変換しません
❌ 移行ウィザード - ステップごとに変換するツールはありません
❌ コード変換 - フレームワーク固有のコードを書き換えません
❌ API の移行 - バックエンドへの呼び出しを自動的に置き換えません
変換の完全チェックリスト
- すべての SSR コードを検出して削除する
- API ルート(
pages/api/、app/api/)を削除する - バックエンドへの
fetch()呼び出しを Domo API に置き換える - ルーティングを
BrowserRouterからHashRouterに更新する - Vite の
base: './'を設定する - Vite の設定から Lovable 固有のプラグイン(
lovable-tagger)を削除する -
next-themesを削除し、useTheme()をハードコードされたテーマに置き換える - Lovable のファイル(
playwright.config.ts、playwright-fixture.ts、bun.lock*)を削除する - Domo API との統合用に
ryuu.js依存関係を追加する - 環境変数を置き換える(代わりに Domo API を使用する)
- インポートを更新する(Next.js/Remix/Lovable 固有のものを削除する)
- アプリ名、バージョン、サイズを含む
manifest.jsonを作成する - プロジェクトルートに
thumbnail.png(300x300 PNG)を作成する - ビルドスクリプトを更新し、
manifest.jsonとthumbnail.pngをdist/にコピーする - すべてのデータ取得が Domo API で動作することをテストする
- アプリがローカルでビルドおよび実行されることを確認する(
npm run build) -
dist/ディレクトリから公開する(cd dist && domo publish)
例:シンプルなダッシュボードの変換
変換前(Lovable/Next.js):
// pages/dashboard.tsx
export async function getServerSideProps() {
const sales = await fetch('http://api.company.com/sales').then(r => r.json());
return { props: { sales } };
}
export default function Dashboard({ sales }) {
return <div>{/* Render sales data */}</div>;
}
変換後(Domo):
// src/components/Dashboard/Dashboard.tsx
import { useEffect, useState } from 'react';
import domo from 'ryuu.js';
export default function Dashboard() {
const [sales, setSales] = useState([]);
useEffect(() => {
// Fetch from Domo dataset instead of backend
domo.get('/data/v1/sales').then(setSales);
}, []);
return <div>{/* Render sales data */}</div>;
}