feat: simplify HMR implementation (#11132)

* chore: simplify HMR implementation

* changeset

* unused

* prettier
pull/11134/head
Rich Harris 1 year ago committed by GitHub
parent a740b7bb43
commit d5776c3ec3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
feat: simplify HMR implementation

@ -422,32 +422,31 @@ export function client_component(source, analysis, options) {
) )
]; ];
// In order for hmr to work correctly, we need associate each component with a unique key. if (options.hmr) {
// This is because bundlers might put many components into a the same module (usuaully as a chunk).
// `import.meta.hot` will then be the same object for all components in that modules.
if (options.hmr && options.filename) {
body.push( body.push(
b.export_default(
b.conditional(
b.import_meta_hot(),
b.call(
'$.hmr',
b.member(b.import_meta_hot(), b.id('data')),
b.id(analysis.name),
b.literal(options.filename)
),
b.id(analysis.name)
)
),
b.if( b.if(
b.import_meta_hot(), b.id('import.meta.hot'),
b.stmt(b.call('import.meta.hot.acceptExports', b.literal('default'))) b.block([
b.const(b.id('s'), b.call('$.source', b.id(analysis.name))),
b.stmt(b.assignment('=', b.id(analysis.name), b.call('$.hmr', b.id('s')))),
b.stmt(
b.call(
'import.meta.hot.accept',
b.arrow(
[b.id('module')],
b.block([
b.stmt(b.call('$.set', b.id('s'), b.member(b.id('module'), b.id('default'))))
])
)
)
)
])
) )
); );
} else {
body.push(b.export_default(b.id(analysis.name)));
} }
body.push(b.export_default(b.id(analysis.name)));
if (options.dev) { if (options.dev) {
if (options.filename) { if (options.filename) {
let filename = options.filename; let filename = options.filename;

@ -600,20 +600,6 @@ export function throw_error(str) {
}; };
} }
/**
* @return {import('estree').MemberExpression}
*/
export function import_meta_hot() {
return member(
{
type: 'MetaProperty',
meta: id('import'),
property: id('meta')
},
id('hot')
);
}
export { export {
await_builder as await, await_builder as await,
let_builder as let, let_builder as let,

@ -1,55 +1,38 @@
import { block, branch, destroy_effect } from '../reactivity/effects.js'; import { block, branch, destroy_effect } from '../reactivity/effects.js';
import { set, 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 {{ components: Map<string, { source: import("#client").Source<Component>; wrapper: null | Component; }> }} hot_data * @param {import("#client").Source<Component>} source
* @param {string} key
* @param {Component} component
*/ */
export function hmr(hot_data, component, key) { export function hmr(source) {
var components = (hot_data.components ??= new Map()); /**
var data = components.get(key); * @param {Comment} anchor
* @param {any} props
if (data === undefined) { */
components.set( return (anchor, props) => {
key, let instance = {};
(data = {
source: source(component), /** @type {import("#client").Effect} */
wrapper: null let effect;
})
); block(() => {
} else { const component = get(source);
set(data.source, component);
} if (effect) {
const component_source = data.source; // @ts-ignore
for (var k in instance) delete instance[k];
return (data.wrapper ??= /** @type {Component} */ ( destroy_effect(effect);
(anchor, props) => { }
let instance = {};
effect = branch(() => {
/** @type {import("#client").Effect} */ set_should_intro(false);
let effect; Object.assign(instance, component(anchor, props));
set_should_intro(true);
block(() => {
const component = get(component_source);
if (effect) {
// @ts-ignore
for (var k in instance) delete instance[k];
destroy_effect(effect);
}
effect = branch(() => {
set_should_intro(false);
Object.assign(instance, component(anchor, props));
set_should_intro(true);
});
}); });
});
return instance; return instance;
} };
));
} }

@ -0,0 +1,7 @@
import { test } from '../../test';
export default test({
compileOptions: {
hmr: true
}
});

@ -0,0 +1,28 @@
// index.svelte (Svelte VERSION)
// Note: compiler output will change before 5.0 is released!
import "svelte/internal/disclose-version";
import * as $ from "svelte/internal/client";
var root = $.template(`<h1>hello world</h1>`);
function Hmr($$anchor, $$props) {
$.push($$props, false);
$.init();
var h1 = root();
$.append($$anchor, h1);
$.pop();
}
if (import.meta.hot) {
const s = $.source(Hmr);
Hmr = $.hmr(s);
import.meta.hot.accept((module) => {
$.set(s, module.default);
});
}
export default Hmr;

@ -0,0 +1,9 @@
// index.svelte (Svelte VERSION)
// Note: compiler output will change before 5.0 is released!
import * as $ from "svelte/internal/server";
export default function Hmr($$payload, $$props) {
$.push(false);
$$payload.out += `<h1>hello world</h1>`;
$.pop();
}
Loading…
Cancel
Save