Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic import #5

Closed
yamiteru opened this issue Mar 7, 2021 · 10 comments
Closed

Dynamic import #5

yamiteru opened this issue Mar 7, 2021 · 10 comments

Comments

@yamiteru
Copy link

yamiteru commented Mar 7, 2021

Will it be possible in the v3 to use import("./image.jpg?width=100")?

Why would this be useful? Let's say I create imageLoader function like this:

const imageLoader = (sizes: number[]) => {
    return (src: string) => {
        const res = {};
    
        sizes.forEach((size: number) => 
            res[size] = import(`${src}?width=${size}`)
        );

        return res;
    };
};

I then use it to create image loader with predefined widths like this:

const img = imageLoader([100, 200, 300]);

Then when I need to import images anywhere in my app I do it like this:

{
    main: img("./main.jpg"),
    other: [
        img("./01.jpg"),
        img("./02.jpg"),
        img("./03.jpg")
    ]
}

The value of the main prop would be something along the lines of this:

{
    aspectRatio: 0.6,
    original: "./main.jpg",
    variants: {
        jpg: {
            100: "cache/main.jpg?width=100",
            200: "cache/main.jpg?width=200",
            300: "cache/main.jpg?width=300"
        }
    }
}

I could then use it in JSX like this:

return (
    <div>
        <div style={`height:0;padding-bottom:${image.main.aspectRatio*100}%;`} />
        <img src={image.main.variants.jpg[parentWidth]} />
    </div>
);

Thanks to tree-shaking the resulting code would be minimal.

All of this is not possible using normal imports like:

import main from "/image.jpg?width=100";
@JonasKruckenberg
Copy link
Owner

The answer is the famous "Yes! but..." 😄
Yes it works the way you proposed right out of the box, but there are a few caveats:

  1. Glob import is very picky.
    Lets say you have an import like this: import(`assets/${id}.jpg`).
    Vite will try it's best to resolve the glob pattern and infer all possible values for id. It it is able to do so, then no problem, your import statement works like a charm, but in my experience this is not always the case.
    Vite seems to have some issues tracking the values through a lot of function calls and the like, so be careful.
    (See https://vitejs.dev/guide/features.html#glob-import for more info)

  2. Avoid combinatory hell
    Let's say you use your function like you said: const img = imageLoader([100, 200, 300]) and you then call it with 4 images,
    a.jpg,b.jpg,c.jpg and d.jpg. Vite-imagetools now has to calculate 4 * 3 = 12 different images and this increases linearly with the number of images you import, so img is now a very expensive function to run.
    This may be irrelevant to you, because you know full well what you're importing, but just keep an eye out.
    (This turns extra crazy once you try to generate different output formats as well. For example the query ${id}?width=100,200,300&format=webp,avif will generate 6 images per input image. So 4 * 6 = 24 in the case above)

But all in all it's totally possible!

@yamiteru
Copy link
Author

yamiteru commented Mar 8, 2021

I see I've read a few things about vite and I stumbled upon glob import.

Is width=100,200,300&format=webp,avif just a pseudocode or can I import images like this? If yes it makes my image loader even simpler.

@JonasKruckenberg
Copy link
Owner

The comma separated syntax is new with v3 it does not work with the current version.
But you can check out the next branch if you're curious.
It does not have real docs yet and it may be broken, so beware of the bugs 😄

@yamiteru
Copy link
Author

yamiteru commented Mar 8, 2021

Huuu that's really cool! Thank you

@JonasKruckenberg
Copy link
Owner

The next branch is now publishing on npm under the tag 'next'.
You can check it out by running npm install --save-dev vite-imagetools@next

This issue is addressed in that release.

@yamiteru
Copy link
Author

So it looks my imageLoader falls exactly into the dynamic imports limitations https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#limitations

This is also not possible:

const query = "format=webp&width=1920&metadata";
const imageDir = "../../../images";
const imageNames = [
	"01.jpg", "02.jpg", "03.jpg", "04.jpg", "05.jpg"
];
const images = imageNames
	.map(fileName => import(`${imageDir}/${fileName}?${query}`));

Not even this:

const images = {
	"01.jpg": import(`${imageDir}/01.jpg?${query}`),
	"02.jpg": import(`${imageDir}/02.jpg?${query}`),
	"03.jpg": import(`${imageDir}/03.jpg?${query}`),
	"04.jpg": import(`${imageDir}/04.jpg?${query}`),
	"05.jpg": import(`${imageDir}/05.jpg?${query}`)
};

Only this works:

const images = {
	"01.jpg": import(`../../../images/01.jpg?${query}`),
	"02.jpg": import(`../../../images/02.jpg?${query}`),
	"03.jpg": import(`../../../images/03.jpg?${query}`),
	"04.jpg": import(`../../../images/04.jpg?${query}`),
	"05.jpg": import(`../../../images/05.jpg?${query}`)
};

@michaeloliverx
Copy link

Does it work when you create an alias for $images which maps to your images directory?

const images = {
	"01.jpg": import(`$images/01.jpg?${query}`),
	"02.jpg": import(`$images/02.jpg?${query}`),
	"03.jpg": import(`$images/03.jpg?${query}`),
	"04.jpg": import(`$images/04.jpg?${query}`),
	"05.jpg": import(`$images/05.jpg?${query}`)
};

@urbantrout
Copy link

I am using this plugin and it works fine in development mode. But whenever I try to build via adapter-static the dynamic import fails. I think it has something to do with this limitation:

https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#imports-must-end-with-a-file-extension

As soon as I remove the query string from the dynamic import, the build succeeds. But without any transformations obviously…

@kylesloper
Copy link

As soon as I remove the query string from the dynamic import, the build succeeds. But without any transformations obviously…

Currently fighting with this, did you find a workaround?

@AaronNBrock
Copy link

AaronNBrock commented Apr 26, 2023

Perhaps some jank, but here's my workaround:

const images = import.meta.glob('../example/path/*.png', {
	query: { width: '100,200,300' },
	eager: true,
});

return {
	image: images[`../example/path/${myVar}.png`]?.default || null,
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants