withastro / astro

The web framework for content-driven websites. ⭐️ Star to support our work!
https://astro.build
Other
46.54k stars 2.47k forks source link

`astro dev` & `astro build` crash with `Unknown file extension ".svg"` when importing an SVG inside a dependency #12299

Open codethief opened 1 week ago

codethief commented 1 week ago

Astro Info

pnpm exec astro info
Astro                    v4.16.7
Node                     v20.14.0
System                   Linux (x64)
Package Manager          pnpm
Output                   static
Adapter                  none
Integrations             @astrojs/vue

If this issue only occurs in one browser, which browser is a problem?

/

Describe the Bug

I have a Vue component library in which some components import SVG files as ?url, so that they can be used inside e.g. <svg><use href="…" /></svg>. In that library, as a workaround for https://github.com/vitejs/vite/issues/3295 and https://github.com/vitejs/vite/issues/4454, I have manually configured Vite to emit the SVGs as standalone asset files and preserve the ?url imports inside the emitted .js files.

My Astro project depends on that library. But when I import one of those components that import an SVG, astro dev and astro build fail with

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".svg" for […]/vitejs-vue-component-library-with-assets/astro-consumer/node_modules/.pnpm/my-library@file+..+library+my-library-0.0.0.tgz_typescript@5.6.3/node_modules/my-library/dist/assets/logo.svg
    at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:160:9)
    at defaultGetFormat (node:internal/modules/esm/get_format:203:36)
    at defaultLoad (node:internal/modules/esm/load:143:22)
    at async ModuleLoader.load (node:internal/modules/esm/loader:396:7)
    at async ModuleLoader.moduleProvider (node:internal/modules/esm/loader:278:45)

Interestingly, this error does not occur if I pnpm link the library into my Astro project (as opposed to installing the tarball or literally copying the library folder into my Astro project's node_modules).

What's the expected result?

The SVG import should work as expected. Notably, it works fine if I use a pure Vite dev server without Astro, see /consumer in the repo linked below. Moreover, in my real-life Astro project things worked fine when I didn't transpile the library components to JS first and instead exposed the raw Vue components and imported them in my Astro project. (However, this is not an option for us for various reasons.)

Link to Minimal Reproducible Example

https://github.com/codethief/vitejs-vue-component-library-with-assets/tree/7e12eb72ef59036992976cf892f0fda583f141a0?tab=readme-ov-file#caveat-in-astro-consumer

Impact

We have dozens of SVGs in our library and cannot possibly inline them in our Vue components because

In other words: For us there is simply no way around keeping SVGs in separate asset files. The present bug currently prevents this from working, unfortunately. If anyone happens to have a workaround, please let me know!

Participation

ematipico commented 1 week ago

This feels like an issue with how you set up your monorepo, I am not sure this is an Astro issue. As you said, using pnpm link fixed the issue, so probably there's something incorrect with how you sep your monorepo and your dependencies/peer dependencies.

bluwy commented 1 week ago

When you're linking or copying the library locally, you're doing the inverse of https://vite.dev/guide/dep-pre-bundling.html#monorepos-and-linked-dependencies. The issue here is that since the component library uses bundler specific features, like importing SVGs, that library needs to be processed by the bundler.

By default, that does not happen as most libraries are generic and work everywhere, however to opt-in a library to be processed by the bundler, you can configure vite.optimizeDeps.exclude: ['the-library-name'] or vite.ssr.noExternal: ['the-library-name']. The former applies to the browser so it's not prebundled by Vite, the latter applies to the server-side so it's not externalized (loaded by plain node).

codethief commented 5 days ago

@ematipico I don't think it's a monorepo issue. The issue shows up when I install the library from a tarball, not from the monorepo / a relative path. Besides, my actual setup is not a monorepo in the first place. I just put every together in a single repo for the purposes of providing a reproducing example.

codethief commented 5 days ago

@bluwy

The issue here is that since the component library uses bundler specific features, like importing SVGs, that library needs to be processed by the bundler.

Yes, that was a conscious decision because all our downstream projects use Vite. And in a pure Vite project it does work, it's only in Astro projects (and only in the dev server) where it's failing.

In any case, thanks for your suggestions! I will give them a try! That being said, can you make sense of the fact that everything works fine in a regular Vite/Vue project and also in an Astro/Vite/Vue production build but not in the Astro dev server?

codethief commented 3 days ago

In any case, thanks for your suggestions! I will give them a try!

Following up on this, it seems adding the library to the downstream project's ssr.noExternal setting indeed does the trick! However, I still want to do some further testing, so consider this a preliminary report. Besides, I'm still surprised the production build worked without ssr.noExternal and in a pure Vite project (without Astro) there was no issue, either, so I'd still like to understand this better.

bluwy commented 1 day ago

With Astro, both dev and build didn't work for me without ssr.noExternal.

For plain Vite, it works because you'd eventually bundle everything for the browser, so SVG imports get transformed on the way during build. In dev, usually you have to specify optimizeDeps.exclude for your library, but I think it coincidentally worked because you're not using some Vite features that would've triggered it, so not having that also worked in dev in Vite.