- Apply consistent prefix naming: AE__, GUIDE__, PROJECT__, MODULE__, TODO__ - Move superseded/session docs to documentation/history/ - Migrate old/ directory contents to history/ with updated naming - README.md: replace stale Modules section with accurate current routes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
99 lines
8.2 KiB
Markdown
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 };` |