From 08579461b19095d5cc5d06496313b2cb4c43eebb Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 9 Jan 2024 21:50:07 +0000 Subject: [PATCH 01/26] fix: improve animation heuristics (#10119) * fix: improve animation heuristics better fix better fix * improve-animation * more fixes * use rAF * feedback * fix absolute positioning * fix more * revert * more fixes --- .changeset/soft-tigers-wink.md | 5 +++ packages/svelte/src/internal/client/each.js | 29 +++++++-------- .../svelte/src/internal/client/runtime.js | 29 ++++++++------- .../svelte/src/internal/client/transitions.js | 35 ++++++++----------- .../svelte/src/internal/client/types.d.ts | 9 +---- .../dynamic-element-animation/_config.js | 6 ++-- 6 files changed, 52 insertions(+), 61 deletions(-) create mode 100644 .changeset/soft-tigers-wink.md diff --git a/.changeset/soft-tigers-wink.md b/.changeset/soft-tigers-wink.md new file mode 100644 index 0000000000..561fbb69a6 --- /dev/null +++ b/.changeset/soft-tigers-wink.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve animation transition heuristics diff --git a/packages/svelte/src/internal/client/each.js b/packages/svelte/src/internal/client/each.js index c51af78572..e66574fba0 100644 --- a/packages/svelte/src/internal/client/each.js +++ b/packages/svelte/src/internal/client/each.js @@ -25,7 +25,6 @@ import { mutable_source, push_destroy_fn, render_effect, - schedule_task, set_signal_value, source } from './runtime.js'; @@ -486,6 +485,17 @@ function reconcile_tracked_array( key = is_computed_key ? keys[a] : item; map_set(item_index, key, a); } + // If keys are animated, we need to do updates before actual moves + if (is_animated) { + for (b = start; b <= a_end; ++b) { + a = map_get(item_index, /** @type {V} */ (a_blocks[b].k)); + if (a !== undefined) { + item = array[a]; + block = a_blocks[b]; + update_each_item_block(block, item, a, flags); + } + } + } for (b = start; b <= a_end; ++b) { a = map_get(item_index, /** @type {V} */ (a_blocks[b].k)); block = a_blocks[b]; @@ -501,22 +511,9 @@ function reconcile_tracked_array( if (pos === MOVED_BLOCK) { mark_lis(sources); } - // If keys are animated, we need to do updates before actual moves - var should_create; - if (is_animated) { - var i = b_length; - while (i-- > 0) { - b_end = i + start; - a = sources[i]; - if (pos === MOVED_BLOCK) { - block = b_blocks[b_end]; - item = array[b_end]; - update_each_item_block(block, item, b_end, flags); - } - } - } var last_block; var last_sibling; + var should_create; while (b_length-- > 0) { b_end = b_length + start; a = sources[b_length]; @@ -715,7 +712,7 @@ function update_each_item_block(block, item, index, type) { // Handle each item animations const each_animation = block.a; if (transitions !== null && (type & EACH_KEYED) !== 0 && each_animation !== null) { - each_animation(block, transitions, index, index_is_reactive); + each_animation(block, transitions); } if (index_is_reactive) { set_signal_value(/** @type {import('./types.js').Signal} */ (block.i), index); diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 38c7e3d12b..a475796e5c 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -38,6 +38,7 @@ let current_scheduler_mode = FLUSH_MICROTASK; // Used for handling scheduling let is_micro_task_queued = false; let is_task_queued = false; +let is_raf_queued = false; // Used for $inspect export let is_batching_effect = false; @@ -52,7 +53,7 @@ let current_queued_effects = []; /** @type {Array<() => void>} */ let current_queued_tasks = []; /** @type {Array<() => void>} */ -let current_queued_microtasks = []; +let current_raf_tasks = []; let flush_count = 0; // Handle signal reactivity tree dependencies and consumer @@ -586,11 +587,6 @@ function flush_queued_effects(effects) { function process_microtask() { is_micro_task_queued = false; - if (current_queued_microtasks.length > 0) { - const tasks = current_queued_microtasks.slice(); - current_queued_microtasks = []; - run_all(tasks); - } if (flush_count > 101) { return; } @@ -637,6 +633,13 @@ function process_task() { run_all(tasks); } +function process_raf_task() { + is_raf_queued = false; + const tasks = current_raf_tasks.slice(); + current_raf_tasks = []; + run_all(tasks); +} + /** * @param {() => void} fn * @returns {void} @@ -653,12 +656,12 @@ export function schedule_task(fn) { * @param {() => void} fn * @returns {void} */ -export function schedule_microtask(fn) { - if (!is_micro_task_queued) { - is_micro_task_queued = true; - queueMicrotask(process_microtask); +export function schedule_raf_task(fn) { + if (!is_raf_queued) { + is_raf_queued = true; + requestAnimationFrame(process_raf_task); } - current_queued_microtasks.push(fn); + current_raf_tasks.push(fn); } /** @@ -721,8 +724,8 @@ export function flushSync(fn) { if (current_queued_pre_and_render_effects.length > 0 || effects.length > 0) { flushSync(); } - if (is_micro_task_queued) { - process_microtask(); + if (is_raf_queued) { + process_raf_task(); } if (is_task_queued) { process_task(); diff --git a/packages/svelte/src/internal/client/transitions.js b/packages/svelte/src/internal/client/transitions.js index 3317520e6d..6ce0f2f403 100644 --- a/packages/svelte/src/internal/client/transitions.js +++ b/packages/svelte/src/internal/client/transitions.js @@ -21,7 +21,7 @@ import { managed_effect, managed_pre_effect, mark_subtree_inert, - schedule_microtask, + schedule_raf_task, untrack } from './runtime.js'; import { raf } from './timing.js'; @@ -646,7 +646,8 @@ function each_item_transition(transition) { transitions.delete(transition); if (transition.r !== 'key') { for (let other of transitions) { - if (other.r === 'key' || other.r === 'in') { + const type = other.r; + if (type === 'key' || type === 'in') { transitions.delete(other); } } @@ -664,26 +665,18 @@ function each_item_transition(transition) { * * @param {import('./types.js').EachItemBlock} block * @param {Set} transitions - * @param {number} index - * @param {boolean} index_is_reactive */ -function each_item_animate(block, transitions, index, index_is_reactive) { - let prev_index = block.i; - if (index_is_reactive) { - prev_index = /** @type {import('./types.js').Signal} */ (prev_index).v; - } - const items = block.p.v; - if (prev_index !== index && /** @type {number} */ (index) < items.length) { - const from_dom = /** @type {Element} */ (get_first_element(block)); - const from = from_dom.getBoundingClientRect(); - // Cancel any existing key transitions - for (const transition of transitions) { - if (transition.r === 'key') { - transition.c(); - } +function each_item_animate(block, transitions) { + const from_dom = /** @type {Element} */ (get_first_element(block)); + const from = from_dom.getBoundingClientRect(); + // Cancel any existing key transitions + for (const transition of transitions) { + const type = transition.r; + if (type === 'key') { + transition.c(); } - schedule_microtask(() => { - trigger_transitions(transitions, 'key', from); - }); } + schedule_raf_task(() => { + trigger_transitions(transitions, 'key', from); + }); } diff --git a/packages/svelte/src/internal/client/types.d.ts b/packages/svelte/src/internal/client/types.d.ts index af21817c69..31dd4f1162 100644 --- a/packages/svelte/src/internal/client/types.d.ts +++ b/packages/svelte/src/internal/client/types.d.ts @@ -288,14 +288,7 @@ export type EachBlock = { export type EachItemBlock = { /** transition */ - a: - | null - | (( - block: EachItemBlock, - transitions: Set, - index: number, - index_is_reactive: boolean - ) => void); + a: null | ((block: EachItemBlock, transitions: Set) => void); /** dom */ d: null | TemplateNode | Array; /** effect */ diff --git a/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js index 5a6f0818fb..6315a32d86 100644 --- a/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js @@ -53,9 +53,9 @@ export default test({ divs = target.querySelectorAll('div'); assert.ok(~divs[0].style.transform); - assert.equal(divs[1].style.transform, ''); - assert.equal(divs[2].style.transform, ''); - assert.equal(divs[3].style.transform, ''); + assert.equal(divs[1].style.transform, 'translate(1px, 0px)'); + assert.equal(divs[2].style.transform, 'translate(1px, 0px)'); + assert.equal(divs[3].style.transform, 'translate(1px, 0px)'); assert.ok(~divs[4].style.transform); raf.tick(100); From 960fe308a531e7d0dfd3f40da51c4822cd203ad7 Mon Sep 17 00:00:00 2001 From: brunnerh Date: Tue, 9 Jan 2024 22:50:48 +0100 Subject: [PATCH 02/26] chore: remove internal functions from `svelte/transition` exports (#10132) * chore: remove internal functions from `svelte/transition` exports * regenerate types --- .changeset/lucky-toes-begin.md | 5 +++++ packages/svelte/src/transition/index.js | 4 ++-- packages/svelte/types/index.d.ts | 6 ------ 3 files changed, 7 insertions(+), 8 deletions(-) create mode 100644 .changeset/lucky-toes-begin.md diff --git a/.changeset/lucky-toes-begin.md b/.changeset/lucky-toes-begin.md new file mode 100644 index 0000000000..07d78c0d14 --- /dev/null +++ b/.changeset/lucky-toes-begin.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: remove internal functions from `svelte/transition` exports diff --git a/packages/svelte/src/transition/index.js b/packages/svelte/src/transition/index.js index 3b28b16f9d..b3ebbbba0a 100644 --- a/packages/svelte/src/transition/index.js +++ b/packages/svelte/src/transition/index.js @@ -12,14 +12,14 @@ function cubic_out(t) { * @param {number} t * @returns {number} */ -export function cubic_in_out(t) { +function cubic_in_out(t) { return t < 0.5 ? 4.0 * t * t * t : 0.5 * Math.pow(2.0 * t - 2.0, 3.0) + 1.0; } /** @param {number | string} value * @returns {[number, string]} */ -export function split_css_unit(value) { +function split_css_unit(value) { const split = typeof value === 'string' && value.match(/^\s*(-?[\d.]+)([^\s]*)\s*$/); return split ? [parseFloat(split[1]), split[2] || 'px'] : [/** @type {number} */ (value), 'px']; } diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index c2a22a6e9b..72ec552cf8 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -2079,12 +2079,6 @@ declare module 'svelte/transition' { duration?: number | ((len: number) => number); easing?: EasingFunction; } - /** - * https://svelte.dev/docs/svelte-easing - * */ - export function cubic_in_out(t: number): number; - - export function split_css_unit(value: number | string): [number, string]; /** * Animates a `blur` filter alongside an element's opacity. * From 4ad5b73341b7bdd775865dd1ecf0c010860f3859 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Tue, 9 Jan 2024 23:58:10 +0000 Subject: [PATCH 03/26] fix: further animation transition improvements (#10138) * fix: further animation transition improvements * clever hack --- .changeset/slow-beds-shave.md | 5 +++ .../svelte/src/internal/client/transitions.js | 39 ++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 .changeset/slow-beds-shave.md diff --git a/.changeset/slow-beds-shave.md b/.changeset/slow-beds-shave.md new file mode 100644 index 0000000000..793c66b6a0 --- /dev/null +++ b/.changeset/slow-beds-shave.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: further animation transition improvements diff --git a/packages/svelte/src/internal/client/transitions.js b/packages/svelte/src/internal/client/transitions.js index 6ce0f2f403..6334c6310f 100644 --- a/packages/svelte/src/internal/client/transitions.js +++ b/packages/svelte/src/internal/client/transitions.js @@ -369,6 +369,8 @@ function create_transition(dom, init, direction, effect) { }, // out o() { + // @ts-ignore + const has_keyed_transition = dom.__animate; const needs_reverse = direction === 'both' && curr_direction !== 'out'; curr_direction = 'out'; if (animation === null || cancelled) { @@ -385,6 +387,35 @@ function create_transition(dom, init, direction, effect) { /** @type {Animation | TickAnimation} */ (animation).play(); } } + // If we're outroing an element that has an animation, then we need to fix + // its position to ensure it behaves nicely without causing layout shift. + if (has_keyed_transition) { + const style = getComputedStyle(dom); + const position = style.position; + + if (position !== 'absolute' && position !== 'fixed') { + const { width, height } = style; + const a = dom.getBoundingClientRect(); + dom.style.position = 'absolute'; + + dom.style.width = width; + dom.style.height = height; + const b = dom.getBoundingClientRect(); + if (a.left !== b.left || a.top !== b.top) { + // Previously, in the Svelte 4, we'd just apply the transform the the DOM element. However, + // because we're now using Web Animations, we can't do that as it won't work properly if the + // animation is also making use of the same transformations. So instead, we apply an instantaneous + // animation and pause it on the first frame, just applying the same behavior. + const style = getComputedStyle(dom); + const transform = style.transform === 'none' ? '' : style.transform; + const frame = { + transform: `${transform} translate(${a.left - b.left}px, ${a.top - b.top}px)` + }; + const animation = dom.animate([frame, frame], { duration: 1 }); + animation.pause(); + } + } + } }, // cancel c() { @@ -432,10 +463,16 @@ function is_transition_block(block) { export function bind_transition(dom, get_transition_fn, props_fn, direction, global) { const transition_effect = /** @type {import('./types.js').EffectSignal} */ (current_effect); const block = current_block; + const is_keyed_transition = direction === 'key'; let can_show_intro_on_mount = true; let can_apply_lazy_transitions = false; + if (is_keyed_transition) { + // @ts-ignore + dom.__animate = true; + } + /** @type {import('./types.js').Block | null} */ let transition_block = block; while (transition_block !== null) { @@ -479,7 +516,7 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo const init = (from) => untrack(() => { const props = props_fn === null ? {} : props_fn(); - return direction === 'key' + return is_keyed_transition ? /** @type {import('./types.js').AnimateFn} */ (transition_fn)( dom, { from: /** @type {DOMRect} */ (from), to: dom.getBoundingClientRect() }, From 0e9b83c20d533f50e1188f6b361c9cc897efb7c3 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 10 Jan 2024 02:46:00 +0000 Subject: [PATCH 04/26] fix: improve outro behavior with transitions (#10139) * fix: improve outro behavior with transitions * debug * revise --- .changeset/empty-bulldogs-exercise.md | 5 ++ .../svelte/src/internal/client/runtime.js | 19 +++-- .../svelte/src/internal/client/transitions.js | 69 +++++++++++-------- 3 files changed, 58 insertions(+), 35 deletions(-) create mode 100644 .changeset/empty-bulldogs-exercise.md diff --git a/.changeset/empty-bulldogs-exercise.md b/.changeset/empty-bulldogs-exercise.md new file mode 100644 index 0000000000..087a8f7775 --- /dev/null +++ b/.changeset/empty-bulldogs-exercise.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve outro behavior with transitions diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index a475796e5c..231dd0aebb 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -1012,10 +1012,11 @@ export function mutate_store(store, expression, new_value) { /** * @param {import('./types.js').ComputationSignal} signal + * @param {import('./types.js').ComputationSignal} root * @param {boolean} inert * @returns {void} */ -export function mark_subtree_inert(signal, inert) { +export function mark_subtree_inert(signal, root, inert, visited_blocks = new Set()) { const flags = signal.f; const is_already_inert = (flags & INERT) !== 0; if (is_already_inert !== inert) { @@ -1025,22 +1026,28 @@ export function mark_subtree_inert(signal, inert) { } // Nested if block effects const block = signal.b; - if (block !== null) { + if (block !== null && !visited_blocks.has(block)) { + visited_blocks.add(block); const type = block.t; if (type === IF_BLOCK) { + const condition_effect = block.e; + const root_block = root.b?.p; + if (condition_effect !== null && root_block?.t === IF_BLOCK) { + mark_subtree_inert(condition_effect, root, inert); + } const consequent_effect = block.ce; if (consequent_effect !== null) { - mark_subtree_inert(consequent_effect, inert); + mark_subtree_inert(consequent_effect, root, inert, visited_blocks); } const alternate_effect = block.ae; if (alternate_effect !== null) { - mark_subtree_inert(alternate_effect, inert); + mark_subtree_inert(alternate_effect, root, inert, visited_blocks); } } else if (type === EACH_BLOCK) { const items = block.v; for (let { e: each_item_effect } of items) { if (each_item_effect !== null) { - mark_subtree_inert(each_item_effect, inert); + mark_subtree_inert(each_item_effect, root, inert, visited_blocks); } } } @@ -1050,7 +1057,7 @@ export function mark_subtree_inert(signal, inert) { if (references !== null) { let i; for (i = 0; i < references.length; i++) { - mark_subtree_inert(references[i], inert); + mark_subtree_inert(references[i], root, inert, visited_blocks); } } } diff --git a/packages/svelte/src/internal/client/transitions.js b/packages/svelte/src/internal/client/transitions.js index 6334c6310f..0fb950094b 100644 --- a/packages/svelte/src/internal/client/transitions.js +++ b/packages/svelte/src/internal/client/transitions.js @@ -371,22 +371,6 @@ function create_transition(dom, init, direction, effect) { o() { // @ts-ignore const has_keyed_transition = dom.__animate; - const needs_reverse = direction === 'both' && curr_direction !== 'out'; - curr_direction = 'out'; - if (animation === null || cancelled) { - cancelled = false; - create_animation(); - } - if (animation === null) { - transition.x(); - } else { - dispatch_event(dom, 'outrostart'); - if (needs_reverse) { - /** @type {Animation | TickAnimation} */ (animation).reverse(); - } else { - /** @type {Animation | TickAnimation} */ (animation).play(); - } - } // If we're outroing an element that has an animation, then we need to fix // its position to ensure it behaves nicely without causing layout shift. if (has_keyed_transition) { @@ -402,20 +386,46 @@ function create_transition(dom, init, direction, effect) { dom.style.height = height; const b = dom.getBoundingClientRect(); if (a.left !== b.left || a.top !== b.top) { - // Previously, in the Svelte 4, we'd just apply the transform the the DOM element. However, - // because we're now using Web Animations, we can't do that as it won't work properly if the - // animation is also making use of the same transformations. So instead, we apply an instantaneous - // animation and pause it on the first frame, just applying the same behavior. - const style = getComputedStyle(dom); - const transform = style.transform === 'none' ? '' : style.transform; - const frame = { - transform: `${transform} translate(${a.left - b.left}px, ${a.top - b.top}px)` - }; - const animation = dom.animate([frame, frame], { duration: 1 }); - animation.pause(); + const translate = `translate(${a.left - b.left}px, ${a.top - b.top}px)`; + const existing_transform = style.transform; + if (existing_transform === 'none') { + dom.style.transform = translate; + } else { + // Previously, in the Svelte 4, we'd just apply the transform the the DOM element. However, + // because we're now using Web Animations, we can't do that as it won't work properly if the + // animation is also making use of the same transformations. So instead, we apply an + // instantaneous animation and pause it on the first frame, just applying the same behavior. + // We also need to take into consideration matrix transforms and how they might combine with + // an existing behavior that is already in progress (such as scale). + // > Follow the white rabbit. + const transform = existing_transform.startsWith('matrix(1,') + ? translate + : `matrix(1,0,0,1,0,0)`; + const frame = { + transform + }; + const animation = dom.animate([frame, frame], { duration: 1 }); + animation.pause(); + } } } } + const needs_reverse = direction === 'both' && curr_direction !== 'out'; + curr_direction = 'out'; + if (animation === null || cancelled) { + cancelled = false; + create_animation(); + } + if (animation === null) { + transition.x(); + } else { + dispatch_event(dom, 'outrostart'); + if (needs_reverse) { + /** @type {Animation | TickAnimation} */ (animation).reverse(); + } else { + /** @type {Animation | TickAnimation} */ (animation).play(); + } + } }, // cancel c() { @@ -584,6 +594,7 @@ export function trigger_transitions(transitions, target_direction, from) { const outros = []; for (const transition of transitions) { const direction = transition.r; + const effect = transition.e; if (target_direction === 'in') { if (direction === 'in' || direction === 'both') { transition.in(); @@ -591,7 +602,7 @@ export function trigger_transitions(transitions, target_direction, from) { transition.c(); } transition.d.inert = false; - mark_subtree_inert(transition.e, false); + mark_subtree_inert(effect, effect, false); } else if (target_direction === 'key') { if (direction === 'key') { transition.p = transition.i(/** @type {DOMRect} */ (from)); @@ -603,7 +614,7 @@ export function trigger_transitions(transitions, target_direction, from) { outros.push(transition.o); } transition.d.inert = true; - mark_subtree_inert(transition.e, true); + mark_subtree_inert(effect, effect, true); } } if (outros.length > 0) { From a13c9469664857828db94ca7dcb4f72fc6aa671b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 02:51:38 +0000 Subject: [PATCH 05/26] Version Packages (next) (#10137) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 4 ++++ packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index ea45a1e6e1..ba0fdc4095 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -38,6 +38,7 @@ "early-ads-tie", "eight-steaks-shout", "eighty-bikes-camp", + "empty-bulldogs-exercise", "empty-crabs-think", "fair-crabs-check", "famous-knives-sneeze", @@ -91,6 +92,7 @@ "lovely-items-turn", "lovely-rules-eat", "lucky-schools-hang", + "lucky-toes-begin", "moody-frogs-exist", "moody-owls-cry", "nasty-lions-double", @@ -137,12 +139,14 @@ "short-buses-camp", "slimy-clouds-talk", "slimy-walls-draw", + "slow-beds-shave", "slow-chefs-dream", "small-papayas-laugh", "smart-parents-swim", "smart-zebras-pay", "soft-clocks-remember", "soft-geese-learn", + "soft-tigers-wink", "sour-forks-stare", "sour-rules-march", "spicy-plums-admire", diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 82098fc762..5d06514780 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.0.0-next.32 + +### Patch Changes + +- fix: improve outro behavior with transitions ([#10139](https://github.com/sveltejs/svelte/pull/10139)) + +- chore: remove internal functions from `svelte/transition` exports ([#10132](https://github.com/sveltejs/svelte/pull/10132)) + +- fix: further animation transition improvements ([#10138](https://github.com/sveltejs/svelte/pull/10138)) + +- fix: improve animation transition heuristics ([#10119](https://github.com/sveltejs/svelte/pull/10119)) + ## 5.0.0-next.31 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index d2c29459bc..3e0036a9d4 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.0.0-next.31", + "version": "5.0.0-next.32", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 17bb8b9464..0d87622335 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.0.0-next.31'; +export const VERSION = '5.0.0-next.32'; export const PUBLIC_VERSION = '5'; From e2fc04d0d55e347e31ebc1c1b98104cba3a4f041 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 10 Jan 2024 16:10:04 +0000 Subject: [PATCH 06/26] fix: improve template literal expression output generation (#10147) * fix: improve template literal expression output generation * do not proxy template literal --- .changeset/eighty-days-cheat.md | 5 +++ .../phases/3-transform/client/utils.js | 1 + .../3-transform/client/visitors/template.js | 5 +++ packages/svelte/src/internal/client/render.js | 2 +- .../_expected/client/main.svelte.js | 2 +- .../samples/each-string-template/_config.js | 3 ++ .../_expected/client/index.svelte.js | 30 ++++++++++++++ .../_expected/server/index.svelte.js | 22 +++++++++++ .../samples/each-string-template/index.svelte | 3 ++ .../samples/state-proxy-literal/_config.js | 3 ++ .../_expected/client/index.svelte.js | 39 +++++++++++++++++++ .../_expected/server/index.svelte.js | 20 ++++++++++ .../samples/state-proxy-literal/index.svelte | 17 ++++++++ .../_expected/client/index.svelte.js | 2 +- 14 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 .changeset/eighty-days-cheat.md create mode 100644 packages/svelte/tests/snapshot/samples/each-string-template/_config.js create mode 100644 packages/svelte/tests/snapshot/samples/each-string-template/_expected/client/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/each-string-template/index.svelte create mode 100644 packages/svelte/tests/snapshot/samples/state-proxy-literal/_config.js create mode 100644 packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/state-proxy-literal/index.svelte diff --git a/.changeset/eighty-days-cheat.md b/.changeset/eighty-days-cheat.md new file mode 100644 index 0000000000..1dab9a948f --- /dev/null +++ b/.changeset/eighty-days-cheat.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve template literal expression output generation diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js index 2ecec2eeb3..19fef4e5bd 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -615,6 +615,7 @@ export function should_proxy_or_freeze(node) { if ( !node || node.type === 'Literal' || + node.type === 'TemplateLiteral' || node.type === 'ArrowFunctionExpression' || node.type === 'FunctionExpression' || node.type === 'UnaryExpression' || diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index 8c3cb748c4..b6708bc851 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -1663,6 +1663,11 @@ function serialize_template_literal(values, visit, state) { if (node.type === 'Text') { const last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1)); last.value.raw += sanitize_template_string(node.data); + } else if (node.type === 'ExpressionTag' && node.expression.type === 'Literal') { + const last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1)); + if (node.expression.value != null) { + last.value.raw += sanitize_template_string(node.expression.value + ''); + } } else { if (node.type === 'ExpressionTag' && node.metadata.contains_call_expression) { contains_call_expression = true; diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 30da7f35bb..2062095235 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -2803,7 +2803,7 @@ export function mount(component, options) { ).c = options.context; } // @ts-expect-error the public typings are not what the actual function looks like - accessors = component(anchor, options.props || {}, options.events || {}); + accessors = component(anchor, options.props || {}); if (options.context) { pop(); } diff --git a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js index e233a9d51e..f0960dc93d 100644 --- a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js +++ b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js @@ -45,4 +45,4 @@ export default function Main($$anchor, $$props) { $.close_frag($$anchor, fragment); $.pop(); -} +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/each-string-template/_config.js b/packages/svelte/tests/snapshot/samples/each-string-template/_config.js new file mode 100644 index 0000000000..f47bee71df --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/each-string-template/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../test'; + +export default test({}); diff --git a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/client/index.svelte.js new file mode 100644 index 0000000000..5680d12e63 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/client/index.svelte.js @@ -0,0 +1,30 @@ +// index.svelte (Svelte VERSION) +// Note: compiler output will change before 5.0 is released! +import "svelte/internal/disclose-version"; +import * as $ from "svelte/internal"; + +export default function Each_string_template($$anchor, $$props) { + $.push($$props, false); + + /* Init */ + var fragment = $.comment($$anchor); + var node = $.child_frag(fragment); + + $.each_indexed( + node, + () => ['foo', 'bar', 'baz'], + 1, + ($$anchor, thing, $$index) => { + /* Init */ + var node_1 = $.space($$anchor); + + /* Update */ + $.text_effect(node_1, () => `${$.stringify($.unwrap(thing))}, `); + $.close($$anchor, node_1); + }, + null + ); + + $.close_frag($$anchor, fragment); + $.pop(); +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js new file mode 100644 index 0000000000..a4bbf5a982 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js @@ -0,0 +1,22 @@ +// index.svelte (Svelte VERSION) +// Note: compiler output will change before 5.0 is released! +import * as $ from "svelte/internal/server"; + +export default function Each_string_template($$payload, $$props) { + $.push(false); + + const anchor = $.create_anchor($$payload); + const each_array = $.ensure_array_like(['foo', 'bar', 'baz']); + + $$payload.out += `${anchor}`; + + for (let $$index = 0; $$index < each_array.length; $$index++) { + const thing = each_array[$$index]; + const anchor_1 = $.create_anchor($$payload); + + $$payload.out += `${anchor_1}${$.escape(thing)},${$.escape(' ')}${anchor_1}`; + } + + $$payload.out += `${anchor}`; + $.pop(); +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/each-string-template/index.svelte b/packages/svelte/tests/snapshot/samples/each-string-template/index.svelte new file mode 100644 index 0000000000..f2ab129af6 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/each-string-template/index.svelte @@ -0,0 +1,3 @@ +{#each ['foo', 'bar', 'baz'] as thing} + {thing},{' '} +{/each} diff --git a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_config.js b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_config.js new file mode 100644 index 0000000000..f47bee71df --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../test'; + +export default test({}); diff --git a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js new file mode 100644 index 0000000000..f1a5a0ee42 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js @@ -0,0 +1,39 @@ +// index.svelte (Svelte VERSION) +// Note: compiler output will change before 5.0 is released! +import "svelte/internal/disclose-version"; +import * as $ from "svelte/internal"; + +function reset(_, str, tpl) { + $.set(str, ''); + $.set(str, ``); + $.set(tpl, ''); + $.set(tpl, ``); +} + +var frag = $.template(` `, true); + +export default function State_proxy_literal($$anchor, $$props) { + $.push($$props, true); + + let str = $.source(''); + let tpl = $.source(``); + /* Init */ + var fragment = $.open_frag($$anchor, true, frag); + var node = $.child_frag(fragment); + + $.remove_input_attr_defaults(node); + + var input = $.sibling($.sibling(node)); + + $.remove_input_attr_defaults(input); + + var button = $.sibling($.sibling(input)); + + $.bind_value(node, () => $.get(str), ($$value) => $.set(str, $.proxy($$value))); + $.bind_value(input, () => $.get(tpl), ($$value) => $.set(tpl, $.proxy($$value))); + button.__click = [reset, str, tpl]; + $.close_frag($$anchor, fragment); + $.pop(); +} + +$.delegate(["click"]); \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js new file mode 100644 index 0000000000..c541299e36 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js @@ -0,0 +1,20 @@ +// index.svelte (Svelte VERSION) +// Note: compiler output will change before 5.0 is released! +import * as $ from "svelte/internal/server"; + +export default function State_proxy_literal($$payload, $$props) { + $.push(true); + + let str = ''; + let tpl = ``; + + function reset() { + str = ''; + str = ``; + tpl = ''; + tpl = ``; + } + + $$payload.out += ` `; + $.pop(); +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/state-proxy-literal/index.svelte b/packages/svelte/tests/snapshot/samples/state-proxy-literal/index.svelte new file mode 100644 index 0000000000..155682f955 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/state-proxy-literal/index.svelte @@ -0,0 +1,17 @@ + + + + + + diff --git a/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js index 937e22410f..6fcc18c0c4 100644 --- a/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js @@ -14,4 +14,4 @@ export default function Svelte_element($$anchor, $$props) { $.element(node, tag); $.close_frag($$anchor, fragment); $.pop(); -} +} \ No newline at end of file From ff8dff993d31cf5990697d6bb3dba9c27265b47d Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 10 Jan 2024 17:18:38 +0000 Subject: [PATCH 07/26] fix: improve ssr template code generation (#10151) * fix: improve ssr template code generation * lint --- .changeset/breezy-carrots-flash.md | 5 +++++ .../phases/3-transform/server/transform-server.js | 10 ++++++---- .../_expected/server/index.svelte.js | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 .changeset/breezy-carrots-flash.md diff --git a/.changeset/breezy-carrots-flash.md b/.changeset/breezy-carrots-flash.md new file mode 100644 index 0000000000..8d9fce63f9 --- /dev/null +++ b/.changeset/breezy-carrots-flash.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve ssr template code generation diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index 5cb479b91a..d097e68676 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -187,10 +187,12 @@ function process_children(nodes, parent, { visit, state }) { const node = sequence[i]; if (node.type === 'Text' || node.type === 'Comment') { let last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1)); - last.value.raw += - node.type === 'Comment' - ? `` - : sanitize_template_string(escape_html(node.data)); + last.value.raw += node.type === 'Comment' ? `` : escape_html(node.data); + } else if (node.type === 'ExpressionTag' && node.expression.type === 'Literal') { + let last = /** @type {import('estree').TemplateElement} */ (quasis.at(-1)); + if (node.expression.value != null) { + last.value.raw += escape_html(node.expression.value + ''); + } } else if (node.type === 'Anchor') { expressions.push(node.id); quasis.push(b.quasi('', i + 1 === sequence.length)); diff --git a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js index a4bbf5a982..b23b316e3d 100644 --- a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js @@ -14,7 +14,7 @@ export default function Each_string_template($$payload, $$props) { const thing = each_array[$$index]; const anchor_1 = $.create_anchor($$payload); - $$payload.out += `${anchor_1}${$.escape(thing)},${$.escape(' ')}${anchor_1}`; + $$payload.out += `${anchor_1}${$.escape(thing)}, ${anchor_1}`; } $$payload.out += `${anchor}`; From a1a3e819c9c776338c5b004d43c8a283dc1219fa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 10:05:45 -0800 Subject: [PATCH 08/26] Version Packages (next) (#10148) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 2 ++ packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index ba0fdc4095..c1209698a1 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -13,6 +13,7 @@ "beige-flies-wash", "beige-rabbits-shave", "brave-walls-destroy", + "breezy-carrots-flash", "bright-peas-juggle", "brown-spoons-boil", "chatty-cups-drop", @@ -38,6 +39,7 @@ "early-ads-tie", "eight-steaks-shout", "eighty-bikes-camp", + "eighty-days-cheat", "empty-bulldogs-exercise", "empty-crabs-think", "fair-crabs-check", diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 5d06514780..ce2603fed8 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.0.0-next.33 + +### Patch Changes + +- fix: improve ssr template code generation ([#10151](https://github.com/sveltejs/svelte/pull/10151)) + +- fix: improve template literal expression output generation ([#10147](https://github.com/sveltejs/svelte/pull/10147)) + ## 5.0.0-next.32 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 3e0036a9d4..b5745885fb 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.0.0-next.32", + "version": "5.0.0-next.33", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 0d87622335..8fc4c11b52 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.0.0-next.32'; +export const VERSION = '5.0.0-next.33'; export const PUBLIC_VERSION = '5'; From 8c957776448f7c14945fb121ffbf6cdacca5266d Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 11 Jan 2024 12:17:57 +0000 Subject: [PATCH 09/26] fix: improve how transitions are handled on mount (#10157) --- .changeset/slow-wombats-reply.md | 5 +++ .../svelte/src/internal/client/runtime.js | 16 ++++---- .../svelte/src/internal/client/transitions.js | 38 ++++++++++++------- 3 files changed, 36 insertions(+), 23 deletions(-) create mode 100644 .changeset/slow-wombats-reply.md diff --git a/.changeset/slow-wombats-reply.md b/.changeset/slow-wombats-reply.md new file mode 100644 index 0000000000..78b6b94861 --- /dev/null +++ b/.changeset/slow-wombats-reply.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve how transitions are handled on mount diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 231dd0aebb..8b72e6d52e 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -1012,11 +1012,10 @@ export function mutate_store(store, expression, new_value) { /** * @param {import('./types.js').ComputationSignal} signal - * @param {import('./types.js').ComputationSignal} root * @param {boolean} inert * @returns {void} */ -export function mark_subtree_inert(signal, root, inert, visited_blocks = new Set()) { +export function mark_subtree_inert(signal, inert, visited_blocks = new Set()) { const flags = signal.f; const is_already_inert = (flags & INERT) !== 0; if (is_already_inert !== inert) { @@ -1031,23 +1030,22 @@ export function mark_subtree_inert(signal, root, inert, visited_blocks = new Set const type = block.t; if (type === IF_BLOCK) { const condition_effect = block.e; - const root_block = root.b?.p; - if (condition_effect !== null && root_block?.t === IF_BLOCK) { - mark_subtree_inert(condition_effect, root, inert); + if (condition_effect !== null && block !== current_block) { + mark_subtree_inert(condition_effect, inert); } const consequent_effect = block.ce; if (consequent_effect !== null) { - mark_subtree_inert(consequent_effect, root, inert, visited_blocks); + mark_subtree_inert(consequent_effect, inert, visited_blocks); } const alternate_effect = block.ae; if (alternate_effect !== null) { - mark_subtree_inert(alternate_effect, root, inert, visited_blocks); + mark_subtree_inert(alternate_effect, inert, visited_blocks); } } else if (type === EACH_BLOCK) { const items = block.v; for (let { e: each_item_effect } of items) { if (each_item_effect !== null) { - mark_subtree_inert(each_item_effect, root, inert, visited_blocks); + mark_subtree_inert(each_item_effect, inert, visited_blocks); } } } @@ -1057,7 +1055,7 @@ export function mark_subtree_inert(signal, root, inert, visited_blocks = new Set if (references !== null) { let i; for (i = 0; i < references.length; i++) { - mark_subtree_inert(references[i], root, inert, visited_blocks); + mark_subtree_inert(references[i], inert, visited_blocks); } } } diff --git a/packages/svelte/src/internal/client/transitions.js b/packages/svelte/src/internal/client/transitions.js index 0fb950094b..7b283d5ef9 100644 --- a/packages/svelte/src/internal/client/transitions.js +++ b/packages/svelte/src/internal/client/transitions.js @@ -482,10 +482,10 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo // @ts-ignore dom.__animate = true; } - + let foo = false; /** @type {import('./types.js').Block | null} */ let transition_block = block; - while (transition_block !== null) { + main: while (transition_block !== null) { if (is_transition_block(transition_block)) { if (transition_block.t === EACH_ITEM_BLOCK) { // Lazily apply the each block transition @@ -493,22 +493,32 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo transition_block.a = each_item_animate; transition_block = transition_block.p; } else if (transition_block.t === AWAIT_BLOCK && transition_block.n /* pending */) { - can_show_intro_on_mount = false; + can_show_intro_on_mount = true; } else if (transition_block.t === IF_BLOCK) { transition_block.r = if_block_transition; + if (can_show_intro_on_mount) { + /** @type {import('./types.js').Block | null} */ + let if_block = transition_block; + while (if_block.t === IF_BLOCK) { + // If we have an if block parent that is currently falsy then + // we can show the intro on mount as long as that block is mounted + if (if_block.e !== null && !if_block.v) { + can_show_intro_on_mount = true; + break main; + } + if_block = if_block.p; + } + } } if (!can_apply_lazy_transitions && can_show_intro_on_mount) { - can_show_intro_on_mount = transition_block.e === null; + can_show_intro_on_mount = transition_block.e !== null; + foo = true; } - if (!can_show_intro_on_mount || !global) { + if (can_show_intro_on_mount || !global) { can_apply_lazy_transitions = true; } - } else if ( - !can_apply_lazy_transitions && - transition_block.t === ROOT_BLOCK && - (transition_block.e !== null || transition_block.i) - ) { - can_show_intro_on_mount = false; + } else if (transition_block.t === ROOT_BLOCK && !can_apply_lazy_transitions) { + can_show_intro_on_mount = transition_block.e !== null || transition_block.i; } transition_block = transition_block.p; } @@ -540,7 +550,7 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo transition = create_transition(dom, init, direction, transition_effect); const is_intro = direction === 'in'; - const show_intro = !can_show_intro_on_mount && (is_intro || direction === 'both'); + const show_intro = can_show_intro_on_mount && (is_intro || direction === 'both'); if (show_intro) { transition.p = transition.i(); @@ -602,7 +612,7 @@ export function trigger_transitions(transitions, target_direction, from) { transition.c(); } transition.d.inert = false; - mark_subtree_inert(effect, effect, false); + mark_subtree_inert(effect, false); } else if (target_direction === 'key') { if (direction === 'key') { transition.p = transition.i(/** @type {DOMRect} */ (from)); @@ -614,7 +624,7 @@ export function trigger_transitions(transitions, target_direction, from) { outros.push(transition.o); } transition.d.inert = true; - mark_subtree_inert(effect, effect, true); + mark_subtree_inert(effect, true); } } if (outros.length > 0) { From ab13356008a609158da7493da4758d8d11645ac4 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 11 Jan 2024 12:18:41 +0000 Subject: [PATCH 10/26] fix: improve code generation (#10156) * fix: improve code generation * format --- .changeset/odd-buckets-lie.md | 5 ++++ .../phases/3-transform/client/utils.js | 25 +++++++++++++------ .../3-transform/client/visitors/template.js | 5 +++- .../_expected/client/index.svelte.js | 4 +-- 4 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 .changeset/odd-buckets-lie.md diff --git a/.changeset/odd-buckets-lie.md b/.changeset/odd-buckets-lie.md new file mode 100644 index 0000000000..240cf02d9a --- /dev/null +++ b/.changeset/odd-buckets-lie.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve code generation diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js index 19fef4e5bd..d8e6008086 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -217,9 +217,10 @@ function is_expression_async(expression) { * @param {import('estree').AssignmentExpression} node * @param {import('zimmerframe').Context} context * @param {() => any} fallback + * @param {{skip_proxy_and_freeze?: boolean}} [options] * @returns {import('estree').Expression} */ -export function serialize_set_binding(node, context, fallback) { +export function serialize_set_binding(node, context, fallback, options) { const { state, visit } = context; if ( @@ -242,7 +243,7 @@ export function serialize_set_binding(node, context, fallback) { const value = path.expression?.(b.id(tmp_id)); const assignment = b.assignment('=', path.node, value); original_assignments.push(assignment); - assignments.push(serialize_set_binding(assignment, context, () => assignment)); + assignments.push(serialize_set_binding(assignment, context, () => assignment, options)); } if (assignments.every((assignment, i) => assignment === original_assignments[i])) { @@ -288,7 +289,11 @@ export function serialize_set_binding(node, context, fallback) { if (private_state !== undefined) { if (state.in_constructor) { // See if we should wrap value in $.proxy - if (context.state.analysis.runes && should_proxy_or_freeze(value)) { + if ( + context.state.analysis.runes && + !options?.skip_proxy_and_freeze && + should_proxy_or_freeze(value) + ) { const assignment = fallback(); if (assignment.type === 'AssignmentExpression') { assignment.right = @@ -302,7 +307,9 @@ export function serialize_set_binding(node, context, fallback) { return b.call( '$.set', left, - context.state.analysis.runes && should_proxy_or_freeze(value) + context.state.analysis.runes && + !options?.skip_proxy_and_freeze && + should_proxy_or_freeze(value) ? private_state.kind === 'frozen_state' ? b.call('$.freeze', value) : b.call('$.proxy', value) @@ -321,6 +328,7 @@ export function serialize_set_binding(node, context, fallback) { if ( context.state.analysis.runes && public_state !== undefined && + !options?.skip_proxy_and_freeze && should_proxy_or_freeze(value) ) { const assignment = fallback(); @@ -387,7 +395,9 @@ export function serialize_set_binding(node, context, fallback) { return b.call( '$.set', b.id(left_name), - context.state.analysis.runes && should_proxy_or_freeze(value) + context.state.analysis.runes && + !options?.skip_proxy_and_freeze && + should_proxy_or_freeze(value) ? b.call('$.proxy', value) : value ); @@ -395,7 +405,9 @@ export function serialize_set_binding(node, context, fallback) { return b.call( '$.set', b.id(left_name), - context.state.analysis.runes && should_proxy_or_freeze(value) + context.state.analysis.runes && + !options?.skip_proxy_and_freeze && + should_proxy_or_freeze(value) ? b.call('$.freeze', value) : value ); @@ -624,6 +636,5 @@ export function should_proxy_or_freeze(node) { ) { return false; } - return true; } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index b6708bc851..60c1d2fac2 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -2604,7 +2604,10 @@ export const template_visitors = { serialize_set_binding( assignment, context, - () => /** @type {import('estree').Expression} */ (context.visit(assignment)) + () => /** @type {import('estree').Expression} */ (context.visit(assignment)), + { + skip_proxy_and_freeze: true + } ) ); diff --git a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js index f1a5a0ee42..321496e20b 100644 --- a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js @@ -29,8 +29,8 @@ export default function State_proxy_literal($$anchor, $$props) { var button = $.sibling($.sibling(input)); - $.bind_value(node, () => $.get(str), ($$value) => $.set(str, $.proxy($$value))); - $.bind_value(input, () => $.get(tpl), ($$value) => $.set(tpl, $.proxy($$value))); + $.bind_value(node, () => $.get(str), ($$value) => $.set(str, $$value)); + $.bind_value(input, () => $.get(tpl), ($$value) => $.set(tpl, $$value)); button.__click = [reset, str, tpl]; $.close_frag($$anchor, fragment); $.pop(); From ba13c3d20622b7efdda2f36c212e684b6d154ea2 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 11 Jan 2024 12:56:10 +0000 Subject: [PATCH 11/26] fix: revise inert transition logic (#10159) --- packages/svelte/src/internal/client/runtime.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 8b72e6d52e..84c24d2f7c 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -1034,11 +1034,11 @@ export function mark_subtree_inert(signal, inert, visited_blocks = new Set()) { mark_subtree_inert(condition_effect, inert); } const consequent_effect = block.ce; - if (consequent_effect !== null) { + if (consequent_effect !== null && block.v) { mark_subtree_inert(consequent_effect, inert, visited_blocks); } const alternate_effect = block.ae; - if (alternate_effect !== null) { + if (alternate_effect !== null && !block.v) { mark_subtree_inert(alternate_effect, inert, visited_blocks); } } else if (type === EACH_BLOCK) { From c7cb90c91cd3553ad59126267c9bfddecbb290b4 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Thu, 11 Jan 2024 14:23:20 +0100 Subject: [PATCH 12/26] fix: adjust `$inspect.with` type --- .changeset/seven-jobs-sniff.md | 5 +++++ packages/svelte/src/main/ambient.d.ts | 2 +- packages/svelte/types/index.d.ts | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/seven-jobs-sniff.md diff --git a/.changeset/seven-jobs-sniff.md b/.changeset/seven-jobs-sniff.md new file mode 100644 index 0000000000..4faeced47b --- /dev/null +++ b/.changeset/seven-jobs-sniff.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: adjust `$inspect.with` type diff --git a/packages/svelte/src/main/ambient.d.ts b/packages/svelte/src/main/ambient.d.ts index 8b8eadf048..63b949de73 100644 --- a/packages/svelte/src/main/ambient.d.ts +++ b/packages/svelte/src/main/ambient.d.ts @@ -178,4 +178,4 @@ declare function $props(): T; */ declare function $inspect( ...values: T -): { with: (type: 'init' | 'update', ...values: T) => void }; +): { with: (fn: (type: 'init' | 'update', ...values: T) => void) => void }; diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 72ec552cf8..9945df7735 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -2565,6 +2565,6 @@ declare function $props(): T; */ declare function $inspect( ...values: T -): { with: (type: 'init' | 'update', ...values: T) => void }; +): { with: (fn: (type: 'init' | 'update', ...values: T) => void) => void }; //# sourceMappingURL=index.d.ts.map \ No newline at end of file From a271878abe7018923839401129b18082eb2c811a Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Thu, 11 Jan 2024 14:32:13 +0100 Subject: [PATCH 13/26] fix: adjust `parse` return type It returns the root which contains the Svelte nodes --- .changeset/sour-weeks-fix.md | 5 +++++ packages/svelte/src/compiler/index.js | 4 ++-- packages/svelte/src/compiler/legacy.js | 4 ++-- packages/svelte/src/compiler/types/legacy-nodes.d.ts | 7 +++++++ packages/svelte/types/index.d.ts | 9 ++++++++- 5 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 .changeset/sour-weeks-fix.md diff --git a/.changeset/sour-weeks-fix.md b/.changeset/sour-weeks-fix.md new file mode 100644 index 0000000000..5d639f3a60 --- /dev/null +++ b/.changeset/sour-weeks-fix.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: adjust `parse` return type diff --git a/packages/svelte/src/compiler/index.js b/packages/svelte/src/compiler/index.js index df7034a75a..3e83a17798 100644 --- a/packages/svelte/src/compiler/index.js +++ b/packages/svelte/src/compiler/index.js @@ -91,7 +91,7 @@ function handle_compile_error(error, filename, source) { * https://svelte.dev/docs/svelte-compiler#svelte-parse * @param {string} source * @param {{ filename?: string; modern?: boolean }} [options] - * @returns {import('#compiler').SvelteNode | import('./types/legacy-nodes.js').LegacySvelteNode} + * @returns {import('#compiler').Root | import('./types/legacy-nodes.js').LegacyRoot} */ export function parse(source, options = {}) { /** @type {import('#compiler').Root} */ @@ -108,7 +108,7 @@ export function parse(source, options = {}) { if (options.modern) { // remove things that we don't want to treat as public API - return walk(/** @type {import('#compiler').SvelteNode} */ (ast), null, { + return walk(ast, null, { _(node, { next }) { // @ts-ignore delete node.parent; diff --git a/packages/svelte/src/compiler/legacy.js b/packages/svelte/src/compiler/legacy.js index da20b90696..8d67f654c2 100644 --- a/packages/svelte/src/compiler/legacy.js +++ b/packages/svelte/src/compiler/legacy.js @@ -33,7 +33,7 @@ function remove_surrounding_whitespace_nodes(nodes) { * Transform our nice modern AST into the monstrosity emitted by Svelte 4 * @param {string} source * @param {import('#compiler').Root} ast - * @returns {import('./types/legacy-nodes.js').LegacySvelteNode} + * @returns {import('./types/legacy-nodes.js').LegacyRoot} */ export function convert(source, ast) { const root = @@ -41,7 +41,7 @@ export function convert(source, ast) { ast ); - return /** @type {import('./types/legacy-nodes.js').LegacySvelteNode} */ ( + return /** @type {import('./types/legacy-nodes.js').LegacyRoot} */ ( walk(root, null, { _(node, { next }) { // @ts-ignore diff --git a/packages/svelte/src/compiler/types/legacy-nodes.d.ts b/packages/svelte/src/compiler/types/legacy-nodes.d.ts index 41c1ad7a3b..ba89520e20 100644 --- a/packages/svelte/src/compiler/types/legacy-nodes.d.ts +++ b/packages/svelte/src/compiler/types/legacy-nodes.d.ts @@ -21,6 +21,13 @@ interface BaseElement extends BaseNode { children: Array; } +export interface LegacyRoot extends BaseNode { + html: LegacySvelteNode; + css?: any; + instance?: any; + module?: any; +} + export interface LegacyAction extends BaseNode { type: 'Action'; /** The 'x' in `use:x` */ diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 9945df7735..399b4442c4 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -514,7 +514,7 @@ declare module 'svelte/compiler' { export function parse(source: string, options?: { filename?: string | undefined; modern?: boolean | undefined; - } | undefined): SvelteNode | LegacySvelteNode; + } | undefined): Root | LegacyRoot; /** * @deprecated Replace this with `import { walk } from 'estree-walker'` * */ @@ -779,6 +779,13 @@ declare module 'svelte/compiler' { children: Array; } + interface LegacyRoot extends BaseNode_1 { + html: LegacySvelteNode; + css?: any; + instance?: any; + module?: any; + } + interface LegacyAction extends BaseNode_1 { type: 'Action'; /** The 'x' in `use:x` */ From da9a5bf1cfc8d467a04c37d49c7e339320ec17fa Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 11 Jan 2024 14:42:44 +0100 Subject: [PATCH 14/26] fix: make `@types/estree` a dependency (#10150) types are exposed through our types, so we need to add it as a dependency fixes #10010 --- .changeset/bright-snakes-sing.md | 5 +++++ packages/svelte/package.json | 2 +- pnpm-lock.yaml | 6 +++--- 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 .changeset/bright-snakes-sing.md diff --git a/.changeset/bright-snakes-sing.md b/.changeset/bright-snakes-sing.md new file mode 100644 index 0000000000..23989cab63 --- /dev/null +++ b/.changeset/bright-snakes-sing.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: make `@types/estree` a dependency diff --git a/packages/svelte/package.json b/packages/svelte/package.json index b5745885fb..8610965749 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -106,7 +106,6 @@ "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-virtual": "^3.0.2", "@types/aria-query": "^5.0.3", - "@types/estree": "^1.0.5", "dts-buddy": "^0.4.3", "esbuild": "^0.19.2", "rollup": "^4.1.5", @@ -116,6 +115,7 @@ "dependencies": { "@ampproject/remapping": "^2.2.1", "@jridgewell/sourcemap-codec": "^1.4.15", + "@types/estree": "^1.0.5", "acorn": "^8.10.0", "acorn-typescript": "^1.4.13", "aria-query": "^5.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b4357fcb13..1cb846cab0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,6 +65,9 @@ importers: '@jridgewell/sourcemap-codec': specifier: ^1.4.15 version: 1.4.15 + '@types/estree': + specifier: ^1.0.5 + version: 1.0.5 acorn: specifier: ^8.10.0 version: 8.11.2 @@ -117,9 +120,6 @@ importers: '@types/aria-query': specifier: ^5.0.3 version: 5.0.4 - '@types/estree': - specifier: ^1.0.5 - version: 1.0.5 dts-buddy: specifier: ^0.4.3 version: 0.4.3(typescript@5.2.2) From ef3e0721fc9f9baf6f6cf637dfe6234254d03044 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 11 Jan 2024 16:53:12 +0000 Subject: [PATCH 15/26] fix: improve intro transitions on dynamic mount (#10162) * fix: improve intro transitions on dynamic mount * fix: improve intro transitions on dynamic mount * fix: improve intro transitions on dynamic mount --- .changeset/kind-baboons-approve.md | 5 +++++ .../svelte/src/internal/client/transitions.js | 17 +++++++++++------ .../samples/dynamic-transition/_config.js | 2 +- 3 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 .changeset/kind-baboons-approve.md diff --git a/.changeset/kind-baboons-approve.md b/.changeset/kind-baboons-approve.md new file mode 100644 index 0000000000..720a71cffd --- /dev/null +++ b/.changeset/kind-baboons-approve.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve intro transitions on dynamic mount diff --git a/packages/svelte/src/internal/client/transitions.js b/packages/svelte/src/internal/client/transitions.js index 7b283d5ef9..f2a4363a81 100644 --- a/packages/svelte/src/internal/client/transitions.js +++ b/packages/svelte/src/internal/client/transitions.js @@ -31,6 +31,7 @@ const DELAY_NEXT_TICK = Number.MIN_SAFE_INTEGER; /** @type {undefined | number} */ let active_tick_ref = undefined; +let skip_mount_intro = false; /** * @template T @@ -482,7 +483,6 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo // @ts-ignore dom.__animate = true; } - let foo = false; /** @type {import('./types.js').Block | null} */ let transition_block = block; main: while (transition_block !== null) { @@ -496,7 +496,7 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo can_show_intro_on_mount = true; } else if (transition_block.t === IF_BLOCK) { transition_block.r = if_block_transition; - if (can_show_intro_on_mount) { + if (can_show_intro_on_mount && !skip_mount_intro) { /** @type {import('./types.js').Block | null} */ let if_block = transition_block; while (if_block.t === IF_BLOCK) { @@ -511,14 +511,14 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo } } if (!can_apply_lazy_transitions && can_show_intro_on_mount) { - can_show_intro_on_mount = transition_block.e !== null; - foo = true; + can_show_intro_on_mount = !skip_mount_intro && transition_block.e !== null; } if (can_show_intro_on_mount || !global) { can_apply_lazy_transitions = true; } } else if (transition_block.t === ROOT_BLOCK && !can_apply_lazy_transitions) { - can_show_intro_on_mount = transition_block.e !== null || transition_block.i; + can_show_intro_on_mount = + !skip_mount_intro && (transition_block.e !== null || transition_block.i); } transition_block = transition_block.p; } @@ -529,7 +529,12 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo effect(() => { if (transition !== undefined) { // Destroy any existing transitions first - transition.x(); + try { + skip_mount_intro = true; + transition.x(); + } finally { + skip_mount_intro = false; + } } const transition_fn = get_transition_fn(); /** @param {DOMRect} [from] */ diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-transition/_config.js b/packages/svelte/tests/runtime-runes/samples/dynamic-transition/_config.js index ed9e52b347..6f25556439 100644 --- a/packages/svelte/tests/runtime-runes/samples/dynamic-transition/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/dynamic-transition/_config.js @@ -20,6 +20,6 @@ export default test({ b2.click(); }); - assert.deepEqual(log, ['transition 2', 'transition 1', 'transition 1']); + assert.deepEqual(log, ['transition 2', 'transition 1']); } }); From c3e30efeb201c540f3c99769ddca3059f3fff5cf Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 11 Jan 2024 15:03:38 -0500 Subject: [PATCH 16/26] remove 'alternatives considered' from feature request template (#10146) i don't think this is particularly useful --- .github/ISSUE_TEMPLATE/feature_request.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 08aa9adcb9..d79e8b2e21 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -22,13 +22,6 @@ body: placeholder: I would like to see... validations: required: true - - type: textarea - id: alternatives - attributes: - label: Alternatives considered - description: "Please provide a clear and concise description of any alternative solutions or features you've considered." - validations: - required: true - type: dropdown id: importance attributes: From 2e9e8e84548f48d907f4cecbe6e7fd8b78ac0794 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 20:06:41 +0000 Subject: [PATCH 17/26] Version Packages (next) (#10158) Co-authored-by: github-actions[bot] --- .changeset/pre.json | 6 ++++++ packages/svelte/CHANGELOG.md | 16 ++++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/.changeset/pre.json b/.changeset/pre.json index c1209698a1..1f647d3a24 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -15,6 +15,7 @@ "brave-walls-destroy", "breezy-carrots-flash", "bright-peas-juggle", + "bright-snakes-sing", "brown-spoons-boil", "chatty-cups-drop", "chatty-taxis-juggle", @@ -76,6 +77,7 @@ "itchy-terms-guess", "khaki-mails-draw", "khaki-moose-arrive", + "kind-baboons-approve", "kind-deers-lay", "kind-eagles-join", "large-clouds-carry", @@ -102,6 +104,7 @@ "neat-dingos-clap", "new-boats-wait", "ninety-dingos-walk", + "odd-buckets-lie", "odd-needles-joke", "odd-schools-wait", "odd-shoes-cheat", @@ -132,6 +135,7 @@ "serious-socks-cover", "serious-zebras-scream", "seven-deers-jam", + "seven-jobs-sniff", "seven-ravens-check", "sharp-gorillas-impress", "sharp-kids-happen", @@ -143,6 +147,7 @@ "slimy-walls-draw", "slow-beds-shave", "slow-chefs-dream", + "slow-wombats-reply", "small-papayas-laugh", "smart-parents-swim", "smart-zebras-pay", @@ -151,6 +156,7 @@ "soft-tigers-wink", "sour-forks-stare", "sour-rules-march", + "sour-weeks-fix", "spicy-plums-admire", "spotty-pens-agree", "stale-books-perform", diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index ce2603fed8..2a7d9f9f60 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,21 @@ # svelte +## 5.0.0-next.34 + +### Patch Changes + +- fix: make `@types/estree` a dependency ([#10150](https://github.com/sveltejs/svelte/pull/10150)) + +- fix: improve intro transitions on dynamic mount ([#10162](https://github.com/sveltejs/svelte/pull/10162)) + +- fix: improve code generation ([#10156](https://github.com/sveltejs/svelte/pull/10156)) + +- fix: adjust `$inspect.with` type ([`c7cb90c91`](https://github.com/sveltejs/svelte/commit/c7cb90c91cd3553ad59126267c9bfddecbb290b4)) + +- fix: improve how transitions are handled on mount ([#10157](https://github.com/sveltejs/svelte/pull/10157)) + +- fix: adjust `parse` return type ([`a271878ab`](https://github.com/sveltejs/svelte/commit/a271878abe7018923839401129b18082eb2c811a)) + ## 5.0.0-next.33 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 8610965749..7f6e92ee82 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.0.0-next.33", + "version": "5.0.0-next.34", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 8fc4c11b52..16b21c3490 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.0.0-next.33'; +export const VERSION = '5.0.0-next.34'; export const PUBLIC_VERSION = '5'; From aecd47e8df1da3d1a0e2d586646259ff980bc13d Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Thu, 11 Jan 2024 12:52:13 -0800 Subject: [PATCH 18/26] chore: upgrade prettier to 3.1 (#10134) --- .../20-7guis/05-7guis-crud/App.svelte | 2 +- package.json | 8 ++-- packages/svelte/src/compiler/errors.js | 6 +-- packages/svelte/src/compiler/legacy.js | 2 +- .../compiler/phases/1-parse/state/element.js | 16 ++++---- .../phases/1-parse/utils/mapped_code.js | 4 +- .../src/compiler/phases/2-analyze/index.js | 12 +++--- .../3-transform/client/transform-client.js | 2 +- .../phases/3-transform/client/utils.js | 8 ++-- .../client/visitors/javascript-runes.js | 7 +++- .../3-transform/client/visitors/template.js | 40 +++++++++---------- .../3-transform/server/transform-server.js | 22 +++++----- .../src/compiler/phases/3-transform/utils.js | 2 +- .../svelte/src/compiler/preprocess/index.js | 2 +- .../svelte/src/compiler/utils/mapped_code.js | 4 +- packages/svelte/src/easing/index.js | 12 +++--- packages/svelte/src/internal/client/each.js | 8 ++-- packages/svelte/src/internal/client/render.js | 12 +++--- .../svelte/src/internal/client/runtime.js | 8 ++-- .../svelte/src/internal/client/transitions.js | 4 +- .../svelte/src/internal/client/validate.js | 4 +- packages/svelte/src/main/public.d.ts | 8 ++-- packages/svelte/src/store/index.js | 2 +- packages/svelte/svelte-html.d.ts | 8 ++-- packages/svelte/tests/suite.ts | 2 +- packages/svelte/types/index.d.ts | 12 +++--- pnpm-lock.yaml | 38 ++++++++---------- sites/svelte-5-preview/src/lib/Repl.svelte | 2 +- .../src/lib/workers/bundler/index.js | 2 +- sites/svelte.dev/package.json | 2 +- .../src/routes/auth/login/+server.js | 2 +- 31 files changed, 130 insertions(+), 133 deletions(-) diff --git a/documentation/examples/20-7guis/05-7guis-crud/App.svelte b/documentation/examples/20-7guis/05-7guis-crud/App.svelte index 15a2ee6cd0..fbae236b9f 100644 --- a/documentation/examples/20-7guis/05-7guis-crud/App.svelte +++ b/documentation/examples/20-7guis/05-7guis-crud/App.svelte @@ -16,7 +16,7 @@ ? people.filter((person) => { const name = `${person.last}, ${person.first}`; return name.toLowerCase().startsWith(prefix.toLowerCase()); - }) + }) : people; $: selected = filteredPeople[i]; diff --git a/package.json b/package.json index 9a15be62e6..3d43c2592d 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,8 @@ "build:sites": "pnpm -r --filter=./sites/* build", "preview-site": "npm run build --prefix sites/svelte-5-preview", "check": "cd packages/svelte && pnpm build && cd ../../ && pnpm -r check", - "format": "prettier --write --plugin prettier-plugin-svelte .", - "lint": "prettier --check --plugin prettier-plugin-svelte . && eslint ./", + "format": "prettier --write .", + "lint": "prettier --check . && eslint ./", "test": "vitest run --coverage", "test-output": "vitest run --reporter=json --outputFile=sites/svelte-5-preview/src/routes/status/results.json", "changeset:version": "changeset version && pnpm -r generate:version && git add --all", @@ -38,8 +38,8 @@ "eslint-plugin-lube": "^0.1.7", "jsdom": "22.0.0", "playwright": "^1.35.1", - "prettier": "^3.0.1", - "prettier-plugin-svelte": "^3.0.3", + "prettier": "^3.1.1", + "prettier-plugin-svelte": "^3.1.2", "typescript": "^5.2.2", "vitest": "^0.34.6" } diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index c083604b87..25a21d8402 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -234,8 +234,8 @@ const attributes = { type === 'no-each' ? `An element that uses the animate directive must be the immediate child of a keyed each block` : type === 'each-key' - ? `An element that uses the animate directive must be used inside a keyed each block. Did you forget to add a key to your each block?` - : `An element that uses the animate directive must be the sole child of a keyed each block`, + ? `An element that uses the animate directive must be used inside a keyed each block. Did you forget to add a key to your each block?` + : `An element that uses the animate directive must be the sole child of a keyed each block`, 'duplicate-animation': () => `An element can only have one 'animate' directive`, /** @param {string[] | undefined} [modifiers] */ 'invalid-event-modifier': (modifiers) => @@ -262,7 +262,7 @@ const attributes = { ? `An element can only have one '${directive1}' directive` : `An element cannot have both ${describe(directive1)} directive and ${describe( directive2 - )} directive`; + )} directive`; }, 'invalid-let-directive-placement': () => 'let directive at invalid position' }; diff --git a/packages/svelte/src/compiler/legacy.js b/packages/svelte/src/compiler/legacy.js index 8d67f654c2..e8595fd52f 100644 --- a/packages/svelte/src/compiler/legacy.js +++ b/packages/svelte/src/compiler/legacy.js @@ -108,7 +108,7 @@ export function convert(source, ast) { // @ts-ignore delete node.parent; } - }) + }) : undefined }; }, diff --git a/packages/svelte/src/compiler/phases/1-parse/state/element.js b/packages/svelte/src/compiler/phases/1-parse/state/element.js index 6b38da217e..6f90809596 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -108,12 +108,12 @@ export default function tag(parser) { const type = meta_tags.has(name) ? meta_tags.get(name) : regex_capital_letter.test(name[0]) || name === 'svelte:self' || name === 'svelte:component' - ? 'Component' - : name === 'title' && parent_is_head(parser.stack) - ? 'TitleElement' - : name === 'slot' - ? 'SlotElement' - : 'RegularElement'; + ? 'Component' + : name === 'title' && parent_is_head(parser.stack) + ? 'TitleElement' + : name === 'slot' + ? 'SlotElement' + : 'RegularElement'; /** @type {import('#compiler').ElementLike} */ // @ts-expect-error TODO can't figure out this error @@ -132,7 +132,7 @@ export default function tag(parser) { can_delegate_events: null }, parent: null - } + } : { type: /** @type {import('#compiler').ElementLike['type']} */ (type), start, @@ -141,7 +141,7 @@ export default function tag(parser) { attributes: [], fragment: create_fragment(true), parent: null - }; + }; parser.allow_whitespace(); diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/mapped_code.js b/packages/svelte/src/compiler/phases/1-parse/utils/mapped_code.js index 01be1b492f..95f136effa 100644 --- a/packages/svelte/src/compiler/phases/1-parse/utils/mapped_code.js +++ b/packages/svelte/src/compiler/phases/1-parse/utils/mapped_code.js @@ -275,7 +275,7 @@ export function combine_sourcemaps(filename, sourcemap_list) { sourcemap_list, () => null, true // skip optional field `sourcesContent` - ) + ) : remapping( // use loader interface sourcemap_list[0], // last map @@ -291,7 +291,7 @@ export function combine_sourcemaps(filename, sourcemap_list) { } ), true - ); + ); if (!map.file) delete map.file; // skip optional field `file` diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 3a4e323a77..1789afc5f3 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -713,12 +713,12 @@ const runes_scope_tweaker = { rune === '$state' ? 'state' : rune === '$state.frozen' - ? 'frozen_state' - : rune === '$derived' - ? 'derived' - : path.is_rest - ? 'rest_prop' - : 'prop'; + ? 'frozen_state' + : rune === '$derived' + ? 'derived' + : path.is_rest + ? 'rest_prop' + : 'prop'; } if (rune === '$props') { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 27069478c8..1dfafd64b4 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -201,7 +201,7 @@ export function client_component(source, analysis, options) { b.call('$.validate_store', store_reference, b.literal(name.slice(1))), store_get ]) - ) + ) : b.thunk(store_get) ) ); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js index d8e6008086..7490636a21 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -20,11 +20,11 @@ export function get_assignment_value(node, { state, visit }) { return operator === '=' ? /** @type {import('estree').Expression} */ (visit(node.right)) : // turn something like x += 1 into x = x + 1 - b.binary( + b.binary( /** @type {import('estree').BinaryOperator} */ (operator.slice(0, -1)), serialize_get_binding(node.left, state), /** @type {import('estree').Expression} */ (visit(node.right)) - ); + ); } else if ( node.left.type === 'MemberExpression' && node.left.object.type === 'ThisExpression' && @@ -35,11 +35,11 @@ export function get_assignment_value(node, { state, visit }) { return operator === '=' ? /** @type {import('estree').Expression} */ (visit(node.right)) : // turn something like x += 1 into x = x + 1 - b.binary( + b.binary( /** @type {import('estree').BinaryOperator} */ (operator.slice(0, -1)), /** @type {import('estree').Expression} */ (visit(node.left)), /** @type {import('estree').Expression} */ (visit(node.right)) - ); + ); } else { return /** @type {import('estree').Expression} */ (visit(node.right)); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js index 63c46d8c64..0150c30293 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/javascript-runes.js @@ -87,8 +87,11 @@ export const javascript_visitors_runes = { field.kind === 'state' ? b.call('$.source', should_proxy_or_freeze(init) ? b.call('$.proxy', init) : init) : field.kind === 'frozen_state' - ? b.call('$.source', should_proxy_or_freeze(init) ? b.call('$.freeze', init) : init) - : b.call('$.derived', b.thunk(init)); + ? b.call( + '$.source', + should_proxy_or_freeze(init) ? b.call('$.freeze', init) : init + ) + : b.call('$.derived', b.thunk(init)); } else { // if no arguments, we know it's state as `$derived()` is a compile error value = b.call('$.source'); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index 60c1d2fac2..d029086d3d 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -655,15 +655,15 @@ function serialize_element_special_value_attribute(element, node_id, attribute, // This ensures things stay in sync with the select binding // in case of updates to the option value or new values appearing b.call('$.selected', node_id) - ]) + ]) : needs_option_call - ? b.sequence([ - inner_assignment, - // This ensures a one-way street to the DOM in case it's - b.call('$.select_option', node_id, value) - ]) - : inner_assignment + ? b.sequence([ + inner_assignment, + // This ensures a one-way street to the DOM in case it's + b.call('$.select_option', node_id, value) + ]) + : inner_assignment ); if (is_reactive) { @@ -953,7 +953,7 @@ function serialize_inline_component(node, component_name, context) { : b.call( '$.spread_props', ...props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : p)) - ); + ); /** @param {import('estree').Identifier} node_id */ let fn = (node_id) => b.call( @@ -2345,7 +2345,7 @@ export const template_visitors = { ? b.arrow( [b.id('$$anchor')], /** @type {import('estree').BlockStatement} */ (context.visit(node.fallback)) - ) + ) : b.literal(null); const key_function = node.key && ((each_type & EACH_ITEM_REACTIVE) !== 0 || context.state.options.dev) @@ -2356,7 +2356,7 @@ export const template_visitors = { b.return(/** @type {import('estree').Expression} */ (context.visit(node.key))) ) ) - ) + ) : b.literal(null); if (node.index && each_node_meta.contains_group_binding) { @@ -2418,7 +2418,7 @@ export const template_visitors = { ? b.arrow( [b.id('$$anchor')], /** @type {import('estree').BlockStatement} */ (context.visit(node.alternate)) - ) + ) : b.literal(null) ) ) @@ -2437,7 +2437,7 @@ export const template_visitors = { ? b.arrow( [b.id('$$anchor')], /** @type {import('estree').BlockStatement} */ (context.visit(node.pending)) - ) + ) : b.literal(null), node.then ? b.arrow( @@ -2445,10 +2445,10 @@ export const template_visitors = { ? [ b.id('$$anchor'), /** @type {import('estree').Pattern} */ (context.visit(node.value)) - ] + ] : [b.id('$$anchor')], /** @type {import('estree').BlockStatement} */ (context.visit(node.then)) - ) + ) : b.literal(null), node.catch ? b.arrow( @@ -2456,10 +2456,10 @@ export const template_visitors = { ? [ b.id('$$anchor'), /** @type {import('estree').Pattern} */ (context.visit(node.error)) - ] + ] : [b.id('$$anchor')], /** @type {import('estree').BlockStatement} */ (context.visit(node.catch)) - ) + ) : b.literal(null) ) ) @@ -2876,9 +2876,9 @@ export const template_visitors = { /** @type {import('estree').Expression} */ (node.expression).type === 'ObjectExpression' ? // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine - b.object_pattern(node.expression.properties) + b.object_pattern(node.expression.properties) : // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine - b.array_pattern(node.expression.elements), + b.array_pattern(node.expression.elements), b.member(b.id('$$slotProps'), b.id(node.name)) ), b.return(b.object(bindings.map((binding) => b.init(binding.node.name, binding.node)))) @@ -2971,7 +2971,7 @@ export const template_visitors = { : b.arrow( [b.id('$$anchor')], b.block(create_block(node, 'fallback', node.fragment.nodes, context)) - ); + ); const expression = is_default ? b.member(b.id('$$props'), b.id('children')) diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index d097e68676..e068802b7c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -351,11 +351,11 @@ function get_assignment_value(node, { state, visit }) { return operator === '=' ? /** @type {import('estree').Expression} */ (visit(node.right)) : // turn something like x += 1 into x = x + 1 - b.binary( + b.binary( /** @type {import('estree').BinaryOperator} */ (operator.slice(0, -1)), serialize_get_binding(node.left, state), /** @type {import('estree').Expression} */ (visit(node.right)) - ); + ); } else { return /** @type {import('estree').Expression} */ (visit(node.right)); } @@ -780,7 +780,7 @@ function serialize_element_spread_attributes( b.id('join') ), b.literal(' ') - ) + ) : b.literal(''); args.push( b.object([ @@ -933,7 +933,7 @@ function serialize_inline_component(node, component_name, context) { : b.call( '$.spread_props', b.array(props_and_spreads.map((p) => (Array.isArray(p) ? b.object(p) : p))) - ); + ); /** @type {import('estree').Statement} */ let statement = b.stmt( @@ -942,7 +942,7 @@ function serialize_inline_component(node, component_name, context) { ? b.call( '$.validate_component', typeof component_name === 'string' ? b.id(component_name) : component_name - ) + ) : component_name, b.id('$$payload'), props_expression @@ -1034,7 +1034,7 @@ const javascript_visitors_legacy = { '$.value_or_fallback', prop, /** @type {import('estree').Expression} */ (visit(declarator.init)) - ) + ) : prop; declarations.push(b.declarator(declarator.id, init)); @@ -1179,7 +1179,7 @@ const template_visitors = { template: [], init: [] } - } + } : { ...context, state }; const { hoisted, trimmed } = clean_nodes( @@ -1503,9 +1503,9 @@ const template_visitors = { b.let( node.expression.type === 'ObjectExpression' ? // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine - b.object_pattern(node.expression.properties) + b.object_pattern(node.expression.properties) : // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine - b.array_pattern(node.expression.elements), + b.array_pattern(node.expression.elements), b.member(b.id('$$slotProps'), b.id(node.name)) ), b.return(b.object(bindings.map((binding) => b.init(binding.node.name, binding.node)))) @@ -1721,12 +1721,12 @@ function serialize_element_attributes(node, context) { ? b.call( b.member(attribute.expression, b.id('includes')), serialize_attribute_value(value_attribute.value, context) - ) + ) : b.binary( '===', attribute.expression, serialize_attribute_value(value_attribute.value, context) - ), + ), metadata: { contains_call_expression: false, dynamic: false diff --git a/packages/svelte/src/compiler/phases/3-transform/utils.js b/packages/svelte/src/compiler/phases/3-transform/utils.js index 8ce6c0fd1b..a924a59e1a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/utils.js @@ -198,7 +198,7 @@ export function infer_namespace(namespace, parent, nodes, path) { const parent_node = parent.type === 'Fragment' ? // Messy: We know that Fragment calls create_block directly, so we can do this here - path.at(-1) + path.at(-1) : parent; if ( diff --git a/packages/svelte/src/compiler/preprocess/index.js b/packages/svelte/src/compiler/preprocess/index.js index d12c870760..ad1061c788 100644 --- a/packages/svelte/src/compiler/preprocess/index.js +++ b/packages/svelte/src/compiler/preprocess/index.js @@ -318,7 +318,7 @@ async function process_markup(process, source) { string: processed.code, map: processed.map ? // TODO: can we use decode_sourcemap? - typeof processed.map === 'string' + typeof processed.map === 'string' ? JSON.parse(processed.map) : processed.map : undefined, diff --git a/packages/svelte/src/compiler/utils/mapped_code.js b/packages/svelte/src/compiler/utils/mapped_code.js index aebeaa1c51..a438e48fd7 100644 --- a/packages/svelte/src/compiler/utils/mapped_code.js +++ b/packages/svelte/src/compiler/utils/mapped_code.js @@ -258,7 +258,7 @@ export function combine_sourcemaps(filename, sourcemap_list) { sourcemap_list, () => null, true // skip optional field `sourcesContent` - ) + ) : remapping( // use loader interface sourcemap_list[0], // last map @@ -271,7 +271,7 @@ export function combine_sourcemaps(filename, sourcemap_list) { } }, true - ); + ); if (!map.file) delete map.file; // skip optional field `file` // When source maps are combined and the leading map is empty, sources is not set. // Add the filename to the empty array in this case. diff --git a/packages/svelte/src/easing/index.js b/packages/svelte/src/easing/index.js index ed752d80f2..1c9ced01d6 100644 --- a/packages/svelte/src/easing/index.js +++ b/packages/svelte/src/easing/index.js @@ -59,10 +59,10 @@ export function bounceOut(t) { return t < a ? 7.5625 * t2 : t < b - ? 9.075 * t2 - 9.9 * t + 3.4 - : t < c - ? ca * t2 - cb * t + cc - : 10.8 * t * t - 20.52 * t + 10.72; + ? 9.075 * t2 - 9.9 * t + 3.4 + : t < c + ? ca * t2 - cb * t + cc + : 10.8 * t * t - 20.52 * t + 10.72; } /** @@ -180,8 +180,8 @@ export function expoInOut(t) { return t === 0.0 || t === 1.0 ? t : t < 0.5 - ? +0.5 * Math.pow(2.0, 20.0 * t - 10.0) - : -0.5 * Math.pow(2.0, 10.0 - t * 20.0) + 1.0; + ? +0.5 * Math.pow(2.0, 20.0 * t - 10.0) + : -0.5 * Math.pow(2.0, 10.0 - t * 20.0) + 1.0; } /** diff --git a/packages/svelte/src/internal/client/each.js b/packages/svelte/src/internal/client/each.js index e66574fba0..4c0e5dec7e 100644 --- a/packages/svelte/src/internal/client/each.js +++ b/packages/svelte/src/internal/client/each.js @@ -133,8 +133,8 @@ function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, re array = is_array(maybe_array) ? maybe_array : maybe_array == null - ? [] - : Array.from(maybe_array); + ? [] + : Array.from(maybe_array); if (key_fn !== null) { keys = array.map(key_fn); } else if ((flags & EACH_KEYED) === 0) { @@ -777,8 +777,8 @@ function each_item_block(item, key, index, render_fn, flags) { const item_value = each_item_not_reactive ? item : (flags & EACH_IS_IMMUTABLE) === 0 - ? mutable_source(item) - : source(item); + ? mutable_source(item) + : source(item); const index_value = (flags & EACH_INDEX_REACTIVE) === 0 ? index : source(index); const block = create_each_item_block(item_value, index_value, key); diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 2062095235..db82ed2df9 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -873,8 +873,8 @@ export function bind_resize_observer(dom, type, update) { type === 'contentRect' || type === 'contentBoxSize' ? resize_observer_content_box : type === 'borderBoxSize' - ? resize_observer_border_box - : resize_observer_device_pixel_content_box; + ? resize_observer_border_box + : resize_observer_device_pixel_content_box; const unsub = observer.observe(dom, /** @param {any} entry */ (entry) => update(entry[type])); render_effect(() => unsub); } @@ -1587,8 +1587,8 @@ export function element(anchor_node, tag_fn, render_fn, is_svg = false) { ? current_hydration_fragment !== null ? /** @type {HTMLElement | SVGElement} */ (current_hydration_fragment[0]) : is_svg - ? document.createElementNS('http://www.w3.org/2000/svg', tag) - : document.createElement(tag) + ? document.createElementNS('http://www.w3.org/2000/svg', tag) + : document.createElement(tag) : null; const prev_element = element; if (prev_element !== null) { @@ -2850,7 +2850,7 @@ export function mount(component, options) { PassiveDelegatedEvents.includes(event_name) ? { passive: true - } + } : undefined ); // The document listener ensures we catch events that originate from elements that were @@ -2861,7 +2861,7 @@ export function mount(component, options) { PassiveDelegatedEvents.includes(event_name) ? { passive: true - } + } : undefined ); } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 84c24d2f7c..d43bc75fea 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -553,7 +553,7 @@ function infinite_loop_guard() { 'ERR_SVELTE_TOO_MANY_UPDATES' + (DEV ? ': Maximum update depth exceeded. This can happen when a reactive block or effect ' + - 'repeatedly sets a new value. Svelte limits the number of nested updates to prevent infinite loops.' + 'repeatedly sets a new value. Svelte limits the number of nested updates to prevent infinite loops.' : '') ); } @@ -1117,8 +1117,8 @@ export function set_signal_value(signal, value) { 'ERR_SVELTE_UNSAFE_MUTATION' + (DEV ? ": Unsafe mutations during Svelte's render or derived phase are not permitted in runes mode. " + - 'This can lead to unexpected errors and possibly cause infinite loops.\n\nIf this mutation is not meant ' + - 'to be reactive do not use the "$state" rune for that declaration.' + 'This can lead to unexpected errors and possibly cause infinite loops.\n\nIf this mutation is not meant ' + + 'to be reactive do not use the "$state" rune for that declaration.' : '') ); } @@ -1629,7 +1629,7 @@ export function safe_not_equal(a, b) { // eslint-disable-next-line eqeqeq return a != a ? // eslint-disable-next-line eqeqeq - b == b + b == b : a !== b || (a !== null && typeof a === 'object') || typeof a === 'function'; } diff --git a/packages/svelte/src/internal/client/transitions.js b/packages/svelte/src/internal/client/transitions.js index f2a4363a81..39f87b86bf 100644 --- a/packages/svelte/src/internal/client/transitions.js +++ b/packages/svelte/src/internal/client/transitions.js @@ -547,10 +547,10 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo { from: /** @type {DOMRect} */ (from), to: dom.getBoundingClientRect() }, props, {} - ) + ) : /** @type {import('./types.js').TransitionFn} */ (transition_fn)(dom, props, { direction - }); + }); }); transition = create_transition(dom, init, direction, transition_effect); diff --git a/packages/svelte/src/internal/client/validate.js b/packages/svelte/src/internal/client/validate.js index e3316891eb..c6f590a420 100644 --- a/packages/svelte/src/internal/client/validate.js +++ b/packages/svelte/src/internal/client/validate.js @@ -75,8 +75,8 @@ export function validate_each_keys(collection, key_fn) { const array = is_array(maybe_array) ? maybe_array : maybe_array == null - ? [] - : Array.from(maybe_array); + ? [] + : Array.from(maybe_array); const length = array.length; for (let i = 0; i < length; i++) { const key = key_fn(array[i], i); diff --git a/packages/svelte/src/main/public.d.ts b/packages/svelte/src/main/public.d.ts index 2e8f7a6f00..1667770b62 100644 --- a/packages/svelte/src/main/public.d.ts +++ b/packages/svelte/src/main/public.d.ts @@ -23,8 +23,8 @@ type PropsWithChildren = Props & (Props extends { children?: any } ? {} : Slots extends { default: any } - ? { children?: Snippet } - : {}); + ? { children?: Snippet } + : {}); /** * Can be used to create strongly typed Svelte components. @@ -213,8 +213,8 @@ export interface EventDispatcher> { ...args: null extends EventMap[Type] ? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions] : undefined extends EventMap[Type] - ? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions] - : [type: Type, parameter: EventMap[Type], options?: DispatchOptions] + ? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions] + : [type: Type, parameter: EventMap[Type], options?: DispatchOptions] ): boolean; } diff --git a/packages/svelte/src/store/index.js b/packages/svelte/src/store/index.js index 857eab0af8..e18f7df8b2 100644 --- a/packages/svelte/src/store/index.js +++ b/packages/svelte/src/store/index.js @@ -33,7 +33,7 @@ export function safe_not_equal(a, b) { // eslint-disable-next-line eqeqeq return a != a ? // eslint-disable-next-line eqeqeq - b == b + b == b : a !== b || (a && typeof a === 'object') || typeof a === 'function'; } diff --git a/packages/svelte/svelte-html.d.ts b/packages/svelte/svelte-html.d.ts index 3547ad0d33..614d1fb3bc 100644 --- a/packages/svelte/svelte-html.d.ts +++ b/packages/svelte/svelte-html.d.ts @@ -37,8 +37,8 @@ declare global { ): Key extends keyof ElementTagNameMap ? ElementTagNameMap[Key] : Key extends keyof SVGElementTagNameMap - ? SVGElementTagNameMap[Key] - : any; + ? SVGElementTagNameMap[Key] + : any; function createElement( // "undefined | null" because of element: Key | undefined | null, @@ -47,8 +47,8 @@ declare global { ): Key extends keyof ElementTagNameMap ? ElementTagNameMap[Key] : Key extends keyof SVGElementTagNameMap - ? SVGElementTagNameMap[Key] - : any; + ? SVGElementTagNameMap[Key] + : any; // For backwards-compatibility and ease-of-use, in case someone enhanced the typings from import('svelte/elements').HTMLAttributes/SVGAttributes // eslint-disable-next-line @typescript-eslint/no-unused-vars diff --git a/packages/svelte/tests/suite.ts b/packages/svelte/tests/suite.ts index 34609d5e4a..b694e4cd95 100644 --- a/packages/svelte/tests/suite.ts +++ b/packages/svelte/tests/suite.ts @@ -17,7 +17,7 @@ const filter = process.env.FILTER process.env.FILTER.startsWith('/') ? process.env.FILTER.slice(1, -1).replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') : `^${process.env.FILTER.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')}$` - ) + ) : /./; export function suite(fn: (config: Test, test_dir: string) => void) { diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 399b4442c4..466bbe66a7 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -24,8 +24,8 @@ declare module 'svelte' { (Props extends { children?: any } ? {} : Slots extends { default: any } - ? { children?: Snippet } - : {}); + ? { children?: Snippet } + : {}); /** * Can be used to create strongly typed Svelte components. @@ -214,8 +214,8 @@ declare module 'svelte' { ...args: null extends EventMap[Type] ? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions] : undefined extends EventMap[Type] - ? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions] - : [type: Type, parameter: EventMap[Type], options?: DispatchOptions] + ? [type: Type, parameter?: EventMap[Type] | null | undefined, options?: DispatchOptions] + : [type: Type, parameter: EventMap[Type], options?: DispatchOptions] ): boolean; } /** @@ -1757,8 +1757,8 @@ declare module 'svelte/legacy' { (Props extends { children?: any } ? {} : Slots extends { default: any } - ? { children?: Snippet } - : {}); + ? { children?: Snippet } + : {}); /** * Can be used to create strongly typed Svelte components. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1cb846cab0..1804172ab8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,11 +45,11 @@ importers: specifier: ^1.35.1 version: 1.39.0 prettier: - specifier: ^3.0.1 - version: 3.0.3 + specifier: ^3.1.1 + version: 3.1.1 prettier-plugin-svelte: - specifier: ^3.0.3 - version: 3.1.0(prettier@3.0.3)(svelte@packages+svelte) + specifier: ^3.1.2 + version: 3.1.2(prettier@3.1.1)(svelte@packages+svelte) typescript: specifier: ^5.2.2 version: 5.2.2 @@ -357,11 +357,11 @@ importers: specifier: ^9.0.0 version: 9.1.6 prettier: - specifier: ^3.1.0 - version: 3.1.0 + specifier: ^3.1.1 + version: 3.1.1 prettier-plugin-svelte: specifier: ^3.1.2 - version: 3.1.2(prettier@3.1.0)(svelte@4.2.3) + version: 3.1.2(prettier@3.1.1)(svelte@4.2.3) sass: specifier: ^1.67.0 version: 1.69.5 @@ -6073,24 +6073,24 @@ packages: engines: {node: '>= 0.8.0'} dev: true - /prettier-plugin-svelte@3.1.0(prettier@3.0.3)(svelte@packages+svelte): - resolution: {integrity: sha512-96+AZxs2ESqIFA9j+o+DHqY+BsUglezfl553LQd6VOtTyJq5GPuBEb3ElxF2cerFzKlYKttlH/VcVmRNj5oc3A==} + /prettier-plugin-svelte@3.1.2(prettier@3.1.1)(svelte@4.2.3): + resolution: {integrity: sha512-7xfMZtwgAWHMT0iZc8jN4o65zgbAQ3+O32V6W7pXrqNvKnHnkoyQCGCbKeUyXKZLbYE0YhFRnamfxfkEGxm8qA==} peerDependencies: prettier: ^3.0.0 svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 dependencies: - prettier: 3.0.3 - svelte: link:packages/svelte + prettier: 3.1.1 + svelte: 4.2.3 dev: true - /prettier-plugin-svelte@3.1.2(prettier@3.1.0)(svelte@4.2.3): + /prettier-plugin-svelte@3.1.2(prettier@3.1.1)(svelte@packages+svelte): resolution: {integrity: sha512-7xfMZtwgAWHMT0iZc8jN4o65zgbAQ3+O32V6W7pXrqNvKnHnkoyQCGCbKeUyXKZLbYE0YhFRnamfxfkEGxm8qA==} peerDependencies: prettier: ^3.0.0 svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 dependencies: - prettier: 3.1.0 - svelte: 4.2.3 + prettier: 3.1.1 + svelte: link:packages/svelte dev: true /prettier@2.8.8: @@ -6099,14 +6099,8 @@ packages: hasBin: true dev: true - /prettier@3.0.3: - resolution: {integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==} - engines: {node: '>=14'} - hasBin: true - dev: true - - /prettier@3.1.0: - resolution: {integrity: sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==} + /prettier@3.1.1: + resolution: {integrity: sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==} engines: {node: '>=14'} hasBin: true dev: true diff --git a/sites/svelte-5-preview/src/lib/Repl.svelte b/sites/svelte-5-preview/src/lib/Repl.svelte index 87c465e271..fd30aca10e 100644 --- a/sites/svelte-5-preview/src/lib/Repl.svelte +++ b/sites/svelte-5-preview/src/lib/Repl.svelte @@ -298,7 +298,7 @@ status = message; } - }) + }) : null; /** diff --git a/sites/svelte-5-preview/src/lib/workers/bundler/index.js b/sites/svelte-5-preview/src/lib/workers/bundler/index.js index 924f5becfc..f0a2bcd1f5 100644 --- a/sites/svelte-5-preview/src/lib/workers/bundler/index.js +++ b/sites/svelte-5-preview/src/lib/workers/bundler/index.js @@ -535,7 +535,7 @@ async function bundle({ uid, files }) { exports: 'named' // sourcemap: 'inline' }) - )?.output?.[0] + )?.output?.[0] : null; return { diff --git a/sites/svelte.dev/package.json b/sites/svelte.dev/package.json index 678dc85fc6..161a1571dd 100644 --- a/sites/svelte.dev/package.json +++ b/sites/svelte.dev/package.json @@ -41,7 +41,7 @@ "lightningcss": "^1.21.8", "magic-string": "^0.30.3", "marked": "^9.0.0", - "prettier": "^3.1.0", + "prettier": "^3.1.1", "prettier-plugin-svelte": "^3.1.2", "sass": "^1.67.0", "satori": "^0.10.4", diff --git a/sites/svelte.dev/src/routes/auth/login/+server.js b/sites/svelte.dev/src/routes/auth/login/+server.js index b549ff4202..0f6295d3bd 100644 --- a/sites/svelte.dev/src/routes/auth/login/+server.js +++ b/sites/svelte.dev/src/routes/auth/login/+server.js @@ -12,7 +12,7 @@ export const GET = client_id }).toString(); throw redirect(302, Location); - } + } : () => new Response( ` From f11f451bdbc74d81f6a194781061f15559323233 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Thu, 11 Jan 2024 13:11:43 -0800 Subject: [PATCH 19/26] chore: fix xml2js security warning (#10133) --- package.json | 5 +++++ pnpm-lock.yaml | 3 +++ 2 files changed, 8 insertions(+) diff --git a/package.json b/package.json index 3d43c2592d..0eb0bf4842 100644 --- a/package.json +++ b/package.json @@ -42,5 +42,10 @@ "prettier-plugin-svelte": "^3.1.2", "typescript": "^5.2.2", "vitest": "^0.34.6" + }, + "pnpm": { + "overrides": { + "jimp>xml2js": "^0.6.0" + } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1804172ab8..53926a0aa9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,9 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + jimp>xml2js: ^0.6.0 + importers: .: From a26012fc624e72d4a150206a1265858c1afd00de Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 11 Jan 2024 21:26:39 +0000 Subject: [PATCH 20/26] fix: prevent transition action overfiring (#10163) --- .changeset/spotty-spiders-compare.md | 5 +++ packages/svelte/src/internal/client/render.js | 37 ++++++++++++------- .../svelte/src/internal/client/runtime.js | 10 +++-- .../svelte/src/internal/client/transitions.js | 27 ++++++-------- .../svelte/src/internal/client/types.d.ts | 6 ++- .../samples/dynamic-transition/_config.js | 6 +++ 6 files changed, 59 insertions(+), 32 deletions(-) create mode 100644 .changeset/spotty-spiders-compare.md diff --git a/.changeset/spotty-spiders-compare.md b/.changeset/spotty-spiders-compare.md new file mode 100644 index 0000000000..36b7d98f6a --- /dev/null +++ b/.changeset/spotty-spiders-compare.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent transition action overfiring diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index db82ed2df9..6783603eae 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -1375,7 +1375,10 @@ function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) { /** @type {null | import('./types.js').TemplateNode | Array} */ let alternate_dom = null; let has_mounted = false; - let has_mounted_branch = false; + /** + * @type {import("./types.js").EffectSignal | null} + */ + let current_branch_effect = null; const if_effect = render_effect( () => { @@ -1432,20 +1435,24 @@ function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) { ); // Managed effect const consequent_effect = render_effect( - () => { - if (consequent_dom !== null) { + ( + /** @type {any} */ _, + /** @type {import("./types.js").EffectSignal | null} */ consequent_effect + ) => { + const result = block.v; + if (!result && consequent_dom !== null) { remove(consequent_dom); consequent_dom = null; } - if (block.v) { + if (result && current_branch_effect !== consequent_effect) { consequent_fn(anchor_node); - if (!has_mounted_branch) { + if (current_branch_effect === null) { // Restore previous fragment so that Svelte continues to operate in hydration mode set_current_hydration_fragment(previous_hydration_fragment); - has_mounted_branch = true; } + current_branch_effect = consequent_effect; + consequent_dom = block.d; } - consequent_dom = block.d; block.d = null; }, block, @@ -1454,22 +1461,26 @@ function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) { block.ce = consequent_effect; // Managed effect const alternate_effect = render_effect( - () => { - if (alternate_dom !== null) { + ( + /** @type {any} */ _, + /** @type {import("./types.js").EffectSignal | null} */ alternate_effect + ) => { + const result = block.v; + if (result && alternate_dom !== null) { remove(alternate_dom); alternate_dom = null; } - if (!block.v) { + if (!result && current_branch_effect !== alternate_effect) { if (alternate_fn !== null) { alternate_fn(anchor_node); } - if (!has_mounted_branch) { + if (current_branch_effect === null) { // Restore previous fragment so that Svelte continues to operate in hydration mode set_current_hydration_fragment(previous_hydration_fragment); - has_mounted_branch = true; } + current_branch_effect = alternate_effect; + alternate_dom = block.d; } - alternate_dom = block.d; block.d = null; }, block, diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index d43bc75fea..460a15d564 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -342,9 +342,13 @@ function execute_signal_fn(signal) { try { let res; if (is_render_effect) { - res = /** @type {(block: import('./types.js').Block) => V} */ (init)( - /** @type {import('./types.js').Block} */ (signal.b) - ); + res = + /** @type {(block: import('./types.js').Block, signal: import('./types.js').Signal) => V} */ ( + init + )( + /** @type {import('./types.js').Block} */ (signal.b), + /** @type {import('./types.js').Signal} */ (signal) + ); } else { res = /** @type {() => V} */ (init)(); } diff --git a/packages/svelte/src/internal/client/transitions.js b/packages/svelte/src/internal/client/transitions.js index 39f87b86bf..96ad2fd35a 100644 --- a/packages/svelte/src/internal/client/transitions.js +++ b/packages/svelte/src/internal/client/transitions.js @@ -31,7 +31,6 @@ const DELAY_NEXT_TICK = Number.MIN_SAFE_INTEGER; /** @type {undefined | number} */ let active_tick_ref = undefined; -let skip_mount_intro = false; /** * @template T @@ -496,7 +495,7 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo can_show_intro_on_mount = true; } else if (transition_block.t === IF_BLOCK) { transition_block.r = if_block_transition; - if (can_show_intro_on_mount && !skip_mount_intro) { + if (can_show_intro_on_mount) { /** @type {import('./types.js').Block | null} */ let if_block = transition_block; while (if_block.t === IF_BLOCK) { @@ -511,14 +510,13 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo } } if (!can_apply_lazy_transitions && can_show_intro_on_mount) { - can_show_intro_on_mount = !skip_mount_intro && transition_block.e !== null; + can_show_intro_on_mount = transition_block.e !== null; } if (can_show_intro_on_mount || !global) { can_apply_lazy_transitions = true; } } else if (transition_block.t === ROOT_BLOCK && !can_apply_lazy_transitions) { - can_show_intro_on_mount = - !skip_mount_intro && (transition_block.e !== null || transition_block.i); + can_show_intro_on_mount = transition_block.e !== null || transition_block.i; } transition_block = transition_block.p; } @@ -527,14 +525,11 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo let transition; effect(() => { + let already_mounted = false; if (transition !== undefined) { + already_mounted = true; // Destroy any existing transitions first - try { - skip_mount_intro = true; - transition.x(); - } finally { - skip_mount_intro = false; - } + transition.x(); } const transition_fn = get_transition_fn(); /** @param {DOMRect} [from] */ @@ -557,7 +552,7 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo const is_intro = direction === 'in'; const show_intro = can_show_intro_on_mount && (is_intro || direction === 'both'); - if (show_intro) { + if (show_intro && !already_mounted) { transition.p = transition.i(); } @@ -565,7 +560,7 @@ export function bind_transition(dom, get_transition_fn, props_fn, direction, glo destroy_signal(effect); dom.inert = false; - if (show_intro) { + if (show_intro && !already_mounted) { transition.in(); } @@ -662,7 +657,8 @@ function if_block_transition(transition) { const c = /** @type {Set} */ (consequent_transitions); c.delete(transition); if (c.size === 0) { - execute_effect(/** @type {import('./types.js').EffectSignal} */ (block.ce)); + const consequent_effect = block.ce; + execute_effect(/** @type {import('./types.js').EffectSignal} */ (consequent_effect)); } }); } else { @@ -675,7 +671,8 @@ function if_block_transition(transition) { const a = /** @type {Set} */ (alternate_transitions); a.delete(transition); if (a.size === 0) { - execute_effect(/** @type {import('./types.js').EffectSignal} */ (block.ae)); + const alternate_effect = block.ae; + execute_effect(/** @type {import('./types.js').EffectSignal} */ (alternate_effect)); } }); } diff --git a/packages/svelte/src/internal/client/types.d.ts b/packages/svelte/src/internal/client/types.d.ts index 31dd4f1162..2a36d1fc7b 100644 --- a/packages/svelte/src/internal/client/types.d.ts +++ b/packages/svelte/src/internal/client/types.d.ts @@ -99,7 +99,11 @@ export type ComputationSignal = { /** The types that the signal represent, as a bitwise value */ f: SignalFlags; /** init: The function that we invoke for effects and computeds */ - i: null | (() => V) | (() => void | (() => void)) | ((b: Block) => void | (() => void)); + i: + | null + | (() => V) + | (() => void | (() => void)) + | ((b: Block, s: Signal) => void | (() => void)); /** references: Anything that a signal owns */ r: null | ComputationSignal[]; /** value: The latest value for this signal, doubles as the teardown for effects */ diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-transition/_config.js b/packages/svelte/tests/runtime-runes/samples/dynamic-transition/_config.js index 6f25556439..43c0bd7f86 100644 --- a/packages/svelte/tests/runtime-runes/samples/dynamic-transition/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/dynamic-transition/_config.js @@ -20,6 +20,12 @@ export default test({ b2.click(); }); + assert.deepEqual(log, ['transition 2']); + + flushSync(() => { + b1.click(); + }); + assert.deepEqual(log, ['transition 2', 'transition 1']); } }); From 0eca0ace9457d226409e582f9e275cb8a4ddb0a1 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 12 Jan 2024 13:59:55 +0000 Subject: [PATCH 21/26] fix: improve event handling compatibility with delegation (#10168) * fix: improve event handling compatibility with delegation * fix * lint * add test --- .changeset/tame-spies-drum.md | 5 ++++ packages/svelte/src/internal/client/render.js | 10 +++++++- .../event-attribute-delegation-2/_config.js | 20 +++++++++++++++ .../event-attribute-delegation-2/log.js | 2 ++ .../event-attribute-delegation-2/main.svelte | 8 ++++++ .../event-attribute-delegation-3/_config.js | 20 +++++++++++++++ .../event-attribute-delegation-3/log.js | 2 ++ .../event-attribute-delegation-3/main.svelte | 13 ++++++++++ .../event-attribute-delegation/_config.js | 25 +++++++++++++++++++ .../samples/event-attribute-delegation/log.js | 2 ++ .../event-attribute-delegation/main.svelte | 13 ++++++++++ 11 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 .changeset/tame-spies-drum.md create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/log.js create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-3/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-3/log.js create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-3/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-delegation/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-delegation/log.js create mode 100644 packages/svelte/tests/runtime-runes/samples/event-attribute-delegation/main.svelte diff --git a/.changeset/tame-spies-drum.md b/.changeset/tame-spies-drum.md new file mode 100644 index 0000000000..f22ed7ab62 --- /dev/null +++ b/.changeset/tame-spies-drum.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve event handling compatibility with delegation diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 6783603eae..bb1650748e 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -319,7 +319,15 @@ export function event(event_name, dom, handler, capture, passive) { capture, passive }; - const target_handler = handler; + /** + * @this {EventTarget} + */ + function target_handler(/** @type {Event} */ event) { + handle_event_propagation(dom, event); + if (!event.cancelBubble) { + return handler.call(this, event); + } + } dom.addEventListener(event_name, target_handler, options); // @ts-ignore if (dom === document.body || dom === window || dom === document) { diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/_config.js b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/_config.js new file mode 100644 index 0000000000..262240f5f3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/_config.js @@ -0,0 +1,20 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; +import { log } from './log.js'; + +export default test({ + before_test() { + log.length = 0; + }, + + async test({ assert, target }) { + const [b1] = target.querySelectorAll('button'); + + flushSync(() => { + b1?.click(); + }); + + await Promise.resolve(); + assert.deepEqual(log, ['clicked button']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/log.js b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/log.js new file mode 100644 index 0000000000..d3df521f4d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/log.js @@ -0,0 +1,2 @@ +/** @type {any[]} */ +export const log = []; diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/main.svelte new file mode 100644 index 0000000000..dc5fdd214e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-2/main.svelte @@ -0,0 +1,8 @@ + +
{ log.push('clicked div') }}> + +
diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-3/_config.js b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-3/_config.js new file mode 100644 index 0000000000..262240f5f3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-3/_config.js @@ -0,0 +1,20 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; +import { log } from './log.js'; + +export default test({ + before_test() { + log.length = 0; + }, + + async test({ assert, target }) { + const [b1] = target.querySelectorAll('button'); + + flushSync(() => { + b1?.click(); + }); + + await Promise.resolve(); + assert.deepEqual(log, ['clicked button']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-3/log.js b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-3/log.js new file mode 100644 index 0000000000..d3df521f4d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-3/log.js @@ -0,0 +1,2 @@ +/** @type {any[]} */ +export const log = []; diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-3/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-3/main.svelte new file mode 100644 index 0000000000..4cead08260 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation-3/main.svelte @@ -0,0 +1,13 @@ + +
log.push('clicked container')} onkeydown={() => {}}> +
{ log.push('clicked div 1') }}> +
{ log.push('clicked div 2') }}> + +
+
+
diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation/_config.js b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation/_config.js new file mode 100644 index 0000000000..b1f5931a1e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation/_config.js @@ -0,0 +1,25 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; +import { log } from './log.js'; + +export default test({ + before_test() { + log.length = 0; + }, + + async test({ assert, target }) { + const [b1] = target.querySelectorAll('button'); + + flushSync(() => { + b1?.click(); + }); + + await Promise.resolve(); + assert.deepEqual(log, [ + 'clicked button', + 'clicked div 2', + 'clicked div 1', + 'clicked container' + ]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation/log.js b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation/log.js new file mode 100644 index 0000000000..d3df521f4d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation/log.js @@ -0,0 +1,2 @@ +/** @type {any[]} */ +export const log = []; diff --git a/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation/main.svelte new file mode 100644 index 0000000000..8d3ac9fcc0 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-attribute-delegation/main.svelte @@ -0,0 +1,13 @@ + +
log.push('clicked container')} onkeydown={() => {}}> +
{ log.push('clicked div 1') }}> +
{ log.push('clicked div 2') }}> + +
+
+
From 05bd922f7f8028255910d158376c67b257bce0a7 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Fri, 12 Jan 2024 19:57:06 +0000 Subject: [PATCH 22/26] fix: improve nested effect heuristics (#10171) --- .changeset/angry-books-jam.md | 5 +++ .../svelte/src/internal/client/runtime.js | 32 +++++++++++++++---- .../samples/nested-effect-conflict/_config.js | 28 ++++++++++++++++ .../samples/nested-effect-conflict/log.js | 2 ++ .../nested-effect-conflict/main.svelte | 21 ++++++++++++ 5 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 .changeset/angry-books-jam.md create mode 100644 packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/log.js create mode 100644 packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/main.svelte diff --git a/.changeset/angry-books-jam.md b/.changeset/angry-books-jam.md new file mode 100644 index 0000000000..6552a54e31 --- /dev/null +++ b/.changeset/angry-books-jam.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: improve nested effect heuristics diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 460a15d564..034e9e3c0a 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -624,6 +624,13 @@ export function schedule_effect(signal, sync) { } if ((flags & EFFECT) !== 0) { current_queued_effects.push(signal); + // Prevent any nested user effects from potentially triggering + // before this effect is scheduled. We know they will be destroyed + // so we can make them inert to avoid having to find them in the + // queue and remove them. + if ((flags & MANAGED) === 0) { + mark_subtree_children_inert(signal, true); + } } else { current_queued_pre_and_render_effects.push(signal); } @@ -1017,6 +1024,23 @@ export function mutate_store(store, expression, new_value) { /** * @param {import('./types.js').ComputationSignal} signal * @param {boolean} inert + * @param {Set} [visited_blocks] + * @returns {void} + */ +function mark_subtree_children_inert(signal, inert, visited_blocks) { + const references = signal.r; + if (references !== null) { + let i; + for (i = 0; i < references.length; i++) { + mark_subtree_inert(references[i], inert, visited_blocks); + } + } +} + +/** + * @param {import('./types.js').ComputationSignal} signal + * @param {boolean} inert + * @param {Set} [visited_blocks] * @returns {void} */ export function mark_subtree_inert(signal, inert, visited_blocks = new Set()) { @@ -1055,13 +1079,7 @@ export function mark_subtree_inert(signal, inert, visited_blocks = new Set()) { } } } - const references = signal.r; - if (references !== null) { - let i; - for (i = 0; i < references.length; i++) { - mark_subtree_inert(references[i], inert, visited_blocks); - } - } + mark_subtree_children_inert(signal, inert, visited_blocks); } /** diff --git a/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/_config.js b/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/_config.js new file mode 100644 index 0000000000..6de94097f2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/_config.js @@ -0,0 +1,28 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; +import { log } from './log.js'; + +export default test({ + before_test() { + log.length = 0; + }, + + async test({ assert, target }) { + const [b1] = target.querySelectorAll('button'); + + flushSync(() => { + b1?.click(); + }); + + await Promise.resolve(); + assert.deepEqual(log, [ + 'top level', + 'inner', + 0, + 'destroy inner', + undefined, + 'destroy outer', + undefined + ]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/log.js b/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/log.js new file mode 100644 index 0000000000..d3df521f4d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/log.js @@ -0,0 +1,2 @@ +/** @type {any[]} */ +export const log = []; diff --git a/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/main.svelte b/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/main.svelte new file mode 100644 index 0000000000..a7c6625dc1 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/main.svelte @@ -0,0 +1,21 @@ + + + From d5cab3cb28f61aa6bf3e13b6afd18e5918558063 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Fri, 12 Jan 2024 13:43:02 -0800 Subject: [PATCH 23/26] add funding.json (#10172) --- FUNDING.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 FUNDING.json diff --git a/FUNDING.json b/FUNDING.json new file mode 100644 index 0000000000..2e8beaf8cd --- /dev/null +++ b/FUNDING.json @@ -0,0 +1,7 @@ +{ + "drips": { + "ethereum": { + "ownedBy": "0x99D414693dD65E4a0664a16D155dB66283A162D1" + } + } +} From c62890486140a351afd62a74f10e15bf262314a3 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Sat, 13 Jan 2024 14:40:10 +0000 Subject: [PATCH 24/26] fix: ensure topological order for render effects (#10175) * fix: ensure topological order for render effects * optimize --- .changeset/ten-ties-repair.md | 5 ++ .../svelte/src/internal/client/runtime.js | 53 +++++++++++++++++-- .../svelte/src/internal/client/types.d.ts | 2 + .../samples/if-dependency-order/_config.js | 16 ++++++ .../samples/if-dependency-order/main.svelte | 13 +++++ 5 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 .changeset/ten-ties-repair.md create mode 100644 packages/svelte/tests/runtime-runes/samples/if-dependency-order/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/if-dependency-order/main.svelte diff --git a/.changeset/ten-ties-repair.md b/.changeset/ten-ties-repair.md new file mode 100644 index 0000000000..4689ee2e07 --- /dev/null +++ b/.changeset/ten-ties-repair.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: ensure topological order for render effects diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 034e9e3c0a..126692b1a1 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -214,6 +214,8 @@ function create_computation_signal(flags, value, block) { f: flags, // init i: null, + // level + l: 0, // references r: null, // value @@ -238,6 +240,8 @@ function create_computation_signal(flags, value, block) { e: null, // flags f: flags, + // level + l: 0, // init i: null, // references @@ -632,7 +636,45 @@ export function schedule_effect(signal, sync) { mark_subtree_children_inert(signal, true); } } else { - current_queued_pre_and_render_effects.push(signal); + // We need to ensure we insert the signal in the right topological order. In other words, + // we need to evaluate where to insert the signal based off its level and whether or not it's + // a pre-effect and within the same block. By checking the signals in the queue in reverse order + // we can find the right place quickly. TODO: maybe opt to use a linked list rather than an array + // for these operations. + const length = current_queued_pre_and_render_effects.length; + let should_append = length === 0; + + if (!should_append) { + const target_level = signal.l; + const target_block = signal.b; + const is_pre_effect = (flags & PRE_EFFECT) !== 0; + let target_signal; + let is_target_pre_effect; + let i = length; + while (true) { + target_signal = current_queued_pre_and_render_effects[--i]; + if (target_signal.l <= target_level) { + if (i + 1 === length) { + should_append = true; + } else { + is_target_pre_effect = (target_signal.f & PRE_EFFECT) !== 0; + if (target_signal.b !== target_block || (is_target_pre_effect && !is_pre_effect)) { + i++; + } + current_queued_pre_and_render_effects.splice(i, 0, signal); + } + break; + } + if (i === 0) { + current_queued_pre_and_render_effects.unshift(signal); + break; + } + } + } + + if (should_append) { + current_queued_pre_and_render_effects.push(signal); + } } } } @@ -1310,12 +1352,15 @@ function internal_create_effect(type, init, sync, block, schedule) { const signal = create_computation_signal(type | DIRTY, null, block); signal.i = init; signal.x = current_component_context; + if (current_effect !== null) { + signal.l = current_effect.l + 1; + if ((type & MANAGED) === 0) { + push_reference(current_effect, signal); + } + } if (schedule) { schedule_effect(signal, sync); } - if (current_effect !== null && (type & MANAGED) === 0) { - push_reference(current_effect, signal); - } return signal; } diff --git a/packages/svelte/src/internal/client/types.d.ts b/packages/svelte/src/internal/client/types.d.ts index 2a36d1fc7b..b81ed7c3be 100644 --- a/packages/svelte/src/internal/client/types.d.ts +++ b/packages/svelte/src/internal/client/types.d.ts @@ -108,6 +108,8 @@ export type ComputationSignal = { r: null | ComputationSignal[]; /** value: The latest value for this signal, doubles as the teardown for effects */ v: V; + /** level: the depth from the root signal, used for ordering render/pre-effects topologically **/ + l: number; }; export type Signal = SourceSignal | ComputationSignal; diff --git a/packages/svelte/tests/runtime-runes/samples/if-dependency-order/_config.js b/packages/svelte/tests/runtime-runes/samples/if-dependency-order/_config.js new file mode 100644 index 0000000000..5b96f3f4c5 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/if-dependency-order/_config.js @@ -0,0 +1,16 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: `

expires in 1 click

`, + + async test({ assert, target }) { + const [btn1] = target.querySelectorAll('button'); + + flushSync(() => { + btn1.click(); + }); + + assert.htmlEqual(target.innerHTML, ``); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/if-dependency-order/main.svelte b/packages/svelte/tests/runtime-runes/samples/if-dependency-order/main.svelte new file mode 100644 index 0000000000..8a64001b1a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/if-dependency-order/main.svelte @@ -0,0 +1,13 @@ + + +{#if data} + +

expires in {data.num} click

+{/if} From b3ba25da944fd70cd2492647897bc83524d6a06b Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Sat, 13 Jan 2024 15:40:31 +0100 Subject: [PATCH 25/26] fix: simplify event delegation logic (#10169) * fix: simplify event delegation logic Only delegate event attributes, and don't take into account bindings/actions while determining that. Never delegate `on:` directives. This simplifies the logic and makes it easier to explain, while avoiding any accidental breaking changes when upgrading from Svelte 4 to 5 without changing code. Fixes #10165 Related to #9714 * update types --- .changeset/rich-cobras-exist.md | 5 + .../compiler/phases/1-parse/state/element.js | 3 +- .../src/compiler/phases/2-analyze/index.js | 93 +++---------------- .../3-transform/client/visitors/template.js | 8 +- .../svelte/src/compiler/types/template.d.ts | 9 -- .../samples/inspect-trace/_config.js | 4 +- packages/svelte/types/index.d.ts | 9 -- 7 files changed, 26 insertions(+), 105 deletions(-) create mode 100644 .changeset/rich-cobras-exist.md diff --git a/.changeset/rich-cobras-exist.md b/.changeset/rich-cobras-exist.md new file mode 100644 index 0000000000..ca560d0e63 --- /dev/null +++ b/.changeset/rich-cobras-exist.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: simplify event delegation logic, only delegate event attributes diff --git a/packages/svelte/src/compiler/phases/1-parse/state/element.js b/packages/svelte/src/compiler/phases/1-parse/state/element.js index 6f90809596..5f2eda1f17 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -128,8 +128,7 @@ export default function tag(parser) { fragment: create_fragment(true), metadata: { svg: false, - has_spread: false, - can_delegate_events: null + has_spread: false }, parent: null } diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 1789afc5f3..0c207f9508 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -59,27 +59,23 @@ function get_component_name(filename) { } /** - * @param {Pick & { type: string }} node + * Checks if given event attribute can be delegated/hoisted and returns the corresponding info if so + * @param {string} event_name + * @param {import('estree').Expression | null} handler * @param {import('./types').Context} context * @returns {null | import('#compiler').DelegatedEvent} */ -function get_delegated_event(node, context) { - const handler = node.expression; - const event_name = node.name; - +function get_delegated_event(event_name, handler, context) { // Handle delegated event handlers. Bail-out if not a delegated event. - if (!handler || node.modifiers.includes('capture') || !DelegatedEvents.includes(event_name)) { + if (!handler || !DelegatedEvents.includes(event_name)) { return null; } + // If we are not working with a RegularElement, then bail-out. const element = context.path.at(-1); if (element?.type !== 'RegularElement') { return null; } - // If element says we can't delegate because we have multiple OnDirectives of the same type, bail-out. - if (!element.metadata.can_delegate_events) { - return null; - } /** @type {import('#compiler').DelegatedEvent} */ const non_hoistable = { type: 'non-hoistable' }; @@ -87,7 +83,7 @@ function get_delegated_event(node, context) { let target_function = null; let binding = null; - if (node.type === 'Attribute' && element.metadata.has_spread) { + if (element.metadata.has_spread) { // event attribute becomes part of the dynamic spread array return non_hoistable; } @@ -123,8 +119,7 @@ function get_delegated_event(node, context) { if (element && event_name) { if ( element.type !== 'RegularElement' || - !determine_element_spread_and_delegatable(element).metadata.can_delegate_events || - (element.metadata.has_spread && node.type === 'Attribute') || + determine_element_spread(element).metadata.has_spread || !DelegatedEvents.includes(event_name) ) { return non_hoistable; @@ -183,7 +178,8 @@ function get_delegated_event(node, context) { ) { return non_hoistable; } - // If we referebnce the index within an each block, then bail-out. + + // If we reference the index within an each block, then bail-out. if (binding !== null && binding.initial?.type === 'EachBlock') { return non_hoistable; } @@ -204,6 +200,7 @@ function get_delegated_event(node, context) { } visited_references.add(reference); } + return { type: 'hoistable', function: target_function }; } @@ -858,21 +855,9 @@ const common_visitors = { }); if (is_event_attribute(node)) { - /** @type {string[]} */ - const modifiers = []; const expression = node.value[0].expression; - let name = node.name.slice(2); - - if (is_capture_event(name)) { - name = name.slice(0, -7); - modifiers.push('capture'); - } - - const delegated_event = get_delegated_event( - { type: node.type, name, expression, modifiers }, - context - ); + const delegated_event = get_delegated_event(node.name.slice(2), expression, context); if (delegated_event !== null) { if (delegated_event.type === 'hoistable') { @@ -1032,18 +1017,6 @@ const common_visitors = { ) }; }, - OnDirective(node, context) { - node.metadata = { delegated: null }; - context.next(); - const delegated_event = get_delegated_event(node, context); - - if (delegated_event !== null) { - if (delegated_event.type === 'hoistable') { - delegated_event.function.metadata.hoistable = true; - } - node.metadata.delegated = delegated_event; - } - }, ArrowFunctionExpression: function_visitor, FunctionExpression: function_visitor, FunctionDeclaration: function_visitor, @@ -1052,7 +1025,7 @@ const common_visitors = { node.metadata.svg = true; } - determine_element_spread_and_delegatable(node); + determine_element_spread(node); // Special case: Move the children of