From c3725c9a220b61c04a2f6ddfc79728261c88ac0f Mon Sep 17 00:00:00 2001 From: "k.hiro1818" Date: Sat, 20 Jun 2026 06:35:17 +0000 Subject: [PATCH 1/2] fix(problems): place TabItem children directly inside Tabs to fix HMR context error Co-Authored-By: Claude Sonnet 4.6 --- .claude/rules/svelte-components.md | 2 + .../2026-06-20/tabitem-context-error/plan.md | 87 +++++++++++++++++++ src/routes/problems/+page.svelte | 44 +++++----- 3 files changed, 110 insertions(+), 23 deletions(-) create mode 100644 docs/dev-notes/2026-06-20/tabitem-context-error/plan.md diff --git a/.claude/rules/svelte-components.md b/.claude/rules/svelte-components.md index bc5b6c3e0..ad3bb9fb2 100644 --- a/.claude/rules/svelte-components.md +++ b/.claude/rules/svelte-components.md @@ -57,6 +57,8 @@ Use `$props()`, `$state()`, `$derived()`, `$effect()` in all components: - **Component**: independent logic, >30 lines, multi-file reuse - Define snippets at **top level** (outside tags) +**Context trap:** Snippets capture context at definition site, not render site. Never define a snippet outside a context provider (e.g. ``) and `{@render}` it inside — `TabItem` calls `getContext` on init and throws during HMR when `` is not in its ancestor chain. Place children directly inside the provider. + ## Async/Abort Handling When user can trigger same action multiple times, use `AbortController`: diff --git a/docs/dev-notes/2026-06-20/tabitem-context-error/plan.md b/docs/dev-notes/2026-06-20/tabitem-context-error/plan.md new file mode 100644 index 000000000..3a8d9c68e --- /dev/null +++ b/docs/dev-notes/2026-06-20/tabitem-context-error/plan.md @@ -0,0 +1,87 @@ +# TabItem コンテキストエラー調査メモ + +## エラー内容 + +``` +Error: TabItem must be used within a Tabs component +``` + +flowbite-svelte の `TabItem` が Tabs コンテキストを取得できないときに投げるランタイムエラー。 + +## 発生状況 + +| 環境 | 発生 | +|------|------| +| ローカル dev サーバ(HMR 時) | 出る(修正前) | +| ローカル dev サーバ(F5 リロード) | dev サーバ再起動前は出て見えたが、HMR キャッシュのアーティファクト | +| `pnpm check`(svelte-check) | 出ない | +| staging(本番ビルド) | 出ない | + +## 原因 + +### コード上の問題(2026-02-22 混入) + +コミット `16315c84`(TabItemWrapper リファクタ)で `problems/+page.svelte` に以下の構造が導入された。 + +```svelte + + {@render problemListTab('コンテスト別', ...)} + + +{#snippet problemListTab(...)} + + ... +{/snippet} +``` + +`{#snippet}` が `` の**外**で宣言されている。Svelte 5 においてスニペットはレキシカルスコープでコンテキストをキャプチャするため、`{@render}` が `` 内で呼ばれていても、HMR 時に `TabItem` が Tabs コンテキストを取得できないケースがある。 + +### なぜ F5 でも出て見えたか + +HMR が古いモジュール状態をキャッシュし続けていたため、dev サーバ再起動前は F5 後もエラーが残っているように見えた。コードのバグではなく dev 環境のアーティファクト。 + +### なぜ staging では出ないか + +HMR なしの通常レンダリングでは、スニペットが `` 内で描画される際にコンテキストが正しく伝播するため。ローカル dev の HMR がコンポーネントを単体で再初期化するときにのみ発火する。 + +## 対処方法(実施済み) + +`{#snippet problemListTab}` を廃止し、`ProblemListTabItem` を `` 直下に直接配置する。 + +```svelte + + + + + + + + + + + + + +``` + +これにより `ProblemListTabItem`(→ `TabItemWrapper` → `TabItem`)が `` の直接の子孫として初期化され、Tabs コンテキストが正しく伝播する。 + +`{#snippet problemListTab}` の廃止に伴い、不要になった `import type { Snippet } from 'svelte'` も削除した。 + +## 検証結果 + +- F5 リロード:エラーなし ✓ +- HMR(ファイル保存):エラーなし ✓ +- `pnpm check`:エラーなし ✓ diff --git a/src/routes/problems/+page.svelte b/src/routes/problems/+page.svelte index 504e6f13d..ca943f540 100644 --- a/src/routes/problems/+page.svelte +++ b/src/routes/problems/+page.svelte @@ -1,6 +1,4 @@