0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-16 21:46:22 -05:00

Fix Astro HMR style-only change detection (#10139)

This commit is contained in:
Bjorn Lu 2024-02-19 21:56:13 +08:00 committed by GitHub
parent 43f87467c6
commit 3c73441eb2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 77 additions and 4 deletions

View file

@ -0,0 +1,5 @@
---
"astro": patch
---
Fixes style-only change detection for Astro files if both the markup and styles are updated

View file

@ -65,7 +65,7 @@ const scriptRE = /<script(?:\s.*?)?>.*?<\/script>/gs;
// eslint-disable-next-line regexp/no-super-linear-backtracking
const styleRE = /<style(?:\s.*?)?>.*?<\/style>/gs;
function isStyleOnlyChanged(oldCode: string, newCode: string) {
export function isStyleOnlyChanged(oldCode: string, newCode: string) {
if (oldCode === newCode) return false;
// Before we can regex-capture style tags, we remove the frontmatter and scripts
@ -89,9 +89,14 @@ function isStyleOnlyChanged(oldCode: string, newCode: string) {
// Finally, we can compare styles
const oldStyles: string[] = [];
const newStyles: string[] = [];
oldCode.match(styleRE)?.forEach((m) => oldStyles.push(m));
newCode.match(styleRE)?.forEach((m) => newStyles.push(m));
// The length must also be the same for style only change. If style tags are added/removed,
oldCode = oldCode.replace(styleRE, (m) => (oldStyles.push(m), ''));
newCode = newCode.replace(styleRE, (m) => (newStyles.push(m), ''));
// Remaining of `oldCode` and `newCode` is the markup, return false if they're different
if (oldCode !== newCode) return false;
// Finally, check if only the style changed.
// The length must also be the same for style only change. If style tags are added/removed,
// we need to regenerate the main Astro file so that its CSS imports are also added/removed
return oldStyles.length === newStyles.length && !isArrayEqual(oldStyles, newStyles);
}

View file

@ -0,0 +1,63 @@
import { describe, it } from 'node:test';
import * as assert from 'node:assert/strict';
import { isStyleOnlyChanged } from '../../../dist/vite-plugin-astro/hmr.js';
describe('isStyleOnlyChanged', () => {
it('should return false if nothing change', () => {
const oldCode = 'a';
const newCode = 'a';
assert.equal(isStyleOnlyChanged(oldCode, newCode), false);
});
it('should return false if script has changed', () => {
const oldCode = '<script>console.log("Hello");</script><style>body { color: red; }</style>';
const newCode = '<script>console.log("Hi");</script><style>body { color: red; }</style>';
assert.equal(isStyleOnlyChanged(oldCode, newCode), false);
});
it('should return true if only style has changed', () => {
const oldCode = '<style>body { color: red; }</style>';
const newCode = '<style>body { color: blue; }</style>';
assert.equal(isStyleOnlyChanged(oldCode, newCode), true);
});
it('should return false if style tags are added or removed', () => {
const oldCode = '<style>body { color: red; }</style>';
const newCode = '<style>body { color: red; }</style><style>a { color: blue; }</style>';
assert.equal(isStyleOnlyChanged(oldCode, newCode), false);
});
it('should return false if frontmatter has changed', () => {
const oldCode = `
---
title: Hello
---
<style>body { color: red; }</style>`;
const newCode = `
---
title: Hi
---
<style>body { color: red; }</style>`;
assert.equal(isStyleOnlyChanged(oldCode, newCode), false);
});
it('should return false if both frontmatter and style have changed', () => {
const oldCode = `
---
title: Hello
---
<style>body { color: red; }</style>`;
const newCode = `
---
title: Hi
---
<style>body { color: blue; }</style>`;
assert.equal(isStyleOnlyChanged(oldCode, newCode), false);
});
it('should return false if both markup and style have changed', () => {
const oldCode = '<h1>Hello</h1><style>body { color: red; }</style>';
const newCode = '<h1>Hi</h1><style>body { color: blue; }</style>';
assert.equal(isStyleOnlyChanged(oldCode, newCode), false);
});
});