Rich Harris 3 days ago committed by GitHub
commit f1f3950f86
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: prevent infinite loop when HMRing a component with an `await`

@ -519,14 +519,9 @@ export function client_component(analysis, options) {
if (options.hmr) { if (options.hmr) {
const id = b.id(analysis.name); const id = b.id(analysis.name);
const HMR = b.id('$.HMR');
const existing = b.member(id, HMR, true);
const incoming = b.member(b.id('module.default'), HMR, true);
const accept_fn_body = [ const accept_fn_body = [
b.stmt(b.assignment('=', b.member(incoming, 'source'), b.member(existing, 'source'))), b.stmt(b.call(b.member(id, b.id('$.HMR'), true), b.id('module.default')))
b.stmt(b.call('$.set', b.member(existing, 'source'), b.member(incoming, 'original')))
]; ];
if (analysis.css.hash) { if (analysis.css.hash) {
@ -535,8 +530,7 @@ export function client_component(analysis, options) {
} }
const hmr = b.block([ const hmr = b.block([
b.stmt(b.assignment('=', id, b.call('$.hmr', id, b.thunk(b.member(existing, 'source'))))), b.stmt(b.assignment('=', id, b.call('$.hmr', id))),
b.stmt(b.call('import.meta.hot.accept', b.arrow([b.id('module')], b.block(accept_fn_body)))) b.stmt(b.call('import.meta.hot.accept', b.arrow([b.id('module')], b.block(accept_fn_body))))
]); ]);

@ -1,23 +1,25 @@
/** @import { Source, Effect, TemplateNode } from '#client' */ /** @import { Effect, TemplateNode } from '#client' */
import { FILENAME, HMR } from '../../../constants.js'; import { FILENAME, HMR } from '../../../constants.js';
import { EFFECT_TRANSPARENT } from '#client/constants'; import { EFFECT_TRANSPARENT } from '#client/constants';
import { hydrate_node, hydrating } from '../dom/hydration.js'; import { hydrate_node, hydrating } from '../dom/hydration.js';
import { block, branch, destroy_effect } from '../reactivity/effects.js'; import { block, branch, destroy_effect } from '../reactivity/effects.js';
import { source } from '../reactivity/sources.js'; import { source, update } from '../reactivity/sources.js';
import { set_should_intro } from '../render.js'; import { set_should_intro } from '../render.js';
import { get } from '../runtime.js'; import { get } from '../runtime.js';
/** /**
* @template {(anchor: Comment, props: any) => any} Component * @template {(anchor: Comment, props: any) => any} Component
* @param {Component} original * @param {Component} component
* @param {() => Source<Component>} get_source
*/ */
export function hmr(original, get_source) { export function hmr(component) {
let s = source(0);
/** /**
* @param {TemplateNode} anchor * @param {TemplateNode} anchor
* @param {any} props * @param {any} props
*/ */
function wrapper(anchor, props) { function wrapper(anchor, props) {
let v = -1;
let instance = {}; let instance = {};
/** @type {Effect} */ /** @type {Effect} */
@ -26,8 +28,9 @@ export function hmr(original, get_source) {
let ran = false; let ran = false;
block(() => { block(() => {
const source = get_source(); if (v === (v = get(s))) {
const component = get(source); return;
}
if (effect) { if (effect) {
// @ts-ignore // @ts-ignore
@ -62,16 +65,12 @@ export function hmr(original, get_source) {
} }
// @ts-expect-error // @ts-expect-error
wrapper[FILENAME] = original[FILENAME]; wrapper[FILENAME] = component[FILENAME];
// @ts-ignore // @ts-ignore
wrapper[HMR] = { wrapper[HMR] = (c) => {
// When we accept an update, we set the original source to the new component component = c;
original, update(s);
// The `get_source` parameter reads `wrapper[HMR].source`, but in the `accept`
// function we always replace it with `previous[HMR].source`, which in practice
// means we only ever update the original
source: source(original)
}; };
return wrapper; return wrapper;

@ -11,11 +11,10 @@ function Hmr($$anchor) {
} }
if (import.meta.hot) { if (import.meta.hot) {
Hmr = $.hmr(Hmr, () => Hmr[$.HMR].source); Hmr = $.hmr(Hmr);
import.meta.hot.accept((module) => { import.meta.hot.accept((module) => {
module.default[$.HMR].source = Hmr[$.HMR].source; Hmr[$.HMR](module.default);
$.set(Hmr[$.HMR].source, module.default[$.HMR].original);
}); });
} }

@ -95,6 +95,7 @@ for (const generate of /** @type {const} */ (['client', 'server'])) {
if (generate === 'server' || FROM_HTML) { if (generate === 'server' || FROM_HTML) {
from_html = compile(source, { from_html = compile(source, {
dev: DEV, dev: DEV,
hmr: DEV,
filename: input, filename: input,
generate, generate,
runes: argv.values.runes, runes: argv.values.runes,

Loading…
Cancel
Save