3458 lines
82 KiB
JavaScript
3458 lines
82 KiB
JavaScript
(function () {
|
|
'use strict';
|
|
|
|
// generated during release, do not modify
|
|
|
|
const PUBLIC_VERSION = '5';
|
|
|
|
if (typeof window !== 'undefined') {
|
|
// @ts-expect-error
|
|
((window.__svelte ??= {}).v ??= new Set()).add(PUBLIC_VERSION);
|
|
}
|
|
|
|
let legacy_mode_flag = false;
|
|
let tracing_mode_flag = false;
|
|
|
|
function enable_legacy_mode_flag() {
|
|
legacy_mode_flag = true;
|
|
}
|
|
|
|
enable_legacy_mode_flag();
|
|
|
|
const EACH_ITEM_REACTIVE = 1;
|
|
const EACH_INDEX_REACTIVE = 1 << 1;
|
|
const EACH_ITEM_IMMUTABLE = 1 << 4;
|
|
|
|
const TEMPLATE_FRAGMENT = 1;
|
|
const TEMPLATE_USE_IMPORT_NODE = 1 << 1;
|
|
|
|
const UNINITIALIZED = Symbol();
|
|
|
|
var DEV = false;
|
|
|
|
// Store the references to globals in case someone tries to monkey patch these, causing the below
|
|
// to de-opt (this occurs often when using popular extensions).
|
|
var is_array = Array.isArray;
|
|
var index_of = Array.prototype.indexOf;
|
|
var array_from = Array.from;
|
|
var get_descriptors = Object.getOwnPropertyDescriptors;
|
|
var get_prototype_of = Object.getPrototypeOf;
|
|
|
|
// Adapted from https://github.com/then/is-promise/blob/master/index.js
|
|
// Distributed under MIT License https://github.com/then/is-promise/blob/master/LICENSE
|
|
|
|
/**
|
|
* @template [T=any]
|
|
* @param {any} value
|
|
* @returns {value is PromiseLike<T>}
|
|
*/
|
|
function is_promise(value) {
|
|
return typeof value?.then === 'function';
|
|
}
|
|
|
|
/** @param {Function} fn */
|
|
function run(fn) {
|
|
return fn();
|
|
}
|
|
|
|
/** @param {Array<() => void>} arr */
|
|
function run_all(arr) {
|
|
for (var i = 0; i < arr.length; i++) {
|
|
arr[i]();
|
|
}
|
|
}
|
|
|
|
const DERIVED = 1 << 1;
|
|
const EFFECT = 1 << 2;
|
|
const RENDER_EFFECT = 1 << 3;
|
|
const BLOCK_EFFECT = 1 << 4;
|
|
const BRANCH_EFFECT = 1 << 5;
|
|
const ROOT_EFFECT = 1 << 6;
|
|
const BOUNDARY_EFFECT = 1 << 7;
|
|
const UNOWNED = 1 << 8;
|
|
const DISCONNECTED = 1 << 9;
|
|
const CLEAN = 1 << 10;
|
|
const DIRTY = 1 << 11;
|
|
const MAYBE_DIRTY = 1 << 12;
|
|
const INERT = 1 << 13;
|
|
const DESTROYED = 1 << 14;
|
|
const EFFECT_RAN = 1 << 15;
|
|
/** 'Transparent' effects do not create a transition boundary */
|
|
const EFFECT_TRANSPARENT = 1 << 16;
|
|
const HEAD_EFFECT = 1 << 19;
|
|
const EFFECT_HAS_DERIVED = 1 << 20;
|
|
const EFFECT_IS_UPDATING = 1 << 21;
|
|
|
|
const STATE_SYMBOL = Symbol('$state');
|
|
|
|
/** @import { Equals } from '#client' */
|
|
|
|
/** @type {Equals} */
|
|
function equals(value) {
|
|
return value === this.v;
|
|
}
|
|
|
|
/**
|
|
* @param {unknown} a
|
|
* @param {unknown} b
|
|
* @returns {boolean}
|
|
*/
|
|
function safe_not_equal(a, b) {
|
|
return a != a
|
|
? b == b
|
|
: a !== b || (a !== null && typeof a === 'object') || typeof a === 'function';
|
|
}
|
|
|
|
/** @type {Equals} */
|
|
function safe_equals(value) {
|
|
return !safe_not_equal(value, this.v);
|
|
}
|
|
|
|
/* This file is generated by scripts/process-messages/index.js. Do not edit! */
|
|
|
|
|
|
/**
|
|
* `%rune%` cannot be used inside an effect cleanup function
|
|
* @param {string} rune
|
|
* @returns {never}
|
|
*/
|
|
function effect_in_teardown(rune) {
|
|
{
|
|
throw new Error(`https://svelte.dev/e/effect_in_teardown`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Effect cannot be created inside a `$derived` value that was not itself created inside an effect
|
|
* @returns {never}
|
|
*/
|
|
function effect_in_unowned_derived() {
|
|
{
|
|
throw new Error(`https://svelte.dev/e/effect_in_unowned_derived`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* `%rune%` can only be used inside an effect (e.g. during component initialisation)
|
|
* @param {string} rune
|
|
* @returns {never}
|
|
*/
|
|
function effect_orphan(rune) {
|
|
{
|
|
throw new Error(`https://svelte.dev/e/effect_orphan`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
* @returns {never}
|
|
*/
|
|
function effect_update_depth_exceeded() {
|
|
{
|
|
throw new Error(`https://svelte.dev/e/effect_update_depth_exceeded`);
|
|
}
|
|
}
|
|
|
|
/** @import { ComponentContext } from '#client' */
|
|
|
|
|
|
/** @type {ComponentContext | null} */
|
|
let component_context = null;
|
|
|
|
/** @param {ComponentContext | null} context */
|
|
function set_component_context(context) {
|
|
component_context = context;
|
|
}
|
|
|
|
/**
|
|
* @param {Record<string, unknown>} props
|
|
* @param {any} runes
|
|
* @param {Function} [fn]
|
|
* @returns {void}
|
|
*/
|
|
function push(props, runes = false, fn) {
|
|
var ctx = (component_context = {
|
|
p: component_context,
|
|
c: null,
|
|
d: false,
|
|
e: null,
|
|
m: false,
|
|
s: props,
|
|
x: null,
|
|
l: null
|
|
});
|
|
|
|
if (legacy_mode_flag && !runes) {
|
|
component_context.l = {
|
|
s: null,
|
|
u: null,
|
|
r1: [],
|
|
r2: source(false)
|
|
};
|
|
}
|
|
|
|
teardown(() => {
|
|
/** @type {ComponentContext} */ (ctx).d = true;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @template {Record<string, any>} T
|
|
* @param {T} [component]
|
|
* @returns {T}
|
|
*/
|
|
function pop(component) {
|
|
const context_stack_item = component_context;
|
|
if (context_stack_item !== null) {
|
|
const component_effects = context_stack_item.e;
|
|
if (component_effects !== null) {
|
|
var previous_effect = active_effect;
|
|
var previous_reaction = active_reaction;
|
|
context_stack_item.e = null;
|
|
try {
|
|
for (var i = 0; i < component_effects.length; i++) {
|
|
var component_effect = component_effects[i];
|
|
set_active_effect(component_effect.effect);
|
|
set_active_reaction(component_effect.reaction);
|
|
effect(component_effect.fn);
|
|
}
|
|
} finally {
|
|
set_active_effect(previous_effect);
|
|
set_active_reaction(previous_reaction);
|
|
}
|
|
}
|
|
component_context = context_stack_item.p;
|
|
context_stack_item.m = true;
|
|
}
|
|
// Micro-optimization: Don't set .a above to the empty object
|
|
// so it can be garbage-collected when the return here is unused
|
|
return /** @type {T} */ ({});
|
|
}
|
|
|
|
/** @returns {boolean} */
|
|
function is_runes() {
|
|
return !legacy_mode_flag || (component_context !== null && component_context.l === null);
|
|
}
|
|
|
|
/** @import { Derived, Effect } from '#client' */
|
|
|
|
/**
|
|
* @template V
|
|
* @param {() => V} fn
|
|
* @returns {Derived<V>}
|
|
*/
|
|
/*#__NO_SIDE_EFFECTS__*/
|
|
function derived(fn) {
|
|
var flags = DERIVED | DIRTY;
|
|
var parent_derived =
|
|
active_reaction !== null && (active_reaction.f & DERIVED) !== 0
|
|
? /** @type {Derived} */ (active_reaction)
|
|
: null;
|
|
|
|
if (active_effect === null || (parent_derived !== null && (parent_derived.f & UNOWNED) !== 0)) {
|
|
flags |= UNOWNED;
|
|
} else {
|
|
// Since deriveds are evaluated lazily, any effects created inside them are
|
|
// created too late to ensure that the parent effect is added to the tree
|
|
active_effect.f |= EFFECT_HAS_DERIVED;
|
|
}
|
|
|
|
/** @type {Derived<V>} */
|
|
const signal = {
|
|
ctx: component_context,
|
|
deps: null,
|
|
effects: null,
|
|
equals,
|
|
f: flags,
|
|
fn,
|
|
reactions: null,
|
|
rv: 0,
|
|
v: /** @type {V} */ (null),
|
|
wv: 0,
|
|
parent: parent_derived ?? active_effect
|
|
};
|
|
|
|
return signal;
|
|
}
|
|
|
|
/**
|
|
* @template V
|
|
* @param {() => V} fn
|
|
* @returns {Derived<V>}
|
|
*/
|
|
/*#__NO_SIDE_EFFECTS__*/
|
|
function derived_safe_equal(fn) {
|
|
const signal = derived(fn);
|
|
signal.equals = safe_equals;
|
|
return signal;
|
|
}
|
|
|
|
/**
|
|
* @param {Derived} derived
|
|
* @returns {void}
|
|
*/
|
|
function destroy_derived_effects(derived) {
|
|
var effects = derived.effects;
|
|
|
|
if (effects !== null) {
|
|
derived.effects = null;
|
|
|
|
for (var i = 0; i < effects.length; i += 1) {
|
|
destroy_effect(/** @type {Effect} */ (effects[i]));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {Derived} derived
|
|
* @returns {Effect | null}
|
|
*/
|
|
function get_derived_parent_effect(derived) {
|
|
var parent = derived.parent;
|
|
while (parent !== null) {
|
|
if ((parent.f & DERIVED) === 0) {
|
|
return /** @type {Effect} */ (parent);
|
|
}
|
|
parent = parent.parent;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @template T
|
|
* @param {Derived} derived
|
|
* @returns {T}
|
|
*/
|
|
function execute_derived(derived) {
|
|
var value;
|
|
var prev_active_effect = active_effect;
|
|
|
|
set_active_effect(get_derived_parent_effect(derived));
|
|
|
|
{
|
|
try {
|
|
destroy_derived_effects(derived);
|
|
value = update_reaction(derived);
|
|
} finally {
|
|
set_active_effect(prev_active_effect);
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* @param {Derived} derived
|
|
* @returns {void}
|
|
*/
|
|
function update_derived(derived) {
|
|
var value = execute_derived(derived);
|
|
|
|
if (!derived.equals(value)) {
|
|
derived.v = value;
|
|
derived.wv = increment_write_version();
|
|
}
|
|
|
|
// don't mark derived clean if we're reading it inside a
|
|
// cleanup function, or it will cache a stale value
|
|
if (is_destroying_effect) return;
|
|
|
|
var status =
|
|
(skip_reaction || (derived.f & UNOWNED) !== 0) && derived.deps !== null ? MAYBE_DIRTY : CLEAN;
|
|
|
|
set_signal_status(derived, status);
|
|
}
|
|
|
|
/** @import { Derived, Effect, Source, Value } from '#client' */
|
|
const old_values = new Map();
|
|
|
|
/**
|
|
* @template V
|
|
* @param {V} v
|
|
* @param {Error | null} [stack]
|
|
* @returns {Source<V>}
|
|
*/
|
|
// TODO rename this to `state` throughout the codebase
|
|
function source(v, stack) {
|
|
/** @type {Value} */
|
|
var signal = {
|
|
f: 0, // TODO ideally we could skip this altogether, but it causes type errors
|
|
v,
|
|
reactions: null,
|
|
equals,
|
|
rv: 0,
|
|
wv: 0
|
|
};
|
|
|
|
return signal;
|
|
}
|
|
|
|
/**
|
|
* @template V
|
|
* @param {V} initial_value
|
|
* @param {boolean} [immutable]
|
|
* @returns {Source<V>}
|
|
*/
|
|
/*#__NO_SIDE_EFFECTS__*/
|
|
function mutable_source(initial_value, immutable = false) {
|
|
const s = source(initial_value);
|
|
if (!immutable) {
|
|
s.equals = safe_equals;
|
|
}
|
|
|
|
// bind the signal to the component context, in case we need to
|
|
// track updates to trigger beforeUpdate/afterUpdate callbacks
|
|
if (legacy_mode_flag && component_context !== null && component_context.l !== null) {
|
|
(component_context.l.s ??= []).push(s);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* @template V
|
|
* @param {Source<V>} source
|
|
* @param {V} value
|
|
* @returns {V}
|
|
*/
|
|
function internal_set(source, value) {
|
|
if (!source.equals(value)) {
|
|
var old_value = source.v;
|
|
|
|
if (is_destroying_effect) {
|
|
old_values.set(source, value);
|
|
} else {
|
|
old_values.set(source, old_value);
|
|
}
|
|
|
|
source.v = value;
|
|
|
|
if ((source.f & DERIVED) !== 0) {
|
|
// if we are assigning to a dirty derived we set it to clean/maybe dirty but we also eagerly execute it to track the dependencies
|
|
if ((source.f & DIRTY) !== 0) {
|
|
execute_derived(/** @type {Derived} */ (source));
|
|
}
|
|
set_signal_status(source, (source.f & UNOWNED) === 0 ? CLEAN : MAYBE_DIRTY);
|
|
}
|
|
|
|
source.wv = increment_write_version();
|
|
|
|
mark_reactions(source, DIRTY);
|
|
|
|
// It's possible that the current reaction might not have up-to-date dependencies
|
|
// whilst it's actively running. So in the case of ensuring it registers the reaction
|
|
// properly for itself, we need to ensure the current effect actually gets
|
|
// scheduled. i.e: `$effect(() => x++)`
|
|
if (
|
|
is_runes() &&
|
|
active_effect !== null &&
|
|
(active_effect.f & CLEAN) !== 0 &&
|
|
(active_effect.f & (BRANCH_EFFECT | ROOT_EFFECT)) === 0
|
|
) {
|
|
if (untracked_writes === null) {
|
|
set_untracked_writes([source]);
|
|
} else {
|
|
untracked_writes.push(source);
|
|
}
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* @param {Value} signal
|
|
* @param {number} status should be DIRTY or MAYBE_DIRTY
|
|
* @returns {void}
|
|
*/
|
|
function mark_reactions(signal, status) {
|
|
var reactions = signal.reactions;
|
|
if (reactions === null) return;
|
|
|
|
var runes = is_runes();
|
|
var length = reactions.length;
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
var reaction = reactions[i];
|
|
var flags = reaction.f;
|
|
|
|
// Skip any effects that are already dirty
|
|
if ((flags & DIRTY) !== 0) continue;
|
|
|
|
// In legacy mode, skip the current effect to prevent infinite loops
|
|
if (!runes && reaction === active_effect) continue;
|
|
|
|
set_signal_status(reaction, status);
|
|
|
|
// If the signal a) was previously clean or b) is an unowned derived, then mark it
|
|
if ((flags & (CLEAN | UNOWNED)) !== 0) {
|
|
if ((flags & DERIVED) !== 0) {
|
|
mark_reactions(/** @type {Derived} */ (reaction), MAYBE_DIRTY);
|
|
} else {
|
|
schedule_effect(/** @type {Effect} */ (reaction));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @import { TemplateNode } from '#client' */
|
|
|
|
|
|
/**
|
|
* Use this variable to guard everything related to hydration code so it can be treeshaken out
|
|
* if the user doesn't use the `hydrate` method and these code paths are therefore not needed.
|
|
*/
|
|
let hydrating = false;
|
|
|
|
/** @import { TemplateNode } from '#client' */
|
|
|
|
/** @type {boolean} */
|
|
var is_firefox;
|
|
|
|
/** @type {() => Node | null} */
|
|
var first_child_getter;
|
|
/** @type {() => Node | null} */
|
|
var next_sibling_getter;
|
|
|
|
/**
|
|
* @param {string} value
|
|
* @returns {Text}
|
|
*/
|
|
function create_text(value = '') {
|
|
return document.createTextNode(value);
|
|
}
|
|
|
|
/**
|
|
* @template {Node} N
|
|
* @param {N} node
|
|
* @returns {Node | null}
|
|
*/
|
|
/*@__NO_SIDE_EFFECTS__*/
|
|
function get_first_child(node) {
|
|
return first_child_getter.call(node);
|
|
}
|
|
|
|
/**
|
|
* @template {Node} N
|
|
* @param {N} node
|
|
* @returns {Node | null}
|
|
*/
|
|
/*@__NO_SIDE_EFFECTS__*/
|
|
function get_next_sibling(node) {
|
|
return next_sibling_getter.call(node);
|
|
}
|
|
|
|
/**
|
|
* Don't mark this as side-effect-free, hydration needs to walk all nodes
|
|
* @template {Node} N
|
|
* @param {N} node
|
|
* @param {boolean} is_text
|
|
* @returns {Node | null}
|
|
*/
|
|
function child(node, is_text) {
|
|
{
|
|
return get_first_child(node);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Don't mark this as side-effect-free, hydration needs to walk all nodes
|
|
* @param {DocumentFragment | TemplateNode[]} fragment
|
|
* @param {boolean} is_text
|
|
* @returns {Node | null}
|
|
*/
|
|
function first_child(fragment, is_text) {
|
|
{
|
|
// when not hydrating, `fragment` is a `DocumentFragment` (the result of calling `open_frag`)
|
|
var first = /** @type {DocumentFragment} */ (get_first_child(/** @type {Node} */ (fragment)));
|
|
|
|
// TODO prevent user comments with the empty string when preserveComments is true
|
|
if (first instanceof Comment && first.data === '') return get_next_sibling(first);
|
|
|
|
return first;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Don't mark this as side-effect-free, hydration needs to walk all nodes
|
|
* @param {TemplateNode} node
|
|
* @param {number} count
|
|
* @param {boolean} is_text
|
|
* @returns {Node | null}
|
|
*/
|
|
function sibling(node, count = 1, is_text = false) {
|
|
let next_sibling = node;
|
|
|
|
while (count--) {
|
|
next_sibling = /** @type {TemplateNode} */ (get_next_sibling(next_sibling));
|
|
}
|
|
|
|
{
|
|
return next_sibling;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @template {Node} N
|
|
* @param {N} node
|
|
* @returns {void}
|
|
*/
|
|
function clear_text_content(node) {
|
|
node.textContent = '';
|
|
}
|
|
|
|
/** @import { ComponentContext, ComponentContextLegacy, Derived, Effect, TemplateNode, TransitionManager } from '#client' */
|
|
|
|
/**
|
|
* @param {'$effect' | '$effect.pre' | '$inspect'} rune
|
|
*/
|
|
function validate_effect(rune) {
|
|
if (active_effect === null && active_reaction === null) {
|
|
effect_orphan();
|
|
}
|
|
|
|
if (active_reaction !== null && (active_reaction.f & UNOWNED) !== 0 && active_effect === null) {
|
|
effect_in_unowned_derived();
|
|
}
|
|
|
|
if (is_destroying_effect) {
|
|
effect_in_teardown();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {Effect} effect
|
|
* @param {Effect} parent_effect
|
|
*/
|
|
function push_effect(effect, parent_effect) {
|
|
var parent_last = parent_effect.last;
|
|
if (parent_last === null) {
|
|
parent_effect.last = parent_effect.first = effect;
|
|
} else {
|
|
parent_last.next = effect;
|
|
effect.prev = parent_last;
|
|
parent_effect.last = effect;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {number} type
|
|
* @param {null | (() => void | (() => void))} fn
|
|
* @param {boolean} sync
|
|
* @param {boolean} push
|
|
* @returns {Effect}
|
|
*/
|
|
function create_effect(type, fn, sync, push = true) {
|
|
var parent = active_effect;
|
|
|
|
/** @type {Effect} */
|
|
var effect = {
|
|
ctx: component_context,
|
|
deps: null,
|
|
nodes_start: null,
|
|
nodes_end: null,
|
|
f: type | DIRTY,
|
|
first: null,
|
|
fn,
|
|
last: null,
|
|
next: null,
|
|
parent,
|
|
prev: null,
|
|
teardown: null,
|
|
transitions: null,
|
|
wv: 0
|
|
};
|
|
|
|
if (sync) {
|
|
try {
|
|
update_effect(effect);
|
|
effect.f |= EFFECT_RAN;
|
|
} catch (e) {
|
|
destroy_effect(effect);
|
|
throw e;
|
|
}
|
|
} else if (fn !== null) {
|
|
schedule_effect(effect);
|
|
}
|
|
|
|
// if an effect has no dependencies, no DOM and no teardown function,
|
|
// don't bother adding it to the effect tree
|
|
var inert =
|
|
sync &&
|
|
effect.deps === null &&
|
|
effect.first === null &&
|
|
effect.nodes_start === null &&
|
|
effect.teardown === null &&
|
|
(effect.f & (EFFECT_HAS_DERIVED | BOUNDARY_EFFECT)) === 0;
|
|
|
|
if (!inert && push) {
|
|
if (parent !== null) {
|
|
push_effect(effect, parent);
|
|
}
|
|
|
|
// if we're in a derived, add the effect there too
|
|
if (active_reaction !== null && (active_reaction.f & DERIVED) !== 0) {
|
|
var derived = /** @type {Derived} */ (active_reaction);
|
|
(derived.effects ??= []).push(effect);
|
|
}
|
|
}
|
|
|
|
return effect;
|
|
}
|
|
|
|
/**
|
|
* @param {() => void} fn
|
|
*/
|
|
function teardown(fn) {
|
|
const effect = create_effect(RENDER_EFFECT, null, false);
|
|
set_signal_status(effect, CLEAN);
|
|
effect.teardown = fn;
|
|
return effect;
|
|
}
|
|
|
|
/**
|
|
* Internal representation of `$effect(...)`
|
|
* @param {() => void | (() => void)} fn
|
|
*/
|
|
function user_effect(fn) {
|
|
validate_effect();
|
|
|
|
// Non-nested `$effect(...)` in a component should be deferred
|
|
// until the component is mounted
|
|
var defer =
|
|
active_effect !== null &&
|
|
(active_effect.f & BRANCH_EFFECT) !== 0 &&
|
|
component_context !== null &&
|
|
!component_context.m;
|
|
|
|
if (defer) {
|
|
var context = /** @type {ComponentContext} */ (component_context);
|
|
(context.e ??= []).push({
|
|
fn,
|
|
effect: active_effect,
|
|
reaction: active_reaction
|
|
});
|
|
} else {
|
|
var signal = effect(fn);
|
|
return signal;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Internal representation of `$effect.pre(...)`
|
|
* @param {() => void | (() => void)} fn
|
|
* @returns {Effect}
|
|
*/
|
|
function user_pre_effect(fn) {
|
|
validate_effect();
|
|
return render_effect(fn);
|
|
}
|
|
|
|
/**
|
|
* @param {() => void | (() => void)} fn
|
|
* @returns {Effect}
|
|
*/
|
|
function effect(fn) {
|
|
return create_effect(EFFECT, fn, false);
|
|
}
|
|
|
|
/**
|
|
* @param {() => void | (() => void)} fn
|
|
* @returns {Effect}
|
|
*/
|
|
function render_effect(fn) {
|
|
return create_effect(RENDER_EFFECT, fn, true);
|
|
}
|
|
|
|
/**
|
|
* @param {(...expressions: any) => void | (() => void)} fn
|
|
* @param {Array<() => any>} thunks
|
|
* @returns {Effect}
|
|
*/
|
|
function template_effect(fn, thunks = [], d = derived) {
|
|
const deriveds = thunks.map(d);
|
|
const effect = () => fn(...deriveds.map(get));
|
|
|
|
return block(effect);
|
|
}
|
|
|
|
/**
|
|
* @param {(() => void)} fn
|
|
* @param {number} flags
|
|
*/
|
|
function block(fn, flags = 0) {
|
|
return create_effect(RENDER_EFFECT | BLOCK_EFFECT | flags, fn, true);
|
|
}
|
|
|
|
/**
|
|
* @param {(() => void)} fn
|
|
* @param {boolean} [push]
|
|
*/
|
|
function branch(fn, push = true) {
|
|
return create_effect(RENDER_EFFECT | BRANCH_EFFECT, fn, true, push);
|
|
}
|
|
|
|
/**
|
|
* @param {Effect} effect
|
|
*/
|
|
function execute_effect_teardown(effect) {
|
|
var teardown = effect.teardown;
|
|
if (teardown !== null) {
|
|
const previously_destroying_effect = is_destroying_effect;
|
|
const previous_reaction = active_reaction;
|
|
set_is_destroying_effect(true);
|
|
set_active_reaction(null);
|
|
try {
|
|
teardown.call(null);
|
|
} finally {
|
|
set_is_destroying_effect(previously_destroying_effect);
|
|
set_active_reaction(previous_reaction);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {Effect} signal
|
|
* @param {boolean} remove_dom
|
|
* @returns {void}
|
|
*/
|
|
function destroy_effect_children(signal, remove_dom = false) {
|
|
var effect = signal.first;
|
|
signal.first = signal.last = null;
|
|
|
|
while (effect !== null) {
|
|
var next = effect.next;
|
|
|
|
if ((effect.f & ROOT_EFFECT) !== 0) {
|
|
// this is now an independent root
|
|
effect.parent = null;
|
|
} else {
|
|
destroy_effect(effect, remove_dom);
|
|
}
|
|
|
|
effect = next;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {Effect} signal
|
|
* @returns {void}
|
|
*/
|
|
function destroy_block_effect_children(signal) {
|
|
var effect = signal.first;
|
|
|
|
while (effect !== null) {
|
|
var next = effect.next;
|
|
if ((effect.f & BRANCH_EFFECT) === 0) {
|
|
destroy_effect(effect);
|
|
}
|
|
effect = next;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {Effect} effect
|
|
* @param {boolean} [remove_dom]
|
|
* @returns {void}
|
|
*/
|
|
function destroy_effect(effect, remove_dom = true) {
|
|
var removed = false;
|
|
|
|
if ((remove_dom || (effect.f & HEAD_EFFECT) !== 0) && effect.nodes_start !== null) {
|
|
remove_effect_dom(effect.nodes_start, /** @type {TemplateNode} */ (effect.nodes_end));
|
|
removed = true;
|
|
}
|
|
|
|
destroy_effect_children(effect, remove_dom && !removed);
|
|
remove_reactions(effect, 0);
|
|
set_signal_status(effect, DESTROYED);
|
|
|
|
var transitions = effect.transitions;
|
|
|
|
if (transitions !== null) {
|
|
for (const transition of transitions) {
|
|
transition.stop();
|
|
}
|
|
}
|
|
|
|
execute_effect_teardown(effect);
|
|
|
|
var parent = effect.parent;
|
|
|
|
// If the parent doesn't have any children, then skip this work altogether
|
|
if (parent !== null && parent.first !== null) {
|
|
unlink_effect(effect);
|
|
}
|
|
|
|
// `first` and `child` are nulled out in destroy_effect_children
|
|
// we don't null out `parent` so that error propagation can work correctly
|
|
effect.next =
|
|
effect.prev =
|
|
effect.teardown =
|
|
effect.ctx =
|
|
effect.deps =
|
|
effect.fn =
|
|
effect.nodes_start =
|
|
effect.nodes_end =
|
|
null;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {TemplateNode | null} node
|
|
* @param {TemplateNode} end
|
|
*/
|
|
function remove_effect_dom(node, end) {
|
|
while (node !== null) {
|
|
/** @type {TemplateNode | null} */
|
|
var next = node === end ? null : /** @type {TemplateNode} */ (get_next_sibling(node));
|
|
|
|
node.remove();
|
|
node = next;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Detach an effect from the effect tree, freeing up memory and
|
|
* reducing the amount of work that happens on subsequent traversals
|
|
* @param {Effect} effect
|
|
*/
|
|
function unlink_effect(effect) {
|
|
var parent = effect.parent;
|
|
var prev = effect.prev;
|
|
var next = effect.next;
|
|
|
|
if (prev !== null) prev.next = next;
|
|
if (next !== null) next.prev = prev;
|
|
|
|
if (parent !== null) {
|
|
if (parent.first === effect) parent.first = next;
|
|
if (parent.last === effect) parent.last = prev;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* When a block effect is removed, we don't immediately destroy it or yank it
|
|
* out of the DOM, because it might have transitions. Instead, we 'pause' it.
|
|
* It stays around (in memory, and in the DOM) until outro transitions have
|
|
* completed, and if the state change is reversed then we _resume_ it.
|
|
* A paused effect does not update, and the DOM subtree becomes inert.
|
|
* @param {Effect} effect
|
|
* @param {() => void} [callback]
|
|
*/
|
|
function pause_effect(effect, callback) {
|
|
/** @type {TransitionManager[]} */
|
|
var transitions = [];
|
|
|
|
pause_children(effect, transitions, true);
|
|
|
|
run_out_transitions(transitions, () => {
|
|
destroy_effect(effect);
|
|
if (callback) callback();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {TransitionManager[]} transitions
|
|
* @param {() => void} fn
|
|
*/
|
|
function run_out_transitions(transitions, fn) {
|
|
var remaining = transitions.length;
|
|
if (remaining > 0) {
|
|
var check = () => --remaining || fn();
|
|
for (var transition of transitions) {
|
|
transition.out(check);
|
|
}
|
|
} else {
|
|
fn();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {Effect} effect
|
|
* @param {TransitionManager[]} transitions
|
|
* @param {boolean} local
|
|
*/
|
|
function pause_children(effect, transitions, local) {
|
|
if ((effect.f & INERT) !== 0) return;
|
|
effect.f ^= INERT;
|
|
|
|
if (effect.transitions !== null) {
|
|
for (const transition of effect.transitions) {
|
|
if (transition.is_global || local) {
|
|
transitions.push(transition);
|
|
}
|
|
}
|
|
}
|
|
|
|
var child = effect.first;
|
|
|
|
while (child !== null) {
|
|
var sibling = child.next;
|
|
var transparent = (child.f & EFFECT_TRANSPARENT) !== 0 || (child.f & BRANCH_EFFECT) !== 0;
|
|
// TODO we don't need to call pause_children recursively with a linked list in place
|
|
// it's slightly more involved though as we have to account for `transparent` changing
|
|
// through the tree.
|
|
pause_children(child, transitions, transparent ? local : false);
|
|
child = sibling;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The opposite of `pause_effect`. We call this if (for example)
|
|
* `x` becomes falsy then truthy: `{#if x}...{/if}`
|
|
* @param {Effect} effect
|
|
*/
|
|
function resume_effect(effect) {
|
|
resume_children(effect, true);
|
|
}
|
|
|
|
/**
|
|
* @param {Effect} effect
|
|
* @param {boolean} local
|
|
*/
|
|
function resume_children(effect, local) {
|
|
if ((effect.f & INERT) === 0) return;
|
|
effect.f ^= INERT;
|
|
|
|
// Ensure the effect is marked as clean again so that any dirty child
|
|
// effects can schedule themselves for execution
|
|
if ((effect.f & CLEAN) === 0) {
|
|
effect.f ^= CLEAN;
|
|
}
|
|
|
|
// If a dependency of this effect changed while it was paused,
|
|
// schedule the effect to update
|
|
if (check_dirtiness(effect)) {
|
|
set_signal_status(effect, DIRTY);
|
|
schedule_effect(effect);
|
|
}
|
|
|
|
var child = effect.first;
|
|
|
|
while (child !== null) {
|
|
var sibling = child.next;
|
|
var transparent = (child.f & EFFECT_TRANSPARENT) !== 0 || (child.f & BRANCH_EFFECT) !== 0;
|
|
// TODO we don't need to call resume_children recursively with a linked list in place
|
|
// it's slightly more involved though as we have to account for `transparent` changing
|
|
// through the tree.
|
|
resume_children(child, transparent ? local : false);
|
|
child = sibling;
|
|
}
|
|
|
|
if (effect.transitions !== null) {
|
|
for (const transition of effect.transitions) {
|
|
if (transition.is_global || local) {
|
|
transition.in();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @type {Array<() => void>} */
|
|
let micro_tasks = [];
|
|
|
|
/** @type {Array<() => void>} */
|
|
let idle_tasks = [];
|
|
|
|
function run_micro_tasks() {
|
|
var tasks = micro_tasks;
|
|
micro_tasks = [];
|
|
run_all(tasks);
|
|
}
|
|
|
|
function run_idle_tasks() {
|
|
var tasks = idle_tasks;
|
|
idle_tasks = [];
|
|
run_all(tasks);
|
|
}
|
|
|
|
/**
|
|
* @param {() => void} fn
|
|
*/
|
|
function queue_micro_task(fn) {
|
|
if (micro_tasks.length === 0) {
|
|
queueMicrotask(run_micro_tasks);
|
|
}
|
|
|
|
micro_tasks.push(fn);
|
|
}
|
|
|
|
/**
|
|
* Synchronously run any queued tasks.
|
|
*/
|
|
function flush_tasks() {
|
|
if (micro_tasks.length > 0) {
|
|
run_micro_tasks();
|
|
}
|
|
|
|
if (idle_tasks.length > 0) {
|
|
run_idle_tasks();
|
|
}
|
|
}
|
|
|
|
/** @import { ComponentContext, Derived, Effect, Reaction, Signal, Source, Value } from '#client' */
|
|
let is_throwing_error = false;
|
|
|
|
let is_flushing = false;
|
|
|
|
/** @type {Effect | null} */
|
|
let last_scheduled_effect = null;
|
|
|
|
let is_updating_effect = false;
|
|
|
|
let is_destroying_effect = false;
|
|
|
|
/** @param {boolean} value */
|
|
function set_is_destroying_effect(value) {
|
|
is_destroying_effect = value;
|
|
}
|
|
|
|
// Handle effect queues
|
|
|
|
/** @type {Effect[]} */
|
|
let queued_root_effects = [];
|
|
|
|
/** @type {Effect[]} Stack of effects, dev only */
|
|
let dev_effect_stack = [];
|
|
// Handle signal reactivity tree dependencies and reactions
|
|
|
|
/** @type {null | Reaction} */
|
|
let active_reaction = null;
|
|
|
|
let untracking = false;
|
|
|
|
/** @param {null | Reaction} reaction */
|
|
function set_active_reaction(reaction) {
|
|
active_reaction = reaction;
|
|
}
|
|
|
|
/** @type {null | Effect} */
|
|
let active_effect = null;
|
|
|
|
/** @param {null | Effect} effect */
|
|
function set_active_effect(effect) {
|
|
active_effect = effect;
|
|
}
|
|
|
|
/**
|
|
* When sources are created within a reaction, reading and writing
|
|
* them should not cause a re-run
|
|
* @type {null | Source[]}
|
|
*/
|
|
let reaction_sources = null;
|
|
|
|
/**
|
|
* The dependencies of the reaction that is currently being executed. In many cases,
|
|
* the dependencies are unchanged between runs, and so this will be `null` unless
|
|
* and until a new dependency is accessed — we track this via `skipped_deps`
|
|
* @type {null | Value[]}
|
|
*/
|
|
let new_deps = null;
|
|
|
|
let skipped_deps = 0;
|
|
|
|
/**
|
|
* Tracks writes that the effect it's executed in doesn't listen to yet,
|
|
* so that the dependency can be added to the effect later on if it then reads it
|
|
* @type {null | Source[]}
|
|
*/
|
|
let untracked_writes = null;
|
|
|
|
/** @param {null | Source[]} value */
|
|
function set_untracked_writes(value) {
|
|
untracked_writes = value;
|
|
}
|
|
|
|
/**
|
|
* @type {number} Used by sources and deriveds for handling updates.
|
|
* Version starts from 1 so that unowned deriveds differentiate between a created effect and a run one for tracing
|
|
**/
|
|
let write_version = 1;
|
|
|
|
/** @type {number} Used to version each read of a source of derived to avoid duplicating depedencies inside a reaction */
|
|
let read_version = 0;
|
|
|
|
// If we are working with a get() chain that has no active container,
|
|
// to prevent memory leaks, we skip adding the reaction.
|
|
let skip_reaction = false;
|
|
|
|
function increment_write_version() {
|
|
return ++write_version;
|
|
}
|
|
|
|
/**
|
|
* Determines whether a derived or effect is dirty.
|
|
* If it is MAYBE_DIRTY, will set the status to CLEAN
|
|
* @param {Reaction} reaction
|
|
* @returns {boolean}
|
|
*/
|
|
function check_dirtiness(reaction) {
|
|
var flags = reaction.f;
|
|
|
|
if ((flags & DIRTY) !== 0) {
|
|
return true;
|
|
}
|
|
|
|
if ((flags & MAYBE_DIRTY) !== 0) {
|
|
var dependencies = reaction.deps;
|
|
var is_unowned = (flags & UNOWNED) !== 0;
|
|
|
|
if (dependencies !== null) {
|
|
var i;
|
|
var dependency;
|
|
var is_disconnected = (flags & DISCONNECTED) !== 0;
|
|
var is_unowned_connected = is_unowned && active_effect !== null && !skip_reaction;
|
|
var length = dependencies.length;
|
|
|
|
// If we are working with a disconnected or an unowned signal that is now connected (due to an active effect)
|
|
// then we need to re-connect the reaction to the dependency
|
|
if (is_disconnected || is_unowned_connected) {
|
|
var derived = /** @type {Derived} */ (reaction);
|
|
var parent = derived.parent;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
dependency = dependencies[i];
|
|
|
|
// We always re-add all reactions (even duplicates) if the derived was
|
|
// previously disconnected, however we don't if it was unowned as we
|
|
// de-duplicate dependencies in that case
|
|
if (is_disconnected || !dependency?.reactions?.includes(derived)) {
|
|
(dependency.reactions ??= []).push(derived);
|
|
}
|
|
}
|
|
|
|
if (is_disconnected) {
|
|
derived.f ^= DISCONNECTED;
|
|
}
|
|
// If the unowned derived is now fully connected to the graph again (it's unowned and reconnected, has a parent
|
|
// and the parent is not unowned), then we can mark it as connected again, removing the need for the unowned
|
|
// flag
|
|
if (is_unowned_connected && parent !== null && (parent.f & UNOWNED) === 0) {
|
|
derived.f ^= UNOWNED;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < length; i++) {
|
|
dependency = dependencies[i];
|
|
|
|
if (check_dirtiness(/** @type {Derived} */ (dependency))) {
|
|
update_derived(/** @type {Derived} */ (dependency));
|
|
}
|
|
|
|
if (dependency.wv > reaction.wv) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Unowned signals should never be marked as clean unless they
|
|
// are used within an active_effect without skip_reaction
|
|
if (!is_unowned || (active_effect !== null && !skip_reaction)) {
|
|
set_signal_status(reaction, CLEAN);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param {unknown} error
|
|
* @param {Effect} effect
|
|
*/
|
|
function propagate_error(error, effect) {
|
|
/** @type {Effect | null} */
|
|
var current = effect;
|
|
|
|
while (current !== null) {
|
|
if ((current.f & BOUNDARY_EFFECT) !== 0) {
|
|
try {
|
|
// @ts-expect-error
|
|
current.fn(error);
|
|
return;
|
|
} catch {
|
|
// Remove boundary flag from effect
|
|
current.f ^= BOUNDARY_EFFECT;
|
|
}
|
|
}
|
|
|
|
current = current.parent;
|
|
}
|
|
|
|
is_throwing_error = false;
|
|
throw error;
|
|
}
|
|
|
|
/**
|
|
* @param {Effect} effect
|
|
*/
|
|
function should_rethrow_error(effect) {
|
|
return (
|
|
(effect.f & DESTROYED) === 0 &&
|
|
(effect.parent === null || (effect.parent.f & BOUNDARY_EFFECT) === 0)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param {unknown} error
|
|
* @param {Effect} effect
|
|
* @param {Effect | null} previous_effect
|
|
* @param {ComponentContext | null} component_context
|
|
*/
|
|
function handle_error(error, effect, previous_effect, component_context) {
|
|
if (is_throwing_error) {
|
|
if (previous_effect === null) {
|
|
is_throwing_error = false;
|
|
}
|
|
|
|
if (should_rethrow_error(effect)) {
|
|
throw error;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (previous_effect !== null) {
|
|
is_throwing_error = true;
|
|
}
|
|
|
|
propagate_error(error, effect);
|
|
|
|
if (should_rethrow_error(effect)) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {Value} signal
|
|
* @param {Effect} effect
|
|
* @param {boolean} [root]
|
|
*/
|
|
function schedule_possible_effect_self_invalidation(signal, effect, root = true) {
|
|
var reactions = signal.reactions;
|
|
if (reactions === null) return;
|
|
|
|
for (var i = 0; i < reactions.length; i++) {
|
|
var reaction = reactions[i];
|
|
|
|
if (reaction_sources?.includes(signal)) continue;
|
|
|
|
if ((reaction.f & DERIVED) !== 0) {
|
|
schedule_possible_effect_self_invalidation(/** @type {Derived} */ (reaction), effect, false);
|
|
} else if (effect === reaction) {
|
|
if (root) {
|
|
set_signal_status(reaction, DIRTY);
|
|
} else if ((reaction.f & CLEAN) !== 0) {
|
|
set_signal_status(reaction, MAYBE_DIRTY);
|
|
}
|
|
schedule_effect(/** @type {Effect} */ (reaction));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @template V
|
|
* @param {Reaction} reaction
|
|
* @returns {V}
|
|
*/
|
|
function update_reaction(reaction) {
|
|
var previous_deps = new_deps;
|
|
var previous_skipped_deps = skipped_deps;
|
|
var previous_untracked_writes = untracked_writes;
|
|
var previous_reaction = active_reaction;
|
|
var previous_skip_reaction = skip_reaction;
|
|
var previous_reaction_sources = reaction_sources;
|
|
var previous_component_context = component_context;
|
|
var previous_untracking = untracking;
|
|
|
|
var flags = reaction.f;
|
|
|
|
new_deps = /** @type {null | Value[]} */ (null);
|
|
skipped_deps = 0;
|
|
untracked_writes = null;
|
|
skip_reaction =
|
|
(flags & UNOWNED) !== 0 && (untracking || !is_updating_effect || active_reaction === null);
|
|
active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null;
|
|
|
|
reaction_sources = null;
|
|
set_component_context(reaction.ctx);
|
|
untracking = false;
|
|
read_version++;
|
|
|
|
reaction.f |= EFFECT_IS_UPDATING;
|
|
|
|
try {
|
|
var result = /** @type {Function} */ (0, reaction.fn)();
|
|
var deps = reaction.deps;
|
|
|
|
if (new_deps !== null) {
|
|
var i;
|
|
|
|
remove_reactions(reaction, skipped_deps);
|
|
|
|
if (deps !== null && skipped_deps > 0) {
|
|
deps.length = skipped_deps + new_deps.length;
|
|
for (i = 0; i < new_deps.length; i++) {
|
|
deps[skipped_deps + i] = new_deps[i];
|
|
}
|
|
} else {
|
|
reaction.deps = deps = new_deps;
|
|
}
|
|
|
|
if (!skip_reaction) {
|
|
for (i = skipped_deps; i < deps.length; i++) {
|
|
(deps[i].reactions ??= []).push(reaction);
|
|
}
|
|
}
|
|
} else if (deps !== null && skipped_deps < deps.length) {
|
|
remove_reactions(reaction, skipped_deps);
|
|
deps.length = skipped_deps;
|
|
}
|
|
|
|
// If we're inside an effect and we have untracked writes, then we need to
|
|
// ensure that if any of those untracked writes result in re-invalidation
|
|
// of the current effect, then that happens accordingly
|
|
if (
|
|
is_runes() &&
|
|
untracked_writes !== null &&
|
|
!untracking &&
|
|
deps !== null &&
|
|
(reaction.f & (DERIVED | MAYBE_DIRTY | DIRTY)) === 0
|
|
) {
|
|
for (i = 0; i < /** @type {Source[]} */ (untracked_writes).length; i++) {
|
|
schedule_possible_effect_self_invalidation(
|
|
untracked_writes[i],
|
|
/** @type {Effect} */ (reaction)
|
|
);
|
|
}
|
|
}
|
|
|
|
// If we are returning to an previous reaction then
|
|
// we need to increment the read version to ensure that
|
|
// any dependencies in this reaction aren't marked with
|
|
// the same version
|
|
if (previous_reaction !== null && previous_reaction !== reaction) {
|
|
read_version++;
|
|
|
|
if (untracked_writes !== null) {
|
|
if (previous_untracked_writes === null) {
|
|
previous_untracked_writes = untracked_writes;
|
|
} else {
|
|
previous_untracked_writes.push(.../** @type {Source[]} */ (untracked_writes));
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
} finally {
|
|
new_deps = previous_deps;
|
|
skipped_deps = previous_skipped_deps;
|
|
untracked_writes = previous_untracked_writes;
|
|
active_reaction = previous_reaction;
|
|
skip_reaction = previous_skip_reaction;
|
|
reaction_sources = previous_reaction_sources;
|
|
set_component_context(previous_component_context);
|
|
untracking = previous_untracking;
|
|
|
|
reaction.f ^= EFFECT_IS_UPDATING;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @template V
|
|
* @param {Reaction} signal
|
|
* @param {Value<V>} dependency
|
|
* @returns {void}
|
|
*/
|
|
function remove_reaction(signal, dependency) {
|
|
let reactions = dependency.reactions;
|
|
if (reactions !== null) {
|
|
var index = index_of.call(reactions, signal);
|
|
if (index !== -1) {
|
|
var new_length = reactions.length - 1;
|
|
if (new_length === 0) {
|
|
reactions = dependency.reactions = null;
|
|
} else {
|
|
// Swap with last element and then remove.
|
|
reactions[index] = reactions[new_length];
|
|
reactions.pop();
|
|
}
|
|
}
|
|
}
|
|
// If the derived has no reactions, then we can disconnect it from the graph,
|
|
// allowing it to either reconnect in the future, or be GC'd by the VM.
|
|
if (
|
|
reactions === null &&
|
|
(dependency.f & DERIVED) !== 0 &&
|
|
// Destroying a child effect while updating a parent effect can cause a dependency to appear
|
|
// to be unused, when in fact it is used by the currently-updating parent. Checking `new_deps`
|
|
// allows us to skip the expensive work of disconnecting and immediately reconnecting it
|
|
(new_deps === null || !new_deps.includes(dependency))
|
|
) {
|
|
set_signal_status(dependency, MAYBE_DIRTY);
|
|
// If we are working with a derived that is owned by an effect, then mark it as being
|
|
// disconnected.
|
|
if ((dependency.f & (UNOWNED | DISCONNECTED)) === 0) {
|
|
dependency.f ^= DISCONNECTED;
|
|
}
|
|
// Disconnect any reactions owned by this reaction
|
|
destroy_derived_effects(/** @type {Derived} **/ (dependency));
|
|
remove_reactions(/** @type {Derived} **/ (dependency), 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {Reaction} signal
|
|
* @param {number} start_index
|
|
* @returns {void}
|
|
*/
|
|
function remove_reactions(signal, start_index) {
|
|
var dependencies = signal.deps;
|
|
if (dependencies === null) return;
|
|
|
|
for (var i = start_index; i < dependencies.length; i++) {
|
|
remove_reaction(signal, dependencies[i]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {Effect} effect
|
|
* @returns {void}
|
|
*/
|
|
function update_effect(effect) {
|
|
var flags = effect.f;
|
|
|
|
if ((flags & DESTROYED) !== 0) {
|
|
return;
|
|
}
|
|
|
|
set_signal_status(effect, CLEAN);
|
|
|
|
var previous_effect = active_effect;
|
|
var previous_component_context = component_context;
|
|
var was_updating_effect = is_updating_effect;
|
|
|
|
active_effect = effect;
|
|
is_updating_effect = true;
|
|
|
|
try {
|
|
if ((flags & BLOCK_EFFECT) !== 0) {
|
|
destroy_block_effect_children(effect);
|
|
} else {
|
|
destroy_effect_children(effect);
|
|
}
|
|
|
|
execute_effect_teardown(effect);
|
|
var teardown = update_reaction(effect);
|
|
effect.teardown = typeof teardown === 'function' ? teardown : null;
|
|
effect.wv = write_version;
|
|
|
|
var deps = effect.deps;
|
|
|
|
// In DEV, we need to handle a case where $inspect.trace() might
|
|
// incorrectly state a source dependency has not changed when it has.
|
|
// That's beacuse that source was changed by the same effect, causing
|
|
// the versions to match. We can avoid this by incrementing the version
|
|
var dep; if (DEV && tracing_mode_flag && (effect.f & DIRTY) !== 0 && deps !== null) ;
|
|
|
|
if (DEV) ;
|
|
} catch (error) {
|
|
handle_error(error, effect, previous_effect, previous_component_context || effect.ctx);
|
|
} finally {
|
|
is_updating_effect = was_updating_effect;
|
|
active_effect = previous_effect;
|
|
}
|
|
}
|
|
|
|
function infinite_loop_guard() {
|
|
try {
|
|
effect_update_depth_exceeded();
|
|
} catch (error) {
|
|
// Try and handle the error so it can be caught at a boundary, that's
|
|
// if there's an effect available from when it was last scheduled
|
|
if (last_scheduled_effect !== null) {
|
|
{
|
|
handle_error(error, last_scheduled_effect, null);
|
|
}
|
|
} else {
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
function flush_queued_root_effects() {
|
|
var was_updating_effect = is_updating_effect;
|
|
|
|
try {
|
|
var flush_count = 0;
|
|
is_updating_effect = true;
|
|
|
|
while (queued_root_effects.length > 0) {
|
|
if (flush_count++ > 1000) {
|
|
infinite_loop_guard();
|
|
}
|
|
|
|
var root_effects = queued_root_effects;
|
|
var length = root_effects.length;
|
|
|
|
queued_root_effects = [];
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
var collected_effects = process_effects(root_effects[i]);
|
|
flush_queued_effects(collected_effects);
|
|
}
|
|
old_values.clear();
|
|
}
|
|
} finally {
|
|
is_flushing = false;
|
|
is_updating_effect = was_updating_effect;
|
|
|
|
last_scheduled_effect = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {Array<Effect>} effects
|
|
* @returns {void}
|
|
*/
|
|
function flush_queued_effects(effects) {
|
|
var length = effects.length;
|
|
if (length === 0) return;
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
var effect = effects[i];
|
|
|
|
if ((effect.f & (DESTROYED | INERT)) === 0) {
|
|
try {
|
|
if (check_dirtiness(effect)) {
|
|
update_effect(effect);
|
|
|
|
// Effects with no dependencies or teardown do not get added to the effect tree.
|
|
// Deferred effects (e.g. `$effect(...)`) _are_ added to the tree because we
|
|
// don't know if we need to keep them until they are executed. Doing the check
|
|
// here (rather than in `update_effect`) allows us to skip the work for
|
|
// immediate effects.
|
|
if (effect.deps === null && effect.first === null && effect.nodes_start === null) {
|
|
if (effect.teardown === null) {
|
|
// remove this effect from the graph
|
|
unlink_effect(effect);
|
|
} else {
|
|
// keep the effect in the graph, but free up some memory
|
|
effect.fn = null;
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
handle_error(error, effect, null, effect.ctx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {Effect} signal
|
|
* @returns {void}
|
|
*/
|
|
function schedule_effect(signal) {
|
|
if (!is_flushing) {
|
|
is_flushing = true;
|
|
queueMicrotask(flush_queued_root_effects);
|
|
}
|
|
|
|
var effect = (last_scheduled_effect = signal);
|
|
|
|
while (effect.parent !== null) {
|
|
effect = effect.parent;
|
|
var flags = effect.f;
|
|
|
|
if ((flags & (ROOT_EFFECT | BRANCH_EFFECT)) !== 0) {
|
|
if ((flags & CLEAN) === 0) return;
|
|
effect.f ^= CLEAN;
|
|
}
|
|
}
|
|
|
|
queued_root_effects.push(effect);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* This function both runs render effects and collects user effects in topological order
|
|
* from the starting effect passed in. Effects will be collected when they match the filtered
|
|
* bitwise flag passed in only. The collected effects array will be populated with all the user
|
|
* effects to be flushed.
|
|
*
|
|
* @param {Effect} root
|
|
* @returns {Effect[]}
|
|
*/
|
|
function process_effects(root) {
|
|
/** @type {Effect[]} */
|
|
var effects = [];
|
|
|
|
/** @type {Effect | null} */
|
|
var effect = root;
|
|
|
|
while (effect !== null) {
|
|
var flags = effect.f;
|
|
var is_branch = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) !== 0;
|
|
var is_skippable_branch = is_branch && (flags & CLEAN) !== 0;
|
|
|
|
if (!is_skippable_branch && (flags & INERT) === 0) {
|
|
if ((flags & EFFECT) !== 0) {
|
|
effects.push(effect);
|
|
} else if (is_branch) {
|
|
effect.f ^= CLEAN;
|
|
} else {
|
|
try {
|
|
if (check_dirtiness(effect)) {
|
|
update_effect(effect);
|
|
}
|
|
} catch (error) {
|
|
handle_error(error, effect, null, effect.ctx);
|
|
}
|
|
}
|
|
|
|
/** @type {Effect | null} */
|
|
var child = effect.first;
|
|
|
|
if (child !== null) {
|
|
effect = child;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
var parent = effect.parent;
|
|
effect = effect.next;
|
|
|
|
while (effect === null && parent !== null) {
|
|
effect = parent.next;
|
|
parent = parent.parent;
|
|
}
|
|
}
|
|
|
|
return effects;
|
|
}
|
|
|
|
/**
|
|
* Synchronously flush any pending updates.
|
|
* Returns void if no callback is provided, otherwise returns the result of calling the callback.
|
|
* @template [T=void]
|
|
* @param {(() => T) | undefined} [fn]
|
|
* @returns {T}
|
|
*/
|
|
function flushSync(fn) {
|
|
var result;
|
|
|
|
while (true) {
|
|
flush_tasks();
|
|
|
|
if (queued_root_effects.length === 0) {
|
|
return /** @type {T} */ (result);
|
|
}
|
|
|
|
is_flushing = true;
|
|
flush_queued_root_effects();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @template V
|
|
* @param {Value<V>} signal
|
|
* @returns {V}
|
|
*/
|
|
function get(signal) {
|
|
var flags = signal.f;
|
|
var is_derived = (flags & DERIVED) !== 0;
|
|
|
|
// Register the dependency on the current reaction signal.
|
|
if (active_reaction !== null && !untracking) {
|
|
if (!reaction_sources?.includes(signal)) {
|
|
var deps = active_reaction.deps;
|
|
if (signal.rv < read_version) {
|
|
signal.rv = read_version;
|
|
// If the signal is accessing the same dependencies in the same
|
|
// order as it did last time, increment `skipped_deps`
|
|
// rather than updating `new_deps`, which creates GC cost
|
|
if (new_deps === null && deps !== null && deps[skipped_deps] === signal) {
|
|
skipped_deps++;
|
|
} else if (new_deps === null) {
|
|
new_deps = [signal];
|
|
} else if (!skip_reaction || !new_deps.includes(signal)) {
|
|
// Normally we can push duplicated dependencies to `new_deps`, but if we're inside
|
|
// an unowned derived because skip_reaction is true, then we need to ensure that
|
|
// we don't have duplicates
|
|
new_deps.push(signal);
|
|
}
|
|
}
|
|
}
|
|
} else if (
|
|
is_derived &&
|
|
/** @type {Derived} */ (signal).deps === null &&
|
|
/** @type {Derived} */ (signal).effects === null
|
|
) {
|
|
var derived = /** @type {Derived} */ (signal);
|
|
var parent = derived.parent;
|
|
|
|
if (parent !== null && (parent.f & UNOWNED) === 0) {
|
|
// If the derived is owned by another derived then mark it as unowned
|
|
// as the derived value might have been referenced in a different context
|
|
// since and thus its parent might not be its true owner anymore
|
|
derived.f ^= UNOWNED;
|
|
}
|
|
}
|
|
|
|
if (is_derived) {
|
|
derived = /** @type {Derived} */ (signal);
|
|
|
|
if (check_dirtiness(derived)) {
|
|
update_derived(derived);
|
|
}
|
|
}
|
|
|
|
if (is_destroying_effect && old_values.has(signal)) {
|
|
return old_values.get(signal);
|
|
}
|
|
|
|
return signal.v;
|
|
}
|
|
|
|
/**
|
|
* When used inside a [`$derived`](https://svelte.dev/docs/svelte/$derived) or [`$effect`](https://svelte.dev/docs/svelte/$effect),
|
|
* any state read inside `fn` will not be treated as a dependency.
|
|
*
|
|
* ```ts
|
|
* $effect(() => {
|
|
* // this will run when `data` changes, but not when `time` changes
|
|
* save(data, {
|
|
* timestamp: untrack(() => time)
|
|
* });
|
|
* });
|
|
* ```
|
|
* @template T
|
|
* @param {() => T} fn
|
|
* @returns {T}
|
|
*/
|
|
function untrack(fn) {
|
|
var previous_untracking = untracking;
|
|
try {
|
|
untracking = true;
|
|
return fn();
|
|
} finally {
|
|
untracking = previous_untracking;
|
|
}
|
|
}
|
|
|
|
const STATUS_MASK = -7169;
|
|
|
|
/**
|
|
* @param {Signal} signal
|
|
* @param {number} status
|
|
* @returns {void}
|
|
*/
|
|
function set_signal_status(signal, status) {
|
|
signal.f = (signal.f & STATUS_MASK) | status;
|
|
}
|
|
|
|
/**
|
|
* Possibly traverse an object and read all its properties so that they're all reactive in case this is `$state`.
|
|
* Does only check first level of an object for performance reasons (heuristic should be good for 99% of all cases).
|
|
* @param {any} value
|
|
* @returns {void}
|
|
*/
|
|
function deep_read_state(value) {
|
|
if (typeof value !== 'object' || !value || value instanceof EventTarget) {
|
|
return;
|
|
}
|
|
|
|
if (STATE_SYMBOL in value) {
|
|
deep_read(value);
|
|
} else if (!Array.isArray(value)) {
|
|
for (let key in value) {
|
|
const prop = value[key];
|
|
if (typeof prop === 'object' && prop && STATE_SYMBOL in prop) {
|
|
deep_read(prop);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deeply traverse an object and read all its properties
|
|
* so that they're all reactive in case this is `$state`
|
|
* @param {any} value
|
|
* @param {Set<any>} visited
|
|
* @returns {void}
|
|
*/
|
|
function deep_read(value, visited = new Set()) {
|
|
if (
|
|
typeof value === 'object' &&
|
|
value !== null &&
|
|
// We don't want to traverse DOM elements
|
|
!(value instanceof EventTarget) &&
|
|
!visited.has(value)
|
|
) {
|
|
visited.add(value);
|
|
// When working with a possible SvelteDate, this
|
|
// will ensure we capture changes to it.
|
|
if (value instanceof Date) {
|
|
value.getTime();
|
|
}
|
|
for (let key in value) {
|
|
try {
|
|
deep_read(value[key], visited);
|
|
} catch (e) {
|
|
// continue
|
|
}
|
|
}
|
|
const proto = get_prototype_of(value);
|
|
if (
|
|
proto !== Object.prototype &&
|
|
proto !== Array.prototype &&
|
|
proto !== Map.prototype &&
|
|
proto !== Set.prototype &&
|
|
proto !== Date.prototype
|
|
) {
|
|
const descriptors = get_descriptors(proto);
|
|
for (let key in descriptors) {
|
|
const get = descriptors[key].get;
|
|
if (get) {
|
|
try {
|
|
get.call(value);
|
|
} catch (e) {
|
|
// continue
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @param {string} html */
|
|
function create_fragment_from_html(html) {
|
|
var elem = document.createElement('template');
|
|
elem.innerHTML = html.replaceAll('<!>', '<!---->'); // XHTML compliance
|
|
return elem.content;
|
|
}
|
|
|
|
/** @import { Effect, TemplateNode } from '#client' */
|
|
/** @import { TemplateStructure } from './types' */
|
|
|
|
/**
|
|
* @param {TemplateNode} start
|
|
* @param {TemplateNode | null} end
|
|
*/
|
|
function assign_nodes(start, end) {
|
|
var effect = /** @type {Effect} */ (active_effect);
|
|
if (effect.nodes_start === null) {
|
|
effect.nodes_start = start;
|
|
effect.nodes_end = end;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} content
|
|
* @param {number} flags
|
|
* @returns {() => Node | Node[]}
|
|
*/
|
|
/*#__NO_SIDE_EFFECTS__*/
|
|
function from_html(content, flags) {
|
|
var is_fragment = (flags & TEMPLATE_FRAGMENT) !== 0;
|
|
var use_import_node = (flags & TEMPLATE_USE_IMPORT_NODE) !== 0;
|
|
|
|
/** @type {Node} */
|
|
var node;
|
|
|
|
/**
|
|
* Whether or not the first item is a text/element node. If not, we need to
|
|
* create an additional comment node to act as `effect.nodes.start`
|
|
*/
|
|
var has_start = !content.startsWith('<!>');
|
|
|
|
return () => {
|
|
|
|
if (node === undefined) {
|
|
node = create_fragment_from_html(has_start ? content : '<!>' + content);
|
|
if (!is_fragment) node = /** @type {Node} */ (get_first_child(node));
|
|
}
|
|
|
|
var clone = /** @type {TemplateNode} */ (
|
|
use_import_node || is_firefox ? document.importNode(node, true) : node.cloneNode(true)
|
|
);
|
|
|
|
if (is_fragment) {
|
|
var start = /** @type {TemplateNode} */ (get_first_child(clone));
|
|
var end = /** @type {TemplateNode} */ (clone.lastChild);
|
|
|
|
assign_nodes(start, end);
|
|
} else {
|
|
assign_nodes(clone, clone);
|
|
}
|
|
|
|
return clone;
|
|
};
|
|
}
|
|
|
|
function comment() {
|
|
|
|
var frag = document.createDocumentFragment();
|
|
var start = document.createComment('');
|
|
var anchor = create_text();
|
|
frag.append(start, anchor);
|
|
|
|
assign_nodes(start, anchor);
|
|
|
|
return frag;
|
|
}
|
|
|
|
/**
|
|
* Assign the created (or in hydration mode, traversed) dom elements to the current block
|
|
* and insert the elements into the dom (in client mode).
|
|
* @param {Text | Comment | Element} anchor
|
|
* @param {DocumentFragment | Element} dom
|
|
*/
|
|
function append(anchor, dom) {
|
|
|
|
if (anchor === null) {
|
|
// edge case — void `<svelte:element>` with content
|
|
return;
|
|
}
|
|
|
|
anchor.before(/** @type {Node} */ (dom));
|
|
}
|
|
|
|
/** @import { ComponentContext, Effect, TemplateNode } from '#client' */
|
|
/** @import { Component, ComponentType, SvelteComponent, MountOptions } from '../../index.js' */
|
|
|
|
/**
|
|
* @param {Element} text
|
|
* @param {string} value
|
|
* @returns {void}
|
|
*/
|
|
function set_text(text, value) {
|
|
// For objects, we apply string coercion (which might make things like $state array references in the template reactive) before diffing
|
|
var str = value == null ? '' : typeof value === 'object' ? value + '' : value;
|
|
// @ts-expect-error
|
|
if (str !== (text.__t ??= text.nodeValue)) {
|
|
// @ts-expect-error
|
|
text.__t = str;
|
|
text.nodeValue = str + '';
|
|
}
|
|
}
|
|
|
|
/** @import { Effect, Source, TemplateNode } from '#client' */
|
|
|
|
const PENDING = 0;
|
|
const THEN = 1;
|
|
const CATCH = 2;
|
|
|
|
/**
|
|
* @template V
|
|
* @param {TemplateNode} node
|
|
* @param {(() => Promise<V>)} get_input
|
|
* @param {null | ((anchor: Node) => void)} pending_fn
|
|
* @param {null | ((anchor: Node, value: Source<V>) => void)} then_fn
|
|
* @param {null | ((anchor: Node, error: unknown) => void)} catch_fn
|
|
* @returns {void}
|
|
*/
|
|
function await_block(node, get_input, pending_fn, then_fn, catch_fn) {
|
|
|
|
var anchor = node;
|
|
var runes = is_runes();
|
|
var active_component_context = component_context;
|
|
|
|
/** @type {V | Promise<V> | typeof UNINITIALIZED} */
|
|
var input = UNINITIALIZED;
|
|
|
|
/** @type {Effect | null} */
|
|
var pending_effect;
|
|
|
|
/** @type {Effect | null} */
|
|
var then_effect;
|
|
|
|
/** @type {Effect | null} */
|
|
var catch_effect;
|
|
|
|
var input_source = (runes ? source : mutable_source)(/** @type {V} */ (undefined));
|
|
var error_source = (runes ? source : mutable_source)(undefined);
|
|
var resolved = false;
|
|
|
|
/**
|
|
* @param {PENDING | THEN | CATCH} state
|
|
* @param {boolean} restore
|
|
*/
|
|
function update(state, restore) {
|
|
resolved = true;
|
|
|
|
if (restore) {
|
|
set_active_effect(effect);
|
|
set_active_reaction(effect); // TODO do we need both?
|
|
set_component_context(active_component_context);
|
|
}
|
|
|
|
try {
|
|
if (state === PENDING && pending_fn) {
|
|
if (pending_effect) resume_effect(pending_effect);
|
|
else pending_effect = branch(() => pending_fn(anchor));
|
|
}
|
|
|
|
if (state === THEN && then_fn) {
|
|
if (then_effect) resume_effect(then_effect);
|
|
else then_effect = branch(() => then_fn(anchor, input_source));
|
|
}
|
|
|
|
if (state === CATCH && catch_fn) {
|
|
if (catch_effect) resume_effect(catch_effect);
|
|
else catch_effect = branch(() => catch_fn(anchor, error_source));
|
|
}
|
|
|
|
if (state !== PENDING && pending_effect) {
|
|
pause_effect(pending_effect, () => (pending_effect = null));
|
|
}
|
|
|
|
if (state !== THEN && then_effect) {
|
|
pause_effect(then_effect, () => (then_effect = null));
|
|
}
|
|
|
|
if (state !== CATCH && catch_effect) {
|
|
pause_effect(catch_effect, () => (catch_effect = null));
|
|
}
|
|
} finally {
|
|
if (restore) {
|
|
set_component_context(null);
|
|
set_active_reaction(null);
|
|
set_active_effect(null);
|
|
|
|
// without this, the DOM does not update until two ticks after the promise
|
|
// resolves, which is unexpected behaviour (and somewhat irksome to test)
|
|
flushSync();
|
|
}
|
|
}
|
|
}
|
|
|
|
var effect = block(() => {
|
|
if (input === (input = get_input())) return;
|
|
|
|
if (is_promise(input)) {
|
|
var promise = input;
|
|
|
|
resolved = false;
|
|
|
|
promise.then(
|
|
(value) => {
|
|
if (promise !== input) return;
|
|
// we technically could use `set` here since it's on the next microtick
|
|
// but let's use internal_set for consistency and just to be safe
|
|
internal_set(input_source, value);
|
|
update(THEN, true);
|
|
},
|
|
(error) => {
|
|
if (promise !== input) return;
|
|
// we technically could use `set` here since it's on the next microtick
|
|
// but let's use internal_set for consistency and just to be safe
|
|
internal_set(error_source, error);
|
|
update(CATCH, true);
|
|
if (!catch_fn) {
|
|
// Rethrow the error if no catch block exists
|
|
throw error_source.v;
|
|
}
|
|
}
|
|
);
|
|
|
|
{
|
|
// Wait a microtask before checking if we should show the pending state as
|
|
// the promise might have resolved by the next microtask.
|
|
queue_micro_task(() => {
|
|
if (!resolved) update(PENDING, true);
|
|
});
|
|
}
|
|
} else {
|
|
internal_set(input_source, input);
|
|
update(THEN, false);
|
|
}
|
|
|
|
// Set the input to something else, in order to disable the promise callbacks
|
|
return () => (input = UNINITIALIZED);
|
|
});
|
|
}
|
|
|
|
/** @import { Effect, TemplateNode } from '#client' */
|
|
|
|
/**
|
|
* @param {TemplateNode} node
|
|
* @param {(branch: (fn: (anchor: Node, elseif?: [number,number]) => void, flag?: boolean) => void) => void} fn
|
|
* @param {[number,number]} [elseif]
|
|
* @returns {void}
|
|
*/
|
|
function if_block(node, fn, [root_index, hydrate_index] = [0, 0]) {
|
|
|
|
var anchor = node;
|
|
|
|
/** @type {Effect | null} */
|
|
var consequent_effect = null;
|
|
|
|
/** @type {Effect | null} */
|
|
var alternate_effect = null;
|
|
|
|
/** @type {UNINITIALIZED | boolean | null} */
|
|
var condition = UNINITIALIZED;
|
|
|
|
var flags = root_index > 0 ? EFFECT_TRANSPARENT : 0;
|
|
|
|
var has_branch = false;
|
|
|
|
const set_branch = (
|
|
/** @type {(anchor: Node, elseif?: [number,number]) => void} */ fn,
|
|
flag = true
|
|
) => {
|
|
has_branch = true;
|
|
update_branch(flag, fn);
|
|
};
|
|
|
|
const update_branch = (
|
|
/** @type {boolean | null} */ new_condition,
|
|
/** @type {null | ((anchor: Node, elseif?: [number,number]) => void)} */ fn
|
|
) => {
|
|
if (condition === (condition = new_condition)) return;
|
|
|
|
if (condition) {
|
|
if (consequent_effect) {
|
|
resume_effect(consequent_effect);
|
|
} else if (fn) {
|
|
consequent_effect = branch(() => fn(anchor));
|
|
}
|
|
|
|
if (alternate_effect) {
|
|
pause_effect(alternate_effect, () => {
|
|
alternate_effect = null;
|
|
});
|
|
}
|
|
} else {
|
|
if (alternate_effect) {
|
|
resume_effect(alternate_effect);
|
|
} else if (fn) {
|
|
alternate_effect = branch(() => fn(anchor, [root_index + 1, hydrate_index]));
|
|
}
|
|
|
|
if (consequent_effect) {
|
|
pause_effect(consequent_effect, () => {
|
|
consequent_effect = null;
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
block(() => {
|
|
has_branch = false;
|
|
fn(set_branch);
|
|
if (!has_branch) {
|
|
update_branch(null, null);
|
|
}
|
|
}, flags);
|
|
}
|
|
|
|
/** @import { EachItem, EachState, Effect, MaybeSource, Source, TemplateNode, TransitionManager, Value } from '#client' */
|
|
|
|
/**
|
|
* @param {any} _
|
|
* @param {number} i
|
|
*/
|
|
function index(_, i) {
|
|
return i;
|
|
}
|
|
|
|
/**
|
|
* Pause multiple effects simultaneously, and coordinate their
|
|
* subsequent destruction. Used in each blocks
|
|
* @param {EachState} state
|
|
* @param {EachItem[]} items
|
|
* @param {null | Node} controlled_anchor
|
|
* @param {Map<any, EachItem>} items_map
|
|
*/
|
|
function pause_effects(state, items, controlled_anchor, items_map) {
|
|
/** @type {TransitionManager[]} */
|
|
var transitions = [];
|
|
var length = items.length;
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
pause_children(items[i].e, transitions, true);
|
|
}
|
|
|
|
var is_controlled = length > 0 && transitions.length === 0 && controlled_anchor !== null;
|
|
// If we have a controlled anchor, it means that the each block is inside a single
|
|
// DOM element, so we can apply a fast-path for clearing the contents of the element.
|
|
if (is_controlled) {
|
|
var parent_node = /** @type {Element} */ (
|
|
/** @type {Element} */ (controlled_anchor).parentNode
|
|
);
|
|
clear_text_content(parent_node);
|
|
parent_node.append(/** @type {Element} */ (controlled_anchor));
|
|
items_map.clear();
|
|
link(state, items[0].prev, items[length - 1].next);
|
|
}
|
|
|
|
run_out_transitions(transitions, () => {
|
|
for (var i = 0; i < length; i++) {
|
|
var item = items[i];
|
|
if (!is_controlled) {
|
|
items_map.delete(item.k);
|
|
link(state, item.prev, item.next);
|
|
}
|
|
destroy_effect(item.e, !is_controlled);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @template V
|
|
* @param {Element | Comment} node The next sibling node, or the parent node if this is a 'controlled' block
|
|
* @param {number} flags
|
|
* @param {() => V[]} get_collection
|
|
* @param {(value: V, index: number) => any} get_key
|
|
* @param {(anchor: Node, item: MaybeSource<V>, index: MaybeSource<number>) => void} render_fn
|
|
* @param {null | ((anchor: Node) => void)} fallback_fn
|
|
* @returns {void}
|
|
*/
|
|
function each(node, flags, get_collection, get_key, render_fn, fallback_fn = null) {
|
|
var anchor = node;
|
|
|
|
/** @type {EachState} */
|
|
var state = { flags, items: new Map(), first: null };
|
|
|
|
/** @type {Effect | null} */
|
|
var fallback = null;
|
|
|
|
var was_empty = false;
|
|
|
|
// TODO: ideally we could use derived for runes mode but because of the ability
|
|
// to use a store which can be mutated, we can't do that here as mutating a store
|
|
// will still result in the collection array being the same from the store
|
|
var each_array = derived_safe_equal(() => {
|
|
var collection = get_collection();
|
|
|
|
return is_array(collection) ? collection : collection == null ? [] : array_from(collection);
|
|
});
|
|
|
|
block(() => {
|
|
var array = get(each_array);
|
|
var length = array.length;
|
|
|
|
if (was_empty && length === 0) {
|
|
// ignore updates if the array is empty,
|
|
// and it already was empty on previous run
|
|
return;
|
|
}
|
|
was_empty = length === 0;
|
|
|
|
{
|
|
reconcile(array, state, anchor, render_fn, flags, get_key, get_collection);
|
|
}
|
|
|
|
if (fallback_fn !== null) {
|
|
if (length === 0) {
|
|
if (fallback) {
|
|
resume_effect(fallback);
|
|
} else {
|
|
fallback = branch(() => fallback_fn(anchor));
|
|
}
|
|
} else if (fallback !== null) {
|
|
pause_effect(fallback, () => {
|
|
fallback = null;
|
|
});
|
|
}
|
|
}
|
|
|
|
// When we mount the each block for the first time, the collection won't be
|
|
// connected to this effect as the effect hasn't finished running yet and its deps
|
|
// won't be assigned. However, it's possible that when reconciling the each block
|
|
// that a mutation occurred and it's made the collection MAYBE_DIRTY, so reading the
|
|
// collection again can provide consistency to the reactive graph again as the deriveds
|
|
// will now be `CLEAN`.
|
|
get(each_array);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Add, remove, or reorder items output by an each block as its input changes
|
|
* @template V
|
|
* @param {Array<V>} array
|
|
* @param {EachState} state
|
|
* @param {Element | Comment | Text} anchor
|
|
* @param {(anchor: Node, item: MaybeSource<V>, index: number | Source<number>, collection: () => V[]) => void} render_fn
|
|
* @param {number} flags
|
|
* @param {(value: V, index: number) => any} get_key
|
|
* @param {() => V[]} get_collection
|
|
* @returns {void}
|
|
*/
|
|
function reconcile(array, state, anchor, render_fn, flags, get_key, get_collection) {
|
|
|
|
var length = array.length;
|
|
var items = state.items;
|
|
var first = state.first;
|
|
var current = first;
|
|
|
|
/** @type {undefined | Set<EachItem>} */
|
|
var seen;
|
|
|
|
/** @type {EachItem | null} */
|
|
var prev = null;
|
|
|
|
/** @type {EachItem[]} */
|
|
var matched = [];
|
|
|
|
/** @type {EachItem[]} */
|
|
var stashed = [];
|
|
|
|
/** @type {V} */
|
|
var value;
|
|
|
|
/** @type {any} */
|
|
var key;
|
|
|
|
/** @type {EachItem | undefined} */
|
|
var item;
|
|
|
|
/** @type {number} */
|
|
var i;
|
|
|
|
for (i = 0; i < length; i += 1) {
|
|
value = array[i];
|
|
key = get_key(value, i);
|
|
item = items.get(key);
|
|
|
|
if (item === undefined) {
|
|
var child_anchor = current ? /** @type {TemplateNode} */ (current.e.nodes_start) : anchor;
|
|
|
|
prev = create_item(
|
|
child_anchor,
|
|
state,
|
|
prev,
|
|
prev === null ? state.first : prev.next,
|
|
value,
|
|
key,
|
|
i,
|
|
render_fn,
|
|
flags,
|
|
get_collection
|
|
);
|
|
|
|
items.set(key, prev);
|
|
|
|
matched = [];
|
|
stashed = [];
|
|
|
|
current = prev.next;
|
|
continue;
|
|
}
|
|
|
|
{
|
|
update_item(item, value, i);
|
|
}
|
|
|
|
if ((item.e.f & INERT) !== 0) {
|
|
resume_effect(item.e);
|
|
}
|
|
|
|
if (item !== current) {
|
|
if (seen !== undefined && seen.has(item)) {
|
|
if (matched.length < stashed.length) {
|
|
// more efficient to move later items to the front
|
|
var start = stashed[0];
|
|
var j;
|
|
|
|
prev = start.prev;
|
|
|
|
var a = matched[0];
|
|
var b = matched[matched.length - 1];
|
|
|
|
for (j = 0; j < matched.length; j += 1) {
|
|
move(matched[j], start, anchor);
|
|
}
|
|
|
|
for (j = 0; j < stashed.length; j += 1) {
|
|
seen.delete(stashed[j]);
|
|
}
|
|
|
|
link(state, a.prev, b.next);
|
|
link(state, prev, a);
|
|
link(state, b, start);
|
|
|
|
current = start;
|
|
prev = b;
|
|
i -= 1;
|
|
|
|
matched = [];
|
|
stashed = [];
|
|
} else {
|
|
// more efficient to move earlier items to the back
|
|
seen.delete(item);
|
|
move(item, current, anchor);
|
|
|
|
link(state, item.prev, item.next);
|
|
link(state, item, prev === null ? state.first : prev.next);
|
|
link(state, prev, item);
|
|
|
|
prev = item;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
matched = [];
|
|
stashed = [];
|
|
|
|
while (current !== null && current.k !== key) {
|
|
// If the each block isn't inert and an item has an effect that is already inert,
|
|
// skip over adding it to our seen Set as the item is already being handled
|
|
if ((current.e.f & INERT) === 0) {
|
|
(seen ??= new Set()).add(current);
|
|
}
|
|
stashed.push(current);
|
|
current = current.next;
|
|
}
|
|
|
|
if (current === null) {
|
|
continue;
|
|
}
|
|
|
|
item = current;
|
|
}
|
|
|
|
matched.push(item);
|
|
prev = item;
|
|
current = item.next;
|
|
}
|
|
|
|
if (current !== null || seen !== undefined) {
|
|
var to_destroy = seen === undefined ? [] : array_from(seen);
|
|
|
|
while (current !== null) {
|
|
// If the each block isn't inert, then inert effects are currently outroing and will be removed once the transition is finished
|
|
if ((current.e.f & INERT) === 0) {
|
|
to_destroy.push(current);
|
|
}
|
|
current = current.next;
|
|
}
|
|
|
|
var destroy_length = to_destroy.length;
|
|
|
|
if (destroy_length > 0) {
|
|
var controlled_anchor = null;
|
|
|
|
pause_effects(state, to_destroy, controlled_anchor, items);
|
|
}
|
|
}
|
|
|
|
/** @type {Effect} */ (active_effect).first = state.first && state.first.e;
|
|
/** @type {Effect} */ (active_effect).last = prev && prev.e;
|
|
}
|
|
|
|
/**
|
|
* @param {EachItem} item
|
|
* @param {any} value
|
|
* @param {number} index
|
|
* @param {number} type
|
|
* @returns {void}
|
|
*/
|
|
function update_item(item, value, index, type) {
|
|
{
|
|
internal_set(item.v, value);
|
|
}
|
|
|
|
{
|
|
item.i = index;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @template V
|
|
* @param {Node} anchor
|
|
* @param {EachState} state
|
|
* @param {EachItem | null} prev
|
|
* @param {EachItem | null} next
|
|
* @param {V} value
|
|
* @param {unknown} key
|
|
* @param {number} index
|
|
* @param {(anchor: Node, item: V | Source<V>, index: number | Value<number>, collection: () => V[]) => void} render_fn
|
|
* @param {number} flags
|
|
* @param {() => V[]} get_collection
|
|
* @returns {EachItem}
|
|
*/
|
|
function create_item(
|
|
anchor,
|
|
state,
|
|
prev,
|
|
next,
|
|
value,
|
|
key,
|
|
index,
|
|
render_fn,
|
|
flags,
|
|
get_collection
|
|
) {
|
|
var reactive = (flags & EACH_ITEM_REACTIVE) !== 0;
|
|
var mutable = (flags & EACH_ITEM_IMMUTABLE) === 0;
|
|
|
|
var v = reactive ? (mutable ? mutable_source(value) : source(value)) : value;
|
|
var i = (flags & EACH_INDEX_REACTIVE) === 0 ? index : source(index);
|
|
|
|
/** @type {EachItem} */
|
|
var item = {
|
|
i,
|
|
v,
|
|
k: key,
|
|
a: null,
|
|
// @ts-expect-error
|
|
e: null,
|
|
prev,
|
|
next
|
|
};
|
|
|
|
try {
|
|
item.e = branch(() => render_fn(anchor, v, i, get_collection), hydrating);
|
|
|
|
item.e.prev = prev && prev.e;
|
|
item.e.next = next && next.e;
|
|
|
|
if (prev === null) {
|
|
state.first = item;
|
|
} else {
|
|
prev.next = item;
|
|
prev.e.next = item.e;
|
|
}
|
|
|
|
if (next !== null) {
|
|
next.prev = item;
|
|
next.e.prev = item.e;
|
|
}
|
|
|
|
return item;
|
|
} finally {
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {EachItem} item
|
|
* @param {EachItem | null} next
|
|
* @param {Text | Element | Comment} anchor
|
|
*/
|
|
function move(item, next, anchor) {
|
|
var end = item.next ? /** @type {TemplateNode} */ (item.next.e.nodes_start) : anchor;
|
|
|
|
var dest = next ? /** @type {TemplateNode} */ (next.e.nodes_start) : anchor;
|
|
var node = /** @type {TemplateNode} */ (item.e.nodes_start);
|
|
|
|
while (node !== end) {
|
|
var next_node = /** @type {TemplateNode} */ (get_next_sibling(node));
|
|
dest.before(node);
|
|
node = next_node;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {EachState} state
|
|
* @param {EachItem | null} prev
|
|
* @param {EachItem | null} next
|
|
*/
|
|
function link(state, prev, next) {
|
|
if (prev === null) {
|
|
state.first = next;
|
|
} else {
|
|
prev.next = next;
|
|
prev.e.next = next && next.e;
|
|
}
|
|
|
|
if (next !== null) {
|
|
next.prev = prev;
|
|
next.e.prev = prev && prev.e;
|
|
}
|
|
}
|
|
|
|
/** @import { ComponentContextLegacy } from '#client' */
|
|
|
|
/**
|
|
* Legacy-mode only: Call `onMount` callbacks and set up `beforeUpdate`/`afterUpdate` effects
|
|
* @param {boolean} [immutable]
|
|
*/
|
|
function init(immutable = false) {
|
|
const context = /** @type {ComponentContextLegacy} */ (component_context);
|
|
|
|
const callbacks = context.l.u;
|
|
if (!callbacks) return;
|
|
|
|
let props = () => deep_read_state(context.s);
|
|
|
|
if (immutable) {
|
|
let version = 0;
|
|
let prev = /** @type {Record<string, any>} */ ({});
|
|
|
|
// In legacy immutable mode, before/afterUpdate only fire if the object identity of a prop changes
|
|
const d = derived(() => {
|
|
let changed = false;
|
|
const props = context.s;
|
|
for (const key in props) {
|
|
if (props[key] !== prev[key]) {
|
|
prev[key] = props[key];
|
|
changed = true;
|
|
}
|
|
}
|
|
if (changed) version++;
|
|
return version;
|
|
});
|
|
|
|
props = () => get(d);
|
|
}
|
|
|
|
// beforeUpdate
|
|
if (callbacks.b.length) {
|
|
user_pre_effect(() => {
|
|
observe_all(context, props);
|
|
run_all(callbacks.b);
|
|
});
|
|
}
|
|
|
|
// onMount (must run before afterUpdate)
|
|
user_effect(() => {
|
|
const fns = untrack(() => callbacks.m.map(run));
|
|
return () => {
|
|
for (const fn of fns) {
|
|
if (typeof fn === 'function') {
|
|
fn();
|
|
}
|
|
}
|
|
};
|
|
});
|
|
|
|
// afterUpdate
|
|
if (callbacks.a.length) {
|
|
user_effect(() => {
|
|
observe_all(context, props);
|
|
run_all(callbacks.a);
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Invoke the getter of all signals associated with a component
|
|
* so they can be registered to the effect this function is called in.
|
|
* @param {ComponentContextLegacy} context
|
|
* @param {(() => void)} props
|
|
*/
|
|
function observe_all(context, props) {
|
|
if (context.l.s) {
|
|
for (const signal of context.l.s) get(signal);
|
|
}
|
|
|
|
props();
|
|
}
|
|
|
|
var root_1$8 = from_html(`<p>Left data is null</p>`);
|
|
var root_2$2 = from_html(`<li> </li>`);
|
|
|
|
function Left($$anchor, $$props) {
|
|
push($$props, true);
|
|
|
|
var fragment = comment();
|
|
var node = first_child(fragment);
|
|
|
|
{
|
|
var consequent = ($$anchor) => {
|
|
var p = root_1$8();
|
|
|
|
append($$anchor, p);
|
|
};
|
|
|
|
var alternate = ($$anchor) => {
|
|
var li = root_2$2();
|
|
var text = child(li);
|
|
template_effect(() => set_text(text, `Left: ${$$props.data.RelativePath ?? ''} - ${$$props.data.Size ?? ''} - ${$$props.data.Ticks ?? ''}`));
|
|
append($$anchor, li);
|
|
};
|
|
|
|
if_block(node, ($$render) => {
|
|
if (!$$props.data) $$render(consequent); else $$render(alternate, false);
|
|
});
|
|
}
|
|
|
|
append($$anchor, fragment);
|
|
pop();
|
|
}
|
|
|
|
var root_1$7 = from_html(`<p>Right data is null</p>`);
|
|
var root_2$1 = from_html(`<li> </li>`);
|
|
|
|
function Right($$anchor, $$props) {
|
|
push($$props, true);
|
|
|
|
var fragment = comment();
|
|
var node = first_child(fragment);
|
|
|
|
{
|
|
var consequent = ($$anchor) => {
|
|
var p = root_1$7();
|
|
|
|
append($$anchor, p);
|
|
};
|
|
|
|
var alternate = ($$anchor) => {
|
|
var li = root_2$1();
|
|
var text = child(li);
|
|
template_effect(() => set_text(text, `Right: ${$$props.data.RelativePath ?? ''} - ${$$props.data.Size ?? ''} - ${$$props.data.Ticks ?? ''}`));
|
|
append($$anchor, li);
|
|
};
|
|
|
|
if_block(node, ($$render) => {
|
|
if (!$$props.data) $$render(consequent); else $$render(alternate, false);
|
|
});
|
|
}
|
|
|
|
append($$anchor, fragment);
|
|
pop();
|
|
}
|
|
|
|
var root_1$6 = from_html(`<p>AreEqual data is null</p>`);
|
|
var root_3$7 = from_html(`<p>AreEqual data is empty</p>`);
|
|
var root_5$7 = from_html(`AreEqual: <!> <!>`, 1);
|
|
|
|
function AreEqual($$anchor, $$props) {
|
|
push($$props, true);
|
|
|
|
var fragment = comment();
|
|
var node = first_child(fragment);
|
|
|
|
{
|
|
var consequent = ($$anchor) => {
|
|
var p = root_1$6();
|
|
|
|
append($$anchor, p);
|
|
};
|
|
|
|
var alternate = ($$anchor, $$elseif) => {
|
|
{
|
|
var consequent_1 = ($$anchor) => {
|
|
var p_1 = root_3$7();
|
|
|
|
append($$anchor, p_1);
|
|
};
|
|
|
|
var alternate_1 = ($$anchor) => {
|
|
var fragment_1 = comment();
|
|
var node_1 = first_child(fragment_1);
|
|
|
|
each(node_1, 17, () => $$props.data, index, ($$anchor, segment) => {
|
|
|
|
var fragment_2 = root_5$7();
|
|
var node_2 = sibling(first_child(fragment_2));
|
|
|
|
Left(node_2, {
|
|
get data() {
|
|
return get(segment).Left;
|
|
}
|
|
});
|
|
|
|
var node_3 = sibling(node_2, 2);
|
|
|
|
Right(node_3, {
|
|
get data() {
|
|
return get(segment).Right;
|
|
}
|
|
});
|
|
|
|
append($$anchor, fragment_2);
|
|
});
|
|
|
|
append($$anchor, fragment_1);
|
|
};
|
|
|
|
if_block(
|
|
$$anchor,
|
|
($$render) => {
|
|
if ($$props.data.length === 0) $$render(consequent_1); else $$render(alternate_1, false);
|
|
},
|
|
$$elseif
|
|
);
|
|
}
|
|
};
|
|
|
|
if_block(node, ($$render) => {
|
|
if (!$$props.data) $$render(consequent); else $$render(alternate, false);
|
|
});
|
|
}
|
|
|
|
append($$anchor, fragment);
|
|
pop();
|
|
}
|
|
|
|
var root_1$5 = from_html(`<p>LeftSideIsNewer data is null</p>`);
|
|
var root_3$6 = from_html(`<p>LeftSideIsNewer data is empty</p>`);
|
|
var root_5$6 = from_html(`LeftSideIsNewer: <!> <!>`, 1);
|
|
|
|
function LeftSideIsNewer($$anchor, $$props) {
|
|
push($$props, true);
|
|
|
|
var fragment = comment();
|
|
var node = first_child(fragment);
|
|
|
|
{
|
|
var consequent = ($$anchor) => {
|
|
var p = root_1$5();
|
|
|
|
append($$anchor, p);
|
|
};
|
|
|
|
var alternate = ($$anchor, $$elseif) => {
|
|
{
|
|
var consequent_1 = ($$anchor) => {
|
|
var p_1 = root_3$6();
|
|
|
|
append($$anchor, p_1);
|
|
};
|
|
|
|
var alternate_1 = ($$anchor) => {
|
|
var fragment_1 = comment();
|
|
var node_1 = first_child(fragment_1);
|
|
|
|
each(node_1, 17, () => $$props.data, index, ($$anchor, segment) => {
|
|
|
|
var fragment_2 = root_5$6();
|
|
var node_2 = sibling(first_child(fragment_2));
|
|
|
|
Left(node_2, {
|
|
get data() {
|
|
return get(segment).Left;
|
|
}
|
|
});
|
|
|
|
var node_3 = sibling(node_2, 2);
|
|
|
|
Right(node_3, {
|
|
get data() {
|
|
return get(segment).Right;
|
|
}
|
|
});
|
|
|
|
append($$anchor, fragment_2);
|
|
});
|
|
|
|
append($$anchor, fragment_1);
|
|
};
|
|
|
|
if_block(
|
|
$$anchor,
|
|
($$render) => {
|
|
if ($$props.data.length === 0) $$render(consequent_1); else $$render(alternate_1, false);
|
|
},
|
|
$$elseif
|
|
);
|
|
}
|
|
};
|
|
|
|
if_block(node, ($$render) => {
|
|
if (!$$props.data) $$render(consequent); else $$render(alternate, false);
|
|
});
|
|
}
|
|
|
|
append($$anchor, fragment);
|
|
pop();
|
|
}
|
|
|
|
var root_1$4 = from_html(`<p>LeftSideOnly data is null</p>`);
|
|
var root_3$5 = from_html(`<p>LeftSideOnly data is empty</p>`);
|
|
var root_5$5 = from_html(`LeftSideOnly: <!> <!>`, 1);
|
|
|
|
function LeftSideOnly($$anchor, $$props) {
|
|
push($$props, true);
|
|
|
|
var fragment = comment();
|
|
var node = first_child(fragment);
|
|
|
|
{
|
|
var consequent = ($$anchor) => {
|
|
var p = root_1$4();
|
|
|
|
append($$anchor, p);
|
|
};
|
|
|
|
var alternate = ($$anchor, $$elseif) => {
|
|
{
|
|
var consequent_1 = ($$anchor) => {
|
|
var p_1 = root_3$5();
|
|
|
|
append($$anchor, p_1);
|
|
};
|
|
|
|
var alternate_1 = ($$anchor) => {
|
|
var fragment_1 = comment();
|
|
var node_1 = first_child(fragment_1);
|
|
|
|
each(node_1, 17, () => $$props.data, index, ($$anchor, segment) => {
|
|
|
|
var fragment_2 = root_5$5();
|
|
var node_2 = sibling(first_child(fragment_2));
|
|
|
|
Left(node_2, {
|
|
get data() {
|
|
return get(segment).Left;
|
|
}
|
|
});
|
|
|
|
var node_3 = sibling(node_2, 2);
|
|
|
|
Right(node_3, {
|
|
get data() {
|
|
return get(segment).Right;
|
|
}
|
|
});
|
|
|
|
append($$anchor, fragment_2);
|
|
});
|
|
|
|
append($$anchor, fragment_1);
|
|
};
|
|
|
|
if_block(
|
|
$$anchor,
|
|
($$render) => {
|
|
if ($$props.data.length === 0) $$render(consequent_1); else $$render(alternate_1, false);
|
|
},
|
|
$$elseif
|
|
);
|
|
}
|
|
};
|
|
|
|
if_block(node, ($$render) => {
|
|
if (!$$props.data) $$render(consequent); else $$render(alternate, false);
|
|
});
|
|
}
|
|
|
|
append($$anchor, fragment);
|
|
pop();
|
|
}
|
|
|
|
var root_1$3 = from_html(`<p>NotEqualBut data is null</p>`);
|
|
var root_3$4 = from_html(`<p>NotEqualBut data is empty</p>`);
|
|
var root_5$4 = from_html(`NotEqualBut: <!> <!>`, 1);
|
|
|
|
function NotEqualBut($$anchor, $$props) {
|
|
push($$props, true);
|
|
|
|
var fragment = comment();
|
|
var node = first_child(fragment);
|
|
|
|
{
|
|
var consequent = ($$anchor) => {
|
|
var p = root_1$3();
|
|
|
|
append($$anchor, p);
|
|
};
|
|
|
|
var alternate = ($$anchor, $$elseif) => {
|
|
{
|
|
var consequent_1 = ($$anchor) => {
|
|
var p_1 = root_3$4();
|
|
|
|
append($$anchor, p_1);
|
|
};
|
|
|
|
var alternate_1 = ($$anchor) => {
|
|
var fragment_1 = comment();
|
|
var node_1 = first_child(fragment_1);
|
|
|
|
each(node_1, 17, () => $$props.data, index, ($$anchor, segment) => {
|
|
|
|
var fragment_2 = root_5$4();
|
|
var node_2 = sibling(first_child(fragment_2));
|
|
|
|
Left(node_2, {
|
|
get data() {
|
|
return get(segment).Left;
|
|
}
|
|
});
|
|
|
|
var node_3 = sibling(node_2, 2);
|
|
|
|
Right(node_3, {
|
|
get data() {
|
|
return get(segment).Right;
|
|
}
|
|
});
|
|
|
|
append($$anchor, fragment_2);
|
|
});
|
|
|
|
append($$anchor, fragment_1);
|
|
};
|
|
|
|
if_block(
|
|
$$anchor,
|
|
($$render) => {
|
|
if ($$props.data.length === 0) $$render(consequent_1); else $$render(alternate_1, false);
|
|
},
|
|
$$elseif
|
|
);
|
|
}
|
|
};
|
|
|
|
if_block(node, ($$render) => {
|
|
if (!$$props.data) $$render(consequent); else $$render(alternate, false);
|
|
});
|
|
}
|
|
|
|
append($$anchor, fragment);
|
|
pop();
|
|
}
|
|
|
|
var root_1$2 = from_html(`<p>Records data is null</p>`);
|
|
var root_3$3 = from_html(`<p>Records data is empty</p>`);
|
|
var root_5$3 = from_html(`<li> </li>`);
|
|
var root_4$1 = from_html(`Records: <!>`, 1);
|
|
|
|
function Records($$anchor, $$props) {
|
|
push($$props, true);
|
|
|
|
var fragment = comment();
|
|
var node = first_child(fragment);
|
|
|
|
{
|
|
var consequent = ($$anchor) => {
|
|
var p = root_1$2();
|
|
|
|
append($$anchor, p);
|
|
};
|
|
|
|
var alternate = ($$anchor, $$elseif) => {
|
|
{
|
|
var consequent_1 = ($$anchor) => {
|
|
var p_1 = root_3$3();
|
|
|
|
append($$anchor, p_1);
|
|
};
|
|
|
|
var alternate_1 = ($$anchor) => {
|
|
var fragment_1 = root_4$1();
|
|
var node_1 = sibling(first_child(fragment_1));
|
|
|
|
each(node_1, 17, () => $$props.data, index, ($$anchor, record) => {
|
|
var li = root_5$3();
|
|
var text = child(li);
|
|
template_effect(() => set_text(text, get(record).Left.Records));
|
|
append($$anchor, li);
|
|
});
|
|
|
|
append($$anchor, fragment_1);
|
|
};
|
|
|
|
if_block(
|
|
$$anchor,
|
|
($$render) => {
|
|
if ($$props.data.length === 0) $$render(consequent_1); else $$render(alternate_1, false);
|
|
},
|
|
$$elseif
|
|
);
|
|
}
|
|
};
|
|
|
|
if_block(node, ($$render) => {
|
|
if (!$$props.data) $$render(consequent); else $$render(alternate, false);
|
|
});
|
|
}
|
|
|
|
append($$anchor, fragment);
|
|
pop();
|
|
}
|
|
|
|
var root_1$1 = from_html(`<p>RightSideIsNewer data is null</p>`);
|
|
var root_3$2 = from_html(`<p>RightSideIsNewer data is empty</p>`);
|
|
var root_5$2 = from_html(`RightSideIsNewer: <!> <!>`, 1);
|
|
|
|
function RightSideIsNewer($$anchor, $$props) {
|
|
push($$props, true);
|
|
|
|
var fragment = comment();
|
|
var node = first_child(fragment);
|
|
|
|
{
|
|
var consequent = ($$anchor) => {
|
|
var p = root_1$1();
|
|
|
|
append($$anchor, p);
|
|
};
|
|
|
|
var alternate = ($$anchor, $$elseif) => {
|
|
{
|
|
var consequent_1 = ($$anchor) => {
|
|
var p_1 = root_3$2();
|
|
|
|
append($$anchor, p_1);
|
|
};
|
|
|
|
var alternate_1 = ($$anchor) => {
|
|
var fragment_1 = comment();
|
|
var node_1 = first_child(fragment_1);
|
|
|
|
each(node_1, 17, () => $$props.data, index, ($$anchor, segment) => {
|
|
|
|
var fragment_2 = root_5$2();
|
|
var node_2 = sibling(first_child(fragment_2));
|
|
|
|
Left(node_2, {
|
|
get data() {
|
|
return get(segment).Left;
|
|
}
|
|
});
|
|
|
|
var node_3 = sibling(node_2, 2);
|
|
|
|
Right(node_3, {
|
|
get data() {
|
|
return get(segment).Right;
|
|
}
|
|
});
|
|
|
|
append($$anchor, fragment_2);
|
|
});
|
|
|
|
append($$anchor, fragment_1);
|
|
};
|
|
|
|
if_block(
|
|
$$anchor,
|
|
($$render) => {
|
|
if ($$props.data.length === 0) $$render(consequent_1); else $$render(alternate_1, false);
|
|
},
|
|
$$elseif
|
|
);
|
|
}
|
|
};
|
|
|
|
if_block(node, ($$render) => {
|
|
if (!$$props.data) $$render(consequent); else $$render(alternate, false);
|
|
});
|
|
}
|
|
|
|
append($$anchor, fragment);
|
|
pop();
|
|
}
|
|
|
|
var root_1 = from_html(`<p>RightSideOnly data is null</p>`);
|
|
var root_3$1 = from_html(`<p>RightSideOnly data is empty</p>`);
|
|
var root_5$1 = from_html(`RightSideOnly: <!> <!>`, 1);
|
|
|
|
function RightSideOnly($$anchor, $$props) {
|
|
push($$props, true);
|
|
|
|
var fragment = comment();
|
|
var node = first_child(fragment);
|
|
|
|
{
|
|
var consequent = ($$anchor) => {
|
|
var p = root_1();
|
|
|
|
append($$anchor, p);
|
|
};
|
|
|
|
var alternate = ($$anchor, $$elseif) => {
|
|
{
|
|
var consequent_1 = ($$anchor) => {
|
|
var p_1 = root_3$1();
|
|
|
|
append($$anchor, p_1);
|
|
};
|
|
|
|
var alternate_1 = ($$anchor) => {
|
|
var fragment_1 = comment();
|
|
var node_1 = first_child(fragment_1);
|
|
|
|
each(node_1, 17, () => $$props.data, index, ($$anchor, segment) => {
|
|
|
|
var fragment_2 = root_5$1();
|
|
var node_2 = sibling(first_child(fragment_2));
|
|
|
|
Left(node_2, {
|
|
get data() {
|
|
return get(segment).Left;
|
|
}
|
|
});
|
|
|
|
var node_3 = sibling(node_2, 2);
|
|
|
|
Right(node_3, {
|
|
get data() {
|
|
return get(segment).Right;
|
|
}
|
|
});
|
|
|
|
append($$anchor, fragment_2);
|
|
});
|
|
|
|
append($$anchor, fragment_1);
|
|
};
|
|
|
|
if_block(
|
|
$$anchor,
|
|
($$render) => {
|
|
if ($$props.data.length === 0) $$render(consequent_1); else $$render(alternate_1, false);
|
|
},
|
|
$$elseif
|
|
);
|
|
}
|
|
};
|
|
|
|
if_block(node, ($$render) => {
|
|
if (!$$props.data) $$render(consequent); else $$render(alternate, false);
|
|
});
|
|
}
|
|
|
|
append($$anchor, fragment);
|
|
pop();
|
|
}
|
|
|
|
var root_2 = from_html(`<p>Review data is null</p>`);
|
|
var root_3 = from_html(`<p>Review:</p> <!> <!> <!> <!> <!> <!> <!>`, 1);
|
|
var root_4 = from_html(`<span> </span>`);
|
|
var root_5 = from_html(`<span>waiting</span>`);
|
|
|
|
function Review($$anchor, $$props) {
|
|
push($$props, false);
|
|
|
|
let promise = fetch("http://localhost:5004/api/SyncV1/?", {
|
|
method: "POST",
|
|
body: `
|
|
{
|
|
"LeftDirectory": "D:/Tmp/phares/VisualStudioCodeLeft",
|
|
"RightDirectory": "D:/Tmp/phares/VisualStudioCode",
|
|
"Records": [
|
|
{
|
|
"RelativePath": "D:/Tmp/phares/VisualStudioCode",
|
|
"Size": 0,
|
|
"Ticks": 0
|
|
},
|
|
{
|
|
"RelativePath": "z-exclude-patterns.nsv",
|
|
"Size": 230,
|
|
"Ticks": 638843891271017574
|
|
},
|
|
{
|
|
"RelativePath": "z-include-patterns.nsv",
|
|
"Size": 4,
|
|
"Ticks": 638796666663591762
|
|
}
|
|
]
|
|
}
|
|
`,
|
|
headers: { "Content-Type": "application/json" }
|
|
}).then((x) => x.json());
|
|
|
|
init();
|
|
|
|
var fragment = comment();
|
|
var node = first_child(fragment);
|
|
|
|
await_block(
|
|
node,
|
|
() => promise,
|
|
($$anchor) => {
|
|
var span_1 = root_5();
|
|
|
|
append($$anchor, span_1);
|
|
},
|
|
($$anchor, data) => {
|
|
var fragment_1 = comment();
|
|
var node_1 = first_child(fragment_1);
|
|
|
|
{
|
|
var consequent = ($$anchor) => {
|
|
var p = root_2();
|
|
|
|
append($$anchor, p);
|
|
};
|
|
|
|
var alternate = ($$anchor) => {
|
|
var fragment_2 = root_3();
|
|
var node_2 = sibling(first_child(fragment_2), 2);
|
|
|
|
AreEqual(node_2, {
|
|
get data() {
|
|
return get(data).AreEqual;
|
|
}
|
|
});
|
|
|
|
var node_3 = sibling(node_2, 2);
|
|
|
|
LeftSideIsNewer(node_3, {
|
|
get data() {
|
|
return get(data).LeftSideIsNewer;
|
|
}
|
|
});
|
|
|
|
var node_4 = sibling(node_3, 2);
|
|
|
|
LeftSideOnly(node_4, {
|
|
get data() {
|
|
return get(data).LeftSideOnly;
|
|
}
|
|
});
|
|
|
|
var node_5 = sibling(node_4, 2);
|
|
|
|
NotEqualBut(node_5, {
|
|
get data() {
|
|
return get(data).NotEqualBut;
|
|
}
|
|
});
|
|
|
|
var node_6 = sibling(node_5, 2);
|
|
|
|
Records(node_6, {
|
|
get data() {
|
|
return get(data).Records;
|
|
}
|
|
});
|
|
|
|
var node_7 = sibling(node_6, 2);
|
|
|
|
RightSideIsNewer(node_7, {
|
|
get data() {
|
|
return get(data).RightSideIsNewer;
|
|
}
|
|
});
|
|
|
|
var node_8 = sibling(node_7, 2);
|
|
|
|
RightSideOnly(node_8, {
|
|
get data() {
|
|
return get(data).RightSideOnly;
|
|
}
|
|
});
|
|
|
|
append($$anchor, fragment_2);
|
|
};
|
|
|
|
if_block(node_1, ($$render) => {
|
|
if (!get(data)) $$render(consequent); else $$render(alternate, false);
|
|
});
|
|
}
|
|
|
|
append($$anchor, fragment_1);
|
|
},
|
|
($$anchor, error) => {
|
|
var span = root_4();
|
|
var text = child(span);
|
|
template_effect(() => set_text(text, get(error)));
|
|
append($$anchor, span);
|
|
}
|
|
);
|
|
|
|
append($$anchor, fragment);
|
|
pop();
|
|
}
|
|
|
|
let div = document.createElement("div");
|
|
let script = document.currentScript;
|
|
script.parentNode.insertBefore(div, script);
|
|
|
|
new Review({
|
|
target: div,
|
|
props: {},
|
|
});
|
|
|
|
})();
|