withastro / astro

The web framework for content-driven websites. ⭐️ Star to support our work!
https://astro.build
Other
46.84k stars 2.49k forks source link

Error when using a Reference and an inline Loader #12087

Closed mollimoll closed 1 month ago

mollimoll commented 1 month ago

Astro Info

Astro                    v4.15.9
Node                     v18.20.3
System                   Linux (x64)
Package Manager          npm
Output                   static
Adapter                  none
Integrations             none

If this issue only occurs in one browser, which browser is a problem?

No response

Describe the Bug

I'm using an inline loader, combined with a schema reference to another collection. When I run npm run build I receive an error stating the "Collection does not exist or is empty."

I can reproduce this issue on StackBlitz with the experimental contentLayer flag, but it is also occurring in the Astro 5 beta.

What's the expected result?

I'd expect the build to pass, and that Astro does not fail if a collection is empty (if that is indeed the issue).

Link to Minimal Reproducible Example

https://stackblitz.com/edit/withastro-astro-mn5gwx?file=src%2Fcontent%2Fconfig.ts

Participation

StevenDepa commented 1 month ago

I was going to experiment with the new content layer because I really need this feature, but i saw this issue, and since it seems to be an use case I need, I tried to investigate. The issue appears to be the same in either dev or build, and both in Astro v4.15.9 and the last beta. Looking at the code, the problem seems to be that the schema validation code tries to load the data store https://github.com/withastro/astro/blob/2594eb088d53a98181ac820243bcb1a765856ecf/packages/astro/src/content/runtime.ts#L590 to find the referenced collection, but the store is loaded completely empty

https://github.com/withastro/astro/blob/2594eb088d53a98181ac820243bcb1a765856ecf/packages/astro/src/content/vite-plugin-content-virtual-mod.ts#L136

because the data store is not written to disk until all the collection have finished loading, and before that every collection tries to validate the schema.

https://github.com/withastro/astro/blob/2594eb088d53a98181ac820243bcb1a765856ecf/packages/astro/src/content/content-layer.ts#L172

I can see that the mutable data store tries so hard to save the file to disk

https://github.com/withastro/astro/blob/2594eb088d53a98181ac820243bcb1a765856ecf/packages/astro/src/content/mutable-data-store.ts#L188

but it's file path reference is empty until writeToDisk is called at the end of collection loading. https://github.com/withastro/astro/blob/2594eb088d53a98181ac820243bcb1a765856ecf/packages/astro/src/content/content-layer.ts#L227 I may be missing something but I think that every collection "existence" should be initialized before trying to validate schemas, otherwise references will never work.

carlosmonterocanabal commented 1 month ago

I had the same problem with inline loader with Astro beta 5. I show an example of my use case:

const categories = defineCollection({
    loader: glob({ pattern: "**\/*.yml", base: "./src/data/categories/" }),
    schema: z.object({
        title: z.string(),
        description: z.string(),
    }),
});
const products = defineCollection({
    loader: async() => {
        const categories = await getCollection('categories'); // <= HERE is the error
        // call external API with id from each category
        return products.map((p) => ({
            id: p.id,
            ...p
        }));
    },
    schema: z.object({
        title: z.string(),
        description: z.string(),
        price: z.number(),
    }),
});

When I run npm run build I get

The collection "categories" does not exist or is empty. Ensure a collection directory with this name exists.
ascorbic commented 1 month ago

@mollimoll thanks for the report. I'm investigating this now.

@carlosmonterocanabal your issue is different: you shouldn't be trying to use getCollection inside the loader, because the order of execution is undefined and (as you see) you can't tell which collection will be loaded first. One collection can't refer to another in that way.

@StevenDepa Are you also trying to getCollection inside a loader? Can you share your code? The runtime is not supposed to be used before the loaders have run.

ascorbic commented 1 month ago

@mollimoll ok, I'm reproduced it and can see where the issue is. It's a race condition. I'll get a fix out. Thanks again for the report and great repro!