diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index b9d6a5bc4d..86079bd724 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -36,7 +36,8 @@ import { BLOCK_ANCHOR, BLOCK_CLOSE, BLOCK_CLOSE_ELSE, - BLOCK_OPEN + BLOCK_OPEN, + BLOCK_OPEN_ELSE } from '../../../../internal/server/hydration.js'; import { filename, locator } from '../../../state.js'; @@ -1006,7 +1007,8 @@ function serialize_inline_component(node, expression, context) { } else if (context.state.skip_hydration_boundaries) { context.state.template.push(statement); } else { - context.state.template.push(block_open, statement, block_close); + context.state.template.push(statement); + // context.state.template.push(block_open, statement, block_close); } } @@ -1411,12 +1413,15 @@ const template_visitors = { ? /** @type {import('estree').BlockStatement} */ (context.visit(node.alternate)) : b.block([]); - consequent.body.push(b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(BLOCK_CLOSE)))); - alternate.body.push( - b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(BLOCK_CLOSE_ELSE))) + consequent.body.unshift( + b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(BLOCK_OPEN))) + ); + + alternate.body.unshift( + b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(BLOCK_OPEN_ELSE))) ); - context.state.template.push(block_open, b.if(test, consequent, alternate)); + context.state.template.push(b.if(test, consequent, alternate), block_close); }, AwaitBlock(node, context) { context.state.template.push( diff --git a/packages/svelte/src/constants.js b/packages/svelte/src/constants.js index a8963d6854..0c7efcc94c 100644 --- a/packages/svelte/src/constants.js +++ b/packages/svelte/src/constants.js @@ -20,6 +20,7 @@ export const TEMPLATE_FRAGMENT = 1; export const TEMPLATE_USE_IMPORT_NODE = 1 << 1; export const HYDRATION_START = '['; +export const HYDRATION_START_ELSE = '[!'; export const HYDRATION_END = ']'; export const HYDRATION_ANCHOR = ''; export const HYDRATION_END_ELSE = `${HYDRATION_END}!`; // used to indicate that an `{:else}...` block was rendered diff --git a/packages/svelte/src/internal/client/dom/blocks/await.js b/packages/svelte/src/internal/client/dom/blocks/await.js index 204c7779b3..e6e76beee6 100644 --- a/packages/svelte/src/internal/client/dom/blocks/await.js +++ b/packages/svelte/src/internal/client/dom/blocks/await.js @@ -11,7 +11,7 @@ import { import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js'; import { DEV } from 'esm-env'; import { queue_micro_task } from '../task.js'; -import { hydrate_node, hydrating } from '../hydration.js'; +import { hydrate_node, hydrating, set_hydrate_node } from '../hydration.js'; import { mutable_source, set, source } from '../../reactivity/sources.js'; const PENDING = 0; @@ -149,5 +149,6 @@ export function await_block(anchor, get_input, pending_fn, then_fn, catch_fn) { if (hydrating) { anchor = hydrate_node.nextSibling; + set_hydrate_node(anchor); } } diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index b60b6f9c01..a9085b529b 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -8,7 +8,15 @@ import { HYDRATION_END_ELSE, HYDRATION_START } from '../../../../constants.js'; -import { hydrate_nodes, hydrate_start, hydrating, set_hydrating } from '../hydration.js'; +import { + hydrate_node, + hydrate_nodes, + hydrate_start, + hydrating, + set_hydrate_node, + set_hydrate_open, + set_hydrating +} from '../hydration.js'; import { clear_text_content, empty } from '../operations.js'; import { remove } from '../reconciler.js'; import { @@ -144,7 +152,7 @@ export function each(anchor, flags, get_collection, get_key, render_fn, fallback if (hydrating) { var is_else = /** @type {Comment} */ (anchor).data === HYDRATION_END_ELSE; - if (is_else !== (length === 0) || hydrate_start === undefined) { + if (is_else !== (length === 0) || hydrate_node === undefined) { // hydration mismatch — remove the server-rendered DOM and start over remove(hydrate_nodes); set_hydrating(false); @@ -155,7 +163,7 @@ export function each(anchor, flags, get_collection, get_key, render_fn, fallback // this is separate to the previous block because `hydrating` might change if (hydrating) { /** @type {Node} */ - var child_anchor = hydrate_start; + var child_anchor = hydrate_node; /** @type {import('#client').EachItem | null} */ var prev = null; @@ -175,22 +183,27 @@ export function each(anchor, flags, get_collection, get_key, render_fn, fallback break; } + set_hydrate_open(child_anchor); + var value = array[i]; var key = get_key(value, i); item = create_item(child_anchor, state, prev, null, value, key, i, render_fn, flags); state.items.set(key, item); - child_anchor = /** @type {Comment} */ (child_anchor.nextSibling); + var close = hydrate_node.nextSibling; // TODO validate. or replace `` with `` + set_hydrate_node(close); + child_anchor = /** @type {Comment} */ (close.nextSibling); prev = item; } // remove excess nodes if (length > 0) { - while (child_anchor !== anchor) { - var next = /** @type {import('#client').TemplateNode} */ (child_anchor.nextSibling); - /** @type {import('#client').TemplateNode} */ (child_anchor).remove(); - child_anchor = next; - } + // TODO reinstate + // while (child_anchor !== anchor) { + // var next = /** @type {import('#client').TemplateNode} */ (child_anchor.nextSibling); + // /** @type {import('#client').TemplateNode} */ (child_anchor).remove(); + // child_anchor = next; + // } } } @@ -217,6 +230,11 @@ export function each(anchor, flags, get_collection, get_key, render_fn, fallback set_hydrating(true); } }); + + if (hydrating) { + anchor = hydrate_node.nextSibling; + set_hydrate_node(anchor); + } } /** diff --git a/packages/svelte/src/internal/client/dom/blocks/if.js b/packages/svelte/src/internal/client/dom/blocks/if.js index 747797cd49..015a774ae1 100644 --- a/packages/svelte/src/internal/client/dom/blocks/if.js +++ b/packages/svelte/src/internal/client/dom/blocks/if.js @@ -1,9 +1,14 @@ import { EFFECT_TRANSPARENT } from '../../constants.js'; -import { hydrate_node, hydrate_nodes, hydrating, set_hydrating } from '../hydration.js'; +import { + hydrate_node, + hydrate_nodes, + hydrating, + set_hydrate_node, + set_hydrating +} from '../hydration.js'; import { remove } from '../reconciler.js'; import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js'; -import { HYDRATION_END_ELSE } from '../../../../constants.js'; -import { current_effect } from '../../runtime.js'; +import { HYDRATION_START_ELSE } from '../../../../constants.js'; /** * @param {Comment} anchor @@ -38,7 +43,7 @@ export function if_block( let mismatch = false; if (hydrating) { - const is_else = anchor.data === HYDRATION_END_ELSE; + const is_else = anchor.data === HYDRATION_START_ELSE; if (condition === is_else) { // Hydration mismatch: remove everything inside the anchor and start fresh. @@ -83,5 +88,6 @@ export function if_block( if (hydrating) { anchor = hydrate_node.nextSibling; + set_hydrate_node(anchor); } } diff --git a/packages/svelte/src/internal/client/dom/operations.js b/packages/svelte/src/internal/client/dom/operations.js index 3aa0cc4b63..bb2ac1b0bb 100644 --- a/packages/svelte/src/internal/client/dom/operations.js +++ b/packages/svelte/src/internal/client/dom/operations.js @@ -59,12 +59,15 @@ export function empty() { */ /*#__NO_SIDE_EFFECTS__*/ export function child(node) { - const child = node.firstChild; - if (!hydrating) return child; + if (!hydrating) { + return node.firstChild; + } + + var child = hydrate_node.firstChild; // Child can be null if we have an element with a single child, like `

{text}

`, where `text` is empty if (child === null) { - return node.appendChild(empty()); + return hydrate_node.appendChild(empty()); } return child; diff --git a/packages/svelte/src/internal/client/dom/template.js b/packages/svelte/src/internal/client/dom/template.js index 65379cc833..34c219599a 100644 --- a/packages/svelte/src/internal/client/dom/template.js +++ b/packages/svelte/src/internal/client/dom/template.js @@ -267,15 +267,15 @@ export function append(anchor, dom) { if (hydrating) { /** @type {Effect & { nodes: EffectNodes }} */ (current_effect).nodes.end = hydrate_node; - // console.log('append', hydrate_node); + // console.log('effect.nodes.end', hydrate_node); - // next node should be a - var next = hydrate_node.nextSibling; - if (next === null || next.nodeType !== 8) { - throw new Error('TODO'); - } + // // next node should be a + // var next = hydrate_node.nextSibling; + // if (next === null || next.nodeType !== 8) { + // throw new Error('TODO'); + // } - set_hydrate_node(/** @type {Comment} */ (next)); + // set_hydrate_node(/** @type {Comment} */ (next)); return; } diff --git a/packages/svelte/src/internal/server/hydration.js b/packages/svelte/src/internal/server/hydration.js index d860d759d2..c6a062132c 100644 --- a/packages/svelte/src/internal/server/hydration.js +++ b/packages/svelte/src/internal/server/hydration.js @@ -2,10 +2,12 @@ import { HYDRATION_ANCHOR, HYDRATION_END, HYDRATION_END_ELSE, - HYDRATION_START + HYDRATION_START, + HYDRATION_START_ELSE } from '../../constants.js'; export const BLOCK_OPEN = ``; +export const BLOCK_OPEN_ELSE = ``; export const BLOCK_CLOSE = ``; export const BLOCK_ANCHOR = ``; export const BLOCK_CLOSE_ELSE = ``; diff --git a/playgrounds/demo/server.js b/playgrounds/demo/server.js index 82a75e70e7..c72ce1b328 100644 --- a/playgrounds/demo/server.js +++ b/playgrounds/demo/server.js @@ -23,10 +23,16 @@ async function createServer() { app.use('*', async (req, res) => { if (req.originalUrl !== '/') { - res.writeHead(200, { - 'Content-Type': 'application/javascript' - }); - res.end(fs.createReadStream(path.resolve('./dist' + req.originalUrl))); + const file = path.resolve('./dist' + req.originalUrl); + if (fs.existsSync(file)) { + res.writeHead(200, { + 'Content-Type': 'application/javascript' + }); + res.end(fs.createReadStream(file)); + } else { + res.writeHead(404); + res.end('not found'); + } return; }