Skip to content

Commit

Permalink
Improved HMR (#3138)
Browse files Browse the repository at this point in the history
* WIP: improved HMR

* fix(hmr): improve hmr filtering to avoid full reloads

* chore: add changeset
  • Loading branch information
natemoo-re authored Apr 20, 2022
1 parent e621c2f commit 37a7a83
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 16 deletions.
5 changes: 5 additions & 0 deletions .changeset/cyan-dots-admire.md
Original file line number Diff line number Diff line change
@@ -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)
30 changes: 24 additions & 6 deletions packages/astro/src/runtime/client/hmr.ts
Original file line number Diff line number Diff line change
@@ -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');

Expand All @@ -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);
});
}
15 changes: 7 additions & 8 deletions packages/astro/src/vite-plugin-astro/hmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
17 changes: 15 additions & 2 deletions packages/astro/src/vite-plugin-astro/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -242,7 +255,7 @@ export default function astro({ config, logging }: AstroPluginOptions): vite.Plu
}
}

throw err;
throw err;
}
},
async handleHotUpdate(context) {
Expand Down

0 comments on commit 37a7a83

Please sign in to comment.