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

🐛 BUG: Dynamic import fails with paths unknown at compile time for astro build #3373

Closed
1 task
cpetrov opened this issue May 15, 2022 · 14 comments
Closed
1 task
Assignees
Labels
- P3: minor bug An edge case that only affects very specific usage (priority) ecosystem: upstream Upstream package has issue

Comments

@cpetrov
Copy link

cpetrov commented May 15, 2022

What version of astro are you using?

1.0.0-beta.28

Are you using an SSR adapter? If so, which one?

no

What package manager are you using?

npm

What operating system are you using?

Mac

Describe the Bug

Using a dynamic import (await import(...)) with paths that are unknown at compile time fails for astro build but works for astro start.

---
const teideImg = (await import('../images/teide.jpg')).default
---
<img src={teideImg} />

... works in all cases (https://codesandbox.io/s/bold-tesla-ihmnuh), while:

---
function getImagePath() {
  return '../images/teide.jpg';
}
const teideImg = (await import(getImagePath())).default
---
<img src={teideImg} />

... fails for astro build with Cannot find module '/sandbox/images/teide.jpg' imported from /sandbox/dist/entry.mjs (https://codesandbox.io/s/loving-bhaskara-el5c9e) but works for astro dev (https://codesandbox.io/s/intelligent-lucy-lpzief).

Link to Minimal Reproducible Example

https://codesandbox.io/s/loving-bhaskara-el5c9e

Participation

  • I am willing to submit a pull request for this issue.
@natemoo-re natemoo-re added the needs discussion Issue needs to be discussed label May 19, 2022
@natemoo-re
Copy link
Member

I believe this is a known limitation, but I'll make sure to confirm this with the rest of the core team. We definitely need to disable this or warn during dev if we can't make it work in build.

Internally, I'm fairly certain that Vite is only able to scan dynamic imports using string literals or occasionally dynamic strings using @rollup/plugin-dynamic-import-vars, but something fully dynamic like a function call won't work.

One potential workaround is to use Vite's import.meta.glob() feature to ensure everything in your ../images/ directory is available in this file. This should allow you to dynamically reference a filepath constructed at runtime, provided it exists.

@natemoo-re
Copy link
Member

Action item from Community Call

  • Looks like Vite has a built-in warning for this! We seem to be swallowing it, but shouldn't be.
  • We want to remove any differences between dev and build if possible, this is a larger ongoing task.

@natemoo-re natemoo-re added - P3: minor bug An edge case that only affects very specific usage (priority) s1-small and removed needs discussion Issue needs to be discussed labels May 31, 2022
@simonwiles
Copy link

See also #2647. It's especially frustrating when you have everything working really nicely in dev and it won't build...

@cpeaustriajc
Copy link

cpeaustriajc commented Aug 9, 2022

I had this issue when using <Picture/> and <Image/> component also when I was trying to get all of the markdown files in my ./src/pages/posts folder, a workaround that I did was to make a function that gets the actual url of the image in the ./src/assets folder then check if I was on dev or prod, not the prettiest solution but it worked for me.

The problem seems to happen when astro builds your site as it does not use root relative path, so #3997 should also fix this issue.

This is my directory structure:

.
├── astro.config.mjs
├── env.d.ts
├── LICENSE
├── package.json
├── pnpm-lock.yaml
├── public
│   ├── android-chrome-192x192.png
│   ├── android-chrome-512x512.png
│   ├── apple-touch-icon.png
│   ├── favicon-16x16.png
│   ├── favicon-32x32.png
│   ├── favicon.ico
│   ├── robots.txt
│   └── site.webmanifest
├── src
│   ├── assets
│   │   ├── astro.png
│   │   └── me.jpeg
│   ├── components
│   │   ├── Footer.astro
│   │   ├── Head.astro
│   │   ├── Header.astro
│   │   └── Posts.astro
│   ├── layouts
│   │   ├── DefaultLayout.astro
│   │   └── MarkdownLayout.astro
│   └── pages
│       ├── 404.astro
│       ├── about.astro
│       ├── blog.astro
│       ├── index.astro
│       └── posts
│           └── astro-is-amazing.md
├── tailwind.config.cjs
└── tsconfig.json

In my markdown file:

---
layout: ../../layouts/MarkdownLayout.astro
title: Astro Is Amazing!
description:
    Building my personal site with Astro is so good! I would recommend Astro if you want to rebuild
    your personal site!
date: Aug 4, 2022
image_url: /assets/astro.png
image_alt: The banner for Astro, a static site generator
---


Bla bla bla, markdown post

In my Posts.astro component file:

---
// For TypeScript IntelliSense
interface Frontmatter {
    description: string;
    title: string;
    date: Date
    image_url: string;
    image_alt: string;
}

const post = await Astro.glob<Frontmatter>("../pages/posts/*.md")

const correctURL = (post: MarkdownInstance<Frontmatter>) => import.meta.env.PROD ? post.frontmatter.image_url : import("../" + post.frontmatter.image_url) as unknown as string
---
<ul class="py-4 px-0 list-none">
{posts.map((post) => (
    <li class="my-4">
        <figure class="rounded-md dark:bg-slate-900 bg-slate-100 max-w-[640px] h-fit p-4">
            <h3 class="text-2xl font-bold py-4">
                {post.frontmatter.title}
            </h3>
            <Picture src={getCorrectURL(post)} alt={post.frontmatter.image_alt} widths={[1280, 1920]}
                sizes="(max-width: 800px), 100vw, 800px" aspectRatio="16:9" />
            <figcaption class="py-4">{post.frontmatter.description}</figcaption>
            <a href={post.url} class="text-blue-800 dark:text-blue-300 underline">
                See more &RightArrow;
            </a>
        </figure>
    </li>
    ))}
</ul>

@ChristineTham
Copy link

ChristineTham commented Aug 13, 2022

I had to resort to this really ugly hack ....

---
import { Image } from '@astrojs/image/components'

export interface Props {
  image?: string
}

const { image } = Astro.props as Props

const defaultImage = '../images/default.png'
const images = import.meta.glob('../images/*/*')
const imagesrc = await images[image || defaultImage]() as Record<string,any>
---
<Image  src={imagesrc.default} />

@obennaci
Copy link
Contributor

obennaci commented Aug 18, 2022

Using Vite's import.meta.glob works. I guess the snippet/typing could be improved a little with:

---
// ...
const images = import.meta.glob<ImageMetadata>('../images/*/*', { import: 'default' })
const imagesrc = await images[image || defaultImage]()
---
<Image src={imagesrc} />

@bluwy
Copy link
Member

bluwy commented Aug 18, 2022

Looks like the reason the warning isn't reported by Vite is because it only reports for non-ssr transforms, in which case we're running in SSR instead. I feel like it's still worth providing a warning in SSR regardless, so I'll test out this change in Vite.

@ChristineTham
Copy link

ChristineTham commented Aug 19, 2022

Using Vite's import.meta.glob works. I guess the snippet/typing could be improved a little with:

---
// ...
const images = import.meta.glob<ImageMetadata>('../images/*/*', { import: 'default' })
const imagesrc = await images[image || defaultImage]()
---
<Image src={imagesrc} />

Thanks - that is slightly cleaner and avoids the ugly typecast.

@bluwy
Copy link
Member

bluwy commented Aug 26, 2022

This should be fixed upstream in Vite 3.0.9 which the latest Astro version uses. It will now warn when the import can't be resolved in dev mode as it needs to be static in order for Vite to "link" it.

@bluwy bluwy closed this as completed Aug 26, 2022
@bluwy bluwy added the ecosystem: upstream Upstream package has issue label Aug 26, 2022
@jtomek
Copy link

jtomek commented Sep 3, 2022

Vite import has certain limitations. One of them is that that dynamic imports must end with a file extension.

<Image
  src={import(`../data/partners/${logoName}.png`)}
/>

Try passing only a variable without its extension. That worked for me. GL.

@ciokan
Copy link

ciokan commented Sep 18, 2022

Vite import has certain limitations. One of them is that that dynamic imports must end with a file extension.

<Image
  src={import(`../data/partners/${logoName}.png`)}
/>

Try passing only a variable without its extension. That worked for me. GL.

Still doesn't work in the frontmatter/component script part of the astro file. It is passed to getPicture though:

        features.features.map((feature, index) => {
		promises.push(
			getPicture({
				src: import(
					`../static/images/screenshots/${feature.identifier}.png`
				),
				widths: sizes,
				aspectRatio: 1400 / 947,
				formats: ["png"],
			}).then((picture) => {
				pictures[feature.identifier] = picture;
			})
		);
	});

@jtomek
Copy link

jtomek commented Sep 19, 2022

What's the error message?

@fubits1
Copy link

fubits1 commented Jan 26, 2023

Maybe helpful to whomever lands here: As a workaround (working with Astro 2.0) I wrote a utility function to satisfy Vite's explicit extension requirement. Receive filename, extract extension, return dynamic import with explicit extension. Can confirm that this works with a plain Astro (2.0) component with 15 images.

import path from "path";
function dynamicViteAssetImport(imageFileName) {
  const filename = path.parse(imageFileName);
  const name = filename.name;
  const ext = filename.ext;
  switch (ext) {
    case ".webp":
      return import(`../assets/${name}.webp`);
    case ".jpg":
      return import(`../assets/${name}.jpg`);
    case ".png":
      return import(`../assets/${name}.png`);
    case ".svg":
      return import(`../assets/${name}.svg`);
    case ".gif":
      return import(`../assets/${name}.gif`);
    case ".avif":
      return import(`../assets/${name}.avif`);
    case ".jpeg":
      return import(`../assets/${name}.jpeg`);
    default:
      return import(`../assets/${name}.jpg`);
  }
}

Consume with:

 <Image src={dynamicViteAssetImport(imageFileName)}  />

@kireerik
Copy link

Maybe it would make sense to improve the current warning by adding something like this to it as well:

It may not work as expected during (Astro) `build`.

 
Full example:

The above dynamic import cannot be analyzed by Vite. It may not work as expected during (Astro) `build`.
See https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#limitations for supported dynamic import formats. If this is intended to be left as-is, you can use the /* @vite-ignore */ comment inside the import() call to suppress this warning.

 
Current warning:

The above dynamic import cannot be analyzed by Vite.
See https://github.com/rollup/plugins/tree/master/packages/dynamic-import-vars#limitations for supported dynamic import formats. If this is intended to be left as-is, you can use the /* @vite-ignore */ comment inside the import() call to suppress this warning.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
- P3: minor bug An edge case that only affects very specific usage (priority) ecosystem: upstream Upstream package has issue
Projects
None yet
Development

No branches or pull requests