Improved HMR (#3138)
* WIP: improved HMR * fix(hmr): improve hmr filtering to avoid full reloads * chore: add changeset
This commit is contained in:
parent
e621c2f7d3
commit
37a7a8347c
4 changed files with 51 additions and 16 deletions
5
.changeset/cyan-dots-admire.md
Normal file
5
.changeset/cyan-dots-admire.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
General HMR Improvements, including new HMR support for framework components that are only server-side rendered (do not have a `client:*` directive)
|
|
@ -1,11 +1,8 @@
|
|||
if (import.meta.hot) {
|
||||
// signal to Vite that we accept HMR
|
||||
import.meta.hot.accept((mod) => mod);
|
||||
import.meta.hot.accept(mod => mod);
|
||||
const parser = new DOMParser();
|
||||
import.meta.hot.on('astro:update', async ({ file }) => {
|
||||
async function updatePage() {
|
||||
const { default: diff } = await import('micromorph');
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`[vite] hot updated: ${file}`);
|
||||
const html = await fetch(`${window.location}`).then((res) => res.text());
|
||||
const doc = parser.parseFromString(html, 'text/html');
|
||||
|
||||
|
@ -17,6 +14,27 @@ if (import.meta.hot) {
|
|||
root.innerHTML = current?.innerHTML;
|
||||
}
|
||||
}
|
||||
diff(document, doc);
|
||||
return diff(document, doc);
|
||||
}
|
||||
async function updateAll(files: any[]) {
|
||||
let hasAstroUpdate = false;
|
||||
for (const file of files) {
|
||||
if (file.acceptedPath.endsWith('.astro')) {
|
||||
hasAstroUpdate = true;
|
||||
continue;
|
||||
}
|
||||
if (file.acceptedPath.includes('vue&type=style')) {
|
||||
const link = document.querySelector(`link[href="${file.acceptedPath}"]`);
|
||||
if (link) {
|
||||
link.replaceWith(link.cloneNode(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasAstroUpdate) {
|
||||
return updatePage()
|
||||
}
|
||||
}
|
||||
import.meta.hot.on('vite:beforeUpdate', async (event) => {
|
||||
await updateAll(event.updates);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -82,18 +82,17 @@ export async function handleHotUpdate(ctx: HmrContext, config: AstroConfig, logg
|
|||
invalidateCompilation(config, file);
|
||||
}
|
||||
|
||||
const mod = ctx.modules.find((m) => m.file === ctx.file);
|
||||
// Bugfix: sometimes style URLs get normalized and end with `lang.css=`
|
||||
// These will cause full reloads, so filter them out here
|
||||
const mods = ctx.modules.filter(m => !m.url.endsWith('='));
|
||||
const isSelfAccepting = mods.every(m => m.isSelfAccepting || m.url.endsWith('.svelte'));
|
||||
|
||||
// Note: this intentionally ONLY applies to Astro components
|
||||
// HMR is handled for other file types by their respective plugins
|
||||
const file = ctx.file.replace(config.root.pathname, '/');
|
||||
if (ctx.file.endsWith('.astro')) {
|
||||
ctx.server.ws.send({ type: 'custom', event: 'astro:update', data: { file } });
|
||||
}
|
||||
if (mod?.isSelfAccepting) {
|
||||
if (isSelfAccepting) {
|
||||
info(logging, 'astro', msg.hmr({ file }));
|
||||
} else {
|
||||
info(logging, 'astro', msg.reload({ file }));
|
||||
}
|
||||
return Array.from(filtered);
|
||||
|
||||
return mods
|
||||
}
|
||||
|
|
|
@ -174,7 +174,20 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu
|
|||
let SUFFIX = '';
|
||||
// Add HMR handling in dev mode.
|
||||
if (!resolvedConfig.isProduction) {
|
||||
SUFFIX += `\nif (import.meta.hot) import.meta.hot.accept((mod) => mod);`;
|
||||
// HACK: extract dependencies from metadata until compiler static extraction handles them
|
||||
const metadata = transformResult.code.split('$$createMetadata(')[1].split('});\n')[0]
|
||||
const pattern = /specifier:\s*'([^']*)'/g;
|
||||
const deps = new Set();
|
||||
let match;
|
||||
while (match = pattern.exec(metadata)?.[1]) {
|
||||
deps.add(match);
|
||||
}
|
||||
// // import.meta.hot.accept(["${id}", "${Array.from(deps).join('","')}"], (...mods) => mods);
|
||||
// We need to be self-accepting, AND
|
||||
// we need an explicity array of deps to track changes for SSR-only components
|
||||
SUFFIX += `\nif (import.meta.hot) {
|
||||
import.meta.hot.accept(mod => mod);
|
||||
}`;
|
||||
}
|
||||
// Add handling to inject scripts into each page JS bundle, if needed.
|
||||
if (isPage) {
|
||||
|
|
Loading…
Reference in a new issue