tailwindlabs / tailwindcss

A utility-first CSS framework for rapid UI development.
https://tailwindcss.com/
MIT License
83.37k stars 4.22k forks source link

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

Closed spaceemotion closed 8 hours ago

spaceemotion commented 5 days ago

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/enhanced-resolve@5.17.1/node_modules/enhanced-resolve/lib/Resolver.js:564:18)
  astro:vite     at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/enhanced-resolve@5.17.1/node_modules/enhanced-resolve/lib/Resolver.js:656:15
  astro:vite     at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/enhanced-resolve@5.17.1/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/tapable@2.2.1/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
  astro:vite     at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/enhanced-resolve@5.17.1/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/tapable@2.2.1/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:27:1)
  astro:vite     at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/enhanced-resolve@5.17.1/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js:89:43
  astro:vite     at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/enhanced-resolve@5.17.1/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/tapable@2.2.1/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1)
  astro:vite     at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/enhanced-resolve@5.17.1/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/enhanced-resolve@5.17.1/node_modules/enhanced-resolve/lib/Resolver.js:564:18)
    at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/enhanced-resolve@5.17.1/node_modules/enhanced-resolve/lib/Resolver.js:718:5
    at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/enhanced-resolve@5.17.1/node_modules/enhanced-resolve/lib/Resolver.js:718:5
    at /home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/enhanced-resolve@5.17.1/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js:89:43
    at eval (eval at create (/home/spaceemotion/code/nc-website/novelcrafter/node_modules/.pnpm/tapable@2.2.1/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 commented 5 days ago

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

https://github.com/tailwindlabs/tailwindcss/blob/7b19b00b576973356f5b5c9ac978df3ed58bc2d6/packages/%40tailwindcss-node/src/compile.ts#L201-L212

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

philipp-spiess commented 5 days ago

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:

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 commented 5 days ago

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 :)

philipp-spiess commented 8 hours ago

@spaceemotion I was trying to create a reproduction with your code above but it seems that Vite, even without @tailwindcss/vite, will not be able to import virtual CSS modules. Check this out: https://github.com/philipp-spiess/vite-virtual-css-modules

This is using only Vite 6.

Were you able to use this feature before you upgraded to Tailwind 4?

There is still the need to use the Vite resolver for css modules for issues like #15159 though, but it won't resolve your concrete issue I'm afraid.

spaceemotion commented 8 hours ago

@philipp-spiess thanks for the repo. That is indeed weird. When the virtual module ID is being used from the index.html it all works, but not from within the CSS file.

I have not tested this with a TailwindCSS 3 setup yet, the virtual module is new for this project as we're upgrading the tech in general. I'll do a bit more digging, whether it might be something with the way vite handles CSS files + postcss internals.

I also just checked and the issue is also there with Vite v5, so it's not something with the latest v6 beta.

spaceemotion commented 8 hours ago

Okay, so here's what I found:

  1. Vite uses postcss-import, which in turn has custom resolve behavior as well: https://github.com/vitejs/vite/blob/cc55e36dd39fef134568f53acc66514cbb7175ea/packages/vite/src/node/plugins/css.ts#L1313-L1344
  2. This means that virtual modules in CSS do not seem to be a thing, really
  3. However, if I add the import to the index.html, it will resolve correctly, and even merge the files during build, which would be a possible workaround.