Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/beige-llamas-grab.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': patch
---

feat: allow usage of getContext() within $derived runes
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,9 @@ export function build_component(node, component_name, context, anchor = context.
return (
n.type === 'ExpressionTag' &&
n.expression.type !== 'Identifier' &&
n.expression.type !== 'MemberExpression'
n.expression.type !== 'MemberExpression' &&
n.expression.type !== 'UpdateExpression' &&
n.expression.type !== 'AssignmentExpression'
);
});

Expand Down
6 changes: 4 additions & 2 deletions packages/svelte/src/internal/client/reactivity/deriveds.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import {
skip_reaction,
update_reaction,
increment_version,
set_active_effect
set_active_effect,
component_context
} from '../runtime.js';
import { equals, safe_equals } from './equality.js';
import * as e from '../errors.js';
Expand All @@ -44,6 +45,7 @@ export function derived(fn) {
/** @type {Derived<V>} */
const signal = {
children: null,
ctx: component_context,
deps: null,
equals,
f: flags,
Expand Down Expand Up @@ -169,5 +171,5 @@ export function destroy_derived(signal) {
set_signal_status(signal, DESTROYED);

// TODO we need to ensure we remove the derived from any parent derives
signal.v = signal.children = signal.deps = signal.reactions = null;
signal.v = signal.children = signal.deps = signal.ctx = signal.reactions = null;
}
4 changes: 2 additions & 2 deletions packages/svelte/src/internal/client/reactivity/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export interface Value<V = unknown> extends Signal {
}

export interface Reaction extends Signal {
/** The associated component context */
ctx: null | ComponentContext;
/** The reaction function */
fn: null | Function;
/** Signals that this signal reads from */
Expand All @@ -40,8 +42,6 @@ export interface Effect extends Reaction {
*/
nodes_start: null | TemplateNode;
nodes_end: null | TemplateNode;
/** The associated component context */
ctx: null | ComponentContext;
/** Reactions created inside this signal */
deriveds: null | Derived[];
/** The effect function */
Expand Down
5 changes: 3 additions & 2 deletions packages/svelte/src/internal/client/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ export function update_reaction(reaction) {
var previous_reaction = active_reaction;
var previous_skip_reaction = skip_reaction;
var prev_derived_sources = derived_sources;
var previous_component_context = component_context;
var flags = reaction.f;

new_deps = /** @type {null | Value[]} */ (null);
Expand All @@ -310,6 +311,7 @@ export function update_reaction(reaction) {
active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null;
skip_reaction = !is_flushing_effect && (flags & UNOWNED) !== 0;
derived_sources = null;
component_context = reaction.ctx;

try {
var result = /** @type {Function} */ (0, reaction.fn)();
Expand Down Expand Up @@ -347,6 +349,7 @@ export function update_reaction(reaction) {
active_reaction = previous_reaction;
skip_reaction = previous_skip_reaction;
derived_sources = prev_derived_sources;
component_context = previous_component_context;
}
}

Expand Down Expand Up @@ -422,7 +425,6 @@ export function update_effect(effect) {
var previous_component_context = component_context;

active_effect = effect;
component_context = effect.ctx;

if (DEV) {
var previous_component_fn = dev_current_component_function;
Expand All @@ -449,7 +451,6 @@ export function update_effect(effect) {
handle_error(/** @type {Error} */ (error), effect, previous_component_context);
} finally {
active_effect = previous_effect;
component_context = previous_component_context;

if (DEV) {
dev_current_component_function = previous_component_fn;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script>
import { getContext } from 'svelte'

let count = $state(0);
let total = $derived(multiply(count));

function multiply(num) {
const context = getContext("key");
return num * context;
}
</script>

<button onclick={() => count++}>{total}</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { flushSync } from 'svelte';
import { test } from '../../test';

export default test({
html: `<button>0</button>`,

test({ assert, target }) {
const btn = target.querySelector('button');

flushSync(() => {
btn?.click();
});

assert.htmlEqual(target.innerHTML, `<button>10</button>`);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<script>
import { setContext } from 'svelte'
import Child from './Child.svelte'
setContext("key", 10)
</script>

<Child />

Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,24 @@ export default test({
},

test({ assert, target, warnings }) {
/** @type {any} */
let error;

const handler = (/** @type {any}} */ e) => {
error = e.error;
e.stopImmediatePropagation();
};

window.addEventListener('error', handler, true);

target.querySelector('button')?.click();

assert.throws(() => {
throw error;
}, /state_unsafe_mutation/);

window.removeEventListener('error', handler, true);

assert.deepEqual(warnings, [
'`click` handler at Button.svelte:5:9 should be a function. Did you mean to add a leading `() =>`?'
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,24 @@ export default test({
},

test({ assert, target, warnings }) {
/** @type {any} */
let error;

const handler = (/** @type {any} */ e) => {
error = e.error;
e.stopImmediatePropagation();
};

window.addEventListener('error', handler, true);

target.querySelector('button')?.click();

assert.throws(() => {
throw error;
}, /state_unsafe_mutation/);

window.removeEventListener('error', handler, true);

assert.deepEqual(warnings, [
'`click` handler at main.svelte:9:17 should be a function. Did you mean to remove the trailing `()`?'
]);
Expand Down