Merge branch 'main' into inline-nodes-effect

pull/12878/head
Dominic Gannaway 2 years ago
commit 7b7871d852

@ -0,0 +1,5 @@
---
'svelte': patch
---
feat: better destructuring assignments

@ -11,7 +11,13 @@ import { visit_assignment_expression } from '../../shared/assignments.js';
* @param {Context} context * @param {Context} context
*/ */
export function AssignmentExpression(node, context) { export function AssignmentExpression(node, context) {
return visit_assignment_expression(node, context, build_assignment); const expression = /** @type {Expression} */ (
visit_assignment_expression(node, context, build_assignment) ?? context.next()
);
return is_ignored(node, 'ownership_invalid_mutation')
? b.call('$.skip_ownership_validation', b.thunk(expression))
: expression;
} }
/** /**
@ -109,19 +115,17 @@ function build_assignment(operator, left, right, context) {
return transform.assign(object, value); return transform.assign(object, value);
} }
/** @type {Expression} */ // mutation
let mutation = b.assignment( if (transform?.mutate) {
return transform.mutate(
object,
b.assignment(
operator, operator,
/** @type {Pattern} */ (context.visit(left)), /** @type {Pattern} */ (context.visit(left)),
/** @type {Expression} */ (context.visit(right)) /** @type {Expression} */ (context.visit(right))
)
); );
// mutation
if (transform?.mutate) {
mutation = transform.mutate(object, mutation);
} }
return is_ignored(left, 'ownership_invalid_mutation') return null;
? b.call('$.skip_ownership_validation', b.thunk(mutation))
: mutation;
} }

@ -10,7 +10,7 @@ import { visit_assignment_expression } from '../../shared/assignments.js';
* @param {Context} context * @param {Context} context
*/ */
export function AssignmentExpression(node, context) { export function AssignmentExpression(node, context) {
return visit_assignment_expression(node, context, build_assignment); return visit_assignment_expression(node, context, build_assignment) ?? context.next();
} }
/** /**

@ -41,7 +41,7 @@ export function visit_assignment_expression(node, context, build_assignment) {
if (!changed) { if (!changed) {
// No change to output -> nothing to transform -> we can keep the original assignment // No change to output -> nothing to transform -> we can keep the original assignment
return context.next(); return null;
} }
const is_standalone = /** @type {Node} */ (context.path.at(-1)).type.endsWith('Statement'); const is_standalone = /** @type {Node} */ (context.path.at(-1)).type.endsWith('Statement');
@ -70,8 +70,5 @@ export function visit_assignment_expression(node, context, build_assignment) {
throw new Error(`Unexpected assignment type ${node.left.type}`); throw new Error(`Unexpected assignment type ${node.left.type}`);
} }
return ( return build_assignment(node.operator, node.left, node.right, context);
build_assignment(node.operator, node.left, node.right, context) ??
/** @type {Expression} */ (context.next())
);
} }

@ -572,71 +572,81 @@ export function schedule_effect(signal) {
} }
/** /**
*
* This function both runs render effects and collects user effects in topological order
* from the starting effect passed in. Effects will be collected when they match the filtered
* bitwise flag passed in only. The collected effects array will be populated with all the user
* effects to be flushed.
*
* @param {Effect} effect * @param {Effect} effect
* @param {Effect[]} effects * @param {Effect[]} collected_effects
* @returns {void}
*/ */
function process_effect_children(effect, effects) { function process_effects(effect, collected_effects) {
var current = effect.first; var current_effect = effect.first;
var effects = [];
while (current !== null) {
var next = current.next;
process_effect(current, effects);
current = next;
}
}
/** main_loop: while (current_effect !== null) {
* @param {Effect} effect var flags = current_effect.f;
* @param {Effect[]} effects
*/
function process_effect(effect, effects) {
var flags = effect.f;
// TODO: we probably don't need to check for destroyed as it shouldn't be encountered? // TODO: we probably don't need to check for destroyed as it shouldn't be encountered?
var is_active = (flags & (DESTROYED | INERT)) === 0; var is_active = (flags & (DESTROYED | INERT)) === 0;
var is_branch = (flags & BRANCH_EFFECT) !== 0; var is_branch = (flags & BRANCH_EFFECT) !== 0;
var is_clean = (flags & CLEAN) !== 0; var is_clean = (flags & CLEAN) !== 0;
var child = current_effect.first;
// Skip this branch if it's clean // Skip this branch if it's clean
if (is_active && (!is_branch || !is_clean)) { if (is_active && (!is_branch || !is_clean)) {
if (is_branch) { if (is_branch) {
set_signal_status(effect, CLEAN); set_signal_status(current_effect, CLEAN);
} }
if ((flags & RENDER_EFFECT) !== 0) { if ((flags & RENDER_EFFECT) !== 0) {
if (!is_branch && check_dirtiness(effect)) { if (!is_branch && check_dirtiness(current_effect)) {
update_effect(effect); update_effect(current_effect);
// Child might have been mutated since running the effect
child = current_effect.first;
} }
process_effect_children(effect, effects); if (child !== null) {
current_effect = child;
continue;
}
} else if ((flags & EFFECT) !== 0) { } else if ((flags & EFFECT) !== 0) {
if (is_branch || is_clean) { if (is_branch || is_clean) {
process_effect_children(effect, effects); if (child !== null) {
current_effect = child;
continue;
}
} else { } else {
effects.push(effect); effects.push(current_effect);
} }
} }
} }
var sibling = current_effect.next;
if (sibling === null) {
let parent = current_effect.parent;
while (parent !== null) {
if (effect === parent) {
break main_loop;
}
var parent_sibling = parent.next;
if (parent_sibling !== null) {
current_effect = parent_sibling;
continue main_loop;
}
parent = parent.parent;
}
}
current_effect = sibling;
} }
/**
*
* This function both runs render effects and collects user effects in topological order
* from the starting effect passed in. Effects will be collected when they match the filtered
* bitwise flag passed in only. The collected effects array will be populated with all the user
* effects to be flushed.
*
* @param {Effect} effect
* @param {Effect[]} collected_effects
* @returns {void}
*/
function process_effects(effect, collected_effects) {
/** @type {Effect[]} */
var effects = [];
process_effect_children(effect, effects);
// We might be dealing with many effects here, far more than can be spread into // We might be dealing with many effects here, far more than can be spread into
// an array push call (callstack overflow). So let's deal with each effect in a loop. // an array push call (callstack overflow). So let's deal with each effect in a loop.
for (var i = 0; i < effects.length; i++) { for (var i = 0; i < effects.length; i++) {
var child = effects[i]; child = effects[i];
collected_effects.push(child); collected_effects.push(child);
process_effects(child, collected_effects); process_effects(child, collected_effects);
} }

@ -3,10 +3,14 @@ import * as $ from "svelte/internal/client";
let a = $.source(1); let a = $.source(1);
let b = $.source(2); let b = $.source(2);
let c = 3;
let d = 4;
export function update(array) { export function update(array) {
( (
$.set(a, $.proxy(array[0])), $.set(a, $.proxy(array[0])),
$.set(b, $.proxy(array[1])) $.set(b, $.proxy(array[1]))
); );
[c, d] = array;
} }

@ -3,7 +3,10 @@ import * as $ from "svelte/internal/server";
let a = 1; let a = 1;
let b = 2; let b = 2;
let c = 3;
let d = 4;
export function update(array) { export function update(array) {
[a, b] = array; [a, b] = array;
[c, d] = array;
} }

@ -1,6 +1,9 @@
let a = $state(1); let a = $state(1);
let b = $state(2); let b = $state(2);
let c = 3;
let d = 4;
export function update(array) { export function update(array) {
[a, b] = array; [a, b] = array;
[c, d] = array;
} }

@ -82,6 +82,8 @@ When authoring custom elements, use the new [host rune](/docs/runes#$host) to di
>greet</button> >greet</button>
``` ```
Note that using `$props` and `$host` will put you in [runes mode](/docs/runes) — be sure to update your props and state accordingly.
## `<svelte:component>` in runes mode ## `<svelte:component>` in runes mode
In previous versions of Svelte, the component constructor was fixed when the component was rendered. In other words, if you wanted `<X>` to re-render when `X` changed, you would either have to use `<svelte:component this={X}>` or put the component inside a `{#key X}...{/key}` block. In previous versions of Svelte, the component constructor was fixed when the component was rendered. In other words, if you wanted `<X>` to re-render when `X` changed, you would either have to use `<svelte:component this={X}>` or put the component inside a `{#key X}...{/key}` block.
@ -112,8 +114,6 @@ A derived value may be used in other contexts:
+ <Component /> + <Component />
``` ```
Note that using `$props` and `$host` will put you in [runes mode](/docs/runes) — be sure to update your props and state accordingly.
## `immutable` ## `immutable`
The `immutable` compiler option is deprecated. Use runes mode instead, where all state is immutable (which means that assigning to `object.property` won't cause updates for anything that is observing `object` itself, or a different property of it). The `immutable` compiler option is deprecated. Use runes mode instead, where all state is immutable (which means that assigning to `object.property` won't cause updates for anything that is observing `object` itself, or a different property of it).

Loading…
Cancel
Save