From 95a020acead1a403ae2d4320bea57227e52143ec Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 11 Apr 2025 10:01:51 -0400 Subject: [PATCH 1/3] fix: update `state_referenced_locally` message (#15733) * fix: update state_referenced_locally message * changeset * update message --- .changeset/stale-gorillas-judge.md | 5 ++++ .../.generated/compile-warnings.md | 9 +++--- .../messages/compile-warnings/script.md | 9 +++--- .../phases/2-analyze/visitors/Identifier.js | 30 ++++++++++++++++++- packages/svelte/src/compiler/warnings.js | 8 +++-- .../static-state-reference/input.svelte | 1 + .../static-state-reference/warnings.json | 24 +++++++++++---- 7 files changed, 68 insertions(+), 18 deletions(-) create mode 100644 .changeset/stale-gorillas-judge.md diff --git a/.changeset/stale-gorillas-judge.md b/.changeset/stale-gorillas-judge.md new file mode 100644 index 000000000000..3d91f401ddf4 --- /dev/null +++ b/.changeset/stale-gorillas-judge.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: update `state_referenced_locally` message diff --git a/documentation/docs/98-reference/.generated/compile-warnings.md b/documentation/docs/98-reference/.generated/compile-warnings.md index 57396bd7fd52..0e94cbadb2e8 100644 --- a/documentation/docs/98-reference/.generated/compile-warnings.md +++ b/documentation/docs/98-reference/.generated/compile-warnings.md @@ -823,15 +823,16 @@ See [the migration guide](v5-migration-guide#Snippets-instead-of-slots) for more ### state_referenced_locally ``` -State referenced in its own scope will never update. Did you mean to reference it inside a closure? +This reference only captures the initial value of `%name%`. Did you mean to reference it inside a %type% instead? ``` This warning is thrown when the compiler detects the following: + - A reactive variable is declared -- the variable is reassigned -- the variable is referenced inside the same scope it is declared and it is a non-reactive context +- ...and later reassigned... +- ...and referenced in the same scope -In this case, the state reassignment will not be noticed by whatever you passed it to. For example, if you pass the state to a function, that function will not notice the updates: +This 'breaks the link' to the original state declaration. For example, if you pass the state to a function, the function loses access to the state once it is reassigned: ```svelte diff --git a/packages/svelte/messages/compile-warnings/script.md b/packages/svelte/messages/compile-warnings/script.md index 8c32fb708266..66037591562b 100644 --- a/packages/svelte/messages/compile-warnings/script.md +++ b/packages/svelte/messages/compile-warnings/script.md @@ -54,14 +54,15 @@ To fix this, wrap your variable declaration with `$state`. ## state_referenced_locally -> State referenced in its own scope will never update. Did you mean to reference it inside a closure? +> This reference only captures the initial value of `%name%`. Did you mean to reference it inside a %type% instead? This warning is thrown when the compiler detects the following: + - A reactive variable is declared -- the variable is reassigned -- the variable is referenced inside the same scope it is declared and it is a non-reactive context +- ...and later reassigned... +- ...and referenced in the same scope -In this case, the state reassignment will not be noticed by whatever you passed it to. For example, if you pass the state to a function, that function will not notice the updates: +This 'breaks the link' to the original state declaration. For example, if you pass the state to a function, the function loses access to the state once it is reassigned: ```svelte diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js index 79dccd5a7cf5..dcbe564543bf 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js @@ -7,6 +7,7 @@ import * as e from '../../../errors.js'; import * as w from '../../../warnings.js'; import { is_rune } from '../../../../utils.js'; import { mark_subtree_dynamic } from './shared/fragment.js'; +import { get_rune } from '../../scope.js'; /** * @param {Identifier} node @@ -111,7 +112,34 @@ export function Identifier(node, context) { (parent.type !== 'AssignmentExpression' || parent.left !== node) && parent.type !== 'UpdateExpression' ) { - w.state_referenced_locally(node); + let type = 'closure'; + + let i = context.path.length; + while (i--) { + const parent = context.path[i]; + + if ( + parent.type === 'ArrowFunctionExpression' || + parent.type === 'FunctionDeclaration' || + parent.type === 'FunctionExpression' + ) { + break; + } + + if ( + parent.type === 'CallExpression' && + parent.arguments.includes(/** @type {any} */ (context.path[i + 1])) + ) { + const rune = get_rune(parent, context.state.scope); + + if (rune === '$state' || rune === '$state.raw') { + type = 'derived'; + break; + } + } + } + + w.state_referenced_locally(node, node.name, type); } if ( diff --git a/packages/svelte/src/compiler/warnings.js b/packages/svelte/src/compiler/warnings.js index a9ea617d3f01..e6fc8caba54f 100644 --- a/packages/svelte/src/compiler/warnings.js +++ b/packages/svelte/src/compiler/warnings.js @@ -641,11 +641,13 @@ export function reactive_declaration_module_script_dependency(node) { } /** - * State referenced in its own scope will never update. Did you mean to reference it inside a closure? + * This reference only captures the initial value of `%name%`. Did you mean to reference it inside a %type% instead? * @param {null | NodeLike} node + * @param {string} name + * @param {string} type */ -export function state_referenced_locally(node) { - w(node, 'state_referenced_locally', `State referenced in its own scope will never update. Did you mean to reference it inside a closure?\nhttps://svelte.dev/e/state_referenced_locally`); +export function state_referenced_locally(node, name, type) { + w(node, 'state_referenced_locally', `This reference only captures the initial value of \`${name}\`. Did you mean to reference it inside a ${type} instead?\nhttps://svelte.dev/e/state_referenced_locally`); } /** diff --git a/packages/svelte/tests/validator/samples/static-state-reference/input.svelte b/packages/svelte/tests/validator/samples/static-state-reference/input.svelte index cd0c7b734925..577527ee60c7 100644 --- a/packages/svelte/tests/validator/samples/static-state-reference/input.svelte +++ b/packages/svelte/tests/validator/samples/static-state-reference/input.svelte @@ -2,6 +2,7 @@ let obj = $state({ a: 0 }); let count = $state(0); let doubled = $derived(count * 2); + let tripled = $state(count * 3); console.log(obj); console.log(count); diff --git a/packages/svelte/tests/validator/samples/static-state-reference/warnings.json b/packages/svelte/tests/validator/samples/static-state-reference/warnings.json index 9ba09415190b..a118d5e4a0d7 100644 --- a/packages/svelte/tests/validator/samples/static-state-reference/warnings.json +++ b/packages/svelte/tests/validator/samples/static-state-reference/warnings.json @@ -1,26 +1,38 @@ [ { "code": "state_referenced_locally", - "message": "State referenced in its own scope will never update. Did you mean to reference it inside a closure?", + "message": "This reference only captures the initial value of `count`. Did you mean to reference it inside a derived instead?", + "start": { + "column": 22, + "line": 5 + }, + "end": { + "column": 27, + "line": 5 + } + }, + { + "code": "state_referenced_locally", + "message": "This reference only captures the initial value of `count`. Did you mean to reference it inside a closure instead?", "start": { "column": 13, - "line": 7 + "line": 8 }, "end": { "column": 18, - "line": 7 + "line": 8 } }, { "code": "state_referenced_locally", - "message": "State referenced in its own scope will never update. Did you mean to reference it inside a closure?", + "message": "This reference only captures the initial value of `doubled`. Did you mean to reference it inside a closure instead?", "start": { "column": 13, - "line": 8 + "line": 9 }, "end": { "column": 20, - "line": 8 + "line": 9 } } ] From 9b2507131ca0134b59cef7da1fdd59603c519f0a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 11 Apr 2025 10:50:11 -0400 Subject: [PATCH 2/3] chore: remove unused documentation markdown files (#15738) --- .../docs/01-introduction/xx-props.md | 139 ----------------- .../xx-reactivity-fundamentals.md | 144 ------------------ .../03-template-syntax/xx-control-flow.md | 111 -------------- .../03-template-syntax/xx-data-fetching.md | 20 --- .../docs/07-misc/xx-reactivity-indepth.md | 6 - 5 files changed, 420 deletions(-) delete mode 100644 documentation/docs/01-introduction/xx-props.md delete mode 100644 documentation/docs/01-introduction/xx-reactivity-fundamentals.md delete mode 100644 documentation/docs/03-template-syntax/xx-control-flow.md delete mode 100644 documentation/docs/03-template-syntax/xx-data-fetching.md delete mode 100644 documentation/docs/07-misc/xx-reactivity-indepth.md diff --git a/documentation/docs/01-introduction/xx-props.md b/documentation/docs/01-introduction/xx-props.md deleted file mode 100644 index cad854d8785d..000000000000 --- a/documentation/docs/01-introduction/xx-props.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: Public API of a component ---- - -### Public API of a component - -Svelte uses the `$props` rune to declare _properties_ or _props_, which means describing the public interface of the component which becomes accessible to consumers of the component. - -> [!NOTE] `$props` is one of several runes, which are special hints for Svelte's compiler to make things reactive. - -```svelte - -``` - -You can specify a fallback value for a prop. It will be used if the component's consumer doesn't specify the prop on the component when instantiating the component, or if the passed value is `undefined` at some point. - -```svelte - -``` - -To get all properties, use rest syntax: - -```svelte - -``` - -You can use reserved words as prop names. - -```svelte - -``` - -If you're using TypeScript, you can declare the prop types: - -```svelte - -``` - -If you're using JavaScript, you can declare the prop types using JSDoc: - -```svelte - -``` - -If you export a `const`, `class` or `function`, it is readonly from outside the component. - -```svelte - -``` - -Readonly props can be accessed as properties on the element, tied to the component using [`bind:this` syntax](bindings#bind:this). - -### Reactive variables - -To change component state and trigger a re-render, just assign to a locally declared variable that was declared using the `$state` rune. - -Update expressions (`count += 1`) and property assignments (`obj.x = y`) have the same effect. - -```svelte - -``` - -Svelte's ` -``` - -If you'd like to react to changes to a prop, use the `$derived` or `$effect` runes instead. - -```svelte - -``` - -For more information on reactivity, read the documentation around runes. diff --git a/documentation/docs/01-introduction/xx-reactivity-fundamentals.md b/documentation/docs/01-introduction/xx-reactivity-fundamentals.md deleted file mode 100644 index d5e67ada71c8..000000000000 --- a/documentation/docs/01-introduction/xx-reactivity-fundamentals.md +++ /dev/null @@ -1,144 +0,0 @@ ---- -title: Reactivity fundamentals ---- - -Reactivity is at the heart of interactive UIs. When you click a button, you expect some kind of response. It's your job as a developer to make this happen. It's Svelte's job to make your job as intuitive as possible, by providing a good API to express reactive systems. - -## Runes - -Svelte 5 uses _runes_, a powerful set of primitives for controlling reactivity inside your Svelte components and inside `.svelte.js` and `.svelte.ts` modules. - -Runes are function-like symbols that provide instructions to the Svelte compiler. You don't need to import them from anywhere — when you use Svelte, they're part of the language. - -The following sections introduce the most important runes for declare state, derived state and side effects at a high level. For more details refer to the later sections on [state](state) and [side effects](side-effects). - -## `$state` - -Reactive state is declared with the `$state` rune: - -```svelte - - - -``` - -You can also use `$state` in class fields (whether public or private): - -```js -// @errors: 7006 2554 -class Todo { - done = $state(false); - text = $state(); - - constructor(text) { - this.text = text; - } -} -``` - -> [!LEGACY] -> In Svelte 4, state was implicitly reactive if the variable was declared at the top level -> -> ```svelte -> -> -> -> ``` - -## `$derived` - -Derived state is declared with the `$derived` rune: - -```svelte - - - - -

{count} doubled is {doubled}

-``` - -The expression inside `$derived(...)` should be free of side-effects. Svelte will disallow state changes (e.g. `count++`) inside derived expressions. - -As with `$state`, you can mark class fields as `$derived`. - -> [!LEGACY] -> In Svelte 4, you could use reactive statements for this. -> -> ```svelte -> -> -> -> ->

{count} doubled is {doubled}

-> ``` -> -> This only worked at the top level of a component. - -## `$effect` - -To run _side-effects_ when the component is mounted to the DOM, and when values change, we can use the `$effect` rune ([demo](/playground/untitled#H4sIAAAAAAAAE31T24rbMBD9lUG7kAQ2sbdlX7xOYNk_aB_rQhRpbAsU2UiTW0P-vbrYubSlYGzmzMzROTPymdVKo2PFjzMzfIusYB99z14YnfoQuD1qQh-7bmdFQEonrOppVZmKNBI49QthCc-OOOH0LZ-9jxnR6c7eUpOnuv6KeT5JFdcqbvbcBcgDz1jXKGg6ncFyBedYR6IzLrAZwiN5vtSxaJA-EzadfJEjKw11C6GR22-BLH8B_wxdByWpvUYtqqal2XB6RVkG1CoHB6U1WJzbnYFDiwb3aGEdDa3Bm1oH12sQLTcNPp7r56m_00mHocSG97_zd7ICUXonA5fwKbPbkE2ZtMJGGVkEdctzQi4QzSwr9prnFYNk5hpmqVuqPQjNnfOJoMF22lUsrq_UfIN6lfSVyvQ7grB3X2mjMZYO3XO9w-U5iLx42qg29md3BP_ni5P4gy9ikTBlHxjLzAtPDlyYZmRdjAbGq7HprEQ7p64v4LU_guu0kvAkhBim3nMplWl8FreQD-CW20aZR0wq12t-KqDWeBywhvexKC3memmDwlHAv9q4Vo2ZK8KtK0CgX7u9J8wXbzdKv-nRnfF_2baTqlYoWUF2h5efl9-n0O6koAMAAA==)): - -```svelte - - - -``` - -The function passed to `$effect` will run when the component mounts, and will re-run after any changes to the values it reads that were declared with `$state` or `$derived` (including those passed in with `$props`). Re-runs are batched (i.e. changing `color` and `size` in the same moment won't cause two separate runs), and happen after any DOM updates have been applied. - -> [!LEGACY] -> In Svelte 4, you could use reactive statements for this. -> -> ```svelte -> -> -> -> ``` -> -> This only worked at the top level of a component. diff --git a/documentation/docs/03-template-syntax/xx-control-flow.md b/documentation/docs/03-template-syntax/xx-control-flow.md deleted file mode 100644 index b73917997bc5..000000000000 --- a/documentation/docs/03-template-syntax/xx-control-flow.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: Control flow ---- - -- if -- each -- await (or move that into some kind of data loading section?) -- NOT: key (move into transition section, because that's the common use case) - -Svelte augments HTML with control flow blocks to be able to express conditionally rendered content or lists. - -The syntax between these blocks is the same: - -- `{#` denotes the start of a block -- `{:` denotes a different branch part of the block. Depending on the block, there can be multiple of these -- `{/` denotes the end of a block - -## {#if ...} - -## {#each ...} - -```svelte - -{#each expression as name}...{/each} -``` - -```svelte - -{#each expression as name, index}...{/each} -``` - -```svelte - -{#each expression as name (key)}...{/each} -``` - -```svelte - -{#each expression as name, index (key)}...{/each} -``` - -```svelte - -{#each expression as name}...{:else}...{/each} -``` - -Iterating over lists of values can be done with an each block. - -```svelte -

Shopping list

-
    - {#each items as item} -
  • {item.name} x {item.qty}
  • - {/each} -
-``` - -You can use each blocks to iterate over any array or array-like value — that is, any object with a `length` property. - -An each block can also specify an _index_, equivalent to the second argument in an `array.map(...)` callback: - -```svelte -{#each items as item, i} -
  • {i + 1}: {item.name} x {item.qty}
  • -{/each} -``` - -If a _key_ expression is provided — which must uniquely identify each list item — Svelte will use it to diff the list when data changes, rather than adding or removing items at the end. The key can be any object, but strings and numbers are recommended since they allow identity to persist when the objects themselves change. - -```svelte -{#each items as item (item.id)} -
  • {item.name} x {item.qty}
  • -{/each} - - -{#each items as item, i (item.id)} -
  • {i + 1}: {item.name} x {item.qty}
  • -{/each} -``` - -You can freely use destructuring and rest patterns in each blocks. - -```svelte -{#each items as { id, name, qty }, i (id)} -
  • {i + 1}: {name} x {qty}
  • -{/each} - -{#each objects as { id, ...rest }} -
  • {id}
  • -{/each} - -{#each items as [id, ...rest]} -
  • {id}
  • -{/each} -``` - -An each block can also have an `{:else}` clause, which is rendered if the list is empty. - -```svelte -{#each todos as todo} -

    {todo.text}

    -{:else} -

    No tasks today!

    -{/each} -``` - -It is possible to iterate over iterables like `Map` or `Set`. Iterables need to be finite and static (they shouldn't change while being iterated over). Under the hood, they are transformed to an array using `Array.from` before being passed off to rendering. If you're writing performance-sensitive code, try to avoid iterables and use regular arrays as they are more performant. - -## Other block types - -Svelte also provides [`#snippet`](snippets), [`#key`](transitions-and-animations) and [`#await`](data-fetching) blocks. You can find out more about them in their respective sections. diff --git a/documentation/docs/03-template-syntax/xx-data-fetching.md b/documentation/docs/03-template-syntax/xx-data-fetching.md deleted file mode 100644 index 4526d5133561..000000000000 --- a/documentation/docs/03-template-syntax/xx-data-fetching.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: Data fetching ---- - -Fetching data is a fundamental part of apps interacting with the outside world. Svelte is unopinionated with how you fetch your data. The simplest way would be using the built-in `fetch` method: - -```svelte - -``` - -While this works, it makes working with promises somewhat unergonomic. Svelte alleviates this problem using the `#await` block. - -## {#await ...} - -## SvelteKit loaders - -Fetching inside your components is great for simple use cases, but it's prone to data loading waterfalls and makes code harder to work with because of the promise handling. SvelteKit solves this problem by providing a opinionated data loading story that is coupled to its router. Learn more about it [in the docs](../kit). diff --git a/documentation/docs/07-misc/xx-reactivity-indepth.md b/documentation/docs/07-misc/xx-reactivity-indepth.md deleted file mode 100644 index b40072552f9a..000000000000 --- a/documentation/docs/07-misc/xx-reactivity-indepth.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Reactivity in depth ---- - -- how to think about Runes ("just JavaScript" with added reactivity, what this means for keeping reactivity alive across boundaries) -- signals From fd57eb362ddf6e9e43936a7d098087f5e4720181 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Apr 2025 17:16:49 -0400 Subject: [PATCH 3/3] Version Packages (#15737) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/stale-gorillas-judge.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/stale-gorillas-judge.md diff --git a/.changeset/stale-gorillas-judge.md b/.changeset/stale-gorillas-judge.md deleted file mode 100644 index 3d91f401ddf4..000000000000 --- a/.changeset/stale-gorillas-judge.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: update `state_referenced_locally` message diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 68a880366361..8b46efc94c82 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.26.1 + +### Patch Changes + +- fix: update `state_referenced_locally` message ([#15733](https://github.com/sveltejs/svelte/pull/15733)) + ## 5.26.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 8267cb521852..a06c73429a24 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.26.0", + "version": "5.26.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index ade51aaf178a..e5cb34ecd5be 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.26.0'; +export const VERSION = '5.26.1'; export const PUBLIC_VERSION = '5'; pFad - Phonifier reborn

    Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

    Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


    Alternative Proxies:

    Alternative Proxy

    pFad Proxy

    pFad v3 Proxy

    pFad v4 Proxy