# Gemini's Svelte 5 Learnings and Best Practices

This document outlines key insights and strategies developed during Svelte 5 (with runes mode) refactoring and bug-fixing tasks. It specifically addresses common pitfalls and effective patterns for an AI agent working with Svelte.

## 1. Async/Await in Svelte (Runes Mode)

The most frequent source of errors has been related to asynchronous operations and the `await` keyword.

- **Rule:** Any function or block containing `await` _must_ be explicitly marked `async`.
    - This applies to callbacks within promise chains (`.then()`, `.catch()`, `.finally()`), DOM event handlers (`onclick`, `onchange`), and Svelte lifecycle functions (`onMount`).
    - **Common Error:** `Cannot use keyword 'await' outside an async function`. This happens when `await` is used in a function/block that is not `async`.
    - **Solution:** Ensure the surrounding function or the callback itself is declared `async`.
        - `somePromise.finally(async () => { await someAsyncOperation(); });`
        - `onclick={async () => { await someAsyncOperation(); }}`
        - `onMount(async () => { await someAsyncOperation(); });`

- **Asynchronous Navigation (`goto`):**
    - The correct pattern for asynchronous navigation using `goto` in Svelte 5 is `await goto(await resolve(path), options);`.
    - `import { resolve } from '$app/paths';` is crucial when using `resolve()`. A missing import will lead to `no-undef` errors for `resolve`.
    - `resolve(path)` is crucial to ensure the path is correctly resolved before navigation, especially in universal applications.
    - The `await` before `goto` is necessary if subsequent code depends on the navigation completing or if `invalidateAll` is used.
- **Clearing Stale Caches:** When encountering confusing linting or build errors that don't seem to match the current code, especially after significant refactorings or dependency changes, always try running `npm run clean` to clear stale build artifacts and then re-lint/re-build.

- **Refactoring Promise Chains:**
    - When converting `.then().catch().finally()` chains to `async/await` structure, encapsulate the asynchronous operation within a `try...catch...finally` block.
    - **Incorrect:**
        ```javascript
        someAsyncFunc()
            .then(() => { await anotherAsyncFunc(); }) // ERROR: .then() callback is not async
            .catch(() => { /* ... */ })
            .finally(() => { await lastAsyncFunc(); }); // ERROR: .finally() callback is not async
        ```
    - **Correct:**
        ```javascript
        async () => {
            try {
                const result = await someAsyncFunc();
                await anotherAsyncFunc(result);
            } catch (error) {
                console.error(error);
            } finally {
                await lastAsyncFunc();
            }
        };
        ```
    - **Note:** If the entire handler (e.g., `onclick`) is already `async`, you can `await` the promise directly within it.

## 2. Reactive Declarations & Scoping

Svelte 5's runes mode introduces new ways of managing reactivity, and understanding variable lifecycles is key.

- **`$state` for Reactive Variables:**
    - Variables that are expected to trigger re-renders when their values change, or whose changes need to be observed, should be declared with `$state`.
    - **Common Error:** Warnings like "This reference only captures the initial value of `data`. Did you mean to reference it inside a closure instead?" or "Variable `x` is updated, but is not declared with `$state(...)`."
    - **Solution:** Declare the variable using `$state(initialValue)`.
    - **Context for `data` prop:** When a `data` prop (from a SvelteKit `load` function) is accessed outside of reactive declarations (like `$effect` or event handlers), Svelte might warn that it only captures its initial value. To ensure reactivity or to correctly process it, use it within `$effect` or derive a `$state` variable from it.

- **Function Scoping and Redeclaration:**
    - **Common Error:** `Identifier 'function_name' has already been declared`. This occurs when functions with the same name are defined in overlapping scopes.
    - **Solution:**
        - If a function is only needed within a specific block (e.g., an `onMount` callback), define it _inside_ that block to limit its scope.
        - If a function is truly global and needs to be accessible from templates and multiple `onMount` blocks, define it once outside of any `onMount` and ensure it doesn't conflict with other definitions.
        - Be mindful of helper functions that might be implicitly pulled into global scope by the Svelte compiler if not correctly encapsulated.

## 3. `replace` Tool Usage Strategy (Critical for AI)

My efficiency heavily relies on the `replace` tool, and precision is paramount.

- **Exact `old_string` Matching:**
    - The `old_string` parameter _must_ precisely match the target text in the file, including all whitespace, indentation, newlines, and comments. Even a single character difference will cause the tool to fail with "0 occurrences found".
    - For multi-line replacements, always copy the exact block from the `read_file` output.
- **Contextual Specificity:**
    - Avoid generic `old_string` patterns (e.g., `onclick={() => {`) if there are many such occurrences in a file. Instead, expand the `old_string` to include enough surrounding unique context (e.g., the entire button element or parent `div`) to ensure it matches only one instance (`expected_replacements: 1`).
- **Iterative Refinement:**
    - For complex refactorings involving multiple changes in a file, perform changes in small, atomic steps.
    - **Always `read_file` before each `replace` operation.** This ensures the `old_string` is based on the absolute latest content of the file, preventing mismatches due to previous modifications or unexpected formatting.
    - After each `replace` operation, immediately run `npm run build` (or `npm run lint`) to validate the change and catch new errors early. This is crucial for catching cascading issues introduced by partial refactorings.

## 4. Error Debugging Workflow

- **Prioritize Compiler/Build Errors:** These are blocking issues that prevent the application from running. Address them first.
- **Analyze Error Messages:** Read the full error message carefully, including line numbers, and look for keywords (e.g., `await`, `async`, `declared`, `undefined`).
- **Consult Svelte Documentation:** The Svelte compiler often provides helpful links (`https://svelte.dev/e/js_parse_error`) which should be a first point of reference if the error is unfamiliar.
- **Re-read File Content:** If a `replace` operation fails or produces unexpected results, immediately use `read_file` to verify the exact state of the file before attempting another change.

## 5. Safe Property Binding (Preventing `props_invalid_value`)

Svelte 5 enforces strict contracts for bound properties (`bind:prop`). If a component expects a property to have a fallback/default value, you cannot bind `undefined` to it.

- **The Error:** `Uncaught Svelte error: props_invalid_value. Cannot do bind:prop={undefined} when prop has a fallback value`.
- **The Cause:** Attempting to bind a variable that is currently `undefined` to a component prop that has a default value (e.g., `let { prop = false } = $props()`). Svelte cannot determine whether to use the bound `undefined` or the component's internal default, so it throws an error.
- **The Fix:** **Always initialize bound variables.**
    - **Initialization:** Ensure the variable you are binding to is initialized to a valid value (matching the prop's type) *before* the component mounts.
        - Example: `let myVar = $state(false);` instead of `let myVar = $state();`.
    - **Data Fetching:** If the data comes from an asynchronous source (API, DB), ensure the object properties have default values *immediately* upon assignment, even before the data is fully populated.
        - **Good:** `$slct.obj = { ...apiResult, myBoundProp: apiResult.myBoundProp ?? false };`
        - **Bad:** `$slct.obj = apiResult;` (if `apiResult.myBoundProp` is missing/undefined).
    - **Updates/resets:** When resetting or updating the object, explicitly re-initialize the bound properties.
        - `obj = {};` -> `obj = { myBoundProp: false };`