#Trying to display images from a content collection using the helper, works in dev, build fails

71 messages · Page 1 of 1 (latest)

mental gulch
#

I am trying to build images referenced in a content collection using the image helper, and it works locally in dev mode, but it breaks when i try to build it. Any idea what's going on? I'm at loss what's going on here. Using the latest version of everything Astro 5.1.8.

So i have a collection that goes like this (omitting unimportant parts):

const demonsCategoriesCollection = defineCollection({
    loader: file("./src/content/demons/categories.yaml"),
    schema: ({ image }) => z.object({
        name: z.string(),
        iconSrc: image(),
    })
});

The corresponding YAML has a bunch of entries (the @alias being set up properly in tsconfig.json):

- id: "something"
  name: "Something"
  iconSrc: "@content/demons/something/icon.webp"

I am trying to build those images in layouts and components. Here's a simplified Post.astro layout testing the problem:

---
import { Image, getImage } from "astro:assets";
import BaseLayout from "@layouts/demons/BaseLayout.astro";
import getCategory from "@utils/demons/getCategory";

const { props } = Astro.props;

let category = await getCategory(props.id); // returns a category entry

// Trying both with getimage
let testGetImage = await getImage({
  src: category.data.iconSrc,
  height: 128,
});
---

<BaseLayout {props}>
  <Image src={category.data.iconSrc} alt="" width="128" />
  <img src={testGetImage.src} alt="" />
  <slot />
</BaseLayout>

#

As i mentioned, it works on the dev server, but breaks when i try to build:

src/layouts/demons/Post.astro:17:10 - error ts(2322): Type '{ src?: string; width?: number; height?: number; format?: "png" | "jpg" | "jpeg" | "tiff" | "webp" | "gif" | "svg" | "avif"; }' is not assignable to type 'string | ImageMetadata | Promise<{ default: ImageMetadata; }>'.
  Type '{ src?: string; width?: number; height?: number; format?: "png" | "jpg" | "jpeg" | "tiff" | "webp" | "gif" | "svg" | "avif"; }' is not assignable to type 'ImageMetadata'.

17   <Image src={category.data.iconSrc} alt="" width="128" />
            ~~~
src/layouts/demons/Post.astro:11:3 - error ts(2322): Type '{ src?: string; width?: number; height?: number; format?: "png" | "jpg" | "jpeg" | "tiff" | "webp" | "gif" | "svg" | "avif"; }' is not assignable to type 'string | ImageMetadata | Promise<{ default: ImageMetadata; }>'.
  Type '{ src?: string; width?: number; height?: number; format?: "png" | "jpg" | "jpeg" | "tiff" | "webp" | "gif" | "svg" | "avif"; }' is not assignable to type 'ImageMetadata'.

11   src: category.data.iconSrc,
     ~~~

Any idea what's happening? I'm at loss what's going on here.

Obviously, optimizing and importing the images manually or putting them in public is not a reasonable option for my use case, i have a need for using a content collection here.

mossy wigeon
#
Type '{ src?: string; width?: number; height?: number; format?: "png" | "jpg" | "jpeg" | "tiff" | "webp" | "gif" | "svg" | "avif"; }' is not assignable to type 'string | ImageMetadata | Promise<{ default: ImageMetadata; }>'.
  Type '{ src?: string; width?: number; height?: number; format?: "png" | "jpg" | "jpeg" | "tiff" | "webp" | "gif" | "svg" | "avif"; }' is not assignable to type 'ImageMetadata'.

This is saying that your Image is coming through as a type of ImageMetadata.

If you do a console.log(category.data.iconSrc) What do you get?

mental gulch
#

Here's what i get:

19:28:20 [200] /demons/about/ 6ms
{
  src: '/@fs/home/aria/aria-dog/src/content/demons/nexus/icon.webp?origWidth=1024&origHeight=1024&origFormat=webp',
  width: 1024,
  height: 1024,
  format: 'webp'
}
mossy wigeon
#

That seems right

#

import getCategory from "@utils/demons/getCategory";

Whats this?

mental gulch
#

just a little helper, it still breaks without.

import { getEntry } from "astro:content";

const getCategory = async (id: string) => {
  /**
   * Returns a category collection entry from a id URL slug
   */

  // Keep only the category id, not the full url slug
  let categoryId = id.split("/")[0];
  if (!categoryId) {
    categoryId = "nexus"; // Fallback, e.g. for homepage
  }

  // Get the corresponding category object
  let category = await getEntry("demonsCategories", categoryId);
  if (!category) {
    // No guarantees the categoryId exists so we need a fallback
    category = await getEntry("demonsCategories", "nexus");
  }
  return category;

};

export default getCategory;
mossy wigeon
#

Is your repo public?

mental gulch
mossy wigeon
#

No worries. Let me take a look.

#

Still checking it out. But for your Footer. You would want to do this

  <Footer
    backgroundCredits={category.data.backgroundCredits}
    backgroundFullUrl={backgroundFullImage.src}
  />

Instead of having like this

  <Footer
    backgroundCredits="{category.data.backgroundCredits}"
    backgroundFullUrl="{backgroundFullImage.src}"
  />
mental gulch
#

Haha yeah i was testing something and left it in a version i pushed on git

mossy wigeon
#

Okay. I got a different error now but im not sure why its throwing it... Im testing something else out real quick

mental gulch
#

Thanks a bunch for looking, my code is kind of a huge mess started around Astro 3 and i'm mostly learning on the job here lol

mossy wigeon
#

No worries!

#

<@&1129102257422610512> Trying to figure out why these images wont get imported.

Im getting this error now. But we are using the image helper in content.config.ts

I did change the yaml around to use a different path

- id: "code-vein"
  name: "Code Vein"
  iconSrc: "./code-vein/icon.webp"
  sculptureSrc: "./code-vein/sculpture.webp"
  backgroundSrc: "./code-vein/background.jpg"
  backgroundCredits: "Background: screenshot of my character from Code Vein"
ivory viper
#

I think it needs to be the relative path, just not as a string?

#
- id: "code-vein"
  name: "Code Vein"
  iconSrc: ./code-vein/icon.webp
  sculptureSrc: ./code-vein/sculpture.webp
  backgroundSrc: ./code-vein/background.jpg
  backgroundCredits: "Background: screenshot of my character from Code Vein"

This basically

glossy violet
#

That usually means that there's something wrong with your config file and it's not getting loaded, so it doesn't know about image()

gentle shuttle
#

Nah, that's still a string in yaml. I have the same

icon: "@assets/images/wiki/items/minecolonies/blockhutalchemist.png"
mossy wigeon
#

Ahh. Didn’t know the alias like that works in yaml

#

Okay. So something is
Up
With the config

gentle shuttle
#

You don't strictly need aliases if I remember correctly

willow oxide
#

and where are the images used?

#

at first I thought you owned git.gay domain btw I was like dang that’s gotta be valuable

gentle shuttle
#

Probably would still have to quote it

glossy violet
#

Yes, IIRC, you have to use quotes when it starts with @

mental gulch
#

even if i remove the @ paths on my end, the builds continue to break in the same way

gentle shuttle
#

So which error are you currently getting

mental gulch
#

Still the one in the op about types

gentle shuttle
#

For some reason all fields in your typing are optional

#

ImageMetadata has no optional fields

#
export type ImageMetadata = {
    src: string;
    width: number;
    height: number;
    format: ImageInputFormat;
    orientation?: number;
    [isESMImport]?: true;
};
#

That's why it can't be converted to ImageMetadata

ivory viper
#

I've seen that a bit ago on a completely different issue, let me check where that was

#

nvm that as well. is the type provided by the image() function just wrong then?

gentle shuttle
#

My editor does seem to resolve it properly

#

Let me try building

mental gulch
#

I disabled some strict checks while trying to figure it out if that could be related to the mystery error somehow

ivory viper
#

@mental gulch if you delete the .astro/ and node_modules folders, then re-install and try re-building, what happens?

mossy wigeon
#

Technically I did that since I cloned it down and did a fresh install.

gentle shuttle
#

Also have you done all of the tsconfig migration, like you no longer need env.d.ts, etc

mossy wigeon
#

True

gentle shuttle
#

tsconfig is missing

  "include": [".astro/types.d.ts", "**/*"],
  "exclude": ["dist"]
#

Could help maybe

#

And remove env.d.ts, that's part of the Astro 5 migration guides

mental gulch
#

Ohhhh, that was promising but didn't fix it, my bad, i entirely forgot that part of the migration

#

nuking .astro and node_modules didn't help either sadly

ivory viper
#

Worth a shot, I'll let the others take it from here, best of luck!

gentle shuttle
#

It's weird, for some reason all your typings are just becoming optional

#

Despite the typings stating everything is required

#

Turning on strict null checks fixes it @mental gulch

#

I'm getting different errors now, but those image errors are gone

#

Note that with the new content layer, all results you get from getEntry is now by default possibly undefined

#

So you have to update your code to respect that

mental gulch
#

Let me try to quickly clean up the errors see if i can make a build then

gentle shuttle
#

getCategory now possibly returns undefined

#

You could fix that by making it throw an error if the final value is undefined

#

For example statements like

category = await getEntry("demonsCategories", "nexus");

used to guarantee a return value because content collections had compile time knowledge about all possible entries

#

Content layer no longer has this, so even if you are certain this should give back an item, it won't

mossy wigeon
#

Now that you mention that. I ran into this issue as well. But I used Null Assertion to fix it.

mental gulch
#

Thanks a ton @gentle shuttle - i got it to build with that. Now THAT was a mysterious one to unravel, there's so much magic in Astro i have no idea how all the moving parts are doing that

#

So for the benefit of anyone looking at this, my fix:

gentle shuttle
#

I went through all that pain myself 🙂

mental gulch
#
import { getEntry } from "astro:content";

const getCategory = async (id: string) => {
  /**
   * Returns a category collection entry from a id URL slug
   */

  // Keep only the category id, not the full url slug
  let categoryId = id.split("/")[0];
  if (!categoryId) {
    categoryId = "nexus"; // Fallback, e.g. for homepage
  }

  // Get the corresponding category object
  let category = await getEntry("demonsCategories", categoryId);
  if (!category) {
    // No guarantees the categoryId exists so we need a fallback
    category = await getEntry("demonsCategories", "nexus");
  }

  // This can never happen but keeps typescript happy
  if (!category) {
    throw new Error(`Category not found: ${id}`);
  }

  return category;

};

export default getCategory;
#

Thanks everyone who looked at this mess 🙂