mirror of https://github.com/sveltejs/svelte
commit
b7f3b88761
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: correctly scope CSS selectors with descendant combinators
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
feat: implement support for `:is(...)` and `:where(...)`
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
chore: improve code generation for `bind:this` in SSR mode
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
chore: treeshake unused store subscriptions in SSR mode
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: improve bind:this support for each blocks
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: improve effect over-fire on store subscription init
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: use init properties when exporting non-state values in prod
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
feat: implement nested CSS support
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: improve each block item equality for immutable mode
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: handle member expressions in directives
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: improve handling of unowned derived signals
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: better handling of empty text node hydration
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: improve $inspect handling of derived objects
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: remove memory leak
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: ensure update methods of actions and reactive statements work with fine-grained `$state`
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: don't execute scripts inside `@html` when instantiated on the client
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: make `bind_this` implementation more robust
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: only escape characters in SSR template
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
breaking: remove `createRoot`, adjust `mount`/`hydrate` APIs, introduce `unmount`
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: call beforeUpdate/afterUpdate callbacks when props are mutated
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
feat: allow state/derived/props to be explicitly exported from components
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: replace proxy-based readonly validation with stack-trace-based ownership tracking
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: permit whitespace within template scripts
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: wire up `events` in `mount` correctly and fix its types
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: inherit ownerlessness when creating child proxies
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: allow boolean `contenteditable` attribute
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
breaking: encapsulate/remove selectors inside `:is(...)` and `:where(...)`
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: improve props spreading logic
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: correct context applied to batch_inspect
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: visit expression node in directives
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: better handling of derived signals that have no dependencies
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: improve state store mutation compiler output
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
"svelte": patch
|
||||
---
|
||||
|
||||
fix: improve import event handler support
|
||||
@ -0,0 +1,26 @@
|
||||
export const SOURCE = 1;
|
||||
export const DERIVED = 1 << 1;
|
||||
export const EFFECT = 1 << 2;
|
||||
export const PRE_EFFECT = 1 << 3;
|
||||
export const RENDER_EFFECT = 1 << 4;
|
||||
export const MANAGED = 1 << 6;
|
||||
export const UNOWNED = 1 << 7;
|
||||
export const CLEAN = 1 << 8;
|
||||
export const DIRTY = 1 << 9;
|
||||
export const MAYBE_DIRTY = 1 << 10;
|
||||
export const INERT = 1 << 11;
|
||||
export const DESTROYED = 1 << 12;
|
||||
|
||||
export const ROOT_BLOCK = 0;
|
||||
export const IF_BLOCK = 1;
|
||||
export const EACH_BLOCK = 2;
|
||||
export const EACH_ITEM_BLOCK = 3;
|
||||
export const AWAIT_BLOCK = 4;
|
||||
export const KEY_BLOCK = 5;
|
||||
export const HEAD_BLOCK = 6;
|
||||
export const DYNAMIC_COMPONENT_BLOCK = 7;
|
||||
export const DYNAMIC_ELEMENT_BLOCK = 8;
|
||||
export const SNIPPET_BLOCK = 9;
|
||||
|
||||
export const UNINITIALIZED = Symbol();
|
||||
export const STATE_SYMBOL = Symbol('$state');
|
||||
@ -0,0 +1,154 @@
|
||||
/** @typedef {{ file: string, line: number, column: number }} Location */
|
||||
|
||||
import { STATE_SYMBOL } from '../constants.js';
|
||||
import { untrack } from '../runtime.js';
|
||||
|
||||
/** @type {Record<string, Array<{ start: Location, end: Location, component: Function }>>} */
|
||||
const boundaries = {};
|
||||
|
||||
const chrome_pattern = /at (?:.+ \()?(.+):(\d+):(\d+)\)?$/;
|
||||
const firefox_pattern = /@(.+):(\d+):(\d+)$/;
|
||||
|
||||
export function get_stack() {
|
||||
const stack = new Error().stack;
|
||||
if (!stack) return null;
|
||||
|
||||
const entries = [];
|
||||
|
||||
for (const line of stack.split('\n')) {
|
||||
let match = chrome_pattern.exec(line) ?? firefox_pattern.exec(line);
|
||||
|
||||
if (match) {
|
||||
entries.push({
|
||||
file: match[1],
|
||||
line: +match[2],
|
||||
column: +match[3]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines which `.svelte` component is responsible for a given state change
|
||||
* @returns {Function | null}
|
||||
*/
|
||||
export function get_component() {
|
||||
const stack = get_stack();
|
||||
if (!stack) return null;
|
||||
|
||||
for (const entry of stack) {
|
||||
const modules = boundaries[entry.file];
|
||||
if (!modules) continue;
|
||||
|
||||
for (const module of modules) {
|
||||
if (module.start.line < entry.line && module.end.line > entry.line) {
|
||||
return module.component;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Together with `mark_module_end`, this function establishes the boundaries of a `.svelte` file,
|
||||
* such that subsequent calls to `get_component` can tell us which component is responsible
|
||||
* for a given state change
|
||||
* @param {Function} component
|
||||
*/
|
||||
export function mark_module_start(component) {
|
||||
const start = get_stack()?.[2];
|
||||
|
||||
if (start) {
|
||||
(boundaries[start.file] ??= []).push({
|
||||
start,
|
||||
// @ts-expect-error
|
||||
end: null,
|
||||
component
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function mark_module_end() {
|
||||
const end = get_stack()?.[2];
|
||||
|
||||
if (end) {
|
||||
// @ts-expect-error
|
||||
boundaries[end.file].at(-1).end = end;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any} object
|
||||
* @param {any} owner
|
||||
*/
|
||||
export function add_owner(object, owner) {
|
||||
untrack(() => {
|
||||
add_owner_to_object(object, owner);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} object
|
||||
* @param {Function} owner
|
||||
*/
|
||||
function add_owner_to_object(object, owner) {
|
||||
if (object?.[STATE_SYMBOL]?.o && !object[STATE_SYMBOL].o.has(owner)) {
|
||||
object[STATE_SYMBOL].o.add(owner);
|
||||
|
||||
for (const key in object) {
|
||||
add_owner_to_object(object[key], owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} object
|
||||
*/
|
||||
export function strip_owner(object) {
|
||||
untrack(() => {
|
||||
strip_owner_from_object(object);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} object
|
||||
*/
|
||||
function strip_owner_from_object(object) {
|
||||
if (object?.[STATE_SYMBOL]?.o) {
|
||||
object[STATE_SYMBOL].o = null;
|
||||
|
||||
for (const key in object) {
|
||||
strip_owner(object[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Set<Function>} owners
|
||||
*/
|
||||
export function check_ownership(owners) {
|
||||
const component = get_component();
|
||||
|
||||
if (component && !owners.has(component)) {
|
||||
let original = [...owners][0];
|
||||
|
||||
let message =
|
||||
// @ts-expect-error
|
||||
original.filename !== component.filename
|
||||
? // @ts-expect-error
|
||||
`${component.filename} mutated a value owned by ${original.filename}. This is strongly discouraged`
|
||||
: 'Mutating a value outside the component that created it is strongly discouraged';
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`${message}. Consider passing values to child components with \`bind:\`, or use a callback instead.`
|
||||
);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.trace();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,200 @@
|
||||
import { is_promise } from '../../../common.js';
|
||||
import { hydrate_block_anchor } from '../../hydration.js';
|
||||
import { remove } from '../../reconciler.js';
|
||||
import {
|
||||
current_block,
|
||||
destroy_signal,
|
||||
execute_effect,
|
||||
flushSync,
|
||||
push_destroy_fn
|
||||
} from '../../runtime.js';
|
||||
import { render_effect } from '../../reactivity/computations.js';
|
||||
import { trigger_transitions } from '../../transitions.js';
|
||||
import { AWAIT_BLOCK, UNINITIALIZED } from '../../constants.js';
|
||||
|
||||
/** @returns {import('../../types.js').AwaitBlock} */
|
||||
export function create_await_block() {
|
||||
return {
|
||||
// dom
|
||||
d: null,
|
||||
// effect
|
||||
e: null,
|
||||
// parent
|
||||
p: /** @type {import('../../types.js').Block} */ (current_block),
|
||||
// pending
|
||||
n: true,
|
||||
// transition
|
||||
r: null,
|
||||
// type
|
||||
t: AWAIT_BLOCK
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {Comment} anchor_node
|
||||
* @param {(() => Promise<V>)} input
|
||||
* @param {null | ((anchor: Node) => void)} pending_fn
|
||||
* @param {null | ((anchor: Node, value: V) => void)} then_fn
|
||||
* @param {null | ((anchor: Node, error: unknown) => void)} catch_fn
|
||||
* @returns {void}
|
||||
*/
|
||||
export function await_block(anchor_node, input, pending_fn, then_fn, catch_fn) {
|
||||
const block = create_await_block();
|
||||
|
||||
/** @type {null | import('../../types.js').Render} */
|
||||
let current_render = null;
|
||||
hydrate_block_anchor(anchor_node);
|
||||
|
||||
/** @type {{}} */
|
||||
let latest_token;
|
||||
|
||||
/** @type {typeof UNINITIALIZED | V} */
|
||||
let resolved_value = UNINITIALIZED;
|
||||
|
||||
/** @type {unknown} */
|
||||
let error = UNINITIALIZED;
|
||||
let pending = false;
|
||||
block.r =
|
||||
/**
|
||||
* @param {import('../../types.js').Transition} transition
|
||||
* @returns {void}
|
||||
*/
|
||||
(transition) => {
|
||||
const render = /** @type {import('../../types.js').Render} */ (current_render);
|
||||
const transitions = render.s;
|
||||
transitions.add(transition);
|
||||
transition.f(() => {
|
||||
transitions.delete(transition);
|
||||
if (transitions.size === 0) {
|
||||
// If the current render has changed since, then we can remove the old render
|
||||
// effect as it's stale.
|
||||
if (current_render !== render && render.e !== null) {
|
||||
if (render.d !== null) {
|
||||
remove(render.d);
|
||||
render.d = null;
|
||||
}
|
||||
destroy_signal(render.e);
|
||||
render.e = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
const create_render_effect = () => {
|
||||
/** @type {import('../../types.js').Render} */
|
||||
const render = {
|
||||
d: null,
|
||||
e: null,
|
||||
s: new Set(),
|
||||
p: current_render
|
||||
};
|
||||
const effect = render_effect(
|
||||
() => {
|
||||
if (error === UNINITIALIZED) {
|
||||
if (resolved_value === UNINITIALIZED) {
|
||||
// pending = true
|
||||
block.n = true;
|
||||
if (pending_fn !== null) {
|
||||
pending_fn(anchor_node);
|
||||
}
|
||||
} else if (then_fn !== null) {
|
||||
// pending = false
|
||||
block.n = false;
|
||||
then_fn(anchor_node, resolved_value);
|
||||
}
|
||||
} else if (catch_fn !== null) {
|
||||
// pending = false
|
||||
block.n = false;
|
||||
catch_fn(anchor_node, error);
|
||||
}
|
||||
render.d = block.d;
|
||||
block.d = null;
|
||||
},
|
||||
block,
|
||||
true,
|
||||
true
|
||||
);
|
||||
render.e = effect;
|
||||
current_render = render;
|
||||
};
|
||||
const render = () => {
|
||||
const render = current_render;
|
||||
if (render === null) {
|
||||
create_render_effect();
|
||||
return;
|
||||
}
|
||||
const transitions = render.s;
|
||||
if (transitions.size === 0) {
|
||||
if (render.d !== null) {
|
||||
remove(render.d);
|
||||
render.d = null;
|
||||
}
|
||||
if (render.e) {
|
||||
execute_effect(render.e);
|
||||
} else {
|
||||
create_render_effect();
|
||||
}
|
||||
} else {
|
||||
create_render_effect();
|
||||
trigger_transitions(transitions, 'out');
|
||||
}
|
||||
};
|
||||
const await_effect = render_effect(
|
||||
() => {
|
||||
const token = {};
|
||||
latest_token = token;
|
||||
const promise = input();
|
||||
if (is_promise(promise)) {
|
||||
promise.then(
|
||||
/** @param {V} v */
|
||||
(v) => {
|
||||
if (latest_token === token) {
|
||||
// Ensure UI is in sync before resolving value.
|
||||
flushSync();
|
||||
resolved_value = v;
|
||||
pending = false;
|
||||
render();
|
||||
}
|
||||
},
|
||||
/** @param {unknown} _error */
|
||||
(_error) => {
|
||||
error = _error;
|
||||
pending = false;
|
||||
render();
|
||||
}
|
||||
);
|
||||
if (resolved_value !== UNINITIALIZED || error !== UNINITIALIZED) {
|
||||
error = UNINITIALIZED;
|
||||
resolved_value = UNINITIALIZED;
|
||||
}
|
||||
if (!pending) {
|
||||
pending = true;
|
||||
render();
|
||||
}
|
||||
} else {
|
||||
error = UNINITIALIZED;
|
||||
resolved_value = promise;
|
||||
pending = false;
|
||||
render();
|
||||
}
|
||||
},
|
||||
block,
|
||||
false
|
||||
);
|
||||
push_destroy_fn(await_effect, () => {
|
||||
let render = current_render;
|
||||
latest_token = {};
|
||||
while (render !== null) {
|
||||
const dom = render.d;
|
||||
if (dom !== null) {
|
||||
remove(dom);
|
||||
}
|
||||
const effect = render.e;
|
||||
if (effect !== null) {
|
||||
destroy_signal(effect);
|
||||
}
|
||||
render = render.p;
|
||||
}
|
||||
});
|
||||
block.e = await_effect;
|
||||
}
|
||||
@ -0,0 +1,181 @@
|
||||
import { IF_BLOCK } from '../../constants.js';
|
||||
import {
|
||||
current_hydration_fragment,
|
||||
hydrate_block_anchor,
|
||||
hydrating,
|
||||
set_current_hydration_fragment
|
||||
} from '../../hydration.js';
|
||||
import { remove } from '../../reconciler.js';
|
||||
import { current_block, destroy_signal, execute_effect, push_destroy_fn } from '../../runtime.js';
|
||||
import { render_effect } from '../../reactivity/computations.js';
|
||||
import { trigger_transitions } from '../../transitions.js';
|
||||
|
||||
/** @returns {import('../../types.js').IfBlock} */
|
||||
function create_if_block() {
|
||||
return {
|
||||
// alternate transitions
|
||||
a: null,
|
||||
// alternate effect
|
||||
ae: null,
|
||||
// consequent transitions
|
||||
c: null,
|
||||
// consequent effect
|
||||
ce: null,
|
||||
// dom
|
||||
d: null,
|
||||
// effect
|
||||
e: null,
|
||||
// parent
|
||||
p: /** @type {import('../../types.js').Block} */ (current_block),
|
||||
// transition
|
||||
r: null,
|
||||
// type
|
||||
t: IF_BLOCK,
|
||||
// value
|
||||
v: false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Comment} anchor_node
|
||||
* @param {() => boolean} condition_fn
|
||||
* @param {(anchor: Node) => void} consequent_fn
|
||||
* @param {null | ((anchor: Node) => void)} alternate_fn
|
||||
* @returns {void}
|
||||
*/
|
||||
export function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) {
|
||||
const block = create_if_block();
|
||||
hydrate_block_anchor(anchor_node);
|
||||
/** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */
|
||||
let mismatch = false;
|
||||
|
||||
/** @type {null | import('../../types.js').TemplateNode | Array<import('../../types.js').TemplateNode>} */
|
||||
let consequent_dom = null;
|
||||
/** @type {null | import('../../types.js').TemplateNode | Array<import('../../types.js').TemplateNode>} */
|
||||
let alternate_dom = null;
|
||||
let has_mounted = false;
|
||||
/**
|
||||
* @type {import('../../types.js').EffectSignal | null}
|
||||
*/
|
||||
let current_branch_effect = null;
|
||||
|
||||
const if_effect = render_effect(
|
||||
() => {
|
||||
const result = !!condition_fn();
|
||||
if (block.v !== result || !has_mounted) {
|
||||
block.v = result;
|
||||
if (has_mounted) {
|
||||
const consequent_transitions = block.c;
|
||||
const alternate_transitions = block.a;
|
||||
if (result) {
|
||||
if (alternate_transitions === null || alternate_transitions.size === 0) {
|
||||
execute_effect(alternate_effect);
|
||||
} else {
|
||||
trigger_transitions(alternate_transitions, 'out');
|
||||
}
|
||||
if (consequent_transitions === null || consequent_transitions.size === 0) {
|
||||
execute_effect(consequent_effect);
|
||||
} else {
|
||||
trigger_transitions(consequent_transitions, 'in');
|
||||
}
|
||||
} else {
|
||||
if (consequent_transitions === null || consequent_transitions.size === 0) {
|
||||
execute_effect(consequent_effect);
|
||||
} else {
|
||||
trigger_transitions(consequent_transitions, 'out');
|
||||
}
|
||||
if (alternate_transitions === null || alternate_transitions.size === 0) {
|
||||
execute_effect(alternate_effect);
|
||||
} else {
|
||||
trigger_transitions(alternate_transitions, 'in');
|
||||
}
|
||||
}
|
||||
} else if (hydrating) {
|
||||
const comment_text = /** @type {Comment} */ (current_hydration_fragment?.[0])?.data;
|
||||
if (
|
||||
!comment_text ||
|
||||
(comment_text === 'ssr:if:true' && !result) ||
|
||||
(comment_text === 'ssr:if:false' && result)
|
||||
) {
|
||||
// Hydration mismatch: remove everything inside the anchor and start fresh.
|
||||
// This could happen using when `{#if browser} .. {/if}` in SvelteKit.
|
||||
remove(current_hydration_fragment);
|
||||
set_current_hydration_fragment(null);
|
||||
mismatch = true;
|
||||
} else {
|
||||
// Remove the ssr:if comment node or else it will confuse the subsequent hydration algorithm
|
||||
current_hydration_fragment.shift();
|
||||
}
|
||||
}
|
||||
has_mounted = true;
|
||||
}
|
||||
},
|
||||
block,
|
||||
false
|
||||
);
|
||||
// Managed effect
|
||||
const consequent_effect = render_effect(
|
||||
(
|
||||
/** @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 (result && current_branch_effect !== consequent_effect) {
|
||||
consequent_fn(anchor_node);
|
||||
if (mismatch && current_branch_effect === null) {
|
||||
// Set fragment so that Svelte continues to operate in hydration mode
|
||||
set_current_hydration_fragment([]);
|
||||
}
|
||||
current_branch_effect = consequent_effect;
|
||||
consequent_dom = block.d;
|
||||
}
|
||||
block.d = null;
|
||||
},
|
||||
block,
|
||||
true
|
||||
);
|
||||
block.ce = consequent_effect;
|
||||
// Managed effect
|
||||
const alternate_effect = render_effect(
|
||||
(
|
||||
/** @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 (!result && current_branch_effect !== alternate_effect) {
|
||||
if (alternate_fn !== null) {
|
||||
alternate_fn(anchor_node);
|
||||
}
|
||||
if (mismatch && current_branch_effect === null) {
|
||||
// Set fragment so that Svelte continues to operate in hydration mode
|
||||
set_current_hydration_fragment([]);
|
||||
}
|
||||
current_branch_effect = alternate_effect;
|
||||
alternate_dom = block.d;
|
||||
}
|
||||
block.d = null;
|
||||
},
|
||||
block,
|
||||
true
|
||||
);
|
||||
block.ae = alternate_effect;
|
||||
push_destroy_fn(if_effect, () => {
|
||||
if (consequent_dom !== null) {
|
||||
remove(consequent_dom);
|
||||
}
|
||||
if (alternate_dom !== null) {
|
||||
remove(alternate_dom);
|
||||
}
|
||||
destroy_signal(consequent_effect);
|
||||
destroy_signal(alternate_effect);
|
||||
});
|
||||
block.e = if_effect;
|
||||
}
|
||||
@ -0,0 +1,140 @@
|
||||
import { UNINITIALIZED, KEY_BLOCK } from '../../constants.js';
|
||||
import { hydrate_block_anchor } from '../../hydration.js';
|
||||
import { remove } from '../../reconciler.js';
|
||||
import { current_block, destroy_signal, execute_effect, push_destroy_fn } from '../../runtime.js';
|
||||
import { render_effect } from '../../reactivity/computations.js';
|
||||
import { trigger_transitions } from '../../transitions.js';
|
||||
import { safe_not_equal } from '../../reactivity/equality.js';
|
||||
|
||||
/** @returns {import('../../types.js').KeyBlock} */
|
||||
function create_key_block() {
|
||||
return {
|
||||
// dom
|
||||
d: null,
|
||||
// effect
|
||||
e: null,
|
||||
// parent
|
||||
p: /** @type {import('../../types.js').Block} */ (current_block),
|
||||
// transition
|
||||
r: null,
|
||||
// type
|
||||
t: KEY_BLOCK
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {Comment} anchor_node
|
||||
* @param {() => V} key
|
||||
* @param {(anchor: Node) => void} render_fn
|
||||
* @returns {void}
|
||||
*/
|
||||
export function key_block(anchor_node, key, render_fn) {
|
||||
const block = create_key_block();
|
||||
|
||||
/** @type {null | import('../../types.js').Render} */
|
||||
let current_render = null;
|
||||
hydrate_block_anchor(anchor_node);
|
||||
|
||||
/** @type {V | typeof UNINITIALIZED} */
|
||||
let key_value = UNINITIALIZED;
|
||||
let mounted = false;
|
||||
block.r =
|
||||
/**
|
||||
* @param {import('../../types.js').Transition} transition
|
||||
* @returns {void}
|
||||
*/
|
||||
(transition) => {
|
||||
const render = /** @type {import('../../types.js').Render} */ (current_render);
|
||||
const transitions = render.s;
|
||||
transitions.add(transition);
|
||||
transition.f(() => {
|
||||
transitions.delete(transition);
|
||||
if (transitions.size === 0) {
|
||||
// If the current render has changed since, then we can remove the old render
|
||||
// effect as it's stale.
|
||||
if (current_render !== render && render.e !== null) {
|
||||
if (render.d !== null) {
|
||||
remove(render.d);
|
||||
render.d = null;
|
||||
}
|
||||
destroy_signal(render.e);
|
||||
render.e = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
const create_render_effect = () => {
|
||||
/** @type {import('../../types.js').Render} */
|
||||
const render = {
|
||||
d: null,
|
||||
e: null,
|
||||
s: new Set(),
|
||||
p: current_render
|
||||
};
|
||||
const effect = render_effect(
|
||||
() => {
|
||||
render_fn(anchor_node);
|
||||
render.d = block.d;
|
||||
block.d = null;
|
||||
},
|
||||
block,
|
||||
true,
|
||||
true
|
||||
);
|
||||
render.e = effect;
|
||||
current_render = render;
|
||||
};
|
||||
const render = () => {
|
||||
const render = current_render;
|
||||
if (render === null) {
|
||||
create_render_effect();
|
||||
return;
|
||||
}
|
||||
const transitions = render.s;
|
||||
if (transitions.size === 0) {
|
||||
if (render.d !== null) {
|
||||
remove(render.d);
|
||||
render.d = null;
|
||||
}
|
||||
if (render.e) {
|
||||
execute_effect(render.e);
|
||||
} else {
|
||||
create_render_effect();
|
||||
}
|
||||
} else {
|
||||
trigger_transitions(transitions, 'out');
|
||||
create_render_effect();
|
||||
}
|
||||
};
|
||||
const key_effect = render_effect(
|
||||
() => {
|
||||
const prev_key_value = key_value;
|
||||
key_value = key();
|
||||
if (mounted && safe_not_equal(prev_key_value, key_value)) {
|
||||
render();
|
||||
}
|
||||
},
|
||||
block,
|
||||
false
|
||||
);
|
||||
// To ensure topological ordering of the key effect to the render effect,
|
||||
// we trigger the effect after.
|
||||
render();
|
||||
mounted = true;
|
||||
push_destroy_fn(key_effect, () => {
|
||||
let render = current_render;
|
||||
while (render !== null) {
|
||||
const dom = render.d;
|
||||
if (dom !== null) {
|
||||
remove(dom);
|
||||
}
|
||||
const effect = render.e;
|
||||
if (effect !== null) {
|
||||
destroy_signal(effect);
|
||||
}
|
||||
render = render.p;
|
||||
}
|
||||
});
|
||||
block.e = key_effect;
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
import { run_all } from '../../common.js';
|
||||
|
||||
let is_task_queued = false;
|
||||
let is_raf_queued = false;
|
||||
|
||||
/** @type {Array<() => void>} */
|
||||
let current_queued_tasks = [];
|
||||
/** @type {Array<() => void>} */
|
||||
let current_raf_tasks = [];
|
||||
|
||||
function process_task() {
|
||||
is_task_queued = false;
|
||||
const tasks = current_queued_tasks.slice();
|
||||
current_queued_tasks = [];
|
||||
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}
|
||||
*/
|
||||
export function schedule_task(fn) {
|
||||
if (!is_task_queued) {
|
||||
is_task_queued = true;
|
||||
setTimeout(process_task, 0);
|
||||
}
|
||||
current_queued_tasks.push(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {() => void} fn
|
||||
* @returns {void}
|
||||
*/
|
||||
export function schedule_raf_task(fn) {
|
||||
if (!is_raf_queued) {
|
||||
is_raf_queued = true;
|
||||
requestAnimationFrame(process_raf_task);
|
||||
}
|
||||
current_raf_tasks.push(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously run any queued tasks.
|
||||
*/
|
||||
export function flush_tasks() {
|
||||
if (is_task_queued) {
|
||||
process_task();
|
||||
}
|
||||
if (is_raf_queued) {
|
||||
process_raf_task();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,258 @@
|
||||
import { DEV } from 'esm-env';
|
||||
import {
|
||||
current_block,
|
||||
current_component_context,
|
||||
current_consumer,
|
||||
current_effect,
|
||||
destroy_signal,
|
||||
flush_local_render_effects,
|
||||
schedule_effect
|
||||
} from '../runtime.js';
|
||||
import { default_equals, safe_equal } from './equality.js';
|
||||
import {
|
||||
DIRTY,
|
||||
MANAGED,
|
||||
RENDER_EFFECT,
|
||||
EFFECT,
|
||||
PRE_EFFECT,
|
||||
DERIVED,
|
||||
UNOWNED,
|
||||
CLEAN,
|
||||
UNINITIALIZED
|
||||
} from '../constants.js';
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {import('../types.js').SignalFlags} flags
|
||||
* @param {V} value
|
||||
* @param {import('../types.js').Block | null} block
|
||||
*/
|
||||
function create_computation_signal(flags, value, block) {
|
||||
/** @type {import('../types.js').ComputationSignal<V>} */
|
||||
const signal = {
|
||||
b: block,
|
||||
c: null,
|
||||
d: null,
|
||||
e: null,
|
||||
f: flags,
|
||||
l: 0,
|
||||
i: null,
|
||||
r: null,
|
||||
v: value,
|
||||
w: 0,
|
||||
x: null,
|
||||
y: null
|
||||
};
|
||||
|
||||
if (DEV) {
|
||||
// @ts-expect-error
|
||||
signal.inspect = new Set();
|
||||
}
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('../types.js').ComputationSignal} target_signal
|
||||
* @param {import('../types.js').ComputationSignal} ref_signal
|
||||
* @returns {void}
|
||||
*/
|
||||
export function push_reference(target_signal, ref_signal) {
|
||||
const references = target_signal.r;
|
||||
if (references === null) {
|
||||
target_signal.r = [ref_signal];
|
||||
} else {
|
||||
references.push(ref_signal);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('../types.js').EffectType} type
|
||||
* @param {(() => void | (() => void)) | ((b: import('../types.js').Block) => void | (() => void))} fn
|
||||
* @param {boolean} sync
|
||||
* @param {null | import('../types.js').Block} block
|
||||
* @param {boolean} schedule
|
||||
* @returns {import('../types.js').EffectSignal}
|
||||
*/
|
||||
function internal_create_effect(type, fn, sync, block, schedule) {
|
||||
const signal = create_computation_signal(type | DIRTY, null, block);
|
||||
signal.i = fn;
|
||||
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);
|
||||
}
|
||||
return signal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function effect_active() {
|
||||
return current_effect ? (current_effect.f & MANAGED) === 0 : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal representation of `$effect(...)`
|
||||
* @param {() => void | (() => void)} fn
|
||||
* @returns {import('../types.js').EffectSignal}
|
||||
*/
|
||||
export function user_effect(fn) {
|
||||
if (current_effect === null) {
|
||||
throw new Error(
|
||||
'ERR_SVELTE_ORPHAN_EFFECT' +
|
||||
(DEV ? ': The Svelte $effect rune can only be used during component initialisation.' : '')
|
||||
);
|
||||
}
|
||||
|
||||
const apply_component_effect_heuristics =
|
||||
current_effect.f & RENDER_EFFECT &&
|
||||
current_component_context !== null &&
|
||||
!current_component_context.m;
|
||||
|
||||
const effect = internal_create_effect(
|
||||
EFFECT,
|
||||
fn,
|
||||
false,
|
||||
current_block,
|
||||
!apply_component_effect_heuristics
|
||||
);
|
||||
|
||||
if (apply_component_effect_heuristics) {
|
||||
const context = /** @type {import('../types.js').ComponentContext} */ (
|
||||
current_component_context
|
||||
);
|
||||
(context.e ??= []).push(effect);
|
||||
}
|
||||
|
||||
return effect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal representation of `$effect.root(...)`
|
||||
* @param {() => void | (() => void)} fn
|
||||
* @returns {() => void}
|
||||
*/
|
||||
export function user_root_effect(fn) {
|
||||
const effect = render_effect(fn, current_block, true);
|
||||
return () => {
|
||||
destroy_signal(effect);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {() => void | (() => void)} fn
|
||||
* @returns {import('../types.js').EffectSignal}
|
||||
*/
|
||||
export function effect(fn) {
|
||||
return internal_create_effect(EFFECT, fn, false, current_block, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {() => void | (() => void)} fn
|
||||
* @returns {import('../types.js').EffectSignal}
|
||||
*/
|
||||
export function managed_effect(fn) {
|
||||
return internal_create_effect(EFFECT | MANAGED, fn, false, current_block, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {() => void | (() => void)} fn
|
||||
* @param {boolean} sync
|
||||
* @returns {import('../types.js').EffectSignal}
|
||||
*/
|
||||
export function managed_pre_effect(fn, sync) {
|
||||
return internal_create_effect(PRE_EFFECT | MANAGED, fn, sync, current_block, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal representation of `$effect.pre(...)`
|
||||
* @param {() => void | (() => void)} fn
|
||||
* @returns {import('../types.js').EffectSignal}
|
||||
*/
|
||||
export function pre_effect(fn) {
|
||||
if (current_effect === null) {
|
||||
throw new Error(
|
||||
'ERR_SVELTE_ORPHAN_EFFECT' +
|
||||
(DEV
|
||||
? ': The Svelte $effect.pre rune can only be used during component initialisation.'
|
||||
: '')
|
||||
);
|
||||
}
|
||||
const sync = current_effect !== null && (current_effect.f & RENDER_EFFECT) !== 0;
|
||||
return internal_create_effect(
|
||||
PRE_EFFECT,
|
||||
() => {
|
||||
const val = fn();
|
||||
flush_local_render_effects();
|
||||
return val;
|
||||
},
|
||||
sync,
|
||||
current_block,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This effect is used to ensure binding are kept in sync. We use a pre effect to ensure we run before the
|
||||
* bindings which are in later effects. However, we don't use a pre_effect directly as we don't want to flush anything.
|
||||
*
|
||||
* @param {() => void | (() => void)} fn
|
||||
* @returns {import('../types.js').EffectSignal}
|
||||
*/
|
||||
export function invalidate_effect(fn) {
|
||||
return internal_create_effect(PRE_EFFECT, fn, true, current_block, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {import('../types.js').Block} B
|
||||
* @param {(block: B) => void | (() => void)} fn
|
||||
* @param {any} block
|
||||
* @param {any} managed
|
||||
* @param {any} sync
|
||||
* @returns {import('../types.js').EffectSignal}
|
||||
*/
|
||||
export function render_effect(fn, block = current_block, managed = false, sync = true) {
|
||||
let flags = RENDER_EFFECT;
|
||||
if (managed) {
|
||||
flags |= MANAGED;
|
||||
}
|
||||
return internal_create_effect(flags, /** @type {any} */ (fn), sync, block, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {() => V} fn
|
||||
* @returns {import('../types.js').ComputationSignal<V>}
|
||||
*/
|
||||
/*#__NO_SIDE_EFFECTS__*/
|
||||
export function derived(fn) {
|
||||
const is_unowned = current_effect === null;
|
||||
const flags = is_unowned ? DERIVED | UNOWNED : DERIVED;
|
||||
const signal = /** @type {import('../types.js').ComputationSignal<V>} */ (
|
||||
create_computation_signal(flags | CLEAN, UNINITIALIZED, current_block)
|
||||
);
|
||||
signal.i = fn;
|
||||
signal.e = default_equals;
|
||||
if (current_consumer !== null) {
|
||||
push_reference(current_consumer, signal);
|
||||
}
|
||||
return signal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {() => V} fn
|
||||
* @returns {import('../types.js').ComputationSignal<V>}
|
||||
*/
|
||||
/*#__NO_SIDE_EFFECTS__*/
|
||||
export function derived_safe_equal(fn) {
|
||||
const signal = derived(fn);
|
||||
signal.e = safe_equal;
|
||||
return signal;
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* @param {unknown} a
|
||||
* @param {unknown} b
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function default_equals(a, b) {
|
||||
return a === b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {unknown} a
|
||||
* @param {unknown} b
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function safe_not_equal(a, b) {
|
||||
// eslint-disable-next-line eqeqeq
|
||||
return a != a
|
||||
? // eslint-disable-next-line eqeqeq
|
||||
b == b
|
||||
: a !== b || (a !== null && typeof a === 'object') || typeof a === 'function';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {unknown} a
|
||||
* @param {unknown} b
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function safe_equal(a, b) {
|
||||
return !safe_not_equal(a, b);
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
import { DEV } from 'esm-env';
|
||||
import { current_component_context } from '../runtime.js';
|
||||
import { default_equals, safe_equal } from './equality.js';
|
||||
import { CLEAN, SOURCE } from '../constants.js';
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {V} initial_value
|
||||
* @returns {import('../types.js').SourceSignal<V>}
|
||||
*/
|
||||
/*#__NO_SIDE_EFFECTS__*/
|
||||
export function source(initial_value) {
|
||||
return create_source_signal(SOURCE | CLEAN, initial_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {V} initial_value
|
||||
* @returns {import('../types.js').SourceSignal<V>}
|
||||
*/
|
||||
/*#__NO_SIDE_EFFECTS__*/
|
||||
export function mutable_source(initial_value) {
|
||||
const s = source(initial_value);
|
||||
s.e = safe_equal;
|
||||
|
||||
// bind the signal to the component context, in case we need to
|
||||
// track updates to trigger beforeUpdate/afterUpdate callbacks
|
||||
if (current_component_context) {
|
||||
(current_component_context.d ??= []).push(s);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {import('../types.js').SignalFlags} flags
|
||||
* @param {V} value
|
||||
* @returns {import('../types.js').SourceSignal<V> | import('../types.js').SourceSignal<V> & import('../types.js').SourceSignalDebug}
|
||||
*/
|
||||
function create_source_signal(flags, value) {
|
||||
if (DEV) {
|
||||
return {
|
||||
// consumers
|
||||
c: null,
|
||||
// equals
|
||||
e: default_equals,
|
||||
// flags
|
||||
f: flags,
|
||||
// value
|
||||
v: value,
|
||||
// write version
|
||||
w: 0,
|
||||
// this is for DEV only
|
||||
inspect: new Set()
|
||||
};
|
||||
}
|
||||
return {
|
||||
// consumers
|
||||
c: null,
|
||||
// equals
|
||||
e: default_equals,
|
||||
// flags
|
||||
f: flags,
|
||||
// value
|
||||
v: value,
|
||||
// write version
|
||||
w: 0
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,153 @@
|
||||
import { subscribe_to_store } from '../../../store/utils.js';
|
||||
import { noop } from '../../common.js';
|
||||
import { UNINITIALIZED } from '../constants.js';
|
||||
import { get, set, set_ignore_mutation_validation, untrack } from '../runtime.js';
|
||||
import { user_effect } from './computations.js';
|
||||
import { mutable_source } from './sources.js';
|
||||
|
||||
/**
|
||||
* Gets the current value of a store. If the store isn't subscribed to yet, it will create a proxy
|
||||
* signal that will be updated when the store is. The store references container is needed to
|
||||
* track reassignments to stores and to track the correct component context.
|
||||
* @template V
|
||||
* @param {import('../types.js').Store<V> | null | undefined} store
|
||||
* @param {string} store_name
|
||||
* @param {import('../types.js').StoreReferencesContainer} stores
|
||||
* @returns {V}
|
||||
*/
|
||||
export function store_get(store, store_name, stores) {
|
||||
/** @type {import('../types.js').StoreReferencesContainer[''] | undefined} */
|
||||
let entry = stores[store_name];
|
||||
const is_new = entry === undefined;
|
||||
|
||||
if (is_new) {
|
||||
entry = {
|
||||
store: null,
|
||||
last_value: null,
|
||||
value: mutable_source(UNINITIALIZED),
|
||||
unsubscribe: noop
|
||||
};
|
||||
// TODO: can we remove this code? it was refactored out when we split up source/comptued signals
|
||||
// push_destroy_fn(entry.value, () => {
|
||||
// /** @type {import('../types.js').StoreReferencesContainer['']} */ (entry).last_value =
|
||||
// /** @type {import('../types.js').StoreReferencesContainer['']} */ (entry).value.value;
|
||||
// });
|
||||
stores[store_name] = entry;
|
||||
}
|
||||
|
||||
if (is_new || entry.store !== store) {
|
||||
entry.unsubscribe();
|
||||
entry.store = store ?? null;
|
||||
entry.unsubscribe = connect_store_to_signal(store, entry.value);
|
||||
}
|
||||
|
||||
const value = get(entry.value);
|
||||
// This could happen if the store was cleaned up because the component was destroyed and there's a leak on the user side.
|
||||
// In that case we don't want to fail with a cryptic Symbol error, but rather return the last value we got.
|
||||
return value === UNINITIALIZED ? entry.last_value : value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {import('../types.js').Store<V> | null | undefined} store
|
||||
* @param {import('../types.js').SourceSignal<V>} source
|
||||
*/
|
||||
function connect_store_to_signal(store, source) {
|
||||
if (store == null) {
|
||||
set(source, undefined);
|
||||
return noop;
|
||||
}
|
||||
|
||||
/** @param {V} v */
|
||||
const run = (v) => {
|
||||
set_ignore_mutation_validation(true);
|
||||
set(source, v);
|
||||
set_ignore_mutation_validation(false);
|
||||
};
|
||||
return subscribe_to_store(store, run);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new value of a store and returns that value.
|
||||
* @template V
|
||||
* @param {import('../types.js').Store<V>} store
|
||||
* @param {V} value
|
||||
* @returns {V}
|
||||
*/
|
||||
export function store_set(store, value) {
|
||||
store.set(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from all auto-subscribed stores on destroy
|
||||
* @param {import('../types.js').StoreReferencesContainer} stores
|
||||
*/
|
||||
export function unsubscribe_on_destroy(stores) {
|
||||
on_destroy(() => {
|
||||
let store_name;
|
||||
for (store_name in stores) {
|
||||
const ref = stores[store_name];
|
||||
ref.unsubscribe();
|
||||
// TODO: can we remove this code? it was refactored out when we split up source/comptued signals
|
||||
// destroy_signal(ref.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a store with a new value.
|
||||
* @param {import('../types.js').Store<V>} store the store to update
|
||||
* @param {any} expression the expression that mutates the store
|
||||
* @param {V} new_value the new store value
|
||||
* @template V
|
||||
*/
|
||||
export function mutate_store(store, expression, new_value) {
|
||||
store.set(new_value);
|
||||
return expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template V
|
||||
* @param {unknown} val
|
||||
* @returns {val is import('../types.js').Store<V>}
|
||||
*/
|
||||
export function is_store(val) {
|
||||
return (
|
||||
typeof val === 'object' &&
|
||||
val !== null &&
|
||||
typeof (/** @type {import('../types.js').Store<V>} */ (val).subscribe) === 'function'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('../types.js').Store<number>} store
|
||||
* @param {number} store_value
|
||||
* @param {1 | -1} [d]
|
||||
* @returns {number}
|
||||
*/
|
||||
export function update_store(store, store_value, d = 1) {
|
||||
store.set(store_value + d);
|
||||
return store_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('../types.js').Store<number>} store
|
||||
* @param {number} store_value
|
||||
* @param {1 | -1} [d]
|
||||
* @returns {number}
|
||||
*/
|
||||
export function update_pre_store(store, store_value, d = 1) {
|
||||
const value = store_value + d;
|
||||
store.set(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a callback to run immediately before the component is unmounted.
|
||||
* @param {() => any} fn
|
||||
* @returns {void}
|
||||
*/
|
||||
function on_destroy(fn) {
|
||||
user_effect(() => () => untrack(fn));
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,4 +0,0 @@
|
||||
<script>
|
||||
let count = $state(0);
|
||||
export const double = $derived(count * 2);
|
||||
</script>
|
||||
@ -1,10 +0,0 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
error: {
|
||||
code: 'invalid-state-export',
|
||||
message:
|
||||
"Cannot export state if it is reassigned. Either export a function returning the state value or only mutate the state value's properties",
|
||||
position: [59, 99]
|
||||
}
|
||||
});
|
||||
@ -1,15 +0,0 @@
|
||||
<script>
|
||||
export const object = $state({
|
||||
ok: true
|
||||
});
|
||||
|
||||
export const primitive = $state('nope');
|
||||
|
||||
export function update_object() {
|
||||
object.ok = !object.ok;
|
||||
}
|
||||
|
||||
export function update_primitive() {
|
||||
primitive = 'yep';
|
||||
}
|
||||
</script>
|
||||
@ -1,9 +0,0 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
error: {
|
||||
code: 'invalid-prop-export',
|
||||
message:
|
||||
'Cannot export properties. To expose the current value of a property, export a function returning its value'
|
||||
}
|
||||
});
|
||||
@ -1,4 +0,0 @@
|
||||
<script>
|
||||
let { foo } = $props();
|
||||
export { foo };
|
||||
</script>
|
||||
@ -0,0 +1,4 @@
|
||||
|
||||
/* (unused) x y z {
|
||||
color: red;
|
||||
}*/
|
||||
@ -0,0 +1,9 @@
|
||||
<x>
|
||||
<z></z>
|
||||
</x>
|
||||
|
||||
<style>
|
||||
x y z {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
@ -1,12 +1,5 @@
|
||||
import { test } from '../../test';
|
||||
|
||||
export default test({
|
||||
warnings: [
|
||||
{
|
||||
code: 'css-unused-selector',
|
||||
message: 'Unused CSS selector "a:global(.foo) > div"',
|
||||
start: { character: 91, column: 1, line: 8 },
|
||||
end: { character: 111, column: 21, line: 8 }
|
||||
}
|
||||
]
|
||||
warnings: []
|
||||
});
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
div > div.svelte-xyz {
|
||||
a > b > div.svelte-xyz {
|
||||
color: red;
|
||||
}
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
<div class="svelte-xyz">
|
||||
<div class="svelte-xyz"></div>
|
||||
<div></div>
|
||||
</div>
|
||||
@ -1,3 +0,0 @@
|
||||
a > b > div.svelte-xyz {
|
||||
color: red;
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
<div class="svelte-xyz">
|
||||
<div class="svelte-xyz"></div>
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue