From 967ccc5d86a71f319f265d725d7eee327352b810 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Tue, 29 Apr 2025 17:08:20 -0700 Subject: [PATCH] add warning in dev, add docs --- documentation/docs/02-runes/02-$state.md | 13 +++++++++++++ .../98-reference/.generated/client-warnings.md | 6 ++++++ .../svelte/messages/client-warnings/warnings.md | 4 ++++ .../client/visitors/CallExpression.js | 15 +++++---------- packages/svelte/src/internal/client/proxy.js | 16 ++++++++++++++++ packages/svelte/src/internal/client/warnings.js | 11 +++++++++++ .../_expected/client/index.svelte.js | 4 ++-- 7 files changed, 57 insertions(+), 12 deletions(-) diff --git a/documentation/docs/02-runes/02-$state.md b/documentation/docs/02-runes/02-$state.md index 16630a977b..e98c66f08f 100644 --- a/documentation/docs/02-runes/02-$state.md +++ b/documentation/docs/02-runes/02-$state.md @@ -65,6 +65,19 @@ let { done, text } = todos[0]; todos[0].done = !todos[0].done; ``` +You can also use `$state` in return statements to proxy their argument: + +```js +function createCounter() { + return $state({ + count: 0, + increment() { + this.count++; + } + }); +} +``` + ### Classes You can also use `$state` in class fields (whether public or private): diff --git a/documentation/docs/98-reference/.generated/client-warnings.md b/documentation/docs/98-reference/.generated/client-warnings.md index 77d1df4cdd..92b25f806c 100644 --- a/documentation/docs/98-reference/.generated/client-warnings.md +++ b/documentation/docs/98-reference/.generated/client-warnings.md @@ -219,6 +219,12 @@ Reactive `$state(...)` proxies and the values they proxy have different identiti To resolve this, ensure you're comparing values where both values were created with `$state(...)`, or neither were. Note that `$state.raw(...)` will _not_ create a state proxy. +### state_return_not_proxyable + +``` +The argument passed to a `$state` in a return statement must be a plain object or array. Otherwise, the `$state` call will have no effect +``` + ### transition_slide_display ``` diff --git a/packages/svelte/messages/client-warnings/warnings.md b/packages/svelte/messages/client-warnings/warnings.md index f8e9ebd8a0..7c644409ce 100644 --- a/packages/svelte/messages/client-warnings/warnings.md +++ b/packages/svelte/messages/client-warnings/warnings.md @@ -185,6 +185,10 @@ To fix it, either create callback props to communicate changes, or mark `person` To resolve this, ensure you're comparing values where both values were created with `$state(...)`, or neither were. Note that `$state.raw(...)` will _not_ create a state proxy. +## state_return_not_proxyable + +> The argument passed to a `$state` call in a return statement must be a plain object or array. Otherwise, the `$state` call will have no effect + ## transition_slide_display > The `slide` transition does not work correctly for elements with `display: %value%` diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js index b381f56504..5e16a50550 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js @@ -40,16 +40,11 @@ export function CallExpression(node, context) { parent?.type === 'ReturnStatement' || (parent?.type === 'ArrowFunctionExpression' && parent.body === node) ) { - if ( - node.arguments[0] && - should_proxy( - /** @type {Expression} */ (context.visit(node.arguments[0])), - context.state.scope - ) - ) { - return b.call('$.proxy', node.arguments[0]); - } else { - return node.arguments[0] ?? b.void0; + if (node.arguments[0]) { + return b.call( + '$.return_proxy', + /** @type {Expression} */ (context.visit(node.arguments[0] ?? b.void0)) + ); } } } diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index fd5706eaf2..515531e876 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -12,6 +12,7 @@ import { state as source, set } from './reactivity/sources.js'; import { STATE_SYMBOL } from '#client/constants'; import { UNINITIALIZED } from '../../constants.js'; import * as e from './errors.js'; +import * as w from './warnings.js'; import { get_stack } from './dev/tracing.js'; import { tracing_mode_flag } from '../flags/index.js'; @@ -282,6 +283,21 @@ export function proxy(value) { }); } +/** + * @template T + * @param {T} value + * @param {Source} [prev] + * @returns {T | void} + */ +export function return_proxy(value, prev) { + const res = proxy(value, prev); + if (res !== value || (typeof value === 'object' && value !== null && STATE_SYMBOL in value)) { + // if the argument passed was already a proxy, we don't warn + return res; + } + w.state_return_not_proxyable(); +} + /** * @param {Source} signal * @param {1 | -1} [d] diff --git a/packages/svelte/src/internal/client/warnings.js b/packages/svelte/src/internal/client/warnings.js index c84b487e28..5d41c201b4 100644 --- a/packages/svelte/src/internal/client/warnings.js +++ b/packages/svelte/src/internal/client/warnings.js @@ -170,6 +170,17 @@ export function state_proxy_equality_mismatch(operator) { } } +/** + * The argument passed to a `$state` in a return statement must be a plain object or array. Otherwise, the `$state` call will have no effect + */ +export function state_return_not_proxyable() { + if (DEV) { + console.warn(`%c[svelte] state_return_not_proxyable\n%cThe argument passed to a \`$state\` in a return statement must be a plain object or array. Otherwise, the \`$state\` call will have no effect\nhttps://svelte.dev/e/state_return_not_proxyable`, bold, normal); + } else { + console.warn(`https://svelte.dev/e/state_return_not_proxyable`); + } +} + /** * The `slide` transition does not work correctly for elements with `display: %value%` * @param {string} value diff --git a/packages/svelte/tests/snapshot/samples/state-in-return/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/state-in-return/_expected/client/index.svelte.js index 55f38f174e..de55e87b53 100644 --- a/packages/svelte/tests/snapshot/samples/state-in-return/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/state-in-return/_expected/client/index.svelte.js @@ -2,7 +2,7 @@ import * as $ from 'svelte/internal/client'; export default function proxy(object) { - return $.proxy(object); + return $.return_proxy(object); } export function createCounter() { @@ -11,4 +11,4 @@ export function createCounter() { $.update(count); } -export const proxy_in_arrow = (object) => $.proxy(object); \ No newline at end of file +export const proxy_in_arrow = (object) => $.return_proxy(object); \ No newline at end of file