pull/12335/head
Rich Harris 2 years ago
parent e23d99294f
commit ee9008b259

@ -35,7 +35,6 @@ import { sanitize_template_string } from '../../../utils/sanitize_template_strin
import { import {
BLOCK_ANCHOR, BLOCK_ANCHOR,
BLOCK_CLOSE, BLOCK_CLOSE,
BLOCK_CLOSE_ELSE,
BLOCK_OPEN, BLOCK_OPEN,
BLOCK_OPEN_ELSE BLOCK_OPEN_ELSE
} from '../../../../internal/server/hydration.js'; } from '../../../../internal/server/hydration.js';
@ -1343,7 +1342,6 @@ const template_visitors = {
}, },
EachBlock(node, context) { EachBlock(node, context) {
const state = context.state; const state = context.state;
state.template.push(block_open);
const each_node_meta = node.metadata; const each_node_meta = node.metadata;
const collection = /** @type {import('estree').Expression} */ (context.visit(node.expression)); const collection = /** @type {import('estree').Expression} */ (context.visit(node.expression));
@ -1379,26 +1377,27 @@ const template_visitors = {
b.block(each) b.block(each)
); );
const close = b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(BLOCK_CLOSE)));
if (node.fallback) { if (node.fallback) {
const open = b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(BLOCK_OPEN)));
const fallback = /** @type {import('estree').BlockStatement} */ ( const fallback = /** @type {import('estree').BlockStatement} */ (
context.visit(node.fallback) context.visit(node.fallback)
); );
fallback.body.push( fallback.body.unshift(
b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(BLOCK_CLOSE_ELSE))) b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(BLOCK_OPEN_ELSE)))
); );
state.template.push( state.template.push(
b.if( b.if(
b.binary('!==', b.member(array_id, b.id('length')), b.literal(0)), b.binary('!==', b.member(array_id, b.id('length')), b.literal(0)),
b.block([for_loop, close]), b.block([open, for_loop]),
fallback fallback
) ),
block_close
); );
} else { } else {
state.template.push(for_loop, close); state.template.push(block_open, for_loop, block_open);
} }
}, },
IfBlock(node, context) { IfBlock(node, context) {

@ -11,7 +11,7 @@ import {
import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js'; import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js';
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
import { queue_micro_task } from '../task.js'; import { queue_micro_task } from '../task.js';
import { hydrate_node, hydrating, set_hydrate_node } from '../hydration.js'; import { hydrate_next, hydrate_node, hydrating, set_hydrate_node } from '../hydration.js';
import { mutable_source, set, source } from '../../reactivity/sources.js'; import { mutable_source, set, source } from '../../reactivity/sources.js';
const PENDING = 0; const PENDING = 0;
@ -28,6 +28,10 @@ const CATCH = 2;
* @returns {void} * @returns {void}
*/ */
export function await_block(anchor, get_input, pending_fn, then_fn, catch_fn) { export function await_block(anchor, get_input, pending_fn, then_fn, catch_fn) {
if (hydrating) {
hydrate_next();
}
var runes = is_runes(); var runes = is_runes();
var component_context = current_component_context; var component_context = current_component_context;
@ -148,7 +152,6 @@ export function await_block(anchor, get_input, pending_fn, then_fn, catch_fn) {
}); });
if (hydrating) { if (hydrating) {
anchor = hydrate_node.nextSibling; anchor = hydrate_node;
set_hydrate_node(anchor);
} }
} }

@ -5,16 +5,15 @@ import {
EACH_IS_STRICT_EQUALS, EACH_IS_STRICT_EQUALS,
EACH_ITEM_REACTIVE, EACH_ITEM_REACTIVE,
EACH_KEYED, EACH_KEYED,
HYDRATION_END_ELSE, HYDRATION_START_ELSE,
HYDRATION_START HYDRATION_START
} from '../../../../constants.js'; } from '../../../../constants.js';
import { import {
hydrate_next,
hydrate_node, hydrate_node,
hydrate_nodes, hydrate_nodes,
hydrate_start,
hydrating, hydrating,
set_hydrate_node, set_hydrate_node,
set_hydrate_open,
set_hydrating set_hydrating
} from '../hydration.js'; } from '../hydration.js';
import { clear_text_content, empty } from '../operations.js'; import { clear_text_content, empty } from '../operations.js';
@ -107,6 +106,11 @@ function pause_effects(state, items, controlled_anchor, items_map) {
* @returns {void} * @returns {void}
*/ */
export function each(anchor, flags, get_collection, get_key, render_fn, fallback_fn = null) { export function each(anchor, flags, get_collection, get_key, render_fn, fallback_fn = null) {
// console.log('each', { anchor });
if (hydrating) {
hydrate_next();
}
/** @type {import('#client').EachState} */ /** @type {import('#client').EachState} */
var state = { flags, items: new Map(), first: null }; var state = { flags, items: new Map(), first: null };
@ -155,11 +159,11 @@ export function each(anchor, flags, get_collection, get_key, render_fn, fallback
let mismatch = false; let mismatch = false;
if (hydrating) { if (hydrating) {
var is_else = /** @type {Comment} */ (anchor).data === HYDRATION_END_ELSE; var is_else = /** @type {Comment} */ (anchor).data === HYDRATION_START_ELSE;
if (is_else !== (length === 0) || hydrate_node === undefined) { if (is_else !== (length === 0) || hydrate_node === undefined) {
// hydration mismatch — remove the server-rendered DOM and start over // hydration mismatch — remove the server-rendered DOM and start over
remove(hydrate_nodes); // remove(hydrate_nodes);
set_hydrating(false); set_hydrating(false);
mismatch = true; mismatch = true;
} }
@ -168,7 +172,7 @@ export function each(anchor, flags, get_collection, get_key, render_fn, fallback
// this is separate to the previous block because `hydrating` might change // this is separate to the previous block because `hydrating` might change
if (hydrating) { if (hydrating) {
/** @type {Node} */ /** @type {Node} */
var child_anchor = hydrate_node; var child_anchor = hydrate_node; // TODO do we still need child_anchors, in a world without `effect.dom`?
/** @type {import('#client').EachItem | null} */ /** @type {import('#client').EachItem | null} */
var prev = null; var prev = null;
@ -188,15 +192,15 @@ export function each(anchor, flags, get_collection, get_key, render_fn, fallback
break; break;
} }
set_hydrate_open(child_anchor); hydrate_next();
var value = array[i]; var value = array[i];
var key = get_key(value, i); var key = get_key(value, i);
item = create_item(child_anchor, state, prev, null, value, key, i, render_fn, flags); item = create_item(child_anchor, state, prev, null, value, key, i, render_fn, flags);
state.items.set(key, item); state.items.set(key, item);
var close = hydrate_node.nextSibling; // TODO validate. or replace `<!--]--><!--[-->` with `<!---->` var close = hydrate_node.nextSibling; // TODO validate. or replace `<!--]--><!--[-->` with `<!---->`
set_hydrate_node(close); // hydrate_next();
child_anchor = /** @type {Comment} */ (close.nextSibling); child_anchor = /** @type {Comment} */ (hydrate_next());
prev = item; prev = item;
} }
@ -237,9 +241,10 @@ export function each(anchor, flags, get_collection, get_key, render_fn, fallback
}); });
if (hydrating) { if (hydrating) {
anchor = hydrate_node.nextSibling; anchor = hydrate_node;
set_hydrate_node(anchor);
} }
// console.log('each', { anchor });
} }
/** /**

@ -1,9 +1,10 @@
/** @import { TemplateNode } from '#client' */
import { EFFECT_TRANSPARENT } from '../../constants.js'; import { EFFECT_TRANSPARENT } from '../../constants.js';
import { import {
hydrate_next,
hydrate_node, hydrate_node,
hydrate_nodes, hydrate_nodes,
hydrating, hydrating,
set_hydrate_node,
set_hydrating set_hydrating
} from '../hydration.js'; } from '../hydration.js';
import { remove } from '../reconciler.js'; import { remove } from '../reconciler.js';
@ -11,7 +12,7 @@ import { block, branch, pause_effect, resume_effect } from '../../reactivity/eff
import { HYDRATION_START_ELSE } from '../../../../constants.js'; import { HYDRATION_START_ELSE } from '../../../../constants.js';
/** /**
* @param {Comment} anchor * @param {TemplateNode} anchor
* @param {() => boolean} get_condition * @param {() => boolean} get_condition
* @param {(anchor: Node) => import('#client').Dom} consequent_fn * @param {(anchor: Node) => import('#client').Dom} consequent_fn
* @param {null | ((anchor: Node) => import('#client').Dom)} [alternate_fn] * @param {null | ((anchor: Node) => import('#client').Dom)} [alternate_fn]
@ -25,6 +26,10 @@ export function if_block(
alternate_fn = null, alternate_fn = null,
elseif = false elseif = false
) { ) {
if (hydrating) {
hydrate_next();
}
/** @type {import('#client').Effect | null} */ /** @type {import('#client').Effect | null} */
var consequent_effect = null; var consequent_effect = null;
@ -43,7 +48,7 @@ export function if_block(
let mismatch = false; let mismatch = false;
if (hydrating) { if (hydrating) {
const is_else = anchor.data === HYDRATION_START_ELSE; const is_else = /** @type {Comment} */ (anchor).data === HYDRATION_START_ELSE;
if (condition === is_else) { if (condition === is_else) {
// Hydration mismatch: remove everything inside the anchor and start fresh. // Hydration mismatch: remove everything inside the anchor and start fresh.
@ -87,7 +92,6 @@ export function if_block(
}, flags); }, flags);
if (hydrating) { if (hydrating) {
anchor = hydrate_node.nextSibling; anchor = hydrate_node;
set_hydrate_node(anchor);
} }
} }

@ -5,6 +5,7 @@ import {
dev_current_component_function, dev_current_component_function,
set_dev_current_component_function set_dev_current_component_function
} from '../../runtime.js'; } from '../../runtime.js';
import { hydrate_next, hydrate_node, hydrating } from '../hydration.js';
/** /**
* @template {(node: import('#client').TemplateNode, ...args: any[]) => import('#client').Dom} SnippetFn * @template {(node: import('#client').TemplateNode, ...args: any[]) => import('#client').Dom} SnippetFn
@ -14,6 +15,10 @@ import {
* @returns {void} * @returns {void}
*/ */
export function snippet(anchor, get_snippet, ...args) { export function snippet(anchor, get_snippet, ...args) {
if (hydrating) {
hydrate_next();
}
/** @type {SnippetFn | null | undefined} */ /** @type {SnippetFn | null | undefined} */
var snippet; var snippet;
@ -32,6 +37,10 @@ export function snippet(anchor, get_snippet, ...args) {
snippet_effect = branch(() => /** @type {SnippetFn} */ (snippet)(anchor, ...args)); snippet_effect = branch(() => /** @type {SnippetFn} */ (snippet)(anchor, ...args));
} }
}, EFFECT_TRANSPARENT); }, EFFECT_TRANSPARENT);
if (hydrating) {
anchor = hydrate_node;
}
} }
/** /**

@ -1,6 +1,6 @@
/** @import { TemplateNode, Dom, Effect } from '#client' */ /** @import { TemplateNode, Dom, Effect } from '#client' */
import { block, branch, pause_effect } from '../../reactivity/effects.js'; import { block, branch, pause_effect } from '../../reactivity/effects.js';
import { hydrate_node, hydrating, set_hydrate_node } from '../hydration.js'; import { hydrate_next, hydrate_node, hydrating } from '../hydration.js';
/** /**
* @template P * @template P
@ -11,6 +11,10 @@ import { hydrate_node, hydrating, set_hydrate_node } from '../hydration.js';
* @returns {void} * @returns {void}
*/ */
export function component(anchor, get_component, render_fn) { export function component(anchor, get_component, render_fn) {
if (hydrating) {
hydrate_next();
}
/** @type {C} */ /** @type {C} */
let component; let component;
@ -31,7 +35,6 @@ export function component(anchor, get_component, render_fn) {
}); });
if (hydrating) { if (hydrating) {
anchor = hydrate_node.nextSibling; anchor = hydrate_node;
set_hydrate_node(anchor);
} }
} }

@ -12,18 +12,9 @@ export function set_hydrating(value) {
hydrating = value; hydrating = value;
} }
/** @type {Comment} */
export let hydrate_open;
/** @type {TemplateNode} */ /** @type {TemplateNode} */
export let hydrate_node; export let hydrate_node;
/** @param {Comment} node */
export function set_hydrate_open(node) {
hydrate_open = node;
hydrate_node = node.nextSibling;
}
/** @param {TemplateNode} node */ /** @param {TemplateNode} node */
export function set_hydrate_node(node) { export function set_hydrate_node(node) {
hydrate_node = node; hydrate_node = node;
@ -42,8 +33,7 @@ export let hydrate_start;
/** @param {import('#client').TemplateNode[]} nodes */ /** @param {import('#client').TemplateNode[]} nodes */
export function set_hydrate_nodes(nodes) { export function set_hydrate_nodes(nodes) {
hydrate_nodes = nodes; throw new Error('TODO');
hydrate_start = nodes && nodes[0];
} }
/** /**
@ -52,11 +42,7 @@ export function set_hydrate_nodes(nodes) {
* TODO it might be worth storing this value separately rather than retrieving it with `previousSibling` * TODO it might be worth storing this value separately rather than retrieving it with `previousSibling`
*/ */
export function get_start() { export function get_start() {
return hydrate_open; return hydrate_node;
return /** @type {import('#client').TemplateNode} */ (
hydrate_start.previousSibling ?? hydrate_start
);
} }
/** /**
@ -64,9 +50,10 @@ export function get_start() {
* @param {TemplateNode} node * @param {TemplateNode} node
*/ */
export function hydrate_anchor(node) { export function hydrate_anchor(node) {
if (node.nodeType === 8 && /** @type {Comment} */ (node).data === HYDRATION_START) {
set_hydrate_open(/** @type {Comment} */ (node));
}
return node; return node;
} }
export function hydrate_next() {
hydrate_node = /** @type {TemplateNode} */ (hydrate_node.nextSibling);
return hydrate_node;
}

@ -1,5 +1,11 @@
/** @import { Effect, TemplateNode } from '#client' */ /** @import { Effect, TemplateNode } from '#client' */
import { hydrate_anchor, hydrate_node, hydrating, set_hydrate_node } from './hydration.js'; import {
hydrate_anchor,
hydrate_next,
hydrate_node,
hydrating,
set_hydrate_node
} from './hydration.js';
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
import { init_array_prototype_warnings } from '../dev/equality.js'; import { init_array_prototype_warnings } from '../dev/equality.js';
import { current_effect } from '../runtime.js'; import { current_effect } from '../runtime.js';
@ -67,7 +73,7 @@ export function child(node) {
// Child can be null if we have an element with a single child, like `<p>{text}</p>`, where `text` is empty // Child can be null if we have an element with a single child, like `<p>{text}</p>`, where `text` is empty
if (child === null) { if (child === null) {
return hydrate_node.appendChild(empty()); child = hydrate_node.appendChild(empty());
} }
set_hydrate_node(child); set_hydrate_node(child);
@ -91,6 +97,8 @@ export function first_child(fragment, is_text) {
return first; return first;
} }
// hydrate_next();
// if an {expression} is empty during SSR, there might be no // if an {expression} is empty during SSR, there might be no
// text node to hydrate — we must therefore create one // text node to hydrate — we must therefore create one
if (is_text && hydrate_node?.nodeType !== 3) { if (is_text && hydrate_node?.nodeType !== 3) {
@ -102,10 +110,16 @@ export function first_child(fragment, is_text) {
} }
hydrate_node?.before(text); hydrate_node?.before(text);
set_hydrate_node(text);
return text; return text;
} }
return hydrate_anchor(hydrate_node); // console.log('first_child', {
// hydrate_node,
// content: hydrate_node.data ?? hydrate_node.outerHTML
// });
return hydrate_node;
} }
/** /**

@ -1,16 +1,9 @@
/** @import { Effect, EffectNodes, TemplateNode } from '#client' */ /** @import { Effect, EffectNodes, TemplateNode } from '#client' */
import { import { get_start, hydrate_next, hydrate_node, hydrating, set_hydrate_node } from './hydration.js';
get_start,
hydrate_node,
hydrate_nodes,
hydrate_start,
hydrating,
set_hydrate_node
} from './hydration.js';
import { empty } from './operations.js'; import { empty } from './operations.js';
import { create_fragment_from_html } from './reconciler.js'; import { create_fragment_from_html } from './reconciler.js';
import { current_effect } from '../runtime.js'; import { current_effect } from '../runtime.js';
import { HYDRATION_END, TEMPLATE_FRAGMENT, TEMPLATE_USE_IMPORT_NODE } from '../../../constants.js'; import { TEMPLATE_FRAGMENT, TEMPLATE_USE_IMPORT_NODE } from '../../../constants.js';
import { queue_micro_task } from './task.js'; import { queue_micro_task } from './task.js';
/** /**
@ -42,8 +35,7 @@ export function template(content, flags) {
return () => { return () => {
if (hydrating) { if (hydrating) {
assign_nodes(get_start(), null); assign_nodes(hydrate_node, null);
return hydrate_node; return hydrate_node;
} }
@ -117,7 +109,7 @@ export function ns_template(content, flags, ns = 'svg') {
if (hydrating) { if (hydrating) {
assign_nodes(get_start(), null); assign_nodes(get_start(), null);
return hydrate_start; return hydrate_node;
} }
if (!node) { if (!node) {
@ -228,6 +220,8 @@ export function text(anchor) {
var node = hydrate_node; var node = hydrate_node;
// console.log('text', { hydrate_node });
if (node.nodeType !== 3) { if (node.nodeType !== 3) {
// if an {expression} is empty during SSR, `hydrate_nodes` will be empty. // if an {expression} is empty during SSR, `hydrate_nodes` will be empty.
// we need to insert an empty text node // we need to insert an empty text node
@ -244,7 +238,7 @@ export function comment() {
if (hydrating) { if (hydrating) {
assign_nodes(get_start(), null); assign_nodes(get_start(), null);
return hydrate_start; return hydrate_node;
} }
var frag = document.createDocumentFragment(); var frag = document.createDocumentFragment();
@ -266,8 +260,10 @@ export function comment() {
export function append(anchor, dom) { export function append(anchor, dom) {
if (hydrating) { if (hydrating) {
/** @type {Effect & { nodes: EffectNodes }} */ (current_effect).nodes.end = hydrate_node; /** @type {Effect & { nodes: EffectNodes }} */ (current_effect).nodes.end = hydrate_node;
hydrate_next();
// console.log('effect.nodes', current_effect.nodes); // console.log('effect.nodes', current_effect.nodes);
// console.log(dom);
// // next node should be a <!--]--> // // next node should be a <!--]-->
// var next = hydrate_node.nextSibling; // var next = hydrate_node.nextSibling;

@ -4,9 +4,11 @@ import { HYDRATION_ERROR, HYDRATION_START, PassiveDelegatedEvents } from '../../
import { flush_sync, push, pop, current_component_context } from './runtime.js'; import { flush_sync, push, pop, current_component_context } from './runtime.js';
import { effect_root, branch } from './reactivity/effects.js'; import { effect_root, branch } from './reactivity/effects.js';
import { import {
hydrate_next,
hydrate_nodes, hydrate_nodes,
hydrating,
set_hydrate_node,
set_hydrate_nodes, set_hydrate_nodes,
set_hydrate_open,
set_hydrating set_hydrating
} from './dom/hydration.js'; } from './dom/hydration.js';
import { array_from } from './utils.js'; import { array_from } from './utils.js';
@ -56,6 +58,10 @@ export function set_text(text, value) {
* @param {null | ((anchor: Comment) => void)} fallback_fn * @param {null | ((anchor: Comment) => void)} fallback_fn
*/ */
export function slot(anchor, slot_fn, slot_props, fallback_fn) { export function slot(anchor, slot_fn, slot_props, fallback_fn) {
if (hydrating) {
hydrate_next();
}
if (slot_fn === undefined) { if (slot_fn === undefined) {
if (fallback_fn !== null) { if (fallback_fn !== null) {
fallback_fn(anchor); fallback_fn(anchor);
@ -128,6 +134,7 @@ export function hydrate(component, options) {
const target = options.target; const target = options.target;
const previous_hydrate_nodes = hydrate_nodes; const previous_hydrate_nodes = hydrate_nodes;
const was_hydrating = hydrating;
try { try {
// Don't flush previous effects to ensure order of outer effects stays consistent // Don't flush previous effects to ensure order of outer effects stays consistent
@ -146,7 +153,8 @@ export function hydrate(component, options) {
throw HYDRATION_ERROR; throw HYDRATION_ERROR;
} }
set_hydrate_open(/** @type {Comment} */ (anchor)); set_hydrate_node(/** @type {Comment} */ (anchor));
hydrate_next();
const instance = _mount(component, { ...options, anchor }); const instance = _mount(component, { ...options, anchor });
@ -172,8 +180,7 @@ export function hydrate(component, options) {
throw error; throw error;
} finally { } finally {
set_hydrating(!!previous_hydrate_nodes); set_hydrating(was_hydrating);
set_hydrate_nodes(previous_hydrate_nodes);
reset_head_anchor(); reset_head_anchor();
} }
} }

@ -1,7 +1,6 @@
import { import {
HYDRATION_ANCHOR, HYDRATION_ANCHOR,
HYDRATION_END, HYDRATION_END,
HYDRATION_END_ELSE,
HYDRATION_START, HYDRATION_START,
HYDRATION_START_ELSE HYDRATION_START_ELSE
} from '../../constants.js'; } from '../../constants.js';
@ -10,4 +9,3 @@ export const BLOCK_OPEN = `<!--${HYDRATION_START}-->`;
export const BLOCK_OPEN_ELSE = `<!--${HYDRATION_START_ELSE}-->`; export const BLOCK_OPEN_ELSE = `<!--${HYDRATION_START_ELSE}-->`;
export const BLOCK_CLOSE = `<!--${HYDRATION_END}-->`; export const BLOCK_CLOSE = `<!--${HYDRATION_END}-->`;
export const BLOCK_ANCHOR = `<!--${HYDRATION_ANCHOR}-->`; export const BLOCK_ANCHOR = `<!--${HYDRATION_ANCHOR}-->`;
export const BLOCK_CLOSE_ELSE = `<!--${HYDRATION_END_ELSE}-->`;

@ -11,7 +11,7 @@ export default defineConfig({
inspect(), inspect(),
svelte({ svelte({
compilerOptions: { compilerOptions: {
hmr: true hmr: false
} }
}) })
], ],

Loading…
Cancel
Save