mirror of
https://github.com/withastro/astro.git
synced 2025-01-22 10:31:53 -05:00
fix: defer validation of reference entries (#12990)
This commit is contained in:
parent
9f34e3f889
commit
2e12f1d752
5 changed files with 100 additions and 70 deletions
5
.changeset/heavy-fireants-melt.md
Normal file
5
.changeset/heavy-fireants-melt.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Fixes a bug that caused references to be incorrectly reported as invalid
|
|
@ -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
|
||||
|
@ -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 };
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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' }),
|
||||
|
@ -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(),
|
||||
|
@ -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' }),
|
||||
|
|
51
packages/astro/test/fixtures/content-layer/src/data/cats.json
vendored
Normal file
51
packages/astro/test/fixtures/content-layer/src/data/cats.json
vendored
Normal file
|
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
Loading…
Reference in a new issue