add error message if source doesn't exist, cleanup code

pull/15673/head
ComputerGuy 5 months ago
parent 814e089c81
commit 885f3d6153

@ -184,7 +184,7 @@ In the case that you aren't using a proxied `$state` via use of `$state.raw` or
</button>
```
`$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

@ -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
```

@ -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

@ -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':

@ -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}

@ -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 &&

Loading…
Cancel
Save