Skip to content

Commit

Permalink
fix(astro): always return cloned content collection (#11228)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
ascorbic and sarah11918 authored Jun 10, 2024
1 parent 28ee6e7 commit 1e293a1
Show file tree
Hide file tree
Showing 12 changed files with 139 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/early-melons-thank.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Updates `getCollection()` to always return a cloned array
7 changes: 4 additions & 3 deletions packages/astro/src/content/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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();
}
};
}
Expand Down
22 changes: 22 additions & 0 deletions packages/astro/test/content-collections.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});

});

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import mdx from '@astrojs/mdx';
import { defineConfig } from 'astro/config';

export default defineConfig({
integrations: [mdx()],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "@test/content-collections-mutation",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*",
"@astrojs/mdx": "workspace:*"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: "First Blog"
date: 2024-04-05
---

First blog content.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: "Second Blog"
date: 2024-04-06
---

Second blog content.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: "Third Blog"
date: 2024-04-07
---

Third blog content.
Original file line number Diff line number Diff line change
@@ -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,
};
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
@@ -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 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 1e293a1

Please sign in to comment.