Fix Astro HMR from a CSS dependency (#8609)

* Fix Astro HMR from a CSS dependency

* Improve css test

* Create wise-donuts-tickle.md
This commit is contained in:
Bjorn Lu 2023-09-22 00:53:37 +08:00 committed by GitHub
parent e8c997db99
commit 5a988eaf60
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 54 additions and 35 deletions

View file

@ -0,0 +1,5 @@
---
"astro": patch
---
Fix Astro HMR from a CSS dependency

View file

@ -1,5 +1,5 @@
import { expect } from '@playwright/test'; import { expect } from '@playwright/test';
import { getColor, testFactory } from './test-utils.js'; import { testFactory } from './test-utils.js';
const test = testFactory({ root: './fixtures/astro-component/' }); const test = testFactory({ root: './fixtures/astro-component/' });
@ -99,7 +99,7 @@ test.describe('Astro component HMR', () => {
test('update linked dep Astro style', async ({ page, astro }) => { test('update linked dep Astro style', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/')); await page.goto(astro.resolveUrl('/'));
let h1 = page.locator('#astro-linked-lib'); let h1 = page.locator('#astro-linked-lib');
expect(await getColor(h1)).toBe('rgb(255, 0, 0)'); await expect(h1).toHaveCSS('color', 'rgb(255, 0, 0)');
await Promise.all([ await Promise.all([
page.waitForLoadState('networkidle'), page.waitForLoadState('networkidle'),
await astro.editFile('../_deps/astro-linked-lib/Component.astro', (content) => await astro.editFile('../_deps/astro-linked-lib/Component.astro', (content) =>
@ -107,6 +107,6 @@ test.describe('Astro component HMR', () => {
), ),
]); ]);
h1 = page.locator('#astro-linked-lib'); h1 = page.locator('#astro-linked-lib');
expect(await getColor(h1)).toBe('rgb(0, 128, 0)'); await expect(h1).toHaveCSS('color', 'rgb(0, 128, 0)');
}); });
}); });

View file

@ -1,5 +1,5 @@
import { expect } from '@playwright/test'; import { expect } from '@playwright/test';
import { getColor, testFactory } from './test-utils.js'; import { testFactory } from './test-utils.js';
const test = testFactory({ const test = testFactory({
root: './fixtures/css/', root: './fixtures/css/',
@ -20,13 +20,13 @@ test.describe('CSS HMR', () => {
await page.goto(astro.resolveUrl('/')); await page.goto(astro.resolveUrl('/'));
const h = page.locator('h1'); const h = page.locator('h1');
expect(await getColor(h)).toBe('rgb(255, 0, 0)'); await expect(h).toHaveCSS('color', 'rgb(255, 0, 0)');
await astro.editFile('./src/styles/main.css', (original) => await astro.editFile('./src/styles/main.css', (original) =>
original.replace('--h1-color: red;', '--h1-color: green;') original.replace('--h1-color: red;', '--h1-color: green;')
); );
expect(await getColor(h)).toBe('rgb(0, 128, 0)'); await expect(h).toHaveCSS('color', 'rgb(0, 128, 0)');
}); });
test('removes Astro-injected CSS once Vite-injected CSS loads', async ({ page, astro }) => { test('removes Astro-injected CSS once Vite-injected CSS loads', async ({ page, astro }) => {

View file

@ -1,8 +1,9 @@
{ {
"name": "@e2e/invalidate-script-deps", "name": "@e2e/hmr",
"version": "0.0.0", "version": "0.0.0",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"astro": "workspace:*" "astro": "workspace:*",
"sass": "^1.66.1"
} }
} }

View file

@ -0,0 +1,14 @@
<html>
<head>
<title>Test</title>
</head>
<body>
<h1 class="css-dep">This is blue</h1>
<style lang="scss">
@use "../styles/vars.scss" as *;
.css-dep {
color: $color;
}
</style>
</body>
</html>

View file

@ -0,0 +1 @@
$color: blue;

View file

@ -2,7 +2,7 @@ import { expect } from '@playwright/test';
import { testFactory } from './test-utils.js'; import { testFactory } from './test-utils.js';
const test = testFactory({ const test = testFactory({
root: './fixtures/invalidate-script-deps/', root: './fixtures/hmr/',
}); });
let devServer; let devServer;
@ -17,7 +17,7 @@ test.afterAll(async () => {
test.describe('Scripts with dependencies', () => { test.describe('Scripts with dependencies', () => {
test('refresh with HMR', async ({ page, astro }) => { test('refresh with HMR', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/')); await page.goto(astro.resolveUrl('/script-dep'));
const h = page.locator('h1'); const h = page.locator('h1');
await expect(h, 'original text set').toHaveText('before'); await expect(h, 'original text set').toHaveText('before');
@ -29,3 +29,16 @@ test.describe('Scripts with dependencies', () => {
await expect(h, 'text changed').toHaveText('after'); await expect(h, 'text changed').toHaveText('after');
}); });
}); });
test.describe('Styles with dependencies', () => {
test('refresh with HMR', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/css-dep'));
const h = page.locator('h1');
await expect(h).toHaveCSS('color', 'rgb(0, 0, 255)');
await astro.editFile('./src/styles/vars.scss', (original) => original.replace('blue', 'red'));
await expect(h).toHaveCSS('color', 'rgb(255, 0, 0)');
});
});

View file

@ -71,13 +71,6 @@ export async function getErrorOverlayContent(page) {
return { message, hint, absoluteFileLocation, fileLocation }; return { message, hint, absoluteFileLocation, fileLocation };
} }
/**
* @returns {Promise<string>}
*/
export async function getColor(el) {
return await el.evaluate((e) => getComputedStyle(e).color);
}
/** /**
* Wait for `astro-island` that contains the `el` to hydrate * Wait for `astro-island` that contains the `el` to hydrate
* @param {import('@playwright/test').Page} page * @param {import('@playwright/test').Page} page

View file

@ -90,7 +90,7 @@ export async function handleHotUpdate(
// Bugfix: sometimes style URLs get normalized and end with `lang.css=` // Bugfix: sometimes style URLs get normalized and end with `lang.css=`
// These will cause full reloads, so filter them out here // These will cause full reloads, so filter them out here
const mods = ctx.modules.filter((m) => !m.url.endsWith('=')); const mods = [...filtered].filter((m) => !m.url.endsWith('='));
const file = ctx.file.replace(config.root.pathname, '/'); const file = ctx.file.replace(config.root.pathname, '/');
// If only styles are changed, remove the component file from the update list // If only styles are changed, remove the component file from the update list
@ -109,17 +109,6 @@ export async function handleHotUpdate(
} }
} }
// If this is a module that is imported from a <script>, invalidate the Astro
// component so that it is cached by the time the script gets transformed.
for (const mod of filtered) {
if (mod.id && isAstroScript(mod.id) && mod.file) {
const astroMod = ctx.server.moduleGraph.getModuleById(mod.file);
if (astroMod) {
mods.unshift(astroMod);
}
}
}
// TODO: Svelte files should be marked as `isSelfAccepting` but they don't appear to be // TODO: Svelte files should be marked as `isSelfAccepting` but they don't appear to be
const isSelfAccepting = mods.every((m) => m.isSelfAccepting || m.url.endsWith('.svelte')); const isSelfAccepting = mods.every((m) => m.isSelfAccepting || m.url.endsWith('.svelte'));
if (isSelfAccepting) { if (isSelfAccepting) {

View file

@ -992,6 +992,15 @@ importers:
specifier: ^3.3.4 specifier: ^3.3.4
version: 3.3.4 version: 3.3.4
packages/astro/e2e/fixtures/hmr:
devDependencies:
astro:
specifier: workspace:*
version: link:../../..
sass:
specifier: ^1.66.1
version: 1.66.1
packages/astro/e2e/fixtures/hydration-race: packages/astro/e2e/fixtures/hydration-race:
dependencies: dependencies:
'@astrojs/preact': '@astrojs/preact':
@ -1004,12 +1013,6 @@ importers:
specifier: ^10.17.1 specifier: ^10.17.1
version: 10.17.1 version: 10.17.1
packages/astro/e2e/fixtures/invalidate-script-deps:
devDependencies:
astro:
specifier: workspace:*
version: link:../../..
packages/astro/e2e/fixtures/lit-component: packages/astro/e2e/fixtures/lit-component:
dependencies: dependencies:
'@astrojs/lit': '@astrojs/lit':