vanilla-extract-css / vanilla-extract

Zero-runtime Stylesheets-in-TypeScript
https://vanilla-extract.style
MIT License
9.64k stars 295 forks source link

New vite compiler cache skips adapter registration #1424

Open otaviomad opened 5 months ago

otaviomad commented 5 months ago

Describe the bug

We've been using Vanilla Extract to build our styles on our Design System for a few versions now. We have, admittedly, a very specific setup:

  1. We have style packages that are separated from our components, so each components gets a style package, whose dist output is a pre-transpiled css.js file (that does not run through the vanilla extract plugin just yet, transpilation is ts -> js only)
  2. Each individual style package may extend, use and in general depend on other style packages
  3. Our component library has a hard dependency on setting up the Vanilla Extract plugin on their bundlers, facilitated by our own wrapper (which also does some extra non-ve related stuff)

Not all of those details are relevant to this problem, however I think the main one is having packages depending on packages. Pre-4.0 compiler worked nicely with this approach, but once we updated to 4.0, we started having issues. Specifically, when the dependency css is required more than once, the class name registration is skipped. What would happen would be something like this:

  1. Menu button styles depend on button styles, import them and extend them. Menu button styles work fine.
  2. Stepper styles depend on button styles, import them and extend them. Stepper styles look wrong, because instead of the classnames being registered in the adapter, the output is just the raw strings from the vanilla extract compilation. Classes are not prepended with dots and multi-class styles are not summarized to the main class name in the css.

After some investigation, I believe I may have found the cause of the issue. I am somewhat familiar with Vanilla Extract's internals (having done some investigation on other issues in the past) so I think the cause may be here:

https://github.com/vanilla-extract-css/vanilla-extract/blob/master/packages/integration/src/compiler.ts

On line 244, we check if there is a cached built file. If there is, we return the pre-built file (which is only classnames and the css side-effect import). If there isn't then we start building the adapter and then we build it, registering the classNames and composition. What I think may help (heavy emphasis on think, I'm not a maintainer), is that the cached file should keep in memory all the compositions and classname registry, and when it is accessed, the adapter should also receive those informations that are cached.

I still haven't had time to build a proper repro of this, so if there is an absolute necessity to do so I will invest the time, but for now I hope this explanation will suffice.

Reproduction

https://github.com/otaviomad/ve-bug-repro

System Info

System:
    OS: macOS 14.4.1
    CPU: (12) arm64 Apple M3 Pro
    Memory: 97.53 MB / 18.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.13.1 - ~/Library/Caches/fnm_multishells/35131_1718286735138/bin/node
    Yarn: 4.1.1 - ~/Library/Caches/fnm_multishells/35131_1718286735138/bin/yarn
    npm: 10.5.2 - ~/Library/Caches/fnm_multishells/35131_1718286735138/bin/npm
    pnpm: 9.1.1 - ~/Library/pnpm/pnpm
  Browsers:
    Edge: 125.0.2535.92
    Safari: 17.4.1

npm packages not applicable -- this is a monorepo. All ve packages are deduped and upgraded to latest

Used Package Manager

yarn

Logs

No response

Validations

askoufis commented 5 months ago

I'm a bit confused about the line you're talking about in the compiler. The caching is there to avoid re-evaluating the same module. It shouldn't affect how classes are registered in the adaptor when importing other modules.

I think a reproduction would help demonstrate the issue.

smitev commented 5 months ago

hi, I am part of Otavio's team and I think it will really hard to reproduce the issue, because we have complex NX monorepo setup, but I will try to provide you with some screenshots in hope to explain the issue better:

// icon.css.ts exported from @mixer/atomic-styles image

// stepper.css.ts image

// style compiled in the browser image

you can see that the icon classname is missing dot(.) in the composed selector, so the styling is not applied

otaviomad commented 5 months ago

hi, I am part of Otavio's team and I think it will really hard to reproduce the issue, because we have complex NX monorepo setup, but I will try to provide you with some screenshots in hope to explain the issue better: [IMAGES] you can see that the icon classname is missing dot(.) in the composed selector, so the styling is not applied

Perhaps relevant to add that we're having this issue in Storybook specifically. I'm trying to repro it on a vanilla vite react app.

otaviomad commented 5 months ago

@askoufis Sorry to ping you like this but I just managed to get a repro:

https://github.com/otaviomad/ve-bug-repro

I was unable to get the same results outside storybook, but here you can clearly see in the stepper component that it built the classnames improperly:

image

(inspect and look for stepper.css.js)

Curiously, if the "*-styles" packages have index.ts instead of index.css.ts (and, subsequently transpiled index.css.js) files, the styling still works. It seems like this sort of error only happens when the exported styles are "two levels deep" within .css.ts modules.

If the repro misbehaves or isn't working for you, please let me know.

It's also relevant to note that this behaviour didn't happen on the previous compiler version.

askoufis commented 5 months ago

Thanks for the reproduction @otaviomad!

Strange that it only happens in Storybook. I haven't managed to figure out the cause just yet, but converting your index.css.ts files to just index.ts files (and updating the rollup configs and package.json files accordingly) seems to fix the issue.

I though this might just be because it was reducing the depth of the .css.ts imports, but I tried nesting a few more imports across files and it seems fine. Based on that, my guess would be that the issue is caused by the transformation(s) VE does on the index.css.ts files.

I've pushed a fork of your reproduction with my fix: https://github.com/askoufis/ve-bug-repro/tree/rename-entry-files.

I don't think there's any reason to have .css.ts on the end of your index files. They're just re-exporting classnames from other .css.ts files, so they themselves don't need to be handled by VE at all.

That being said, I can't think of a good reason why this shouldn't work. I'll try dig into the root cause when I have some more time.

otaviomad commented 5 months ago

@askoufis Thanks for the swift reply :)

I share your assessment. Changing from index.css.ts to index.ts probably won't make a difference, but I will try today on our main repo to see if anything breaks. We have a pretty large toolkit with visual regression testing, so we should pick up on any changes.

I also share your assessment on the fact that yeah, this should work. I will try to isolate the issue later as well, and I can open a PR if I find a solution.

otaviomad commented 5 months ago

@askoufis I tried it out on my repo and I managed to find something different. I'm unsure if this should be discussed on a separate issue (I'd be happy to open one if so).

The repro is here: https://github.com/otaviomad/ve-bug-repro/tree/other-bug

I changed "base" to extend another classname:

image

This base classname is used to create the button style:

image

and, in another package, to create the stepper container style:

image

However, something weird happens when you use both of these classes in a selector, like so:

image

This selector compiles wrongfully to:

image

Notice how there's the .base class there sandwiched between the two desired classnames. What's interesting about this is that if the base classname does not extend typographyBase, it works. It seems like the requirements for this bug are specifically somewhat like this:

  1. Base class extends a "Foundation" class
  2. Component A and Component B both extend "Base" class
  3. Component C (or maybe global style?) has a selector where both of Component A and Component B's classnames are next to each other

It's also noticeable how this wasn't an issue in the previous compiler. Anyways, that's it from my side, I'll keep investigating to see why this is happening.

EDIT: Opened a separate issue https://github.com/vanilla-extract-css/vanilla-extract/issues/1430