diff --git a/.changeset/popular-taxis-prove.md b/.changeset/popular-taxis-prove.md new file mode 100644 index 000000000000..101dfd7ddaa7 --- /dev/null +++ b/.changeset/popular-taxis-prove.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Overhaul HMR handling for more stable live reload behavior diff --git a/package.json b/package.json index 0595ac436c80..55263594adc4 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "test": "turbo run test --output-logs=new-only --concurrency=1", "test:match": "cd packages/astro && pnpm run test:match", "test:templates": "turbo run test --filter=create-astro --concurrency=1", - "test:smoke": "turbo run build --filter=\"@example/*\" --filter=\"astro.build\" --filter=\"docs\" --output-logs=new-only", + "test:smoke": "turbo run build --filter=\"@example/*\" --filter=\"astro.build\" --filter=\"docs\" --output-logs=new-only --concurrency=1", "test:vite-ci": "turbo run test --output-logs=new-only --no-deps --scope=astro --concurrency=1", "test:e2e": "cd packages/astro && pnpm playwright install && pnpm run test:e2e", "test:e2e:match": "cd packages/astro && pnpm playwright install && pnpm run test:e2e:match", diff --git a/packages/astro/src/core/render/dev/hmr.ts b/packages/astro/src/core/render/dev/hmr.ts deleted file mode 100644 index 3c795fdb139a..000000000000 --- a/packages/astro/src/core/render/dev/hmr.ts +++ /dev/null @@ -1,11 +0,0 @@ -import fs from 'fs'; -import { fileURLToPath } from 'url'; - -let hmrScript: string; -export async function getHmrScript() { - if (hmrScript) return hmrScript; - const filePath = fileURLToPath(new URL('../../../runtime/client/hmr.js', import.meta.url)); - const content = await fs.promises.readFile(filePath); - hmrScript = content.toString(); - return hmrScript; -} diff --git a/packages/astro/src/core/render/dev/index.ts b/packages/astro/src/core/render/dev/index.ts index e3b6f0ac7c96..428c30edf10c 100644 --- a/packages/astro/src/core/render/dev/index.ts +++ b/packages/astro/src/core/render/dev/index.ts @@ -150,11 +150,19 @@ export async function render( let styles = new Set(); [...stylesMap].forEach(([url, content]) => { - // The URL is only used by HMR for Svelte components - // See src/runtime/client/hmr.ts for more details + // Vite handles HMR for styles injected as scripts + scripts.add({ + props: { + type: 'module', + src: url, + 'data-astro-injected': true, + }, + children: '', + }); + // But we still want to inject the styles to avoid FOUC styles.add({ props: { - 'data-astro-injected': svelteStylesRE.test(url) ? url : true, + 'data-astro-injected': url, }, children: content, }); diff --git a/packages/astro/src/runtime/client/hmr.ts b/packages/astro/src/runtime/client/hmr.ts index 98cf839afcba..98153f4b262e 100644 --- a/packages/astro/src/runtime/client/hmr.ts +++ b/packages/astro/src/runtime/client/hmr.ts @@ -1,59 +1,8 @@ /// if (import.meta.hot) { - import.meta.hot.accept((mod) => mod); - - const parser = new DOMParser(); - - const KNOWN_MANUAL_HMR_EXTENSIONS = new Set(['.astro', '.md', '.mdx']); - function needsManualHMR(path: string) { - for (const ext of KNOWN_MANUAL_HMR_EXTENSIONS.values()) { - if (path.endsWith(ext)) return true; - } - return false; - } - - async function updatePage() { - const { default: diff } = await import('micromorph'); - const html = await fetch(`${window.location}`).then((res) => res.text()); - const doc = parser.parseFromString(html, 'text/html'); - for (const style of sheetsMap.values()) { - doc.head.appendChild(style); - } - // Match incoming islands to current state - for (const root of doc.querySelectorAll('astro-island')) { - const uid = root.getAttribute('uid'); - const current = document.querySelector(`astro-island[uid="${uid}"]`); - if (current) { - current.setAttribute('data-persist', ''); - root.replaceWith(current); - } - } - // both Vite and Astro's HMR scripts include `type="text/css"` on injected - // - - -

Testing

- - diff --git a/packages/astro/test/hmr-css.test.js b/packages/astro/test/hmr-css.test.js deleted file mode 100644 index b2b4341b4d00..000000000000 --- a/packages/astro/test/hmr-css.test.js +++ /dev/null @@ -1,34 +0,0 @@ -import { isWindows, loadFixture } from './test-utils.js'; -import { expect } from 'chai'; -import * as cheerio from 'cheerio'; - -describe('HMR - CSS', () => { - if (isWindows) return; - - /** @type {import('./test-utils').Fixture} */ - let fixture; - /** @type {import('./test-utils').DevServer} */ - let devServer; - - before(async () => { - fixture = await loadFixture({ - root: './fixtures/hmr-css/', - }); - devServer = await fixture.startDevServer(); - }); - - after(async () => { - await devServer.stop(); - }); - - it('Timestamp URL used by Vite gets the right mime type', async () => { - // Index page is always loaded first by the browser - await fixture.fetch('/'); - // Now we can simulate what happens in the browser - let res = await fixture.fetch( - '/src/pages/index.astro?astro=&type=style&index=0&lang.css=&t=1653657441095' - ); - let headers = res.headers; - expect(headers.get('content-type')).to.equal('text/css'); - }); -}); diff --git a/packages/astro/test/postcss.test.js b/packages/astro/test/postcss.test.js index 1cf06bee1029..28de600da4a5 100644 --- a/packages/astro/test/postcss.test.js +++ b/packages/astro/test/postcss.test.js @@ -3,12 +3,13 @@ import * as cheerio from 'cheerio'; import eol from 'eol'; import { loadFixture } from './test-utils.js'; -describe('PostCSS', () => { +describe('PostCSS', function () { const PREFIXED_CSS = `{-webkit-appearance:none;appearance:none`; let fixture; let bundledCSS; before(async () => { + this.timeout(45000); // test needs a little more time in CI fixture = await loadFixture({ root: './fixtures/postcss', });