diff --git a/.changeset/tidy-needles-build.md b/.changeset/tidy-needles-build.md new file mode 100644 index 000000000000..140df5c0bbd6 --- /dev/null +++ b/.changeset/tidy-needles-build.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fixes an issue in content collection caching, where two documents with the same contents were generating an error during the build. diff --git a/packages/astro/src/core/build/plugins/plugin-content.ts b/packages/astro/src/core/build/plugins/plugin-content.ts index 7f26ca986826..68ff981a0e29 100644 --- a/packages/astro/src/core/build/plugins/plugin-content.ts +++ b/packages/astro/src/core/build/plugins/plugin-content.ts @@ -40,6 +40,7 @@ interface ContentManifestKey { type: 'content' | 'data'; entry: string; } + interface ContentManifest { version: number; entries: [ContentManifestKey, string][]; @@ -304,6 +305,7 @@ interface ContentEntries { restoreFromCache: ContentManifestKey[]; buildFromSource: ContentManifestKey[]; } + function getEntriesFromManifests( oldManifest: ContentManifest, newManifest: ContentManifest @@ -379,7 +381,7 @@ async function generateContentManifest( promises.push( limit(async () => { const data = await fsMod.promises.readFile(fileURL, { encoding: 'utf8' }); - manifest.entries.push([key, checksum(data)]); + manifest.entries.push([key, checksum(data, fileURL.toString())]); }) ); } diff --git a/packages/astro/test/experimental-content-collections-cache-invalidation.test.js b/packages/astro/test/experimental-content-collections-cache-invalidation.test.js index abc8ea839370..0f4534e5118b 100644 --- a/packages/astro/test/experimental-content-collections-cache-invalidation.test.js +++ b/packages/astro/test/experimental-content-collections-cache-invalidation.test.js @@ -11,14 +11,17 @@ describe('Experimental Content Collections cache - invalidation', () => { this.cacheDir = new URL(relCacheDir, this.root); this.tmpDir = new URL(`./tmp` + relCacheDir.slice(1), this.root); } + backup() { this.rmTmp(); copyFiles(this.cacheDir, this.tmpDir); } + restore() { fs.rmSync(this.cacheDir, { recursive: true }); copyFiles(this.tmpDir, this.cacheDir); } + rmTmp() { fs.rmSync(this.tmpDir, { force: true, recursive: true }); } @@ -26,6 +29,7 @@ describe('Experimental Content Collections cache - invalidation', () => { class ManifestTestPlugin { used = false; + plugin() { return { name: '@test/manifest-used', @@ -99,4 +103,35 @@ describe('Experimental Content Collections cache - invalidation', () => { assert.equal(testPlugin.used, false, 'manifest not used because of lockfile mismatch'); }); }); + + describe('duplicate content', () => { + let fixture, + backup, + /** @type {ManifestTestPlugin} */ + testPlugin; + before(async () => { + testPlugin = new ManifestTestPlugin(); + fixture = await loadFixture({ + root: './fixtures/content-collections-same-contents/', + cacheDir: './cache/same-contents/', + experimental: { contentCollectionCache: true }, + integrations: [testPlugin.plugin()], + }); + backup = new CacheBackup( + './fixtures/content-collections-same-contents/', + './cache/same-contents/' + ); + backup.backup(); + await fixture.build(); + }); + + after(async () => { + backup.restore(); + //await fixture.clean(); + }); + + it('Manifest was not used', () => { + assert.equal(testPlugin.used, false, 'manifest not used because of lockfile mismatch'); + }); + }); }); diff --git a/packages/astro/test/fixtures/content-collections-same-contents/astro.config.mjs b/packages/astro/test/fixtures/content-collections-same-contents/astro.config.mjs new file mode 100644 index 000000000000..417b7c5e9cce --- /dev/null +++ b/packages/astro/test/fixtures/content-collections-same-contents/astro.config.mjs @@ -0,0 +1,11 @@ +import {defineConfig} from 'astro/config'; + +// https://astro.build/config +export default defineConfig({ + base: '/docs', + vite: { + build: { + assetsInlineLimit: 0 + } + } +}); diff --git a/packages/astro/test/fixtures/content-collections-same-contents/package.json b/packages/astro/test/fixtures/content-collections-same-contents/package.json new file mode 100644 index 000000000000..ef61d36fa83c --- /dev/null +++ b/packages/astro/test/fixtures/content-collections-same-contents/package.json @@ -0,0 +1,8 @@ +{ + "name": "@test/content-collections-same-contents", + "version": "0.0.0", + "private": true, + "dependencies": { + "astro": "workspace:*" + } +} diff --git a/packages/astro/test/fixtures/content-collections-same-contents/src/content/config.ts b/packages/astro/test/fixtures/content-collections-same-contents/src/content/config.ts new file mode 100644 index 000000000000..0f52c4ca3624 --- /dev/null +++ b/packages/astro/test/fixtures/content-collections-same-contents/src/content/config.ts @@ -0,0 +1,12 @@ +import { defineCollection, z } from 'astro:content'; + + +const docs = defineCollection({ + schema: z.object({ + title: z.string(), + }), +}); + +export const collections = { + docs, +} diff --git a/packages/astro/test/fixtures/content-collections-same-contents/src/content/docs/one.md b/packages/astro/test/fixtures/content-collections-same-contents/src/content/docs/one.md new file mode 100644 index 000000000000..58c118ceba76 --- /dev/null +++ b/packages/astro/test/fixtures/content-collections-same-contents/src/content/docs/one.md @@ -0,0 +1,8 @@ +--- +title: One +--- + +# Title + +stuff + diff --git a/packages/astro/test/fixtures/content-collections-same-contents/src/content/docs/two.md b/packages/astro/test/fixtures/content-collections-same-contents/src/content/docs/two.md new file mode 100644 index 000000000000..64662cb49a61 --- /dev/null +++ b/packages/astro/test/fixtures/content-collections-same-contents/src/content/docs/two.md @@ -0,0 +1,7 @@ +--- +title: One +--- + +# Title + +stuff diff --git a/packages/astro/test/fixtures/content-collections-same-contents/src/pages/docs.astro b/packages/astro/test/fixtures/content-collections-same-contents/src/pages/docs.astro new file mode 100644 index 000000000000..6b352ce371a8 --- /dev/null +++ b/packages/astro/test/fixtures/content-collections-same-contents/src/pages/docs.astro @@ -0,0 +1,17 @@ +--- +import { getEntryBySlug } from 'astro:content'; +const entry = await getEntryBySlug('docs', 'one'); +const { Content } = await entry.render(); +--- + +
+ + +