Skip to content

Commit

Permalink
Fix Astro HMR style-only change detection (#10139)
Browse files Browse the repository at this point in the history
  • Loading branch information
bluwy committed Feb 19, 2024
1 parent 43f8746 commit 3c73441
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/twenty-monkeys-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"astro": patch
---

Fixes style-only change detection for Astro files if both the markup and styles are updated
13 changes: 9 additions & 4 deletions packages/astro/src/vite-plugin-astro/hmr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
}
Expand Down
63 changes: 63 additions & 0 deletions packages/astro/test/units/vite-plugin-astro/hmr.test.js
Original file line number Diff line number Diff line change
@@ -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);
});
});

0 comments on commit 3c73441

Please sign in to comment.