Files
OSIT-AE-App-Svelte/GEMINI_Svelte_and_Me.md

99 lines
8.2 KiB
Markdown

# 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 };`