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

[v4] Vite plugin providing a virtual file can't be resolved from within main CSS file #15047

Open
spaceemotion opened this issue Nov 19, 2024 · 3 comments

Comments

@spaceemotion
Copy link

spaceemotion commented Nov 19, 2024

What version of Tailwind CSS are you using?

@tailwindcss/cli 4.0.0-alpha.33
└── tailwindcss 4.0.0-alpha.33
@tailwindcss/typography 0.5.15
└── tailwindcss 4.0.0-alpha.33 peer
@tailwindcss/vite 4.0.0-alpha.33
└── tailwindcss 4.0.0-alpha.33
tailwindcss 4.0.0-alpha.33

What build tool (or framework if it abstracts the build tool) are you using?
astro 5.0.0-beta.8, using vite 6.0.0-beta.10

What version of Node.js are you using?
v20

What browser are you using?
Chrome/Edge/Brave

What operating system are you using?
Linux/Windows

Reproduction URL
We got the following stylesheet:

@import 'tailwindcss';
@import 'virtual:fontawesome.css';

The custom vite plugin is quite simple:

import { dom } from '@fortawesome/fontawesome-svg-core';
import type { Plugin } from 'vite';

const VIRTUAL_MODULE_ID = 'virtual:fontawesome.css';
const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID;

export function fontAwesomeCss(): Plugin {
  return {
    name: 'vite-plugin-fontawesome-css',

    resolveId(id) {
      if (id !== VIRTUAL_MODULE_ID) {
        return;
      }

      return RESOLVED_VIRTUAL_MODULE_ID;
    },

    load(id) {
      if (id !== RESOLVED_VIRTUAL_MODULE_ID) {
        return;
      }

      return {
        code: `@layer base { ${dom.css()} }`,
      };
    }
  };
}

We need the fontawesome CSS to be included in the base layer of our CSS, since we want to be able to overwrite some things (like icon size) via the utility classes. We also don't want to include the CSS on every page, and instead have it in the global stylesheet.

Sadly, FontAwesome does not offer a simple static CSS we can use, since the classes are built at run-/build-time.

Describe your issue
I ran astro dev with verbose enabled:

  vite:transform 9.96ms /src/layouts/default.astro +12ms
  vite:load 0.28ms [fs] /src/styles/main.css +14ms
  astro:vite Error when evaluating SSR module /home/spaceemotion/code/nc-website/novelcrafter/src/pages/courses/[...course]/[...lesson].astro:
  astro:vite |- Error: Can't resolve 'virtual:fontawesome.css' in '/home/spaceemotion/code/nc-website/novelcrafter/src/styles'
  astro:vite     at finishWithoutResolve (/home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/[email protected]/node_modules/enhanced-resolve/lib/Resolver.js:564:18)
  astro:vite     at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/[email protected]/node_modules/enhanced-resolve/lib/Resolver.js:656:15
  astro:vite     at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/[email protected]/node_modules/enhanced-resolve/lib/Resolver.js:718:5
  astro:vite     at eval (eval at create (/home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/[email protected]/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
  astro:vite     at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/[email protected]/node_modules/enhanced-resolve/lib/Resolver.js:718:5
  astro:vite     at eval (eval at create (/home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/[email protected]/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:27:1)
  astro:vite     at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/[email protected]/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js:89:43
  astro:vite     at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/[email protected]/node_modules/enhanced-resolve/lib/Resolver.js:718:5
  astro:vite     at eval (eval at create (/home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/[email protected]/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
  astro:vite     at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/[email protected]/node_modules/enhanced-resolve/lib/Resolver.js:718:5
  astro:vite  +0ms
23:17:35 [ERROR] Can't resolve 'virtual:fontawesome.css' in '/home/spaceemotion/code/nc-website/novelcrafter/src/styles'
  Stack trace:
    at finishWithoutResolve (/home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/[email protected]/node_modules/enhanced-resolve/lib/Resolver.js:564:18)
    at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/[email protected]/node_modules/enhanced-resolve/lib/Resolver.js:718:5
    at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/[email protected]/node_modules/enhanced-resolve/lib/Resolver.js:718:5
    at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/[email protected]/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js:89:43
    at eval (eval at create (/home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/[email protected]/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)

From what I can tell, the resolving is being handled by tailwind, and not Astro/Vite? I tried to see if there is a way to still make this worth, but not sure why the virtual module isn't being picked up - the same approach (using virtual modules) works without issue in other parts of the pipeline where we just import things into Vue/Astro/JS.

https://stackblitz.com/edit/vite-u8kysb?file=vite.config.mjs (doesn't load inside of stackblitz due to oxide, but has a minimal example)

@spaceemotion
Copy link
Author

From digging through the source code a bit, it seems that the module resolution is actually done by this bit - if I understand correctly:

function runResolver(
resolver: EnhancedResolve.Resolver,
id: string,
base: string,
): Promise<string | false | undefined> {
return new Promise((resolve, reject) =>
resolver.resolve({}, base, id, {}, (err, result) => {
if (err) return reject(err)
resolve(result)
}),
)
}

Does that mean, custom virtual modules are not supported in CSS files?

@philipp-spiess
Copy link
Member

Hey! @spaceemotion. Yeah that's right we don't consult the Vite module resolution when resolving imports right now. Two things you can try to work around it:

  • Is it possible to load a static css file into a layer like so? @import "fontawesome/fontawesome.css" layer(base) or are they not providing any static files for you?
  • You can use the Tailwind postcss plugin instead of the Vite one and then use postcss-import before the Tailwind plugin as a workaround.

I agree with you that this is unexpected though and that the Vite plugin should use the Vite module resolution system and not a custom one.

@spaceemotion
Copy link
Author

spaceemotion commented Nov 20, 2024

The SVG-Core version of FontAwesome does not provide a static CSS, as they generate it on the fly based on the config. They usually add the styles when FontAwesome loads, but since we want to build a (mostly) static site, this means that icons would look weird if the JavaScript hasn't loaded yet.

For the time being I had it generate a static version, and "hard-coded" an import to that one. Not ideal, but...

Thanks for the tip with the postcss alternative, but I think the current workaround will work until the vite plugin supports the additional module resolution :)

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

No branches or pull requests

2 participants