fix: update original source in HMR update (#12547)

* fix: update original source in HMR update

* tidy up

* comments

* oops
pull/12548/head
Rich Harris 5 months ago committed by GitHub
parent 53d32d4dfe
commit 6fdfc537e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: update original source in HMR update

@ -417,8 +417,19 @@ export function client_component(source, analysis, options) {
); );
if (options.hmr) { if (options.hmr) {
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.call('$.set', b.id('s'), b.member(b.id('module.default'), b.id('$.ORIGINAL'), true))) b.stmt(
b.assignment('=', b.member(incoming, b.id('source')), b.member(existing, b.id('source')))
),
b.stmt(
b.call('$.set', b.member(existing, b.id('source')), b.member(incoming, b.id('original')))
)
]; ];
if (analysis.css.hash) { if (analysis.css.hash) {
@ -438,26 +449,10 @@ export function client_component(source, analysis, options) {
} }
const hmr = b.block([ const hmr = b.block([
b.const(b.id('s'), b.call('$.source', b.id(analysis.name))),
b.const(b.id('$$filename'), b.member(b.id(analysis.name), b.id('$.FILENAME'), true)),
b.const(b.id('$$original'), b.id(analysis.name)),
b.stmt(b.assignment('=', b.id(analysis.name), b.call('$.hmr', b.id('s')))),
b.stmt( b.stmt(
b.assignment( b.assignment('=', id, b.call('$.hmr', id, b.thunk(b.member(existing, b.id('source')))))
'=',
b.member(b.id(analysis.name), b.id('$.FILENAME'), true),
b.id('$$filename')
)
),
// Assign the original component to the wrapper so we can use it on hot reload patching,
// else we would call the HMR function two times
b.stmt(
b.assignment(
'=',
b.member(b.id(analysis.name), b.id('$.ORIGINAL'), true),
b.id('$$original')
)
), ),
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))))
]); ]);

@ -32,7 +32,7 @@ export const UNINITIALIZED = Symbol();
// Dev-time component properties // Dev-time component properties
export const FILENAME = Symbol('filename'); export const FILENAME = Symbol('filename');
export const ORIGINAL = Symbol('original'); export const HMR = Symbol('hmr');
/** List of elements that require raw contents and should not have SSR comments put in them */ /** List of elements that require raw contents and should not have SSR comments put in them */
export const RawTextElements = ['textarea', 'script', 'style', 'title']; export const RawTextElements = ['textarea', 'script', 'style', 'title'];

@ -1,19 +1,22 @@
/** @import { Source, Effect } from '#client' */ /** @import { Source, Effect } from '#client' */
import { FILENAME, HMR } from '../../../constants.js';
import { EFFECT_TRANSPARENT } from '../constants.js'; import { EFFECT_TRANSPARENT } from '../constants.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 { 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 {Source<Component>} source * @param {Component} original
* @param {() => Source<Component>} get_source
*/ */
export function hmr(source) { export function hmr(original, get_source) {
/** /**
* @param {Comment} anchor * @param {Comment} anchor
* @param {any} props * @param {any} props
*/ */
return function (anchor, props) { function wrapper(anchor, props) {
let instance = {}; let instance = {};
/** @type {Effect} */ /** @type {Effect} */
@ -22,6 +25,7 @@ export function hmr(source) {
let ran = false; let ran = false;
block(() => { block(() => {
const source = get_source();
const component = get(source); const component = get(source);
if (effect) { if (effect) {
@ -50,5 +54,20 @@ export function hmr(source) {
ran = true; ran = true;
return instance; return instance;
}
// @ts-expect-error
wrapper[FILENAME] = original[FILENAME];
// @ts-expect-error
wrapper[HMR] = {
// When we accept an update, we set the original source to the new component
original,
// 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;
} }

@ -1,4 +1,4 @@
export { FILENAME, ORIGINAL } from '../../constants.js'; export { FILENAME, HMR } from '../../constants.js';
export { add_locations } from './dev/elements.js'; export { add_locations } from './dev/elements.js';
export { hmr } from './dev/hmr.js'; export { hmr } from './dev/hmr.js';
export { export {

@ -1,6 +1,6 @@
/** @import { Component, Payload, RenderOutput } from '#server' */ /** @import { Component, Payload, RenderOutput } from '#server' */
/** @import { Store } from '#shared' */ /** @import { Store } from '#shared' */
export { FILENAME, ORIGINAL } from '../../constants.js'; export { FILENAME, HMR } from '../../constants.js';
import { is_promise, noop } from '../shared/utils.js'; import { is_promise, noop } from '../shared/utils.js';
import { subscribe_to_store } from '../../store/utils.js'; import { subscribe_to_store } from '../../store/utils.js';
import { import {

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

Loading…
Cancel
Save