more legible

pull/10100/head
rixo 3 years ago
parent 1c380900b7
commit 17848e9413

@ -4,6 +4,19 @@ import { current_hydration_fragment } from './hydration.js';
import { child_frag } from './operations.js';
import { proxy } from './proxy/proxy.js';
/**
* @typedef {Record<string | symbol, any> | undefined} ComponentReturn
*
* @typedef {any[]} ComponentArgs
*
* @typedef {(...args: ComponentArgs) => ComponentReturn} Component
*
* @typedef {{
* set_component: (new_component: Component) => void
* proxy_component?: (...args: ComponentArgs) => ComponentReturn
* }} HotData<Component>
*/
function find_surrounding_ssr_commments() {
if (!current_hydration_fragment?.[0]) return null;
@ -42,76 +55,103 @@ function find_surrounding_ssr_commments() {
}
/**
* @template {any[]} ComponentArgs
* @template {Record<string | symbol, any> | undefined} ComponentReturn
* @template {(...args: ComponentArgs) => ComponentReturn} Component
*
* @param {{
* component_signal: ReturnType<typeof source<Component>>,
* proxy?: (...args: ComponentArgs) => ComponentReturn
* }} hot_data
* @param {Component} new_component
*/
export function hmr(hot_data, new_component) {
if (hot_data.proxy) {
set(hot_data.component_signal, new_component);
} else {
hot_data.component_signal = source(new_component);
function create_proxy_component(new_component) {
const component_signal = source(new_component);
// @ts-ignore
hot_data.proxy = function ($$anchor, ...args) {
const accessors_proxy = proxy(/** @type {import('./proxy/proxy.js').StateObject} */ ({}));
/** @type {Set<string>} */
const accessors_keys = new Set();
let component_name = '';
// 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 this failes, we get an ugly hydration failure message, but HMR should
// still work after that... Maybe we can show a more specific error message than
// the generic hydration failure one (that could be misleading in this case).
//
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);
}
/**
* @type {HotData["set_component"]}
*/
function set_component(new_component) {
component_name = new_component.name;
set(component_signal, new_component);
}
// @ts-ignore
function proxy_component($$anchor, ...args) {
const accessors_proxy = proxy(/** @type {import('./proxy/proxy.js').StateObject} */ ({}));
/** @type {Set<string>} */
const accessors_keys = new Set();
// 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 this fails, we get an ugly hydration failure message, but HMR should
// still work after that... Maybe we can show a more specific error message than
// the generic hydration failure one (that could be misleading in this case).
//
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(
$$anchor,
() => get(hot_data.component_signal),
($$anchor) => {
const component = get(hot_data.component_signal);
// @ts-ignore
const new_accessors = component($$anchor, ...args);
const removed_keys = new Set(accessors_keys);
if (new_accessors) {
for (const [key, value] of Object.entries(new_accessors)) {
accessors_proxy[key] = value;
accessors_keys.add(key);
removed_keys.delete(key);
}
}
key(
$$anchor,
() => get(component_signal),
($$anchor) => {
const component = get(component_signal);
for (const key of removed_keys) {
accessors_keys.delete(key);
accessors_proxy[key] = undefined;
// @ts-ignore
const new_accessors = component($$anchor, ...args);
const removed_keys = new Set(accessors_keys);
if (new_accessors) {
for (const [key, value] of Object.entries(new_accessors)) {
accessors_proxy[key] = value;
accessors_keys.add(key);
removed_keys.delete(key);
}
}
);
return accessors_proxy;
};
for (const key of removed_keys) {
accessors_keys.delete(key);
accessors_proxy[key] = undefined;
}
}
);
try {
Object.defineProperty(proxy_component, 'name', {
get() {
return component_name;
}
});
} catch (err) {
console.warn("[Svelte HMR] Failed to proxy component function's name", err);
}
return accessors_proxy;
}
return { proxy_component, set_component };
}
/**
* @param {HotData} hot_data
* @param {Component} new_component
*/
export function hmr(hot_data, new_component) {
if (hot_data.set_component) {
hot_data.set_component(new_component);
} else {
({
//
proxy_component: hot_data.proxy_component,
set_component: hot_data.set_component
} = create_proxy_component(new_component));
}
return hot_data.proxy;
return hot_data.proxy_component;
}

Loading…
Cancel
Save