mcpField
D
deno-frontend

deno-frontend

github.com/midudev開発7.0k56 回インストール1 週間前に更新
リポジトリ

deno-frontend について

deno-frontendは、DenoとFresh 2.xでフロントエンドを開発するためのスキルです。PreactとTailwind CSSを使い、サーバー描画ページ、ルート、必要な部分だけをハイドレーションするインタラクティブなアイランドを扱います。

主な機能

  1. 01Fresh 2.xのアプリとルートのパターン
  2. 02サーバー描画ページとデータ取得
  3. 03Preactによるインタラクティブなアイランド
  4. 04Tailwind CSSの統合ガイド
  5. 05Fresh 2.xの移行と構文ルール

活用シーン

  1. 01Fresh 2.xのWebアプリの開始
  2. 02サーバー描画ルートとAPIエンドポイントの構築
  3. 03アイランドによるUIへの操作性追加
  4. 04Tailwind CSSによるDenoフロントエンドのスタイリング

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/midudev/autoskills --skill deno-frontend

インストール後、Claude Code で/deno-frontendを入力して読み込まれたか確認します。

詳細

Deno フロントエンド開発

概要

このスキルでは、Fresh 2.x(Deno の Web フレームワーク)、Preact(軽量な React 代替ライブラリ)、Tailwind CSS を使用した Deno でのフロントエンド開発について説明します。Fresh は「アイランドアーキテクチャ」を採用しており、ページはサーバー上でレンダリングされ、インタラクティブな部分だけが JavaScript とともにブラウザーに送られます。

このスキルを使用する場面

  • 新しい Fresh Web アプリケーションを作成するとき
  • インタラクティブな UI コンポーネント(アイランド)を構築するとき
  • サーバーレンダリングされるページやルートを追加するとき
  • スタイリングに Tailwind CSS を統合するとき
  • アイランド(クライアント側)とコンポーネント(サーバー専用)のどちらを使うか選ぶとき
  • Preact のフックやシグナルを使用するとき

Deno で Web アプリケーションを構築する際は、これらのプラクティスを適用してください。

適用範囲

このスキルは Fresh/Deno のフロントエンドに関する質問にのみ適用されます。次のルールに従ってください。

  • ユーザーが Next.js、React Native、Vue、Svelte、Angular、またはその他のフレームワークについて質問した場合は、そのフレームワークを直接使用して回答してください。Fresh の代替案を提案しないでください。
  • 他のフレームワークに関する回答に、Fresh のインポート、useSignal、アイランドのパターン、Fresh のプロジェクト構成を含めないでください。
  • Fresh を推奨するのは、ユーザーが明示的に Fresh について尋ねた場合、または Fresh/Deno プロジェクトで作業している場合に限ります。

重要:非推奨の構文は絶対に示さない

Fresh 1.x からの移行を支援する場合、古いパターンは一般的な表現で説明し、正しい Fresh 2.x のコードのみを示してください。古いドル記号付きインポートパスや非推奨の構文は、「変更前/変更後」の比較であっても絶対に記述しないでください。

  • 「古いドル記号付きインポートパスを、安定版の Fresh 2.x インポートに置き換えてください」と伝え、その後は正しい from "fresh" の方法のみを示してください。
  • ❌ Old: import { App } from "$fresh/server.ts" と記述してはいけません。否定的な例としてであっても、決して許容されません。
  • 文字列 _404.tsx と _500.tsx は、Fresh 2.x と 1.x の比較であっても回答に含めてはいけません。代わりに「古い個別のエラーページ」と表現してください。

Fresh 2.x のパターンのみを示してください。

重要:Fresh 2.x と 1.x

常に Fresh 2.x のパターンを使用してください。 Fresh 1.x は非推奨です。主な違いは次のとおりです。

  • Fresh 2.x では import { App } from "fresh" を使用します。古いドル記号付きインポートパスは非推奨です。
  • Fresh 2.x ではマニフェストファイルを使用しません。古い自動生成マニフェストは不要になりました。
  • Fresh 2.x の開発には vite.config.ts を使用します。古い dev.ts エントリーポイントは廃止されました。
  • Fresh 2.x は new App() で設定します。古い設定ファイルは使用されなくなりました。
  • Fresh 2.x のハンドラーは単一の (ctx) パラメーターを受け取ります。古い 2 つのパラメーターを使うシグネチャは非推奨です。
  • Fresh 2.x では統一された _error.tsx を使用します。古い個別のエラーページは置き換えられました。

常に Fresh 2.x の安定版インポートを使用してください。

// ✅ CORRECT - Fresh 2.x stable
import { App, staticFiles } from "fresh";
import { define } from "./utils/state.ts"; // Project-local define helpers

Fresh フレームワーク

リファレンス:https://fresh.deno.dev/docs

Fresh は Deno の Web フレームワークです。アイランドアーキテクチャを採用しており、ページはサーバー上でレンダリングされ、インタラクティブな部分(「アイランド」)だけが JavaScript とともにクライアントに送られます。

Fresh プロジェクトの作成
deno run -Ar jsr:@fresh/init
cd my-project
deno task dev    # Runs at http://127.0.0.1:5173/
プロジェクト構成(Fresh 2.x)
my-project/
├── deno.json           # Config, dependencies, and tasks
├── main.ts             # Server entry point
├── client.ts           # Client entry point (CSS imports)
├── vite.config.ts      # Vite configuration
├── routes/             # Pages and API routes
│   ├── _app.tsx        # App layout wrapper (outer HTML)
│   ├── _layout.tsx     # Layout component (optional)
│   ├── _error.tsx      # Unified error page (404/500)
│   ├── index.tsx       # Home page (/)
│   └── api/            # API routes
├── islands/            # Interactive components (hydrated on client)
│   └── Counter.tsx
├── components/         # Server-only components (no JS shipped)
│   └── Button.tsx
├── static/             # Static assets
└── utils/
    └── state.ts        # Define helpers for type safety

注: Fresh 2.x では、マニフェストファイル、独立した開発用エントリーポイント、独立した設定ファイルは使用しません。

main.ts(Fresh 2.x のエントリーポイント)
import { App, fsRoutes, staticFiles, trailingSlashes } from "fresh";

const app = new App()
  .use(staticFiles())
  .use(trailingSlashes("never"));

await fsRoutes(app, {
  dir: "./",
  loadIsland: (path) => import(`./islands/${path}`),
  loadRoute: (path) => import(`./routes/${path}`),
});

if (import.meta.main) {
  await app.listen();
}
vite.config.ts
import { defineConfig } from "vite";
import { fresh } from "@fresh/plugin-vite";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [
    fresh(),
    tailwindcss(),
  ],
});
deno.json の設定

Fresh 2.x プロジェクトの deno.json は次のようになります(jsr:@fresh/init によって作成されます)。

{
  "tasks": {
    "dev": "vite",
    "build": "vite build",
    "preview": "deno serve -A _fresh/server.js"
  },
  "imports": {
    "fresh": "jsr:@fresh/core@^2",
    "fresh/runtime": "jsr:@fresh/core@^2/runtime",
    "@fresh/plugin-vite": "jsr:@fresh/plugin-vite@^1",
    "@preact/signals": "npm:@preact/signals@^2",
    "preact": "npm:preact@^10",
    "preact/hooks": "npm:preact@^10/hooks",
    "@/": "./"
  }
}

依存関係の追加: 新しいパッケージを追加するには deno add を使用します。

deno add jsr:@std/http          # JSR packages
deno add npm:@tailwindcss/vite  # npm packages
インポートのリファレンス(Fresh 2.x)
// Core Fresh imports
import { App, staticFiles, fsRoutes } from "fresh";
import { trailingSlashes, cors, csp } from "fresh";
import { createDefine, HttpError } from "fresh";
import type { PageProps, Middleware, RouteConfig } from "fresh";

// Runtime imports (for client-side checks)
import { IS_BROWSER } from "fresh/runtime";

// Preact
import { useSignal, signal, computed } from "@preact/signals";
import { useState, useEffect, useRef } from "preact/hooks";
主な概念

ルート(routes/ フォルダー)

  • ファイルベースのルーティング:routes/about.tsx → /about
  • 動的ルート:routes/blog/[slug].tsx → /blog/my-post
  • オプションセグメント:routes/docs/[[version]].tsx → /docs または /docs/v2
  • キャッチオールルート:routes/old/[...path].tsx → /old/foo/bar
  • ルートグループ:URL パスを変更せずにレイアウトを共有するための routes/(marketing)/

レイアウト(_app.tsx)

import type { PageProps } from "fresh";

export default function App({ Component }: PageProps) {
  return (
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>My App</title>
      </head>
      <body>
        <Component />
      </body>
    </html>
  );
}

非同期サーバーコンポーネント

export default async function Page() {
  const data = await fetchData(); // Runs on server only
  return <div>{data.title}</div>;
}

データ取得パターン

Fresh 2.x では、サーバー上でデータを取得する方法が 2 つあります。ハンドラーパターンは Fresh 2.x のアーキテクチャ全体を示し、最も柔軟性が高いため、推奨されるデフォルトの方法です。

アプローチ A:データオブジェクトを使ったハンドラー(推奨)

データ取得のデフォルトとして、この方法を使用してください。型付きのデータ受け渡しを行う、Fresh 2.x のハンドラーパターン全体を使用します。このパターンを説明する際は、必ず utils/state.ts を含めた完全なセットアップを示してください。

// utils/state.ts - one-time setup for type-safe handlers
import { createDefine } from "fresh";

export interface State {
  user?: { id: string; name: string };
}

export const define = createDefine<State>();
// routes/posts.tsx
import { define } from "@/utils/state.ts";

// Handler fetches data and returns it via { data: {...} }
export const handler = define.handlers(async (ctx) => {
  const response = await fetch("https://jsonplaceholder.typicode.com/posts");
  const posts = await response.json();
  return { data: { posts } };
});

// Page receives typed data
export default define.page<typeof handler>(({ data }) => {
  return (
    <div>
      <h1>Posts</h1>
      <ul>
        {data.posts.map((post) => <li key={post.id}>{post.title}</li>)}
      </ul>
    </div>
  );
});

この方法では、レンダリング前に認証チェック、リダイレクト、その他のロジックを実行することもできます。

アプローチ B:非同期サーバーコンポーネント(簡略形)

認証やリダイレクトを行わず、データを取得して表示するだけの最も単純なケースでは、次の方法を使用します。

// routes/servers.tsx
export default async function ServersPage() {
  const servers = await db.query("SELECT * FROM servers");

  return (
    <div>
      <h1>Servers</h1>
      <ul>
        {servers.map((s) => <li key={s.id}>{s.name}</li>)}
      </ul>
    </div>
  );
}
選択ガイド
Need to fetch data on server?
├─ Yes → Use handler with { data: {...} } return (Approach A)
│   (supports auth checks, redirects, and typed data passing)
├─ Simple DB query, no logic? → Async page component is also fine (Approach B)
└─ No → Just use a regular page component

ハンドラーと Define ヘルパー(Fresh 2.x)

Fresh 2.x のハンドラーでは、単一のコンテキストパラメーターパターンを使用します。パラメーターは常に (ctx) のみを使用してください。

重要: データ取得、フォーム処理、API ルート、認証など、ハンドラーパターンを説明する際は、必ず utils/state.ts のセットアップを示すか参照してください。これにより、Fresh 2.x のアーキテクチャ全体を確認できます。

ルートハンドラー

ファイルベースのルートで型安全なルートハンドラーを使用する場合は、常に define.handlers() を使用してください。

// routes/api/users.ts
import { define } from "@/utils/state.ts";

// Single function handles all methods
export const handler = define.handlers((ctx) => {
  return new Response(`Hello from ${ctx.req.method}`);
});

// Or method-specific handlers
export const handler = define.handlers({
  GET(ctx) {
    return Response.json({ users: [] });
  },
  async POST(ctx) {
    const body = await ctx.req.json();
    return Response.json({ created: true }, { status: 201 });
  },
});

注:ハンドラーを直接エクスポートする形式(export const handler = (ctx) => {...})も使用できますが、TypeScript の型安全性は失われます。define.handlers() を優先してください。

コンテキストオブジェクト

ctx パラメーターには、必要なすべての情報が含まれています。

export const handler = (ctx) => {
  ctx.req          // The Request object
  ctx.url          // URL instance with pathname, searchParams
  ctx.params       // Route parameters { slug: "my-post" }
  ctx.state        // Request-scoped data for middlewares
  ctx.config       // Fresh configuration
  ctx.route        // Matched route pattern
  ctx.error        // Caught error (on error pages)

  // Methods
  ctx.render(<JSX />)           // Render JSX to Response (JSX only, NOT data objects!)
  ctx.render(<JSX />, { status: 201, headers: {...} })  // With response options
  ctx.redirect("/other")        // Redirect (302 default)
  ctx.redirect("/other", 301)   // Permanent redirect
  ctx.next()                    // Call next middleware
};
Define ヘルパー(型安全性)

型安全なハンドラーのために、utils/state.ts ファイルを作成します。

// utils/state.ts
import { createDefine } from "fresh";

// Define your app's state type
export interface State {
  user?: { id: string; name: string };
}

// Export typed define helpers
export const define = createDefine<State>();

ルートで使用します。

// routes/profile.tsx
import { define } from "@/utils/state.ts";
import type { PageProps } from "fresh";

// Typed handler with data
export const handler = define.handlers((ctx) => {
  if (!ctx.state.user) {
    return ctx.redirect("/login");
  }
  return { data: { user: ctx.state.user } };
});

// Page receives typed data
export default define.page<typeof handler>(({ data }) => {
  return <h1>Welcome, {data.user.name}!</h1>;
});
ミドルウェア(Fresh 2.x)
// routes/_middleware.ts
import { define } from "@/utils/state.ts";

export const handler = define.middleware(async (ctx) => {
  // Before route handler
  console.log(`${ctx.req.method} ${ctx.url.pathname}`);

  // Call next middleware/route
  const response = await ctx.next();

  // After route handler
  return response;
});
API ルート
// routes/api/posts/[id].ts
import { define } from "@/utils/state.ts";
import { HttpError } from "fresh";

export const handler = define.handlers({
  async GET(ctx) {
    const post = await getPost(ctx.params.id);
    if (!post) {
      throw new HttpError(404); // Uses _error.tsx
    }
    return Response.json(post);
  },

  async DELETE(ctx) {
    if (!ctx.state.user) {
      throw new HttpError(401);
    }
    await deletePost(ctx.params.id);
    return new Response(null, { status: 204 });
  },
});

アイランド(インタラクティブコンポーネント)

アイランドは、クライアント側でハイドレーション(インタラクティブ化)されるコンポーネントです。islands/ フォルダー、またはルート内の (_islands) フォルダーに配置します。

アイランドを使用する場面
  • ユーザー操作(クリック、フォーム入力)
  • クライアント側の状態(カウンター、切り替え)
  • ブラウザー API(localStorage、geolocation)
アイランドの例
// islands/Counter.tsx
import { useSignal } from "@preact/signals";

export default function Counter() {
  const count = useSignal(0);

  return (
    <div>
      <p>Count: {count.value}</p>
      <button onClick={() => count.value++}>
        Increment
      </button>
    </div>
  );
}
IS_BROWSER を使ったクライアント専用コード
// islands/LocalStorageCounter.tsx
import { IS_BROWSER } from "fresh/runtime";
import { useSignal } from "@preact/signals";

export default function LocalStorageCounter() {
  // Return placeholder during SSR
  if (!IS_BROWSER) {
    return <div>Loading...</div>;
  }

  // Client-only code
  const stored = localStorage.getItem("count");
  const count = useSignal(stored ? parseInt(stored) : 0);

  return (
    <button onClick={() => {
      count.value++;
      localStorage.setItem("count", String(count.value));
    }}>
      Count: {count.value}
    </button>
  );
}
アイランドの Props(シリアライズ可能な型)

アイランドは、次の型の props を受け取ることができます。

  • プリミティブ:string、number、boolean、bigint、undefined、null
  • 特殊な値:Infinity、-Infinity、NaN、-0
  • コレクション:Array、Map、Set
  • オブジェクト:string キーを持つプレーンオブジェクト
  • 組み込み型:URL、Date、RegExp、Uint8Array
  • Preact:JSX 要素、Signals(シリアライズ可能な値を持つもの)
  • 循環参照もサポートされています

関数は props として渡せません。

アイランドのルール
  1. Props はシリアライズ可能である必要があります - 関数は使用せず、JSON 互換のデータのみを使用してください
  2. アイランドは小さく保ちます - クライアントに送信される JavaScript を減らせます
  3. サーバーコンポーネントを優先します - インタラクティブ性が必要な場合にのみアイランドを使用してください

Preact

Preact は React の 3KB の代替です。Fresh は React の代わりに Preact を使用します。

Preact と React の違い
PreactReact
class が使えますclassName が必要です
@preact/signalsuseState
3KB のバンドル約 40KB のバンドル
フック(React と同じ)
import { useState, useEffect, useRef } from "preact/hooks";

function MyComponent() {
  const [value, setValue] = useState(0);
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    console.log("Component mounted");
  }, []);

  return <input ref={inputRef} value={value} />;
}
Signals(Preact のリアクティブな状態)

Signals は、useState より効率的な Preact の代替機能です。

import { signal, computed } from "@preact/signals";

const count = signal(0);
const doubled = computed(() => count.value * 2);

function Counter() {
  return (
    <div>
      <p>Count: {count}</p>
      <p>Doubled: {doubled}</p>
      <button onClick={() => count.value++}>+1</button>
    </div>
  );
}

Signals の利点:

  • より細かな更新(変更された部分だけを再レンダリングします)
  • コンポーネントの外部で定義できます
  • 共有状態のコードをより簡潔にできます

Fresh での Tailwind CSS(任意)

Tailwind CSS は任意です。優れた Fresh アプリを構築するのに必須ではありません。ただし、素早くスタイルを設定できるため、多くの開発者が好んで使用しています。Fresh 2.x はビルドに Vite を使用するため、Tailwind は Vite プラグイン経由で統合されます。

セットアップ

Tailwind のパッケージを両方インストールします。

deno add npm:@tailwindcss/vite npm:tailwindcss

重要: パッケージは両方必要です。@tailwindcss/vite は Vite プラグインで、tailwindcss はその依存先となるコアライブラリです。

vite.config.ts で Vite を設定します。

import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [tailwindcss()],
});

CSS ファイル(例:assets/styles.css)に Tailwind のインポートを追加します。

@import "tailwindcss";

次に、この CSS ファイルを client.ts でインポートします。

import "./assets/styles.css";
使い方
export default function Button({ children }) {
  return (
    <button class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
      {children}
    </button>
  );
}
ベストプラクティス
  1. @apply よりユーティリティクラスを優先します
  2. className ではなく class を使用します(Preact はどちらもサポートしていますが、class のほうがシンプルです)
  3. ダークモード: tailwind.config.js では class 戦略を使用します
<div class="bg-white dark:bg-gray-900">
  <p class="text-gray-900 dark:text-white">Hello</p>
</div>

ビルドとデプロイ

開発
deno task dev      # Start dev server with hot reload (http://127.0.0.1:5173/)
本番ビルド
deno task build    # Build for production
deno task preview  # Preview production build locally
Deno Deploy へのデプロイ
deno task build           # Build first
deno deploy --prod        # Deploy to production

クイックリファレンス

タスクコマンド/パターン
Fresh プロジェクトを作成するdeno run -Ar jsr:@fresh/init
開発サーバーを起動するdeno task dev(ポート 5173)
本番用にビルドするdeno task build
ページを追加するroutes/pagename.tsx を作成します
API ルートを追加するroutes/api/endpoint.ts を作成します
インタラクティブコンポーネントを追加するislands/ComponentName.tsx を作成します
静的コンポーネントを追加するcomponents/ComponentName.tsx を作成します

よくある間違い

Fresh 1.x のパターンを使用する(LLM で最もよくあるエラー)

古いインポート指定子を使用する

古いドル記号付き Fresh インポートパスとアルファ版のバージョンを指定したインポートは非推奨です。必ず安定版の fresh パッケージを使用してください。

// ✅ CORRECT - Fresh 2.x stable imports
import { App, staticFiles } from "fresh";
import type { PageProps } from "fresh";

2 つのパラメーターを取るハンドラーを使用する

古い 2 パラメーターのハンドラーシグネチャは非推奨です。Fresh 2.x では、単一のコンテキストパラメーターを使用します。

// ✅ CORRECT - Fresh 2.x uses single context parameter
export const handler = {
  GET(ctx) {  // Single ctx param
    return ctx.render(<MyPage />);
  }
};

レガシーファイルを作成する

Fresh 2.x では、マニフェストファイル、独立した開発用エントリーポイント、独立した設定ファイルは使用しません。Fresh 2.x の正しいファイル構成は次のとおりです。

main.ts           # Server entry
client.ts         # Client entry
vite.config.ts    # Vite config

非推奨のコンテキストメソッドを使用する

古い renderNotFound()、JSX を使わない単独の render()、および basePath のパターンは非推奨です。代わりに次の Fresh 2.x のパターンを使用してください。

// ✅ CORRECT - Fresh 2.x patterns
throw new HttpError(404)
ctx.render(<MyComponent />)
ctx.config.basePath

ハンドラーからページにデータを渡す(非常によくある間違い)

これは頻繁に発生するエラーです。Fresh 2.x では、ctx.render() を通じてデータを渡すことはできません。代わりに、ハンドラーから data プロパティを含むオブジェクトを返します。

// ✅ CORRECT - Return object with data property from handler
export const handler = define.handlers(async (ctx) => {
  const servers = await getServers();
  return { data: { servers } };  // Return { data: {...} } object
});

// ✅ CORRECT - Link page to handler type with typeof
export default define.page<typeof handler>(({ data }) => {
  return <ul>{data.servers.map((s) => <li>{s.name}</li>)}</ul>;
});

// ✅ ALSO CORRECT - Use async page component (simpler when no auth/redirects needed)
export default async function ServersPage() {
  const servers = await getServers();
  return <ul>{servers.map((s) => <li>{s.name}</li>)}</ul>;
}

deno.json で古いタスクコマンドを使用する

dev.ts を参照する古いタスクコマンドは非推奨です。Vite ベースのタスクを使用してください。

// ✅ CORRECT - Fresh 2.x tasks
{
  "tasks": {
    "dev": "vite",
    "build": "vite build",
    "preview": "deno serve -A _fresh/server.js"
  }
}
Island に関する間違い

Island に JavaScript を詰め込みすぎる

// ❌ Wrong - entire page as an island (ships all JS to client)
// islands/HomePage.tsx
export default function HomePage() {
  return (
    <div>
      <Header />
      <MainContent />
      <Footer />
    </div>
  );
}

// ✅ Correct - only interactive parts are islands
// routes/index.tsx (server component)
import Counter from "../islands/Counter.tsx";

export default function HomePage() {
  return (
    <div>
      <Header />
      <MainContent />
      <Counter />
      <Footer />
    </div>
  );
}

シリアライズできない props を Island に渡す

// ❌ Wrong - functions can't be serialized
<Counter onUpdate={(val) => console.log(val)} />

// ✅ Correct - only pass JSON-serializable data
<Counter initialValue={5} label="Click count" />
その他のよくある間違い

class ではなく className を使用する

// ❌ Works but unnecessary in Preact
<div className="container">

// ✅ Preact supports native HTML attribute
<div class="container">

Fresh 2.x をデプロイする前にビルドするのを忘れる

# ❌ Wrong - Fresh 2.x requires a build step
deno deploy --prod

# ✅ Correct - build first, then deploy
deno task build
deno deploy --prod

インタラクティブでないコンテンツに Island を作成する

// ❌ Wrong - this doesn't need to be an island (no interactivity)
// islands/StaticCard.tsx
export default function StaticCard({ title, body }) {
  return <div class="card"><h2>{title}</h2><p>{body}</p></div>;
}

// ✅ Correct - use a regular component (no JS shipped)
// components/StaticCard.tsx
export default function StaticCard({ title, body }) {
  return <div class="card"><h2>{title}</h2><p>{body}</p></div>;
}

古い Tailwind プラグインを使用する

古い Fresh 1.x の Tailwind プラグインは非推奨です。Fresh 2.x では Vite Tailwind プラグインを使用します。

// ✅ CORRECT - Fresh 2.x uses Vite Tailwind plugin
import tailwindcss from "@tailwindcss/vite";

tailwindcss パッケージが不足している

# Error: Can't resolve 'tailwindcss' in '/path/to/assets'

# ❌ WRONG - Only installed the Vite plugin
deno add npm:@tailwindcss/vite

# ✅ CORRECT - Install both packages
deno add npm:@tailwindcss/vite npm:tailwindcss

@tailwindcss/vite プラグインを使用するには、コアの tailwindcss パッケージを別途インストールする必要があります。

Fresh のアルファ版(2.0.0-alpha.*)

一部のプロジェクトでは Fresh 2.x のアルファ版リリース(例:@fresh/[email protected])が使用されています。これらは Fresh 1.x ではありませんが、安定版 Fresh 2.x とは設定が異なります。

アルファ版のパターン安定版 2.x のパターン
dev.ts エントリーポイントvite.config.ts
@fresh/plugin-tailwind@tailwindcss/vite
deno run -A --watch dev.tsvite
ポート 8000 の開発サーバーポート 5173 の開発サーバー
client.ts なしclient.ts が必要
deno run -A dev.ts buildvite build

重要: deno.json に @fresh/[email protected].* があるプロジェクトで dev.ts を見つけた場合、Fresh 1.x の遺物として扱わないでください。これはアルファ版で正しいエントリーポイントです。変更を提案する前に、deno.json の imports を確認して、使用中のバージョンを特定してください。

アルファ版のプロジェクトでも、define.handlers({ GET(ctx) { ... } }) というハンドラーパターンが使われており、{ data: {...} } を返します。これは安定版 2.x と同じです。

Fresh 1.x から 2.x への移行

既存の Fresh 1.x プロジェクトがある場合は、移行ツールを実行します。

deno run -Ar jsr:@fresh/update

このツールは、次の処理を自動的に行います。

  • 古いインポートパスを新しい fresh パッケージに変換します
  • ハンドラーのシグネチャを更新し、単一の (ctx) パラメーターを使用するようにします
  • 古い生成ファイルと設定ファイルを削除します
  • vite.config.ts と client.ts を作成します
  • deno.json のタスクを更新し、Vite を使用するようにします
  • 個別のエラーページを統合された _error.tsx にまとめます
  • 非推奨のコンテキストメソッド呼び出しを Fresh 2.x の同等のものに更新します
手動移行チェックリスト

ツールで対応できない項目がある場合は、次の点を確認してください。

  1. インポート:Fresh のインポートはすべて fresh または fresh/runtime から行います
  2. ハンドラー:単一の (ctx) パラメーターを使用し、リクエストには ctx.req からアクセスします
  3. ファイル:古い生成ファイル、開発用エントリーポイント、旧設定ファイルを削除します
  4. タスク:vite、vite build、deno serve -A _fresh/server.js を使用するように更新します
  5. エラーページ:統合された単一の _error.tsx を使用します
  6. Tailwind:@tailwindcss/vite(Vite プラグイン)を使用します

コメント