} */
+ const sources = object[PROXY_SOURCES];
+ if (sources.has(property)) {
+ return /** @type {Source} */ (sources.get(property));
+ }
+ }
+ }
+ return null;
+}
diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js
index 7724edad6822..044b80542998 100644
--- a/packages/svelte/src/internal/client/reactivity/sources.js
+++ b/packages/svelte/src/internal/client/reactivity/sources.js
@@ -221,9 +221,12 @@ export function internal_set(source, value) {
}
/**
- * @param {Source} source
+ * @param {Source | null} source
*/
export function invalidate(source) {
+ if (source === null) {
+ return;
+ }
if (
active_reaction !== null &&
!untracking &&
From 885f3d61536c22bb560ff3ce1695e4045524320d Mon Sep 17 00:00:00 2001
From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com>
Date: Tue, 15 Apr 2025 17:27:39 -0700
Subject: [PATCH 09/12] add error message if source doesn't exist, cleanup code
---
documentation/docs/02-runes/02-$state.md | 11 ++++-
.../98-reference/.generated/client-errors.md | 6 +++
.../svelte/messages/client-errors/errors.md | 4 ++
.../client/visitors/CallExpression.js | 43 +++++++++++++++----
packages/svelte/src/internal/client/errors.js | 15 +++++++
.../src/internal/client/reactivity/sources.js | 4 +-
6 files changed, 70 insertions(+), 13 deletions(-)
diff --git a/documentation/docs/02-runes/02-$state.md b/documentation/docs/02-runes/02-$state.md
index 8415ac9947c9..8742e7836916 100644
--- a/documentation/docs/02-runes/02-$state.md
+++ b/documentation/docs/02-runes/02-$state.md
@@ -184,7 +184,7 @@ In the case that you aren't using a proxied `$state` via use of `$state.raw` or
```
-`$state.invalidate` can also be used with reactive class fields:
+`$state.invalidate` can also be used with reactive class fields, and properties of `$state` objects:
```js
class Box {
@@ -199,10 +199,17 @@ class Counter {
count = $state(new Box(0));
increment() {
- this.count.value++;
+ this.count.value += 1;
$state.invalidate(this.count);
}
}
+
+let counter = $state({count: new Box(0)});
+
+function increment() {
+ counter.count.value += 1;
+ $state.invalidate(counter.count);
+}
```
## Passing state into functions
diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md
index 32348bb78182..cf992c8cab3e 100644
--- a/documentation/docs/98-reference/.generated/client-errors.md
+++ b/documentation/docs/98-reference/.generated/client-errors.md
@@ -116,6 +116,12 @@ The `%rune%` rune is only available inside `.svelte` and `.svelte.js/ts` files
Property descriptors defined on `$state` objects must contain `value` and always be `enumerable`, `configurable` and `writable`.
```
+### state_invalidate_invalid_source
+
+```
+The argument passed to `$state.invalidate` must be a variable or class field declared with `$state` or `$state.raw`, or a property of a `$state` object.
+```
+
### state_prototype_fixed
```
diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md
index c4e68f8fee80..7511abf8d695 100644
--- a/packages/svelte/messages/client-errors/errors.md
+++ b/packages/svelte/messages/client-errors/errors.md
@@ -76,6 +76,10 @@ See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-long
> Property descriptors defined on `$state` objects must contain `value` and always be `enumerable`, `configurable` and `writable`.
+## state_invalidate_invalid_source
+
+> The argument passed to `$state.invalidate` must be a variable or class field declared with `$state` or `$state.raw`, or a property of a `$state` object.
+
## state_prototype_fixed
> Cannot set prototype of `$state` object
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 e29ec869482e..d18f9ff7d99d 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
@@ -1,17 +1,31 @@
-/** @import { CallExpression, Expression, Identifier } from 'estree' */
+/** @import { CallExpression, Expression, Identifier, MemberExpression, Node } from 'estree' */
/** @import { Context } from '../types' */
import { dev, is_ignored } from '../../../../state.js';
import * as b from '../../../../utils/builders.js';
import { get_rune } from '../../../scope.js';
import { transform_inspect_rune } from '../../utils.js';
import * as e from '../../../../errors.js';
-import { object } from '../../../../utils/ast.js';
/**
* @param {CallExpression} node
* @param {Context} context
*/
export function CallExpression(node, context) {
+ /**
+ * Some nodes that get replaced should keep their locations (for better source maps and such)
+ * @template {Node} N
+ * @param {N} node
+ * @param {N} replacement
+ * @returns {N}
+ */
+ function attach_locations(node, replacement) {
+ return {
+ ...replacement,
+ start: node.start,
+ end: node.end,
+ loc: node.loc
+ };
+ }
switch (get_rune(node, context.state.scope)) {
case '$host':
return b.id('$$props.$$host');
@@ -28,11 +42,13 @@ export function CallExpression(node, context) {
/* eslint-disable no-fallthrough */
case '$state.invalidate':
if (node.arguments[0].type === 'Identifier') {
- return b.call('$.invalidate', node.arguments[0]);
+ return b.call(
+ attach_locations(/** @type {Expression} */ (node.callee), b.id('$.invalidate')),
+ node.arguments[0]
+ );
} else if (node.arguments[0].type === 'MemberExpression') {
- const { object: obj, property } = node.arguments[0];
- const root = object(node.arguments[0]);
- if (obj.type === 'ThisExpression') {
+ const { object, property } = node.arguments[0];
+ if (object.type === 'ThisExpression') {
let field;
switch (property.type) {
case 'Identifier':
@@ -45,17 +61,26 @@ export function CallExpression(node, context) {
if (!field || (field.kind !== 'state' && field.kind !== 'raw_state')) {
e.state_invalidate_nonreactive_argument(node);
}
- return b.call('$.invalidate', b.member(b.this, field.id));
+ return b.call(
+ attach_locations(/** @type {Expression} */ (node.callee), b.id('$.invalidate')),
+ attach_locations(node.arguments[0], b.member(object, field.id))
+ );
}
/** @type {Expression[]} */
const source_args = /** @type {Expression[]} */ ([
- context.visit(obj),
+ context.visit(object),
node.arguments[0].computed
? context.visit(property)
: b.literal(/** @type {Identifier} */ (property).name)
]);
const arg = b.call('$.lookup_source', ...source_args);
- return b.call('$.invalidate', arg);
+ return b.call(
+ attach_locations(/** @type {Expression} */ (node.callee), b.id('$.invalidate')),
+ attach_locations(
+ /** @type {Expression} */ (node.arguments[0]),
+ /** @type {Expression} */ (arg)
+ )
+ );
}
case '$effect.root':
diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js
index 429dd99da9b9..c869e82a502d 100644
--- a/packages/svelte/src/internal/client/errors.js
+++ b/packages/svelte/src/internal/client/errors.js
@@ -291,6 +291,21 @@ export function state_descriptors_fixed() {
}
}
+/**
+ * The argument passed to `$state.invalidate` must be a variable or class field declared with `$state` or `$state.raw`, or a property of a `$state` object.
+ * @returns {never}
+ */
+export function state_invalidate_invalid_source() {
+ if (DEV) {
+ const error = new Error(`state_invalidate_invalid_source\nThe argument passed to \`$state.invalidate\` must be a variable or class field declared with \`$state\` or \`$state.raw\`, or a property of a \`$state\` object.\nhttps://svelte.dev/e/state_invalidate_invalid_source`);
+
+ error.name = 'Svelte error';
+ throw error;
+ } else {
+ throw new Error(`https://svelte.dev/e/state_invalidate_invalid_source`);
+ }
+}
+
/**
* Cannot set prototype of `$state` object
* @returns {never}
diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js
index 8aa7b650196c..20fa1106baac 100644
--- a/packages/svelte/src/internal/client/reactivity/sources.js
+++ b/packages/svelte/src/internal/client/reactivity/sources.js
@@ -224,8 +224,8 @@ export function internal_set(source, value) {
* @param {Source | null} source
*/
export function invalidate(source) {
- if (source === null) {
- return;
+ if (source === null || (source.f & DERIVED) !== 0) {
+ e.state_invalidate_invalid_source();
}
if (
active_reaction !== null &&
From 0adad95dc0c08e77450cabeb29a526bdfc3738cd Mon Sep 17 00:00:00 2001
From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com>
Date: Thu, 17 Apr 2025 23:39:38 -0700
Subject: [PATCH 10/12] fix
---
packages/svelte/src/internal/client/proxy.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js
index 561b4a4f6ced..32cf1eb664a9 100644
--- a/packages/svelte/src/internal/client/proxy.js
+++ b/packages/svelte/src/internal/client/proxy.js
@@ -9,7 +9,7 @@ import {
object_prototype
} from '../shared/utils.js';
import { state as source, set } from './reactivity/sources.js';
-import { STATE_SYMBOL, PROXY_SOURCES } from '#client/constants.js';
+import { STATE_SYMBOL, PROXY_SOURCES } from './constants.js';
import { UNINITIALIZED } from '../../constants.js';
import * as e from './errors.js';
import { get_stack } from './dev/tracing.js';
From 32cee9f77f9fa508372005b022722246118a77c2 Mon Sep 17 00:00:00 2001
From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com>
Date: Mon, 21 Apr 2025 16:52:27 -0700
Subject: [PATCH 11/12] tweak jsdoc description
---
packages/svelte/src/ambient.d.ts | 4 +++-
packages/svelte/types/index.d.ts | 4 +++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/packages/svelte/src/ambient.d.ts b/packages/svelte/src/ambient.d.ts
index ff8fd370d1b0..11586918b51a 100644
--- a/packages/svelte/src/ambient.d.ts
+++ b/packages/svelte/src/ambient.d.ts
@@ -94,7 +94,7 @@ declare namespace $state {
: never;
/**
- * Forces an update on a `$state` or `$state.raw` variable or class field.
+ * Forces an update on a variable or class field declared with `$state` or `$state.raw`, or a property of a `$state` object.
* This is primarily meant as an escape hatch to be able to use external or native classes
* with Svelte's reactivity system.
* If you used Svelte 3 or 4, this is the equivalent of `foo = foo`.
@@ -114,6 +114,8 @@ declare namespace $state {
* Count is {counter.count}
*
* ```
+ *
+ * https://svelte.dev/docs/svelte/$state#$state.invalidate
*/
export function invalidate(source: unknown): void;
diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts
index b5f9a4b54a02..97a86a3f83de 100644
--- a/packages/svelte/types/index.d.ts
+++ b/packages/svelte/types/index.d.ts
@@ -2926,7 +2926,7 @@ declare namespace $state {
: never;
/**
- * Forces an update on a `$state` or `$state.raw` variable or class field.
+ * Forces an update on a variable or class field declared with `$state` or `$state.raw`, or a property of a `$state` object.
* This is primarily meant as an escape hatch to be able to use external or native classes
* with Svelte's reactivity system.
* If you used Svelte 3 or 4, this is the equivalent of `foo = foo`.
@@ -2946,6 +2946,8 @@ declare namespace $state {
* Count is {counter.count}
*
* ```
+ *
+ * https://svelte.dev/docs/svelte/$state#$state.invalidate
*/
export function invalidate(source: unknown): void;
From ff8dd1033263f8a3ac67628a0c503282a62f8ded Mon Sep 17 00:00:00 2001
From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com>
Date: Mon, 21 Apr 2025 16:58:27 -0700
Subject: [PATCH 12/12] lint
---
packages/svelte/src/ambient.d.ts | 4 ++--
packages/svelte/types/index.d.ts | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/packages/svelte/src/ambient.d.ts b/packages/svelte/src/ambient.d.ts
index 11586918b51a..28373a784532 100644
--- a/packages/svelte/src/ambient.d.ts
+++ b/packages/svelte/src/ambient.d.ts
@@ -94,7 +94,7 @@ declare namespace $state {
: never;
/**
- * Forces an update on a variable or class field declared with `$state` or `$state.raw`, or a property of a `$state` object.
+ * Forces an update on a variable or class field declared with `$state` or `$state.raw`, or a property of a `$state` object.
* This is primarily meant as an escape hatch to be able to use external or native classes
* with Svelte's reactivity system.
* If you used Svelte 3 or 4, this is the equivalent of `foo = foo`.
@@ -114,7 +114,7 @@ declare namespace $state {
* Count is {counter.count}
*
* ```
- *
+ *
* https://svelte.dev/docs/svelte/$state#$state.invalidate
*/
export function invalidate(source: unknown): void;
diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts
index 97a86a3f83de..208a129f31a8 100644
--- a/packages/svelte/types/index.d.ts
+++ b/packages/svelte/types/index.d.ts
@@ -2926,7 +2926,7 @@ declare namespace $state {
: never;
/**
- * Forces an update on a variable or class field declared with `$state` or `$state.raw`, or a property of a `$state` object.
+ * Forces an update on a variable or class field declared with `$state` or `$state.raw`, or a property of a `$state` object.
* This is primarily meant as an escape hatch to be able to use external or native classes
* with Svelte's reactivity system.
* If you used Svelte 3 or 4, this is the equivalent of `foo = foo`.
@@ -2946,7 +2946,7 @@ declare namespace $state {
* Count is {counter.count}
*
* ```
- *
+ *
* https://svelte.dev/docs/svelte/$state#$state.invalidate
*/
export function invalidate(source: unknown): void;
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