kit icon indicating copy to clipboard operation
kit copied to clipboard

Cannot pass "src" as prop while using @sveltejs/enhanced-img

Open victoriusnet opened this issue 1 year ago • 22 comments

Describe the bug

I wanted to created a simple hero component where I would pass the hero image dynamically, but I got error if enhanced:img used instead of img:

TypeError: Cannot read properties of undefined (reading 'src')

Code provided at "Reproduction" working correctly if I use simple img tag.

Reproduction

You need a JPG image named innerhero.jpg at /src/img/ folder

+page.svelte

<script>
import Hero from '$lib/template/Hero.svelte';
</script>

<Hero backgroundImage="$lib/img/innerhero.jpg" />

Hero.svelte

<script>
    export let backgroundImage;
</script>
  
<enhanced:img src="{backgroundImage}" class="absolute inset-0 object-cover w-full h-full" alt="An alt text" />

I have also tried:

+page.svelte:

<script>
import Hero from '$lib/template/Hero.svelte';
import HeroImg from '$lib/img/innerhero.jpg';
</script>
<Hero backgroundImage="{HeroImg}" />

Logs

No response

System Info

System:
    OS: Linux 5.15 Ubuntu 22.04.3 LTS 22.04.3 LTS (Jammy Jellyfish)
    CPU: (8) x64 Intel(R) Core(TM) i5-8350U CPU @ 1.70GHz
    Memory: 4.02 GB / 7.69 GB
    Container: Yes
    Shell: 5.1.16 - /bin/bash
  Binaries:
    Node: 21.1.0 - ~/.nvm/versions/node/v21.1.0/bin/node
    Yarn: 1.22.19 - /usr/bin/yarn
    npm: 10.2.5 - ~/.nvm/versions/node/v21.1.0/bin/npm
    pnpm: 8.12.1 - ~/.nvm/versions/node/v21.1.0/bin/pnpm
    bun: 1.0.15 - ~/.bun/bin/bun
  npmPackages:
    @sveltejs/adapter-auto: ^3.1.0 => 3.1.0 
    @sveltejs/enhanced-img: ^0.1.7 => 0.1.7 
    @sveltejs/kit: ^2.0.6 => 2.0.6 
    @sveltejs/vite-plugin-svelte: ^3.0.1 => 3.0.1 
    svelte: ^4.2.8 => 4.2.8 
    vite: ^5.0.11 => 5.0.11

Severity

annoyance

Additional Information

No response

victoriusnet avatar Jan 07 '24 19:01 victoriusnet

<script>
import Hero from '$lib/template/Hero.svelte';
</script>
<Hero backgroundImage="$lib/img/innerhero.jpg" />

This won't work because only enchanced:img is preprocessed to convert the src into an import.

<script>
import Hero from '$lib/template/Hero.svelte';
import HeroImg from '$lib/img/innerhero.jpg';
</script>
<Hero backgroundImage="{HeroImg}" />

Getting closer! You forgot the ?enhanced query parameter though: https://kit.svelte.dev/docs/images#sveltejs-enhanced-img-dynamically-choosing-an-image

benmccann avatar Jan 08 '24 20:01 benmccann

Is there a minimum working example somewhere we can play with / build from? (I have a blog where posts contain many images and I want image clicks to load a light table/gallery of all images on the page.)

dmorgorg avatar Jan 10 '24 23:01 dmorgorg

I'm running into a similar issue, I can't get <enhanced:img> to work as a re-usable component.

Here's what my code looks like:

// +page.svelte

<script lang="ts">
	import Logo from '$lib/Logo.svelte';
	import companyLogo from '$lib/images/logo.png?enhanced';
</script>

<Logo name="Some Company" src={companyLogo} />
// Logo.svelte

<script lang="ts">
	export let name: string;
	export let src: any;
</script>

<enhanced:img class="logo" alt={`${name} logo`} {src} />

<style lang="scss">
	.logo {
		// https://kit.svelte.dev/docs/images#sveltejs-enhanced-img-intrinsic-dimensions
		width: var(--size);
		height: auto;
	}
</style>

The error this yields is:

2:37:50 PM [vite] Pre-transform error: Error while preprocessing /<path>/src/lib/Logo.svelte - Cannot read properties of undefined (reading 'trim')

If I use <enhanced:img> directly in the +page.svelte template everything works as expected.

For reference, I'm on "@sveltejs/enhanced-img": "^0.1.8"

fmaclen avatar Jan 17 '24 17:01 fmaclen

@fmaclen could you provide a repository that reproduces this error? It's quite time consuming for us to copy code samples into projects for debugging given the number of open issues we have and so we tend to skip issues without repositories out of necessity

benmccann avatar Jan 17 '24 17:01 benmccann

@benmccann here you go: https://github.com/fmaclen/enhancement-img-bug/

This is a npm create svelte@latest generated skeleton repo with TypeScript and nothing else. Only added dependency is @sveltejs/enhanced-img.

EDIT: just added a commented implementation in +page.svelte as the control group.

fmaclen avatar Jan 17 '24 18:01 fmaclen

I'm travelling army the moment and don't have ready access to my computer but, looking at Fernando's repo, mine is functionally identical. My use case uses mdsvex, with the same issue, but is present in svelte also.

Thanks for looking into this. This is very useful functionality.

Dave.

Sent from Proton Mail mobile

-------- Original Message -------- On Jan. 17, 2024, 23:17, Ben McCann wrote:

@.***(https://github.com/fmaclen) could you provide a repository that reproduces this error? It's quite time consuming for us to copy code samples into projects for debugging given the number of open issues we have and so we tend to skip issues without repositories out of necessity

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you commented.Message ID: @.***>

dmorgorg avatar Jan 18 '24 13:01 dmorgorg

I'm also running into this issue in combination with Markdoc preprocessing. I use <enhanced:img> within a wrapper Svelte component. src is provided by the wrapper component.

This is the error I got:

[plugin:vite-plugin-svelte] Error while preprocessing /path/to/src/lib/nodes/image.svelte - Cannot read properties of undefined (reading 'trim')

I guess the pre-processing step @benmccann mentioned further up is causing this.

I think @fmaclen's repro will also cover this case.

maiertech avatar Jan 22 '24 13:01 maiertech

I also have a similar issue when trying to use <enhanced:img> tag in sveltekit.

I have the latest versions:

  • "@sveltejs/enhanced-img": "^0.1.8"
  • "svelte": "^4.2.8",

What I'd like to do, is I'd like to pass strings from my backend, to the frontend, and simply render out a list of images using those strings.

Those string paths are references to images in my $libs/assets/ directory, see below:

<enhanced:img src={sheet.imagePath} alt={sheet.name} />

Where sheet.imagePath would populate as ''/src/lib/assets/placeholder.jpg"

When I simply hardcode this entire string, I have no issue, but then again, that defats the purpose of what I'm trying to do. Any help?

Thanks!

DOZBORNE avatar Jan 24 '24 04:01 DOZBORNE

I ran into the same issues with the enhanced:img component. I created a repository to collect the error cases I was able to identify:

<script lang="ts">
    import svelteLogo from '../images/svelte-logo-square.png?enhanced';
    const fileName = "svelte-logo-square";
    const fileExtension = "png";
    const pathToFileTemplate = `../images/${fileName}.${fileExtension}`;
    const pathToFile = '../images/svelte-logo-square.png';
</script>

<div>
    <!-- these work fine -->
    <enhanced:img src={svelteLogo} alt="" />
    <enhanced:img src='../images/svelte-logo-square.png' alt="" />
    
    <!-- these don't work -->
    <enhanced:img src={'../images/svelte-logo-square.png'} alt="" />
    <enhanced:img src={`../images/${fileName}.${fileExtension}`} alt="" />
    <enhanced:img src={pathToFileTemplate} alt="" />
    <enhanced:img src={pathToFile} alt="" />
</div>

flippbit avatar Feb 13 '24 16:02 flippbit

@benmccann It seems like enhance-img should be marked as a compiler plugin in the docs, since it is not able to (at least currently) resolve any dynamically added vars during runtime (which has a big use-case).

EDIT: consider using the excellent svelte-img instead!

MentalGear avatar Mar 11 '24 08:03 MentalGear

@fmaclen the example in your repository will work if you upgrade to 0.1.9. The issue was that we previously did not support the {src} shorthand, but that's been implemented now

benmccann avatar Mar 28 '24 23:03 benmccann

What I'd like to do, is I'd like to pass strings from my backend, to the frontend, and simply render out a list of images using those strings.

@DOZBORNE that is not supported. @sveltejs/enhanced-img is a build plugin and the file must be located on the filesystem during build. If you get the path from a backend, CMS, database, etc. that's a different usecase that will not be addressed by @sveltejs/enhanced-img. Please see https://kit.svelte.dev/docs/images#loading-images-dynamically-from-a-cdn for that usecase

benmccann avatar Mar 29 '24 00:03 benmccann

@maiertech I'd like to support markdown, but I imagine you probably need some plugin for each markdown system. There's some discussion of that in https://github.com/sveltejs/kit/issues/11132. You could see if the discussion there is helpful to you or file a new issue with a repository that shows what you're trying to do. Let's handle that one outside of this issue as it's a different case than most of what's discussed here

benmccann avatar Mar 29 '24 00:03 benmccann

@benmccann It seems like enhance-img should be marked as a compiler plugin in the docs, since it is not able to (at least currently) resolve any dynamically added vars during runtime (which has a big use-case).

I updated the docs in https://github.com/sveltejs/kit/pull/12058 to highlight this and you can now see them in https://kit.svelte.dev/docs/images

EDIT: consider using the excellent svelte-img instead!

The project is also a Vite plugin. It is built on top of vite-imagetools, which I maintain, just like @sveltejs/enhanced-img is. It is slightly different in that it uses an imported component rather than a preprocessor. That means that it only works with a syntax like import imageMeta from 'path/to/asset?as=run' whereas this project supports both an import syntax and referencing images in the src attribute like <enhanced:img src="path/to/asset" />

benmccann avatar Mar 31 '24 15:03 benmccann

I'm also hitting a similar issue: my goal is to automatically generate it for a list of images in the current folder in order to generate a static website. I tried to do:

import { error } from '@sveltejs/kit';
const imageModules = import.meta.glob("./img/*.jpg");

export const load = ({ params }) => {
  let imgs = [];
  for (const modulePath in imageModules) {
    imageModules[modulePath]().then(({ default: imageUrl }) => {
      imgs.push(modulePath);
    });
  }
  return {imgs: imgs}
};

and then:

<script>
  export let data;
</script>

<h1>Photos</h1>

<p>
  Welcome
  {#each data.imgs as img (img)}
     {img}: <enhanced:img src={img} alt="foo" />
  {/each}
</p>

but I also get an error… Is this supposed to be supported? (or can be made to be supported)

EDIT

Wait, just realized there is a section in the doc I'll try https://kit.svelte.dev/docs/images#sveltejs-enhanced-img-dynamically-choosing-an-image

EDIT2

I finally found the solution to create a mini-galery. It is just a bit technical cause import glob gives you a map file/promise, the promise import something, and then you still need to take the .default. Demo: in the +page.js:

import { error } from '@sveltejs/kit';

const imgsBlur = import.meta.glob(
  './img/*.{avif,gif,heif,jpeg,jpg,png,tiff,webp}',
  {
    query: {
      enhanced: true,
      blur: 3,
      w: 100
    }
  }
);

const imgs = import.meta.glob(
  './img/*.{avif,gif,heif,jpeg,jpg,png,tiff,webp}',
  {
    query: {
      enhanced: true,
    }
  }
);

export const load = async ({ params }) => {
  const imgsBlurLoaded = await Promise.all(Object.entries(imgsBlur).map(async ([key, value]) => {
    const img = await value();
    return [key, img.default]
  }));  
  const imgsLoaded = await Promise.all(Object.entries(imgs).map(async ([key, value]) => {
    const img = await value();
    return [key, img.default]
  }));  
  return {imgsBlur: Object.fromEntries(imgsBlurLoaded),
          imgsFull: Object.fromEntries(imgsLoaded)}
};

and in the page.svelte:

<script>
  export let data;
  let currentFullResImg;
</script>

<h1>Ma galerie</h1>

<p>
  Bienvenue dans ma galerie
  {#if currentFullResImg}
    <enhanced:img src={data.imgsFull[currentFullResImg]} alt="Some alt text"/>
  {/if}
  {#each Object.entries(data.imgsBlur) as [path, img] (path)}
    {path}: <enhanced:img src={img} alt="Some alt text" on:click={() => currentFullResImg = path} />
  {/each}
</p>

tobiasBora avatar May 09 '24 21:05 tobiasBora

Slightly cleaner version of @tobiasBora's fix that I used to make a custom component wrapper to enhance images in mdsvex (markdown importer).

Rolled the whole thing up into a single svelte component.

Posting here so if anyone else ends up at this issue trying to solve this problem for now, hopefully this helps.

<script context="module">
	const images = import.meta.glob(['../images/**/*.{avif,gif,heif,jpeg,jpg,png,tiff,webp}'], {
		eager: true,
		query: { enhanced: true }
	});

	const get_full = (desired_image) => {
		desired_image = '..' + desired_image;
		return images[desired_image].default;
	};
</script>

<script>
	export let src;
	export let alt = 'No alt available';
	const image = get_full(src);
</script>

<figure class="image">
	<enhanced:img src={image} {alt} />
</figure>

dominicdoty avatar Jul 04 '24 00:07 dominicdoty

I'm sorry I am still really struggling with this one when attempting to pass enhanced images to a component. I can't seem to make it work with vite or enhanced-img

The simple use case is,

+page.svelte

<script lang="ts">
    import aqLogo from "$lib/assets/images/logo/aq.webp?enhanced";
    import SimpleDark from "$lib/components/sections/logoclouds/SimpleDark.svelte";
</script>

<SimpleDark
    heading = "Recognized by top industry leaders for excellence in medical education"
    images = {[
        { src:  {aqLogo}, alt: "AQ Solutions" }
         ....
    ]}

SimpleDark.svelte

<script lang="ts">
  export let heading : string;
  type ImageProps = {
      src: any;
      alt?: string;
  };
  export let images : ImageProps[];

</script>

...

<enhanced:img src={image.src} alt={image.alt} class="col-span-2 max-h-12 w-full object-contain lg:col-span-1" />

This just doesn't work with the following errors

TypeError: Cannot read properties of undefined (reading 'src')

Has anyone been able to make enhanced-img work with Components?

mafifi avatar Aug 02 '24 14:08 mafifi

Looks like it's mad that you pass img to the instantiation but declare the parameter as image in Component.

Also I'm not sure if enhanced img supports SVG as it's a vector format and not a typical bitmap image, you might run into that problem after fixing the naming issue.

dominicdoty avatar Aug 02 '24 14:08 dominicdoty

Looks like it's mad that you pass img to the instantiation but declare the parameter as image in Component.

Also I'm not sure if enhanced img supports SVG as it's a vector format and not a typical bitmap image, you might run into that problem after fixing the naming issue.

Sorry, that's what happens when you try and create a simple example...

Placed the real code in...

mafifi avatar Aug 02 '24 14:08 mafifi

Do you have an extra set of curly braces around your images value (aqlogo) you're passing to the component?

I'd maybe try breakpointing or console.log-ing the value of images in your component and see what the value looks like.

I'm assuming there's an omitted "for image of images" in the component also?

dominicdoty avatar Aug 02 '24 14:08 dominicdoty

Do you have an extra set of curly braces around your images value (aqlogo) you're passing to the component?

I'd maybe try breakpointing or console.log-ing the value of images in your component and see what the value looks like.

I'm assuming there's an omitted "for image of images" in the component also?

Sorry I wasted your time, you're correct, there were a slew of issues here in addition to the extra curly you mentioned

  • Extra curly
  • Can't use svg with @sveltejs/enhanced-img
  • Can't mix and match (so even when I changed my ImageProps.src : string | Picture, <enhanced:img/> hated it
  • Can't use $lib for svg images, had to move it under static with a different path

Regardless, my compliments to the package creators. Given the site I am working on will have north of a hundred images by the time I am finished, it's a life saver having this automated at build even with all its limitations.

mafifi avatar Aug 02 '24 16:08 mafifi