Skip to content

Commit

Permalink
fix: defer validation of reference entries (#12990)
Browse files Browse the repository at this point in the history
  • Loading branch information
ascorbic authored Jan 16, 2025
1 parent 9f34e3f commit 2e12f1d
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 70 deletions.
5 changes: 5 additions & 0 deletions .changeset/heavy-fireants-melt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fixes a bug that caused references to be incorrectly reported as invalid
19 changes: 2 additions & 17 deletions packages/astro/src/content/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,6 @@ export function createReference({ lookupMap }: { lookupMap: ContentLookupMap })
}

const flattenedErrorPath = ctx.path.join('.');
const collectionIsInStore = store.hasCollection(collection);

if (typeof lookup === 'object') {
// If these don't match then something is wrong with the reference
Expand All @@ -659,22 +658,8 @@ export function createReference({ lookupMap }: { lookupMap: ContentLookupMap })
return lookup;
}

if (collectionIsInStore) {
const entry = store.get(collection, lookup);
if (!entry) {
ctx.addIssue({
code: ZodIssueCode.custom,
message: `**${flattenedErrorPath}**: Reference to ${collection} invalid. Entry ${lookup} does not exist.`,
});
return;
}
return { id: lookup, collection };
}

// If the collection is not in the lookup map or store, it may be a content layer collection and the store may not yet be populated.
// If the store has 0 or 1 entries it probably means that the entries have not yet been loaded.
// The store may have a single entry even if the collections have not loaded, because the top-level metadata collection is generated early.
if (!lookupMap[collection] && store.collections().size <= 1) {
// If the collection is not in the lookup map it may be a content layer collection and the store may not yet be populated.
if (!lookupMap[collection]) {
// For now, we can't validate this reference, so we'll optimistically convert it to a reference object which we'll validate
// later in the pipeline when we do have access to the store.
return { id: lookup, collection };
Expand Down
18 changes: 18 additions & 0 deletions packages/astro/test/content-layer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,24 @@ describe('Content Layer', () => {
assert.equal(newJson.increment.data.lastValue, 1);
await fixture.resetAllFiles();
});


it('can handle references being renamed after a build', async () => {
let newJson = devalue.parse(await fixture.readFile('/collections.json'));
assert.deepEqual(newJson.entryWithReference.data.cat, { collection: 'cats', id: 'tabby' });
await fixture.editFile('src/data/cats.json', (prev) => {
return prev.replace('tabby', 'tabby-cat');
});
await fixture.editFile('src/content/space/columbia-copy.md', (prev) => {
return prev.replace('cat: tabby', 'cat: tabby-cat');
});
await fixture.build();
newJson = devalue.parse(await fixture.readFile('/collections.json'));
assert.deepEqual(newJson.entryWithReference.data.cat, { collection: 'cats', id: 'tabby-cat' });
await fixture.resetAllFiles();
});


});

describe('Dev', () => {
Expand Down
77 changes: 24 additions & 53 deletions packages/astro/test/fixtures/content-layer/src/content.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { defineCollection, z, reference } from 'astro:content';
import { file, glob } from 'astro/loaders';
import { loader } from './loaders/post-loader.js';
import { parse as parseToml } from 'toml';
import { readFile } from 'fs/promises';

const blog = defineCollection({
loader: loader({ url: 'https://jsonplaceholder.typicode.com/posts' }),
Expand Down Expand Up @@ -71,43 +72,32 @@ const rodents = defineCollection({
nocturnal: z.boolean(),
}),
});
// Absolute paths should also work
const absoluteRoot = new URL('content/space', import.meta.url);

const spacecraft = defineCollection({
loader: glob({ pattern: '*.md', base: absoluteRoot }),
schema: ({ image }) =>
z.object({
title: z.string(),
description: z.string(),
publishedDate: z.coerce.date(),
tags: z.array(z.string()),
heroImage: image().optional(),
cat: reference('cats').default('siamese'),
something: z
.string()
.optional()
.transform((str) => ({ type: 'test', content: str })),
}),
});


const cats = defineCollection({
loader: async function () {
return [
{
breed: 'Siamese',
id: 'siamese',
size: 'Medium',
origin: 'Thailand',
lifespan: '15 years',
temperament: ['Active', 'Affectionate', 'Social', 'Playful'],
},
{
breed: 'Persian',
id: 'persian',
size: 'Medium',
origin: 'Iran',
lifespan: '15 years',
temperament: ['Calm', 'Affectionate', 'Social'],
},
{
breed: 'Tabby',
id: 'tabby',
size: 'Medium',
origin: 'Egypt',
lifespan: '15 years',
temperament: ['Curious', 'Playful', 'Independent'],
},
{
breed: 'Ragdoll',
id: 'ragdoll',
size: 'Medium',
origin: 'United States',
lifespan: '15 years',
temperament: ['Calm', 'Affectionate', 'Social'],
},
];
const file = new URL('data/cats.json', import.meta.url);
const data = await readFile(file, 'utf-8');
return JSON.parse(data);
},
schema: z.object({
breed: z.string(),
Expand Down Expand Up @@ -140,25 +130,6 @@ const birds = defineCollection({
}),
});

// Absolute paths should also work
const absoluteRoot = new URL('content/space', import.meta.url);

const spacecraft = defineCollection({
loader: glob({ pattern: '*.md', base: absoluteRoot }),
schema: ({ image }) =>
z.object({
title: z.string(),
description: z.string(),
publishedDate: z.coerce.date(),
tags: z.array(z.string()),
heroImage: image().optional(),
cat: reference('cats').default('siamese'),
something: z
.string()
.optional()
.transform((str) => ({ type: 'test', content: str })),
}),
});

const probes = defineCollection({
loader: glob({ pattern: ['*.md', '!voyager-*'], base: 'src/data/space-probes' }),
Expand Down
51 changes: 51 additions & 0 deletions packages/astro/test/fixtures/content-layer/src/data/cats.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
[
{
"breed": "Siamese",
"id": "siamese",
"size": "Medium",
"origin": "Thailand",
"lifespan": "15 years",
"temperament": [
"Active",
"Affectionate",
"Social",
"Playful"
]
},
{
"breed": "Persian",
"id": "persian",
"size": "Medium",
"origin": "Iran",
"lifespan": "15 years",
"temperament": [
"Calm",
"Affectionate",
"Social"
]
},
{
"breed": "Tabby",
"id": "tabby",
"size": "Medium",
"origin": "Egypt",
"lifespan": "15 years",
"temperament": [
"Curious",
"Playful",
"Independent"
]
},
{
"breed": "Ragdoll",
"id": "ragdoll",
"size": "Medium",
"origin": "United States",
"lifespan": "15 years",
"temperament": [
"Calm",
"Affectionate",
"Social"
]
}
]

0 comments on commit 2e12f1d

Please sign in to comment.