mirror of https://github.com/sveltejs/svelte
chore: move some code around (#10767)
* move <svelte:component> code * move <svelte:element> code * unused import * move <svelte:head> code * unused import * unused import * typo * move snippet code, remove blocks.js * move CSS props code * move html code * remove unused import --------- Co-authored-by: Rich Harris <rich.harris@vercel.com>pull/10777/head
parent
278155968e
commit
8a4dfb483b
@ -1,93 +0,0 @@
|
|||||||
import {
|
|
||||||
ROOT_BLOCK,
|
|
||||||
HEAD_BLOCK,
|
|
||||||
DYNAMIC_ELEMENT_BLOCK,
|
|
||||||
DYNAMIC_COMPONENT_BLOCK,
|
|
||||||
SNIPPET_BLOCK
|
|
||||||
} from './constants.js';
|
|
||||||
import { current_block } from './runtime.js';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {boolean} intro
|
|
||||||
* @returns {import('./types.js').RootBlock}
|
|
||||||
*/
|
|
||||||
export function create_root_block(intro) {
|
|
||||||
return {
|
|
||||||
// dom
|
|
||||||
d: null,
|
|
||||||
// effect
|
|
||||||
e: null,
|
|
||||||
// intro
|
|
||||||
i: intro,
|
|
||||||
// parent
|
|
||||||
p: null,
|
|
||||||
// transition
|
|
||||||
r: null,
|
|
||||||
// type
|
|
||||||
t: ROOT_BLOCK
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @returns {import('./types.js').HeadBlock} */
|
|
||||||
export function create_head_block() {
|
|
||||||
return {
|
|
||||||
// dom
|
|
||||||
d: null,
|
|
||||||
// effect
|
|
||||||
e: null,
|
|
||||||
// parent
|
|
||||||
p: /** @type {import('./types.js').Block} */ (current_block),
|
|
||||||
// transition
|
|
||||||
r: null,
|
|
||||||
// type
|
|
||||||
t: HEAD_BLOCK
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @returns {import('./types.js').DynamicElementBlock} */
|
|
||||||
export function create_dynamic_element_block() {
|
|
||||||
return {
|
|
||||||
// dom
|
|
||||||
d: null,
|
|
||||||
// effect
|
|
||||||
e: null,
|
|
||||||
// parent
|
|
||||||
p: /** @type {import('./types.js').Block} */ (current_block),
|
|
||||||
// transition
|
|
||||||
r: null,
|
|
||||||
// type
|
|
||||||
t: DYNAMIC_ELEMENT_BLOCK
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @returns {import('./types.js').DynamicComponentBlock} */
|
|
||||||
export function create_dynamic_component_block() {
|
|
||||||
return {
|
|
||||||
// dom
|
|
||||||
d: null,
|
|
||||||
// effect
|
|
||||||
e: null,
|
|
||||||
// parent
|
|
||||||
p: /** @type {import('./types.js').Block} */ (current_block),
|
|
||||||
// transition
|
|
||||||
r: null,
|
|
||||||
// type
|
|
||||||
t: DYNAMIC_COMPONENT_BLOCK
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @returns {import('./types.js').SnippetBlock} */
|
|
||||||
export function create_snippet_block() {
|
|
||||||
return {
|
|
||||||
// dom
|
|
||||||
d: null,
|
|
||||||
// parent
|
|
||||||
p: /** @type {import('./types.js').Block} */ (current_block),
|
|
||||||
// effect
|
|
||||||
e: null,
|
|
||||||
// transition
|
|
||||||
r: null,
|
|
||||||
// type
|
|
||||||
t: SNIPPET_BLOCK
|
|
||||||
};
|
|
||||||
}
|
|
@ -0,0 +1,65 @@
|
|||||||
|
import { namespace_svg } from '../../../../constants.js';
|
||||||
|
import { current_hydration_fragment, hydrate_block_anchor, hydrating } from '../../hydration.js';
|
||||||
|
import { empty } from '../../operations.js';
|
||||||
|
import { render_effect } from '../../reactivity/effects.js';
|
||||||
|
import { insert, remove } from '../../reconciler.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Element | Text | Comment} anchor
|
||||||
|
* @param {boolean} is_html
|
||||||
|
* @param {() => Record<string, string>} props
|
||||||
|
* @param {(anchor: Element | Text | Comment) => any} component
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function css_props(anchor, is_html, props, component) {
|
||||||
|
hydrate_block_anchor(anchor);
|
||||||
|
|
||||||
|
/** @type {HTMLElement | SVGElement} */
|
||||||
|
let tag;
|
||||||
|
|
||||||
|
/** @type {Text | Comment} */
|
||||||
|
let component_anchor;
|
||||||
|
|
||||||
|
if (hydrating) {
|
||||||
|
// Hydration: css props element is surrounded by a ssr comment ...
|
||||||
|
tag = /** @type {HTMLElement | SVGElement} */ (current_hydration_fragment[0]);
|
||||||
|
// ... and the child(ren) of the css props element is also surround by a ssr comment
|
||||||
|
component_anchor = /** @type {Comment} */ (tag.firstChild);
|
||||||
|
} else {
|
||||||
|
if (is_html) {
|
||||||
|
tag = document.createElement('div');
|
||||||
|
tag.style.display = 'contents';
|
||||||
|
} else {
|
||||||
|
tag = document.createElementNS(namespace_svg, 'g');
|
||||||
|
}
|
||||||
|
|
||||||
|
insert(tag, null, anchor);
|
||||||
|
component_anchor = empty();
|
||||||
|
tag.appendChild(component_anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
component(component_anchor);
|
||||||
|
|
||||||
|
/** @type {Record<string, string>} */
|
||||||
|
let current_props = {};
|
||||||
|
|
||||||
|
const effect = render_effect(() => {
|
||||||
|
const next_props = props();
|
||||||
|
|
||||||
|
for (const key in current_props) {
|
||||||
|
if (!(key in next_props)) {
|
||||||
|
tag.style.removeProperty(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const key in next_props) {
|
||||||
|
tag.style.setProperty(key, next_props[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
current_props = next_props;
|
||||||
|
});
|
||||||
|
|
||||||
|
effect.ondestroy = () => {
|
||||||
|
remove(tag);
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
import { render_effect } from '../../reactivity/effects.js';
|
||||||
|
import { reconcile_html, remove } from '../../reconciler.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Element | Text | Comment} dom
|
||||||
|
* @param {() => string} get_value
|
||||||
|
* @param {boolean} svg
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function html(dom, get_value, svg) {
|
||||||
|
/** @type {import('#client').TemplateNode | import('#client').TemplateNode[]} */
|
||||||
|
let html_dom;
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
let value;
|
||||||
|
|
||||||
|
const effect = render_effect(() => {
|
||||||
|
if (value !== (value = get_value())) {
|
||||||
|
if (html_dom) remove(html_dom);
|
||||||
|
html_dom = reconcile_html(dom, value, svg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
effect.ondestroy = () => {
|
||||||
|
if (html_dom) {
|
||||||
|
remove(html_dom);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
import { SNIPPET_BLOCK } from '../../constants.js';
|
||||||
|
import { render_effect } from '../../reactivity/effects.js';
|
||||||
|
import { remove } from '../../reconciler.js';
|
||||||
|
import { current_block, untrack } from '../../runtime.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {() => Function | null | undefined} get_snippet
|
||||||
|
* @param {Node} node
|
||||||
|
* @param {(() => any)[]} args
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function snippet(get_snippet, node, ...args) {
|
||||||
|
/** @type {import('#client').SnippetBlock} */
|
||||||
|
const block = {
|
||||||
|
// dom
|
||||||
|
d: null,
|
||||||
|
// parent
|
||||||
|
p: /** @type {import('#client').Block} */ (current_block),
|
||||||
|
// effect
|
||||||
|
e: null,
|
||||||
|
// transition
|
||||||
|
r: null,
|
||||||
|
// type
|
||||||
|
t: SNIPPET_BLOCK
|
||||||
|
};
|
||||||
|
|
||||||
|
render_effect(() => {
|
||||||
|
// Only rerender when the snippet function itself changes,
|
||||||
|
// not when an eagerly-read prop inside the snippet function changes
|
||||||
|
const snippet = get_snippet();
|
||||||
|
if (snippet) {
|
||||||
|
untrack(() => snippet(node, ...args));
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
if (block.d !== null) {
|
||||||
|
remove(block.d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, block);
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
import { DYNAMIC_COMPONENT_BLOCK } from '../../constants.js';
|
||||||
|
import { hydrate_block_anchor } from '../../hydration.js';
|
||||||
|
import { destroy_effect, render_effect } from '../../reactivity/effects.js';
|
||||||
|
import { remove } from '../../reconciler.js';
|
||||||
|
import { current_block, execute_effect } from '../../runtime.js';
|
||||||
|
import { trigger_transitions } from '../../transitions.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template P
|
||||||
|
* @param {Comment} anchor_node
|
||||||
|
* @param {() => (props: P) => void} component_fn
|
||||||
|
* @param {(component: (props: P) => void) => void} render_fn
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function component(anchor_node, component_fn, render_fn) {
|
||||||
|
/** @type {import('#client').DynamicComponentBlock} */
|
||||||
|
const block = {
|
||||||
|
// dom
|
||||||
|
d: null,
|
||||||
|
// effect
|
||||||
|
e: null,
|
||||||
|
// parent
|
||||||
|
p: /** @type {import('#client').Block} */ (current_block),
|
||||||
|
// transition
|
||||||
|
r: null,
|
||||||
|
// type
|
||||||
|
t: DYNAMIC_COMPONENT_BLOCK
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @type {null | import('#client').Render} */
|
||||||
|
let current_render = null;
|
||||||
|
hydrate_block_anchor(anchor_node);
|
||||||
|
|
||||||
|
/** @type {null | ((props: P) => void)} */
|
||||||
|
let component = null;
|
||||||
|
|
||||||
|
block.r =
|
||||||
|
/**
|
||||||
|
* @param {import('#client').Transition} transition
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
(transition) => {
|
||||||
|
const render = /** @type {import('#client').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_effect(render.e);
|
||||||
|
render.e = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const create_render_effect = () => {
|
||||||
|
/** @type {import('#client').Render} */
|
||||||
|
const render = {
|
||||||
|
d: null,
|
||||||
|
e: null,
|
||||||
|
s: new Set(),
|
||||||
|
p: current_render
|
||||||
|
};
|
||||||
|
|
||||||
|
// Managed effect
|
||||||
|
render.e = render_effect(
|
||||||
|
() => {
|
||||||
|
const current = block.d;
|
||||||
|
if (current !== null) {
|
||||||
|
remove(current);
|
||||||
|
block.d = null;
|
||||||
|
}
|
||||||
|
if (component) {
|
||||||
|
render_fn(component);
|
||||||
|
}
|
||||||
|
render.d = block.d;
|
||||||
|
block.d = null;
|
||||||
|
},
|
||||||
|
block,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
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 component_effect = render_effect(
|
||||||
|
() => {
|
||||||
|
const next_component = component_fn();
|
||||||
|
if (component !== next_component) {
|
||||||
|
component = next_component;
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
block,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
component_effect.ondestroy = () => {
|
||||||
|
let render = current_render;
|
||||||
|
while (render !== null) {
|
||||||
|
const dom = render.d;
|
||||||
|
if (dom !== null) {
|
||||||
|
remove(dom);
|
||||||
|
}
|
||||||
|
const effect = render.e;
|
||||||
|
if (effect !== null) {
|
||||||
|
destroy_effect(effect);
|
||||||
|
}
|
||||||
|
render = render.p;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
block.e = component_effect;
|
||||||
|
}
|
@ -0,0 +1,139 @@
|
|||||||
|
import { namespace_svg } from '../../../../constants.js';
|
||||||
|
import { DYNAMIC_ELEMENT_BLOCK } from '../../constants.js';
|
||||||
|
import { current_hydration_fragment, hydrate_block_anchor, hydrating } from '../../hydration.js';
|
||||||
|
import { empty } from '../../operations.js';
|
||||||
|
import { destroy_effect, render_effect } from '../../reactivity/effects.js';
|
||||||
|
import { insert, remove } from '../../reconciler.js';
|
||||||
|
import { current_block, execute_effect } from '../../runtime.js';
|
||||||
|
import { is_array } from '../../utils.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('#client').Block} block
|
||||||
|
* @param {Element} from
|
||||||
|
* @param {Element} to
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function swap_block_dom(block, from, to) {
|
||||||
|
const dom = block.d;
|
||||||
|
if (is_array(dom)) {
|
||||||
|
for (let i = 0; i < dom.length; i++) {
|
||||||
|
if (dom[i] === from) {
|
||||||
|
dom[i] = to;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (dom === from) {
|
||||||
|
block.d = to;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Comment} anchor_node
|
||||||
|
* @param {() => string} tag_fn
|
||||||
|
* @param {boolean | null} is_svg `null` == not statically known
|
||||||
|
* @param {undefined | ((element: Element, anchor: Node) => void)} render_fn
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function element(anchor_node, tag_fn, is_svg, render_fn) {
|
||||||
|
/** @type {import('#client').DynamicElementBlock} */
|
||||||
|
const block = {
|
||||||
|
// dom
|
||||||
|
d: null,
|
||||||
|
// effect
|
||||||
|
e: null,
|
||||||
|
// parent
|
||||||
|
p: /** @type {import('#client').Block} */ (current_block),
|
||||||
|
// transition
|
||||||
|
r: null,
|
||||||
|
// type
|
||||||
|
t: DYNAMIC_ELEMENT_BLOCK
|
||||||
|
};
|
||||||
|
|
||||||
|
hydrate_block_anchor(anchor_node);
|
||||||
|
let has_mounted = false;
|
||||||
|
|
||||||
|
/** @type {string} */
|
||||||
|
let tag;
|
||||||
|
|
||||||
|
/** @type {null | Element} */
|
||||||
|
let element = null;
|
||||||
|
|
||||||
|
const element_effect = render_effect(
|
||||||
|
() => {
|
||||||
|
tag = tag_fn();
|
||||||
|
if (has_mounted) {
|
||||||
|
execute_effect(render_effect_signal);
|
||||||
|
}
|
||||||
|
has_mounted = true;
|
||||||
|
},
|
||||||
|
block,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
// Managed effect
|
||||||
|
const render_effect_signal = render_effect(
|
||||||
|
() => {
|
||||||
|
// We try our best infering the namespace in case it's not possible to determine statically,
|
||||||
|
// but on the first render on the client (without hydration) the parent will be undefined,
|
||||||
|
// since the anchor is not attached to its parent / the dom yet.
|
||||||
|
const ns =
|
||||||
|
is_svg || tag === 'svg'
|
||||||
|
? namespace_svg
|
||||||
|
: is_svg === false || anchor_node.parentElement?.tagName === 'foreignObject'
|
||||||
|
? null
|
||||||
|
: anchor_node.parentElement?.namespaceURI ?? null;
|
||||||
|
|
||||||
|
const next_element = tag
|
||||||
|
? hydrating
|
||||||
|
? /** @type {Element} */ (current_hydration_fragment[0])
|
||||||
|
: ns
|
||||||
|
? document.createElementNS(ns, tag)
|
||||||
|
: document.createElement(tag)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const prev_element = element;
|
||||||
|
if (prev_element !== null) {
|
||||||
|
block.d = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
element = next_element;
|
||||||
|
if (element !== null && render_fn !== undefined) {
|
||||||
|
let anchor;
|
||||||
|
if (hydrating) {
|
||||||
|
// Use the existing ssr comment as the anchor so that the inner open and close
|
||||||
|
// methods can pick up the existing nodes correctly
|
||||||
|
anchor = /** @type {Comment} */ (element.firstChild);
|
||||||
|
} else {
|
||||||
|
anchor = empty();
|
||||||
|
element.appendChild(anchor);
|
||||||
|
}
|
||||||
|
render_fn(element, anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
const has_prev_element = prev_element !== null;
|
||||||
|
if (has_prev_element) {
|
||||||
|
remove(prev_element);
|
||||||
|
}
|
||||||
|
if (element !== null) {
|
||||||
|
insert(element, null, anchor_node);
|
||||||
|
if (has_prev_element) {
|
||||||
|
const parent_block = block.p;
|
||||||
|
swap_block_dom(parent_block, prev_element, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
block,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
element_effect.ondestroy = () => {
|
||||||
|
if (element !== null) {
|
||||||
|
remove(element);
|
||||||
|
block.d = null;
|
||||||
|
element = null;
|
||||||
|
}
|
||||||
|
destroy_effect(render_effect_signal);
|
||||||
|
};
|
||||||
|
|
||||||
|
block.e = element_effect;
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
import { HEAD_BLOCK } from '../../constants.js';
|
||||||
|
import {
|
||||||
|
current_hydration_fragment,
|
||||||
|
get_hydration_fragment,
|
||||||
|
hydrating,
|
||||||
|
set_current_hydration_fragment
|
||||||
|
} from '../../hydration.js';
|
||||||
|
import { empty } from '../../operations.js';
|
||||||
|
import { render_effect } from '../../reactivity/effects.js';
|
||||||
|
import { remove } from '../../reconciler.js';
|
||||||
|
import { current_block } from '../../runtime.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {(anchor: Node | null) => void} render_fn
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function head(render_fn) {
|
||||||
|
/** @type {import('#client').HeadBlock} */
|
||||||
|
const block = {
|
||||||
|
// dom
|
||||||
|
d: null,
|
||||||
|
// effect
|
||||||
|
e: null,
|
||||||
|
// parent
|
||||||
|
p: /** @type {import('#client').Block} */ (current_block),
|
||||||
|
// transition
|
||||||
|
r: null,
|
||||||
|
// type
|
||||||
|
t: HEAD_BLOCK
|
||||||
|
};
|
||||||
|
|
||||||
|
// The head function may be called after the first hydration pass and ssr comment nodes may still be present,
|
||||||
|
// therefore we need to skip that when we detect that we're not in hydration mode.
|
||||||
|
let hydration_fragment = null;
|
||||||
|
let previous_hydration_fragment = null;
|
||||||
|
|
||||||
|
let is_hydrating = hydrating;
|
||||||
|
if (is_hydrating) {
|
||||||
|
hydration_fragment = get_hydration_fragment(document.head.firstChild);
|
||||||
|
previous_hydration_fragment = current_hydration_fragment;
|
||||||
|
set_current_hydration_fragment(hydration_fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const head_effect = render_effect(
|
||||||
|
() => {
|
||||||
|
const current = block.d;
|
||||||
|
if (current !== null) {
|
||||||
|
remove(current);
|
||||||
|
block.d = null;
|
||||||
|
}
|
||||||
|
let anchor = null;
|
||||||
|
if (!hydrating) {
|
||||||
|
anchor = empty();
|
||||||
|
document.head.appendChild(anchor);
|
||||||
|
}
|
||||||
|
render_fn(anchor);
|
||||||
|
},
|
||||||
|
block,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
head_effect.ondestroy = () => {
|
||||||
|
const current = block.d;
|
||||||
|
if (current !== null) {
|
||||||
|
remove(current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
block.e = head_effect;
|
||||||
|
} finally {
|
||||||
|
if (is_hydrating) {
|
||||||
|
set_current_hydration_fragment(previous_hydration_fragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue