lambda-packages icon indicating copy to clipboard operation
lambda-packages copied to clipboard

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

Open cpetrov opened this issue 3 years ago • 4 comments

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.

cpetrov avatar May 15 '22 14:05 cpetrov

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 avatar May 19 '22 23:05 natemoo-re

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 avatar May 31 '22 18:05 natemoo-re

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

simonwiles avatar Jul 20 '22 19:07 simonwiles

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>

cpeaustriajc avatar Aug 09 '22 08:08 cpeaustriajc

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} />

ChristineTham avatar Aug 13 '22 23:08 ChristineTham

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} />

obennaci avatar Aug 18 '22 13:08 obennaci

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.

bluwy avatar Aug 18 '22 15:08 bluwy

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.

ChristineTham avatar Aug 19 '22 07:08 ChristineTham

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 avatar Aug 26 '22 14:08 bluwy

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.

jtomek avatar Sep 03 '22 13:09 jtomek