fix hydration with HMR (hopefuly)

pull/10100/head
rixo 3 years ago
parent 4b9c4e1f16
commit f0ca244468

@ -1,5 +1,44 @@
import { key } from './render.js';
import { key, comment } from './render.js';
import { source, set, get } from './runtime.js';
import { current_hydration_fragment } from './hydration.js';
import { child_frag } from './operations.js';
function find_surrounding_ssr_commments() {
if (!current_hydration_fragment?.[0]) return null;
/** @type {Comment | undefined} */
let before;
/** @type {Comment | undefined} */
let after;
/** @type {Node | null | undefined} */
let node;
node = current_hydration_fragment[0].previousSibling;
while (node) {
const comment = /** @type {Comment} */ (node);
if (node.nodeType === 8 && comment.data.startsWith('ssr:')) {
before = comment;
break;
}
node = node.previousSibling;
}
node = current_hydration_fragment.at(-1)?.nextSibling;
while (node) {
const comment = /** @type {Comment} */ (node);
if (node.nodeType === 8 && comment.data.startsWith('ssr:')) {
after = comment;
break;
}
node = node.nextSibling;
}
if (before && after && before.data === after.data) {
return [before, after];
}
return null;
}
/**
* @template {any[]} ComponentArgs
@ -10,26 +49,41 @@ import { source, set, get } from './runtime.js';
* component_signal: ReturnType<typeof source<Component>>,
* proxy?: (...args: ComponentArgs) => ComponentReturn
* }} hot_data
* @param {Component} component
* @param {Component} new_component
*/
export function hmr(hot_data, component) {
export function hmr(hot_data, new_component) {
if (hot_data.proxy) {
set(hot_data.component_signal, component);
set(hot_data.component_signal, new_component);
} else {
const component_signal = (hot_data.component_signal = source(component));
const component_signal = source(new_component);
hot_data.component_signal = component_signal;
// @ts-ignore
hot_data.proxy = function (target, ...args) {
const accessors = source(/** @type {ComponentReturn} */ ({}));
hot_data.proxy = function ($$anchor, ...args) {
let accessors = /** @type {ComponentReturn} */ ({});
// During hydration the root component will receive a null $$anchor. The
// following is a hack to get our `key` a node to render to, all while
// avoiding it to "consume" the SSR marker.
// TODO better get the eyes of someone with understanding of hydration on this
if (!$$anchor && current_hydration_fragment?.[0]) {
const ssr0 = find_surrounding_ssr_commments();
if (ssr0) {
const [before, after] = ssr0;
current_hydration_fragment.unshift(before);
current_hydration_fragment.push(after);
$$anchor = child_frag(current_hydration_fragment);
}
}
key(
target,
$$anchor,
() => get(component_signal),
($$anchor) => {
const current_component = get(component_signal);
const component = get(component_signal);
// @ts-ignore
const new_accessors = current_component($$anchor, ...args);
set(accessors, new_accessors);
accessors = component($$anchor, ...args);
}
);
@ -37,11 +91,11 @@ export function hmr(hot_data, component) {
{},
{
get(_, p) {
return get(accessors)?.[p];
return accessors?.[p];
},
set(_, p, value) {
// @ts-ignore (we actually want to crash on undefined, like non HMR code would do)
get(accessors)[p] = value;
accessors[p] = value;
return true;
}
}

Loading…
Cancel
Save