reassign-derived
Dominic Gannaway 2 months ago
parent 59f374c3cf
commit 23e0803711

@ -2,6 +2,7 @@
/** @import { Context } from '../types' */
import {
extract_all_identifiers_from_expression,
is_binding_pattern_declaration,
is_text_attribute,
object
} from '../../../utils/ast.js';
@ -45,8 +46,13 @@ export function BindDirective(node, context) {
e.bind_invalid_value(node.expression);
}
if (binding?.kind === 'derived' && binding.declaration_kind === 'const') {
e.constant_binding(node.expression, 'derived state');
if (binding?.kind === 'derived') {
if (binding.declaration_kind === 'const') {
e.constant_binding(node.expression, 'constant derived state');
}
if (is_binding_pattern_declaration(binding)) {
e.constant_binding(node.expression, 'destructured derived state');
}
}
if (context.state.analysis.runes && binding?.kind === 'each') {

@ -4,7 +4,7 @@
/** @import { Scope } from '../../../scope' */
/** @import { NodeLike } from '../../../../errors.js' */
import * as e from '../../../../errors.js';
import { extract_identifiers, object } from '../../../../utils/ast.js';
import { extract_identifiers, is_binding_pattern_declaration, object } from '../../../../utils/ast.js';
import * as w from '../../../../warnings.js';
/**
@ -23,17 +23,8 @@ export function validate_assignment(node, argument, state) {
if (binding.declaration_kind === 'const') {
e.constant_assignment(node, 'constant derived state');
}
const binding_path = binding.references.find((r) => r.node === binding.node);
if (binding_path) {
let declarator_path = binding_path.path.findLast((n) => n.type === 'VariableDeclarator');
if (
declarator_path &&
(declarator_path.id.type === 'ObjectPattern' ||
declarator_path.id.type === 'ArrayPattern')
) {
e.constant_assignment(node, 'destructured derived state');
}
if (is_binding_pattern_declaration(binding)) {
e.constant_assignment(node, 'destructured derived state');
}
}

@ -50,7 +50,6 @@ export function build_getter(node, state) {
* @param {PrivateIdentifier | string} proxy_reference
*/
export function build_proxy_reassignment(value, proxy_reference) {
debugger
return dev
? b.call(
'$.proxy',

@ -20,6 +20,23 @@ export function object(expression) {
return expression;
}
/**
* @param {import("#compiler").Binding} binding
* @returns {boolean}
*/
export function is_binding_pattern_declaration(binding) {
const binding_path = binding.references.find((r) => r.node === binding.node);
if (binding_path) {
let declarator_path = binding_path.path.findLast((n) => n.type === 'VariableDeclarator');
return (
declarator_path?.id.type === 'ObjectPattern' || declarator_path?.id.type === 'ArrayPattern'
);
}
return false;
}
/**
* Returns true if the attribute contains a single static text node.
* @param {Attribute} attribute

@ -3,6 +3,6 @@ import { test } from '../../test';
export default test({
error: {
code: 'constant_binding',
message: 'Cannot bind to derived state'
message: 'Cannot bind to destructured derived state'
}
});

@ -1,6 +1,6 @@
<script>
let a = $state(0);
let b = $derived({ a });
let {b} = $derived({ a });
</script>
<input type="number" bind:value={b} />

@ -0,0 +1,19 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: `<button>0</button><button>0</button>`,
test({ assert, target }) {
const [btn1, btn2] = target.querySelectorAll('button');
flushSync(() => btn1.click());
assert.htmlEqual(target.innerHTML, `<button>1</button><button>1</button>`);
flushSync(() => btn2.click());
assert.htmlEqual(target.innerHTML, `<button>1</button><button>2</button>`);
flushSync(() => btn1.click());
assert.htmlEqual(target.innerHTML, `<button>2</button><button>2</button>`);
}
});

@ -0,0 +1,10 @@
<script>
class Instance {
a = $state(0);
b = $state.link(this.a);
}
const instance = new Instance();
</script>
<button onclick={() => instance.a++}>{instance.a}</button>
<button onclick={() => instance.b++}>{instance.b}</button>

@ -0,0 +1,19 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: `<button>0</button><button>0</button>`,
test({ assert, target }) {
const [btn1, btn2] = target.querySelectorAll('button');
flushSync(() => btn1.click());
assert.htmlEqual(target.innerHTML, `<button>1</button><button>1</button>`);
flushSync(() => btn2.click());
assert.htmlEqual(target.innerHTML, `<button>1</button><button>2</button>`);
flushSync(() => btn1.click());
assert.htmlEqual(target.innerHTML, `<button>2</button><button>2</button>`);
}
});

@ -0,0 +1,7 @@
<script>
let a = $state(0);
let b = $derived(a);
</script>
<button onclick={() => a++}>{a}</button>
<button onclick={() => b++}>{b}</button>
Loading…
Cancel
Save