0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-01-20 22:12:38 -05:00

fix(astro): always return cloned content collection (#11228)

* fix: always return cloned content collection

* Update .changeset/early-melons-thank.md

Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>

---------

Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
This commit is contained in:
Matt Kane 2024-06-10 20:57:07 +01:00 committed by GitHub
parent 28ee6e7f85
commit 1e293a1b81
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 139 additions and 3 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Updates `getCollection()` to always return a cloned array

View file

@ -79,8 +79,7 @@ export function createGetCollection({
// Cache `getCollection()` calls in production only
// prevents stale cache in development
if (!import.meta.env?.DEV && cacheEntriesByCollection.has(collection)) {
// Always return a new instance so consumers can safely mutate it
entries = [...cacheEntriesByCollection.get(collection)!];
entries = cacheEntriesByCollection.get(collection)!;
} else {
const limit = pLimit(10);
entries = await Promise.all(
@ -115,7 +114,9 @@ export function createGetCollection({
if (typeof filter === 'function') {
return entries.filter(filter);
} else {
return entries;
// Clone the array so users can safely mutate it.
// slice() is faster than ...spread for large arrays.
return entries.slice();
}
};
}

View file

@ -370,4 +370,26 @@ describe('Content Collections', () => {
assert.equal($('script').attr('src').startsWith('/docs'), true);
});
});
describe('Mutation', () => {
let fixture;
before(async () => {
fixture = await loadFixture({
root: './fixtures/content-collections-mutation/',
});
await fixture.build();
});
it('Does not mutate cached collection', async () => {
const html = await fixture.readFile('/index.html');
const index = cheerio.load(html)('h2:first').text();
const html2 = await fixture.readFile('/another_page/index.html');
const anotherPage = cheerio.load(html2)('h2:first').text();
assert.equal(index, anotherPage);
});
});
});

View file

@ -0,0 +1,6 @@
import mdx from '@astrojs/mdx';
import { defineConfig } from 'astro/config';
export default defineConfig({
integrations: [mdx()],
});

View file

@ -0,0 +1,9 @@
{
"name": "@test/content-collections-mutation",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/mdx": "workspace:*"
}
}

View file

@ -0,0 +1,6 @@
---
title: "First Blog"
date: 2024-04-05
---
First blog content.

View file

@ -0,0 +1,6 @@
---
title: "Second Blog"
date: 2024-04-06
---
Second blog content.

View file

@ -0,0 +1,6 @@
---
title: "Third Blog"
date: 2024-04-07
---
Third blog content.

View file

@ -0,0 +1,16 @@
// 1. Import utilities from `astro:content`
import { z, defineCollection } from "astro:content";
// 2. Define a `type` and `schema` for each collection
const blogCollection = defineCollection({
type: "content", // v2.5.0 and later
schema: z.object({
title: z.string(),
date: z.date(),
}),
});
// 3. Export a single `collections` object to register your collection(s)
export const collections = {
blog: blogCollection,
};

View file

@ -0,0 +1,25 @@
---
import { getCollection } from "astro:content";
const blogs = await getCollection("blog");
blogs.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf()); // sort by date most recent first
const latestBlog = blogs.splice(0, 1)[0]; // modifies the collection
---
<html>
<body>
<a href="/">home</a>
<h1>Latest Blog</h1>
<h2>{latestBlog.data.title}</h2>
<p>posted: {latestBlog.data.date.toLocaleString()}</p>
<br />
<h2>Older blogs</h2>
{
blogs.map((b) => (
<>
<h3>{b.data.title}</h3>
<p>posted: {b.data.date.toLocaleString()}</p>
</>
))
}
</body>
</html>

View file

@ -0,0 +1,25 @@
---
import { getCollection } from "astro:content";
const blogs = await getCollection("blog");
blogs.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf()); // sort by date most recent first
const latestBlog = blogs.splice(0, 1)[0]; // modifies the collection
---
<html>
<body>
<a href="/another_page">other page</a>
<h1>Latest Blog</h1>
<h2>{latestBlog.data.title}</h2>
<p>posted: {latestBlog.data.date.toLocaleString()}</p>
<br />
<h2>Older blogs</h2>
{
blogs.map((b) => (
<>
<h3>{b.data.title}</h3>
<p>posted: {b.data.date.toLocaleString()}</p>
</>
))
}
</body>
</html>

9
pnpm-lock.yaml generated
View file

@ -2599,6 +2599,15 @@ importers:
specifier: workspace:*
version: link:../../..
packages/astro/test/fixtures/content-collections-mutation:
dependencies:
'@astrojs/mdx':
specifier: workspace:*
version: link:../../../../integrations/mdx
astro:
specifier: workspace:*
version: link:../../..
packages/astro/test/fixtures/content-collections-with-config-mjs:
dependencies:
astro: