summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/rotten-cups-happen.md5
-rw-r--r--packages/astro/e2e/css-sourcemaps.test.js38
-rw-r--r--packages/astro/e2e/css.test.js18
-rw-r--r--packages/astro/e2e/fixtures/css-sourcemaps/astro.config.mjs7
-rw-r--r--packages/astro/e2e/fixtures/css-sourcemaps/package.json8
-rw-r--r--packages/astro/e2e/fixtures/css-sourcemaps/src/env.d.ts1
-rw-r--r--packages/astro/e2e/fixtures/css-sourcemaps/src/pages/index.astro9
-rw-r--r--packages/astro/e2e/fixtures/css-sourcemaps/src/styles/main.css3
-rw-r--r--packages/astro/src/core/render/dev/index.ts8
-rw-r--r--packages/astro/src/runtime/client/hmr.ts12
-rw-r--r--pnpm-lock.yaml6
11 files changed, 107 insertions, 8 deletions
diff --git a/.changeset/rotten-cups-happen.md b/.changeset/rotten-cups-happen.md
new file mode 100644
index 000000000..e484c098f
--- /dev/null
+++ b/.changeset/rotten-cups-happen.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Fix duplicate CSS in dev mode when `vite.css.devSourcemap` is provided
diff --git a/packages/astro/e2e/css-sourcemaps.test.js b/packages/astro/e2e/css-sourcemaps.test.js
new file mode 100644
index 000000000..07cea4cb0
--- /dev/null
+++ b/packages/astro/e2e/css-sourcemaps.test.js
@@ -0,0 +1,38 @@
+import { expect } from '@playwright/test';
+import { getColor, isWindows, testFactory } from './test-utils.js';
+
+const test = testFactory({
+ root: './fixtures/css/',
+});
+
+let devServer;
+
+test.beforeAll(async ({ astro }) => {
+ devServer = await astro.startDevServer();
+});
+
+test.afterAll(async () => {
+ await devServer.stop();
+});
+
+test.describe('CSS Sourcemap HMR', () => {
+ test.skip(isWindows, 'TODO: fix css hmr in windows');
+
+ test('removes Astro-injected CSS once Vite-injected CSS loads', async ({ page, astro }) => {
+ const html = await astro.fetch('/').then(res => res.text());
+
+ // style[data-astro-dev-id] should exist in initial SSR'd markup
+ expect(html).toMatch('data-astro-dev-id');
+
+ await page.goto(astro.resolveUrl('/'));
+
+ // Ensure JS has initialized
+ await page.waitForTimeout(500);
+
+ // style[data-astro-dev-id] should NOT exist once JS runs
+ expect(await page.locator('style[data-astro-dev-id]').count()).toEqual(0);
+
+ // style[data-vite-dev-id] should exist now
+ expect(await page.locator('style[data-vite-dev-id]').count()).toBeGreaterThan(0);
+ });
+});
diff --git a/packages/astro/e2e/css.test.js b/packages/astro/e2e/css.test.js
index b817c419a..745a540ee 100644
--- a/packages/astro/e2e/css.test.js
+++ b/packages/astro/e2e/css.test.js
@@ -30,4 +30,22 @@ test.describe('CSS HMR', () => {
expect(await getColor(h)).toBe('rgb(0, 128, 0)');
});
+
+ test('removes Astro-injected CSS once Vite-injected CSS loads', async ({ page, astro }) => {
+ const html = await astro.fetch('/').then(res => res.text());
+
+ // style[data-astro-dev-id] should exist in initial SSR'd markup
+ expect(html).toMatch('data-astro-dev-id');
+
+ await page.goto(astro.resolveUrl('/'));
+
+ // Ensure JS has initialized
+ await page.waitForTimeout(500);
+
+ // style[data-astro-dev-id] should NOT exist once JS runs
+ expect(await page.locator('style[data-astro-dev-id]').count()).toEqual(0);
+
+ // style[data-vite-dev-id] should exist now
+ expect(await page.locator('style[data-vite-dev-id]').count()).toBeGreaterThan(0);
+ });
});
diff --git a/packages/astro/e2e/fixtures/css-sourcemaps/astro.config.mjs b/packages/astro/e2e/fixtures/css-sourcemaps/astro.config.mjs
new file mode 100644
index 000000000..7e8fac1e7
--- /dev/null
+++ b/packages/astro/e2e/fixtures/css-sourcemaps/astro.config.mjs
@@ -0,0 +1,7 @@
+export default {
+ vite: {
+ css: {
+ devSourcemap: true,
+ }
+ }
+};
diff --git a/packages/astro/e2e/fixtures/css-sourcemaps/package.json b/packages/astro/e2e/fixtures/css-sourcemaps/package.json
new file mode 100644
index 000000000..1fa4c2c79
--- /dev/null
+++ b/packages/astro/e2e/fixtures/css-sourcemaps/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "@e2e/css-sourcemaps",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "astro": "workspace:*"
+ }
+}
diff --git a/packages/astro/e2e/fixtures/css-sourcemaps/src/env.d.ts b/packages/astro/e2e/fixtures/css-sourcemaps/src/env.d.ts
new file mode 100644
index 000000000..8c34fb45e
--- /dev/null
+++ b/packages/astro/e2e/fixtures/css-sourcemaps/src/env.d.ts
@@ -0,0 +1 @@
+/// <reference types="astro/client" /> \ No newline at end of file
diff --git a/packages/astro/e2e/fixtures/css-sourcemaps/src/pages/index.astro b/packages/astro/e2e/fixtures/css-sourcemaps/src/pages/index.astro
new file mode 100644
index 000000000..7275177f9
--- /dev/null
+++ b/packages/astro/e2e/fixtures/css-sourcemaps/src/pages/index.astro
@@ -0,0 +1,9 @@
+<h1>hello world</h1>
+
+<style>
+ @import "../styles/main.css";
+
+ h1 {
+ color: var(--h1-color);
+ }
+</style>
diff --git a/packages/astro/e2e/fixtures/css-sourcemaps/src/styles/main.css b/packages/astro/e2e/fixtures/css-sourcemaps/src/styles/main.css
new file mode 100644
index 000000000..c80a6cde1
--- /dev/null
+++ b/packages/astro/e2e/fixtures/css-sourcemaps/src/styles/main.css
@@ -0,0 +1,3 @@
+:root {
+ --h1-color: red;
+}
diff --git a/packages/astro/src/core/render/dev/index.ts b/packages/astro/src/core/render/dev/index.ts
index 7b5df9482..7ea008d69 100644
--- a/packages/astro/src/core/render/dev/index.ts
+++ b/packages/astro/src/core/render/dev/index.ts
@@ -10,7 +10,7 @@ import { PAGE_SCRIPT_ID } from '../../../vite-plugin-scripts/index.js';
import { enhanceViteSSRError } from '../../errors/dev/index.js';
import { AggregateError, CSSError, MarkdownError } from '../../errors/index.js';
import type { ModuleLoader } from '../../module-loader/index';
-import { isPage, resolveIdToUrl } from '../../util.js';
+import { isPage, resolveIdToUrl, viteID } from '../../util.js';
import { createRenderContext, renderPage as coreRenderPage } from '../index.js';
import { filterFoundRenderers, loadRenderer } from '../renderer.js';
import { getStylesForURL } from './css.js';
@@ -133,7 +133,11 @@ async function getScriptsAndStyles({ env, filePath }: GetScriptsAndStylesParams)
});
// But we still want to inject the styles to avoid FOUC
styles.add({
- props: {},
+ props: {
+ type: 'text/css',
+ // Track the ID so we can match it to Vite's injected style later
+ 'data-astro-dev-id': viteID(new URL(`.${url}`, env.settings.config.root))
+ },
children: content,
});
});
diff --git a/packages/astro/src/runtime/client/hmr.ts b/packages/astro/src/runtime/client/hmr.ts
index f3a3074f3..b71c39990 100644
--- a/packages/astro/src/runtime/client/hmr.ts
+++ b/packages/astro/src/runtime/client/hmr.ts
@@ -1,15 +1,15 @@
/// <reference types="vite/client" />
if (import.meta.hot) {
- // Vite injects `<style type="text/css">` for ESM imports of styles
- // but Astro also SSRs with `<style>` blocks. This MutationObserver
+ // Vite injects `<style type="text/css" data-vite-dev-id>` for ESM imports of styles
+ // but Astro also SSRs with `<style type="text/css" data-astro-dev-id>` blocks. This MutationObserver
// removes any duplicates as soon as they are hydrated client-side.
const injectedStyles = getInjectedStyles();
const mo = new MutationObserver((records) => {
for (const record of records) {
for (const node of record.addedNodes) {
if (isViteInjectedStyle(node)) {
- injectedStyles.get(node.innerHTML.trim())?.remove();
+ injectedStyles.get(node.getAttribute('data-vite-dev-id')!)?.remove();
}
}
}
@@ -31,8 +31,8 @@ if (import.meta.hot) {
function getInjectedStyles() {
const injectedStyles = new Map<string, Element>();
- document.querySelectorAll<HTMLStyleElement>('style').forEach((el) => {
- injectedStyles.set(el.innerHTML.trim(), el);
+ document.querySelectorAll<HTMLStyleElement>('style[data-astro-dev-id]').forEach((el) => {
+ injectedStyles.set(el.getAttribute('data-astro-dev-id')!, el);
});
return injectedStyles;
}
@@ -42,5 +42,5 @@ function isStyle(node: Node): node is HTMLStyleElement {
}
function isViteInjectedStyle(node: Node): node is HTMLStyleElement {
- return isStyle(node) && node.getAttribute('type') === 'text/css';
+ return isStyle(node) && node.getAttribute('type') === 'text/css' && !!node.getAttribute('data-vite-dev-id');
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 35de7b913..c59bac079 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -657,6 +657,12 @@ importers:
dependencies:
astro: link:../../..
+ packages/astro/e2e/fixtures/css-sourcemaps:
+ specifiers:
+ astro: workspace:*
+ dependencies:
+ astro: link:../../..
+
packages/astro/e2e/fixtures/error-cyclic:
specifiers:
'@astrojs/preact': workspace:*