Skip to content

Commit d5776c3

Browse files
authored
feat: simplify HMR implementation (#11132)
* chore: simplify HMR implementation * changeset * unused * prettier
1 parent a740b7b commit d5776c3

File tree

8 files changed

+97
-79
lines changed

8 files changed

+97
-79
lines changed

.changeset/fair-bags-smoke.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+
feat: simplify HMR implementation

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

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -422,32 +422,31 @@ export function client_component(source, analysis, options) {
422422
)
423423
];
424424

425-
// In order for hmr to work correctly, we need associate each component with a unique key.
426-
// This is because bundlers might put many components into a the same module (usuaully as a chunk).
427-
// `import.meta.hot` will then be the same object for all components in that modules.
428-
if (options.hmr && options.filename) {
425+
if (options.hmr) {
429426
body.push(
430-
b.export_default(
431-
b.conditional(
432-
b.import_meta_hot(),
433-
b.call(
434-
'$.hmr',
435-
b.member(b.import_meta_hot(), b.id('data')),
436-
b.id(analysis.name),
437-
b.literal(options.filename)
438-
),
439-
b.id(analysis.name)
440-
)
441-
),
442427
b.if(
443-
b.import_meta_hot(),
444-
b.stmt(b.call('import.meta.hot.acceptExports', b.literal('default')))
428+
b.id('import.meta.hot'),
429+
b.block([
430+
b.const(b.id('s'), b.call('$.source', b.id(analysis.name))),
431+
b.stmt(b.assignment('=', b.id(analysis.name), b.call('$.hmr', b.id('s')))),
432+
b.stmt(
433+
b.call(
434+
'import.meta.hot.accept',
435+
b.arrow(
436+
[b.id('module')],
437+
b.block([
438+
b.stmt(b.call('$.set', b.id('s'), b.member(b.id('module'), b.id('default'))))
439+
])
440+
)
441+
)
442+
)
443+
])
445444
)
446445
);
447-
} else {
448-
body.push(b.export_default(b.id(analysis.name)));
449446
}
450447

448+
body.push(b.export_default(b.id(analysis.name)));
449+
451450
if (options.dev) {
452451
if (options.filename) {
453452
let filename = options.filename;

packages/svelte/src/compiler/utils/builders.js

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -600,20 +600,6 @@ export function throw_error(str) {
600600
};
601601
}
602602

603-
/**
604-
* @return {import('estree').MemberExpression}
605-
*/
606-
export function import_meta_hot() {
607-
return member(
608-
{
609-
type: 'MetaProperty',
610-
meta: id('import'),
611-
property: id('meta')
612-
},
613-
id('hot')
614-
);
615-
}
616-
617603
export {
618604
await_builder as await,
619605
let_builder as let,
Lines changed: 28 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,38 @@
11
import { block, branch, destroy_effect } from '../reactivity/effects.js';
2-
import { set, source } from '../reactivity/sources.js';
32
import { set_should_intro } from '../render.js';
43
import { get } from '../runtime.js';
54

65
/**
76
* @template {(anchor: Comment, props: any) => any} Component
8-
* @param {{ components: Map<string, { source: import("#client").Source<Component>; wrapper: null | Component; }> }} hot_data
9-
* @param {string} key
10-
* @param {Component} component
7+
* @param {import("#client").Source<Component>} source
118
*/
12-
export function hmr(hot_data, component, key) {
13-
var components = (hot_data.components ??= new Map());
14-
var data = components.get(key);
15-
16-
if (data === undefined) {
17-
components.set(
18-
key,
19-
(data = {
20-
source: source(component),
21-
wrapper: null
22-
})
23-
);
24-
} else {
25-
set(data.source, component);
26-
}
27-
const component_source = data.source;
28-
29-
return (data.wrapper ??= /** @type {Component} */ (
30-
(anchor, props) => {
31-
let instance = {};
32-
33-
/** @type {import("#client").Effect} */
34-
let effect;
35-
36-
block(() => {
37-
const component = get(component_source);
38-
39-
if (effect) {
40-
// @ts-ignore
41-
for (var k in instance) delete instance[k];
42-
destroy_effect(effect);
43-
}
44-
45-
effect = branch(() => {
46-
set_should_intro(false);
47-
Object.assign(instance, component(anchor, props));
48-
set_should_intro(true);
49-
});
9+
export function hmr(source) {
10+
/**
11+
* @param {Comment} anchor
12+
* @param {any} props
13+
*/
14+
return (anchor, props) => {
15+
let instance = {};
16+
17+
/** @type {import("#client").Effect} */
18+
let effect;
19+
20+
block(() => {
21+
const component = get(source);
22+
23+
if (effect) {
24+
// @ts-ignore
25+
for (var k in instance) delete instance[k];
26+
destroy_effect(effect);
27+
}
28+
29+
effect = branch(() => {
30+
set_should_intro(false);
31+
Object.assign(instance, component(anchor, props));
32+
set_should_intro(true);
5033
});
34+
});
5135

52-
return instance;
53-
}
54-
));
36+
return instance;
37+
};
5538
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
compileOptions: {
5+
hmr: true
6+
}
7+
});
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// index.svelte (Svelte VERSION)
2+
// Note: compiler output will change before 5.0 is released!
3+
import "svelte/internal/disclose-version";
4+
import * as $ from "svelte/internal/client";
5+
6+
var root = $.template(`<h1>hello world</h1>`);
7+
8+
function Hmr($$anchor, $$props) {
9+
$.push($$props, false);
10+
$.init();
11+
12+
var h1 = root();
13+
14+
$.append($$anchor, h1);
15+
$.pop();
16+
}
17+
18+
if (import.meta.hot) {
19+
const s = $.source(Hmr);
20+
21+
Hmr = $.hmr(s);
22+
23+
import.meta.hot.accept((module) => {
24+
$.set(s, module.default);
25+
});
26+
}
27+
28+
export default Hmr;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// index.svelte (Svelte VERSION)
2+
// Note: compiler output will change before 5.0 is released!
3+
import * as $ from "svelte/internal/server";
4+
5+
export default function Hmr($$payload, $$props) {
6+
$.push(false);
7+
$$payload.out += `<h1>hello world</h1>`;
8+
$.pop();
9+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<h1>hello world</h1>

0 commit comments

Comments
 (0)