master
  1---
  2import { buildConfig } from '@/config';
  3import { getCachedRemoteImageSize } from '@/utils/image-size-cache';
  4import type { ImageMetadata } from 'astro';
  5import { Image } from 'astro:assets';
  6import path from 'path';
  7
  8interface Props {
  9  id?: string;
 10  src: ImageMetadata | string;
 11  class?: string;
 12  alt?: string;
 13  position?: string;
 14  basePath?: string;
 15}
 16
 17const { id, src, alt, position = 'center', basePath = '/' } = Astro.props;
 18const className = Astro.props.class;
 19
 20const isLocal =
 21  typeof src !== 'string' ||
 22  !(
 23    src.startsWith('/') ||
 24    src.startsWith('http') ||
 25    src.startsWith('https') ||
 26    src.startsWith('data:')
 27  );
 28const isPublic = typeof src === 'string' && src.startsWith('/');
 29const isRemote = !isLocal && !isPublic;
 30
 31let img: ImageMetadata | undefined;
 32if (isLocal) {
 33  if (typeof src === 'string') {
 34    const files = import.meta.glob<ImageMetadata>(
 35      '../../**/*.{png,jpg,jpeg,tiff,webp,gif,svg,avif,jfif,pjpeg,pjp,ico}',
 36      {
 37        import: 'default',
 38      }
 39    );
 40    let normalizedPath = path.normalize(path.join('../../', basePath, src)).replace(/\\/g, '/');
 41    const file = files[normalizedPath];
 42    if (!file) {
 43      console.error(
 44        `\n[ERROR] Image file not found: ${normalizedPath.replace('../../', 'src/')}`
 45      );
 46    }
 47    img = await file();
 48  } else {
 49    img = src;
 50  }
 51}
 52
 53let w, h;
 54if (isRemote && buildConfig.inferRemoteImageSize.enable) {
 55  const dimensions = await getCachedRemoteImageSize(src);
 56  if (dimensions) {
 57    w = dimensions.width;
 58    h = dimensions.height;
 59  } else {
 60    w = buildConfig.inferRemoteImageSize.defaultSize.width;
 61    h = buildConfig.inferRemoteImageSize.defaultSize.height;
 62  }
 63} else {
 64  w = buildConfig.inferRemoteImageSize.defaultSize.width;
 65  h = buildConfig.inferRemoteImageSize.defaultSize.height;
 66}
 67
 68const imageClass = 'w-full h-full object-cover';
 69const imageStyle = `object-position: ${position}`;
 70---
 71
 72<div id={id} class:list={[className, 'relative overflow-hidden']}>
 73  {isLocal && img && <Image src={img} alt={alt || ''} class={imageClass} style={imageStyle} />}
 74  {
 75    isRemote && (
 76      <Image
 77        src={src as string}
 78        alt={alt || ''}
 79        class={imageClass}
 80        style={imageStyle}
 81        width={w}
 82        height={h}
 83      />
 84    )
 85  }
 86  {
 87    !isLocal && isPublic && (
 88      <img
 89        src={new URL(src, Astro.site) as unknown as string}
 90        alt={alt || ''}
 91        class={imageClass}
 92        style={imageStyle}
 93        width={w}
 94        height={h}
 95        loading="lazy"
 96        decoding="async"
 97      />
 98    )
 99  }
100</div>