diff --git a/.changeset/tender-islands-watch.md b/.changeset/tender-islands-watch.md new file mode 100644 index 000000000000..3cf5e5642400 --- /dev/null +++ b/.changeset/tender-islands-watch.md @@ -0,0 +1,5 @@ +--- +'@astrojs/prefetch': minor +--- + +Prefetch CSS files once diff --git a/packages/integrations/prefetch/src/client.ts b/packages/integrations/prefetch/src/client.ts index 0b37946eeca8..f79f42f1266e 100644 --- a/packages/integrations/prefetch/src/client.ts +++ b/packages/integrations/prefetch/src/client.ts @@ -5,6 +5,7 @@ import requestIdleCallback from './requestIdleCallback.js'; const events = ['mouseenter', 'touchstart', 'focus']; const preloaded = new Set(); +const loadedStyles = new Set(); function shouldPreload({ href }: { href: string }) { try { @@ -53,7 +54,14 @@ async function preloadHref(link: HTMLAnchorElement) { const html = parser.parseFromString(contents, 'text/html'); const styles = Array.from(html.querySelectorAll('link[rel="stylesheet"]')); - await Promise.all(styles.map((el) => fetch(el.href))); + await Promise.all( + styles + .filter((el) => !loadedStyles.has(el.href)) + .map((el) => { + loadedStyles.add(el.href); + return fetch(el.href); + }) + ); } catch {} } diff --git a/packages/integrations/prefetch/test/fixtures/style-prefetch/astro.config.mjs b/packages/integrations/prefetch/test/fixtures/style-prefetch/astro.config.mjs new file mode 100644 index 000000000000..092813b22f73 --- /dev/null +++ b/packages/integrations/prefetch/test/fixtures/style-prefetch/astro.config.mjs @@ -0,0 +1,7 @@ +import { defineConfig } from 'astro/config'; +import prefetch from '@astrojs/prefetch'; + +// https://astro.build/config +export default defineConfig({ + integrations: [prefetch()], +}); diff --git a/packages/integrations/prefetch/test/fixtures/style-prefetch/package.json b/packages/integrations/prefetch/test/fixtures/style-prefetch/package.json new file mode 100644 index 000000000000..cf10d7471202 --- /dev/null +++ b/packages/integrations/prefetch/test/fixtures/style-prefetch/package.json @@ -0,0 +1,9 @@ +{ + "name": "@test/astro-prefetch", + "version": "0.0.0", + "private": true, + "dependencies": { + "@astrojs/prefetch": "workspace:*", + "astro": "workspace:*" + } +} diff --git a/packages/integrations/prefetch/test/fixtures/style-prefetch/src/pages/index.astro b/packages/integrations/prefetch/test/fixtures/style-prefetch/src/pages/index.astro new file mode 100644 index 000000000000..9b8b84eec184 --- /dev/null +++ b/packages/integrations/prefetch/test/fixtures/style-prefetch/src/pages/index.astro @@ -0,0 +1,18 @@ +--- +--- + + + + + + + Home + + +

Home

+
    +
  • 1
  • +
  • 2
  • +
+ + \ No newline at end of file diff --git a/packages/integrations/prefetch/test/fixtures/style-prefetch/src/pages/style1.astro b/packages/integrations/prefetch/test/fixtures/style-prefetch/src/pages/style1.astro new file mode 100644 index 000000000000..def02918c9ca --- /dev/null +++ b/packages/integrations/prefetch/test/fixtures/style-prefetch/src/pages/style1.astro @@ -0,0 +1,15 @@ +--- +--- + + + + + + + Style1 + + + +

Style1

+ + \ No newline at end of file diff --git a/packages/integrations/prefetch/test/fixtures/style-prefetch/src/pages/style2.astro b/packages/integrations/prefetch/test/fixtures/style-prefetch/src/pages/style2.astro new file mode 100644 index 000000000000..2e3b6949c771 --- /dev/null +++ b/packages/integrations/prefetch/test/fixtures/style-prefetch/src/pages/style2.astro @@ -0,0 +1,15 @@ +--- +--- + + + + + + + Style2 + + + +

Style2

+ + \ No newline at end of file diff --git a/packages/integrations/prefetch/test/style-prefetch.test.js b/packages/integrations/prefetch/test/style-prefetch.test.js new file mode 100644 index 000000000000..4e56e01eead0 --- /dev/null +++ b/packages/integrations/prefetch/test/style-prefetch.test.js @@ -0,0 +1,55 @@ +import { expect } from '@playwright/test'; +import { testFactory } from './test-utils.js'; + +const test = testFactory({ root: './fixtures/style-prefetch/' }); + +test.describe('Style prefetch', () => { + test.describe('dev', () => { + let devServer; + + test.beforeEach(async ({ astro }) => { + devServer = await astro.startDevServer(); + }); + + test.afterEach(async () => { + await devServer.stop(); + }); + + testPrefetch(); + }); + + test.describe('build', () => { + let previewServer; + + test.beforeAll(async ({ astro }) => { + await astro.build(); + previewServer = await astro.preview(); + }); + + // important: close preview server (free up port and connection) + test.afterAll(async () => { + await previewServer.stop(); + }); + + testPrefetch(); + }); + + function testPrefetch() { + test.describe('prefetches rel="prefetch" links', () => { + test('style fetching', async ({ page, astro }) => { + const requests = []; + + page.on('request', async (request) => requests.push(request.url())); + + await page.goto(astro.resolveUrl('/')); + + await page.waitForLoadState('networkidle'); + + await expect(requests.filter(req => req.includes('/style1'))).toBeTruthy(); + await expect(requests.filter(req => req.includes('/style2'))).toBeTruthy(); + const cssRequestCount = requests.filter(req => req.includes('/main.css')).length; + await expect(cssRequestCount).toBe(1); + }); + }); + } +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f033679c3baa..8c41310a327c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3011,6 +3011,14 @@ importers: '@astrojs/prefetch': link:../../.. astro: link:../../../../../astro + packages/integrations/prefetch/test/fixtures/style-prefetch: + specifiers: + '@astrojs/prefetch': workspace:* + astro: workspace:* + dependencies: + '@astrojs/prefetch': link:../../.. + astro: link:../../../../../astro + packages/integrations/react: specifiers: '@babel/core': '>=7.0.0-0 <8.0.0'