diff --git a/.changeset/full-ties-add.md b/.changeset/full-ties-add.md new file mode 100644 index 000000000000..f261c0bf5fd4 --- /dev/null +++ b/.changeset/full-ties-add.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixed an issue where the API fetch in the frontmatter was not triggered when modifying external CMS content while the development server was running. diff --git a/packages/astro/src/vite-plugin-app/app.ts b/packages/astro/src/vite-plugin-app/app.ts index 0a401aff78e5..ff282dd200bb 100644 --- a/packages/astro/src/vite-plugin-app/app.ts +++ b/packages/astro/src/vite-plugin-app/app.ts @@ -182,6 +182,9 @@ export class AstroServerApp extends BaseApp { const self = this; await self.#loadFetchHandler(); + // Clear the route cache on every dev request so that getStaticPaths() + // re-runs when external data (e.g. a headless CMS) changes between requests. + self.pipeline.clearRouteCache(); let handled = true; await runWithErrorHandling({ diff --git a/packages/astro/test/dev-route-cache.test.ts b/packages/astro/test/dev-route-cache.test.ts new file mode 100644 index 000000000000..00df95c8a7ed --- /dev/null +++ b/packages/astro/test/dev-route-cache.test.ts @@ -0,0 +1,40 @@ +import * as assert from 'node:assert/strict'; +import { after, before, describe, it } from 'node:test'; +import * as cheerio from 'cheerio'; +import { type DevServer, type Fixture, loadFixture } from './test-utils.ts'; + +// getStaticPaths() results were cached indefinitely during `astro dev`, +// so external data changes (e.g. headless CMS) were never reflected without restarting the dev server. +describe('dev: route cache is cleared between requests', () => { + let fixture: Fixture; + let devServer: DevServer; + + before(async () => { + fixture = await loadFixture({ root: './fixtures/hmr-route-cache/' }); + devServer = await fixture.startDevServer(); + }); + + after(async () => { + await devServer?.stop(); + }); + + it('re-runs getStaticPaths() on each request', async () => { + const res1 = await fixture.fetch('/test'); + assert.equal(res1.status, 200); + const time1 = Number.parseInt( + cheerio + .load(await res1.text())('#time') + .text(), + ); + await new Promise((r) => setTimeout(r, 10)); + const res2 = await fixture.fetch('/test'); + assert.equal(res2.status, 200); + const time2 = Number.parseInt( + cheerio + .load(await res2.text())('#time') + .text(), + ); + + assert.notEqual(time1, time2); + }); +}); diff --git a/packages/astro/test/fixtures/hmr-route-cache/package.json b/packages/astro/test/fixtures/hmr-route-cache/package.json new file mode 100644 index 000000000000..05219118a03b --- /dev/null +++ b/packages/astro/test/fixtures/hmr-route-cache/package.json @@ -0,0 +1,8 @@ +{ + "name": "@test/hmr-route-cache", + "version": "0.0.0", + "private": true, + "dependencies": { + "astro": "workspace:*" + } +} diff --git a/packages/astro/test/fixtures/hmr-route-cache/src/pages/[id].astro b/packages/astro/test/fixtures/hmr-route-cache/src/pages/[id].astro new file mode 100644 index 000000000000..d6e261ed2752 --- /dev/null +++ b/packages/astro/test/fixtures/hmr-route-cache/src/pages/[id].astro @@ -0,0 +1,7 @@ +--- +export async function getStaticPaths() { + return [{ params: { id: 'test' }, props: { callTime: Date.now() } }]; +} +const { callTime } = Astro.props; +--- +

{callTime}

diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0ca8c9a01909..73169eb52a8b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3202,6 +3202,12 @@ importers: specifier: workspace:* version: link:../../.. + packages/astro/test/fixtures/hmr-route-cache: + dependencies: + astro: + specifier: workspace:* + version: link:../../.. + packages/astro/test/fixtures/hmr-slots-render: dependencies: astro: