Communitygithub.com

Svelte 5 静的サイト専用(ルーン)

Svelte 5 のルーン($state, $derived, $effect, $props, $bindable)を使って、静的(サーバーレス)な Svelte 5 アプリを構築、移行、レビューします。従来の Svelte 4 リアクティビティ(export let, $:, ストア)は使いません。

Svelte 5 静的サイト専用(ルーン) とは?

このスキルは、静的サイト(サーバーレス)専用の Svelte 5 アプリケーションを、ルーン(runes)と呼ばれる新しいリアクティビティシステムを使って構築、移行、レビューするためのものです。Svelte 5 で導入された runes($state, $derived, $effect, $props, $bindable)を使用し、Svelte 4 の export let や $: といったレガシーな記法は使いません。また、SvelteKit を adapter-static で静的エクスポートする際に、誤ってサーバーサイドのコード(+page.server.js など)を含めてしまうのを防ぎます。さらに、XSS、シークレット漏洩、CSP 設定など、静的ホスティング環境に特化したセキュリティチェックリストも適用します。このスキルが対応するプラットフォームは、Claude Code、Cursor、Codex などの AI エージェントです。GitHub Pages、Netlify、Cloudflare Pages、S3 等の CDN へのデプロイをサポートしています。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/agentmemoir/skills/tree/HEAD/skills/frontend/svelte5-static-runes

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Svelte 5 静的サイト専用(ルーン) は何をしますか?

このスキルの対象

「静的専用」とは、成果物がプリビルドされた HTML/CSS/JS のフォルダであり、リクエスト時に Node/エッジサーバーが一切不要 であることを意味します。GitHub Pages、Netlify/Cloudflare/Vercel の 静的 ホスティング、S3+CloudFront、またはローカルファイルとして直接開くことが可能です。この制約により、+page.server.js、+server.js、フォームアクション、hooks.server.js、およびサーバーが動作していることを前提とするコードパスはすべて除外されます。「ルーン」とは、コンポーネントのリアクティビティが Svelte 5 の $state/$derived/$effect/$props プリミティブで記述されており、Svelte 4 の export let / $: / 暗黙のリアクティビティではないことを意味します。

これら2つの制約は相互に作用します。つまり、静的なビルドが正常に機能するためには、$effect 内を含め、どのコードもリクエストごとにサーバー上で動作することを前提にしていてはなりません。この点を常に念頭に置いてください。SvelteKit アプリを adapter-static に移行する際に発生するバグのほとんどは、ここが原因です。

ステップ 1 — 適切なプロジェクト形状を選択する

アプリにファイルベースのルーティング、複数のプリレンダリングページ、または SEO に適したルートごとの HTML が必要かどうかを尋ねる(またはコンテキストから推測する)。これによって全体のセットアップが決定されるため、飛ばしてはいけません。

ニーズ使用するもの
単一ページ / ダッシュボード / ウィジェット / 埋め込み、ルーティング不要プレーンな Vite + Svelte (npm create vite@latest -- --template svelte-ts)。最もシンプルな静的専用セットアップ — 誤ってサーバーを導入する可能性がありません。
複数のルートがあり、それぞれが個別の HTML ファイルにプリレンダリングされ、SEO が重要SvelteKit + @sveltejs/adapter-static(prerender = true を設定)。
複数のルートがあるが、SEO は重要でない(内部ツール、Chrome 拡張機能、Electron/Tauri UI)SvelteKit + adapter-static の SPA モード(単一の fallback: 'index.html' シェル、クライアントサイドルーティング)。

特にルーティングが必要でない限り、デフォルトではプレーンな Vite+Svelte を使用します。設定を誤る可能性が低いためです。両方のパスに対する完全なセットアップ手順、設定ファイル、およびデプロイに関する注意事項は references/static-build.md にあります。svelte.config.js / vite.config.js を記憶から生成する前に必ず読んでください。prerender/adapter のオプションは Svelte のバージョンによって変更され、微妙に間違えやすいからです(例:trailingSlash を忘れる、プリレンダリングしたいページに ssr = false を設定してしまい、実際の HTML の代わりに空のシェルが生成される)。

ステップ 2 — ルーンを使ってコンポーネントを記述する(Svelte 4 構文は使わない)

新しい Svelte 5 のコードでは、export let、$:、on:click、またはベアの writable() ストアをコンポーネント状態を管理する 主要な 方法として決して記述しないでください。これらはほとんどの設定でまだコンパイルされますが、レガシーパターンであり、ここで求められているのはルーンです。コアとなる対応関係は次のとおりです:

Svelte 4Svelte 5
let count = 0(トップレベルなのでリアクティブ)let count = $state(0)
$: doubled = count * 2let doubled = $derived(count * 2)(または複数ステートメントのロジックには $derived.by(() => ...))
$: { sideEffect(count) }$effect(() => { sideEffect(count) })
export let namelet { name } = $props()
export let value; bind:value(子が値を変更)let { value = $bindable() } = $props()
onMount/onDestroy(DOM のみのセットアップ/クリーンアップ)$effect(() => { ...; return () => cleanup(); })
<slot name="header">{#snippet header()}...{/snippet} + {@render header()}
writable()/readable() ストア、コンポーネント内で $store.svelte.js/.svelte.ts モジュール内の $state(...) を直接インポート(リアクティビティがコンポーネント外でも機能)

重要なルーンコードを書く前に references/runes.md を読んでください。人々が確実に間違える部分をカバーしています:$derived は純粋でなければならない(副作用や内部での状態変更は不可)、$effect は決して読み取った状態に書き込んではならない(無限ループ)、$state.raw(大きなデータや frozen データをミューテートではなく再代入する場合)、$effect.root/untrack(コンポーネントライフサイクル外で状態を共有する場合)、およびコンポーネント間/モジュール間状態のための正確なクラスベースのパターン。これらを間違えると、奇妙な再レンダリングループや古い UI としてのみ現れるバグが発生し、後から診断するのが困難です。最初から正しく記述する方が良いでしょう。

既存の Svelte 4 コードを移行する場合、npx sv migrate svelte-5 が機械的な書き換えを行います(これが現在のツールです。古いドキュメントでは svelte-migrate を参照している場合があります)。ただし、常に差分をレビューしてください。自動マイグレーションでは、ストアをクロスモジュール状態として使用するパターンや、派生値であるべきエフェクトが確実に修正されるとは限りません。

ステップ 3 — セキュリティチェックリストを適用する

静的ホスティングは「セキュリティ面が無い」ことを意味しません。サーバーサイドの攻撃ベクトルは排除されますが、クライアントサイドの攻撃ベクトル(XSS、シークレット漏洩、安全でないサードパーティ埋め込み)は、それをキャッチするサーバーサイドのレイヤーが存在しないため、完全にあなた自身の責任となります。ユーザー生成コンテンツ、{@html}、環境変数、またはサードパーティスクリプトに触れるタスクを完了する前に、references/security.md を確認してください。最も影響度の高いルールを以下に示します。簡単なタスクでも見逃さないようにしてください:

  1. 無害化されていないコンテンツを {@html} に渡さないでください。 Svelte は {expression} 括弧内のすべてを自動的にエスケープしますが、{@html ...} はそれを完全にバイパスします — これは Svelte アプリにおける最大の XSS 原因です。レンダリングの直前に DOMPurify(または厳格な許可リスト)でサニタイズしてください。パイプラインの前の段階でサニタイズすると、後続の編集でバイパスされる可能性があります。
  2. 実際のシークレットを VITE_ プレフィックス(または SvelteKit の PUBLIC_ プレフィックス)付きの環境変数の背後に絶対に配置しないでください。 Vite はこれらをビルド時に出荷される JS バンドルにインライン化します。誰でもビルドファイルから読み取ることができます。静的専用アプリにはシークレットを隠すサーバーがまったく存在しないため、アプリが実際の API キーを必要とする場合、そのキーはサードパーティサービスのサーバー上、または別途デプロイする別のサーバーレス関数/プロキシの背後に属します。静かにキーをクライアントコードに配線するのではなく、この点を私に指摘してください。
  3. ヘッダーを送信するサーバーがなくても、Content-Security-Policy を設定してください。 静的ホストは、app.html/index.html 内の <meta http-equiv="Content-Security-Policy"> タグ、またはホスト固有のヘッダーファイル(Netlify _headers、Cloudflare Pages _headers、Vercel vercel.json ヘッダー)を介してこれを引き続きサポートします。詳細は references/security.md を参照してください。
  4. すべての動的な href/src は、ユーザー入力や API レスポンスから取得される可能性がある場合、攻撃者によって制御されているものとして扱ってください。 Svelte は href バインディング内の javascript: URL をブロックしません。スキームを自分で検証/許可リスト化してください。
  5. 出荷前に npm audit(または同等のもの)を実行し、ロックファイルを固定してください。 静的サイトは依然としてすべての依存関係のコードをブラウザに直接出荷するため、侵害された依存関係は直接的なクライアントサイドの侵害となります。

参照ファイル

  • references/runes.md — 完全なルーンごとのガイド。正しい/間違ったパターン、クラスベースの共有状態、プロップス/スニペットの TypeScript 型付け、および詳細な Svelte-4-to-5 移行テーブル。
  • references/static-build.md — プレーンな Vite+Svelte セットアップ、SvelteKit+adapter-static セットアップ(フルプリレンダリングモードと SPA フォールバックモード)、trailing-slash/base-path の注意点、およびホストごとのデプロイに関する注意事項(GitHub Pages、Netlify、Cloudflare Pages、S3/CloudFront)。
  • references/security.md — 完全なチェックリスト:XSS/{@html}/DOMPurify、静的ホスト向け CSP(メタタグ + _headers テンプレート)、シークレット/環境変数、安全な外部リンク、依存関係監査、Trusted Types、localStorage/sessionStorage の機密性、および出荷前チェックリスト。
  • assets/ — コピー可能なスターターファイル:vite.config.ts、svelte.config.js(adapter-static)、_headers(CSP テンプレート)、および SecureHtml.svelte(DOMPurify ラッパーコンポーネント)。

必要なときにこれらを参照し、内容を記憶から再現しないでください。設定オプション名や CSP ディレクティブの詳細は、まさに古くなったり誤って記憶されたりする種類の詳細です。

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

関連スキル