Skip to content

Commit 6fdfc53

Browse files
authored
fix: update original source in HMR update (#12547)
* fix: update original source in HMR update * tidy up * comments * oops
1 parent 53d32d4 commit 6fdfc53

File tree

7 files changed

+47
-33
lines changed

7 files changed

+47
-33
lines changed

.changeset/small-planets-destroy.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: update original source in HMR update

packages/svelte/src/compiler/phases/3-transform/client/transform-client.js

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,19 @@ export function client_component(source, analysis, options) {
417417
);
418418

419419
if (options.hmr) {
420+
const id = b.id(analysis.name);
421+
const HMR = b.id('$.HMR');
422+
423+
const existing = b.member(id, HMR, true);
424+
const incoming = b.member(b.id('module.default'), HMR, true);
425+
420426
const accept_fn_body = [
421-
b.stmt(b.call('$.set', b.id('s'), b.member(b.id('module.default'), b.id('$.ORIGINAL'), true)))
427+
b.stmt(
428+
b.assignment('=', b.member(incoming, b.id('source')), b.member(existing, b.id('source')))
429+
),
430+
b.stmt(
431+
b.call('$.set', b.member(existing, b.id('source')), b.member(incoming, b.id('original')))
432+
)
422433
];
423434

424435
if (analysis.css.hash) {
@@ -438,26 +449,10 @@ export function client_component(source, analysis, options) {
438449
}
439450

440451
const hmr = b.block([
441-
b.const(b.id('s'), b.call('$.source', b.id(analysis.name))),
442-
b.const(b.id('$$filename'), b.member(b.id(analysis.name), b.id('$.FILENAME'), true)),
443-
b.const(b.id('$$original'), b.id(analysis.name)),
444-
b.stmt(b.assignment('=', b.id(analysis.name), b.call('$.hmr', b.id('s')))),
445452
b.stmt(
446-
b.assignment(
447-
'=',
448-
b.member(b.id(analysis.name), b.id('$.FILENAME'), true),
449-
b.id('$$filename')
450-
)
451-
),
452-
// Assign the original component to the wrapper so we can use it on hot reload patching,
453-
// else we would call the HMR function two times
454-
b.stmt(
455-
b.assignment(
456-
'=',
457-
b.member(b.id(analysis.name), b.id('$.ORIGINAL'), true),
458-
b.id('$$original')
459-
)
453+
b.assignment('=', id, b.call('$.hmr', id, b.thunk(b.member(existing, b.id('source')))))
460454
),
455+
461456
b.stmt(b.call('import.meta.hot.accept', b.arrow([b.id('module')], b.block(accept_fn_body))))
462457
]);
463458

packages/svelte/src/constants.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const UNINITIALIZED = Symbol();
3232

3333
// Dev-time component properties
3434
export const FILENAME = Symbol('filename');
35-
export const ORIGINAL = Symbol('original');
35+
export const HMR = Symbol('hmr');
3636

3737
/** List of elements that require raw contents and should not have SSR comments put in them */
3838
export const RawTextElements = ['textarea', 'script', 'style', 'title'];

packages/svelte/src/internal/client/dev/hmr.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
/** @import { Source, Effect } from '#client' */
2+
import { FILENAME, HMR } from '../../../constants.js';
23
import { EFFECT_TRANSPARENT } from '../constants.js';
34
import { block, branch, destroy_effect } from '../reactivity/effects.js';
5+
import { source } from '../reactivity/sources.js';
46
import { set_should_intro } from '../render.js';
57
import { get } from '../runtime.js';
68

79
/**
810
* @template {(anchor: Comment, props: any) => any} Component
9-
* @param {Source<Component>} source
11+
* @param {Component} original
12+
* @param {() => Source<Component>} get_source
1013
*/
11-
export function hmr(source) {
14+
export function hmr(original, get_source) {
1215
/**
1316
* @param {Comment} anchor
1417
* @param {any} props
1518
*/
16-
return function (anchor, props) {
19+
function wrapper(anchor, props) {
1720
let instance = {};
1821

1922
/** @type {Effect} */
@@ -22,6 +25,7 @@ export function hmr(source) {
2225
let ran = false;
2326

2427
block(() => {
28+
const source = get_source();
2529
const component = get(source);
2630

2731
if (effect) {
@@ -50,5 +54,20 @@ export function hmr(source) {
5054
ran = true;
5155

5256
return instance;
57+
}
58+
59+
// @ts-expect-error
60+
wrapper[FILENAME] = original[FILENAME];
61+
62+
// @ts-expect-error
63+
wrapper[HMR] = {
64+
// When we accept an update, we set the original source to the new component
65+
original,
66+
// The `get_source` parameter reads `wrapper[HMR].source`, but in the `accept`
67+
// function we always replace it with `previous[HMR].source`, which in practice
68+
// means we only ever update the original
69+
source: source(original)
5370
};
71+
72+
return wrapper;
5473
}

packages/svelte/src/internal/client/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export { FILENAME, ORIGINAL } from '../../constants.js';
1+
export { FILENAME, HMR } from '../../constants.js';
22
export { add_locations } from './dev/elements.js';
33
export { hmr } from './dev/hmr.js';
44
export {

packages/svelte/src/internal/server/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @import { Component, Payload, RenderOutput } from '#server' */
22
/** @import { Store } from '#shared' */
3-
export { FILENAME, ORIGINAL } from '../../constants.js';
3+
export { FILENAME, HMR } from '../../constants.js';
44
import { is_promise, noop } from '../shared/utils.js';
55
import { subscribe_to_store } from '../../store/utils.js';
66
import {

packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,11 @@ function Hmr($$anchor) {
1010
}
1111

1212
if (import.meta.hot) {
13-
const s = $.source(Hmr);
14-
const $$filename = Hmr[$.FILENAME];
15-
const $$original = Hmr;
16-
17-
Hmr = $.hmr(s);
18-
Hmr[$.FILENAME] = $$filename;
19-
Hmr[$.ORIGINAL] = $$original;
13+
Hmr = $.hmr(Hmr, () => Hmr[$.HMR].source);
2014

2115
import.meta.hot.accept((module) => {
22-
$.set(s, module.default[$.ORIGINAL]);
16+
module.default[$.HMR].source = Hmr[$.HMR].source;
17+
$.set(Hmr[$.HMR].source, module.default[$.HMR].original);
2318
});
2419
}
2520

0 commit comments

Comments
 (0)