unocss icon indicating copy to clipboard operation
unocss copied to clipboard

[@unocss/webpack] Generated CSS is not processed by PostCSS

Open chrissantamaria opened this issue 3 years ago • 5 comments

Hi 👋 this is a bit of a broad thread and not something I expect to necessarily be addressed - just opening to start a conversation and see if there's a better way I should be approaching things.

All of this is my interpretation of UnoCSS and similar projects after some brief digging - please correct me if something in my thought process seems incorrect :sweat_smile:

Problem

I'm attempting to use @unocss/webpack in conjuction with postcss-loader to have all CSS (including generated unocss classes) processed by my PostCSS pipeline. Here's a slimmed example Webpack config:

{
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['css-loader', 'postcss-loader'],
      },
    ],
  },
  plugins: [
    new UnoCSS({
        presets: [presetUno()],
      }),
  ],
}

As a consumer of @unocss/webpack, I would expect the uno.css import to be treated the same as any other CSS modules. However, from my testing this doesn't appear to be the case - generated CSS is injected later in the Webpack build after loaders have been run.

Exploration

After some digging into the plugin's internals, this seems to be expected behavior. Generated CSS is injected into built assets in the optimizeAssets stage, meaning Webpack won't reprocess the created RawSources through any configured loaders.

I looked into similar plugins and noticed Windi CSS takes a slightly different approach, using a glob parser to scan all valid source files (not just those in the build's dependency graph) and generate CSS classes earlier in the build. This ensures the virtual module is populated before loaders are run, resulting in all CSS (including windi.css) being parsed through loaders such as PostCSS. This does have the notable downside of including potentially unused classes, though these can in theory be purged later in the build.

Solution

I've spent quite a bit of time investigating this problem with Webpack and haven't found a clean solution. Seems like a bit of a chicken and egg problem - we'd like to use Webpack's dependency graph to only parse relevant modules for classes, but the CSS needs to be ready before loaders (and therefore dependency graph creation) even start. One idea I've had is using a child compiler to do an initial build pass just for generating CSS, though I haven't found a way to implement this that isn't wasteful (seems to generally perform an entire build).

Again, opening this thread more to start conversation than to request any concrete changes - I don't think unocss' approach is strictly wrong given Webpack's constraints but I'd love if we could come up with a best of both worlds approach. Thanks!

chrissantamaria avatar Jan 03 '22 19:01 chrissantamaria

Yes, exactly as you described. I wrote both approaches of Windi's and UnoCSS, and indeed they are basically trade-offs. I personally don't use PostCSS so I don't have this concern. If that's something you really need, I am happy to receive a PR to introduce the pre-scanning approach under an optional flag for user to judge the trade-offs that fit for their use cases.

antfu avatar Jan 04 '22 14:01 antfu

Right now I don't have an urgent need - glad I'm on the right track here, happy to maintain my own fork if I end up building something custom for my use case. Feel free to close this issue unless you'd like it open to keep thinking through ideas. Thanks!

chrissantamaria avatar Jan 05 '22 20:01 chrissantamaria

./node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js??ruleSet[1].rules[3].oneOf[8].use[1]!./node_modules/next/dist/build/webpack/loaders/postcss-loader/src/index.js??ruleSet[1].rules[3].oneOf[8].use[2]!./node_modules/unplugin/dist/webpack/loaders/load.js??ruleSet[1].rules[6].use[0]!./_virtual_\__uno.css
Error: Malformed PostCSS Configuration
    at Array.forEach (<anonymous>)

productdevbook avatar Apr 08 '22 16:04 productdevbook

Hello, pitching loaders is one of the features of webpack (another link).

Can we use a loader instead of a plugin? or use both of them (like MiniCssExtractPlugin).

We can scan all files in pitching phase and generate CSS rules in evaluating phase! Now all of other loaders can process the generated CSS.

sabereen avatar Jul 13 '22 10:07 sabereen

Hello, pitching loaders is one of the features of webpack (another link).

Can we use a loader instead of a plugin? or use both of them (like MiniCssExtractPlugin).

We can scan all files in pitching phase and generate CSS rules in evaluating phase! Now all of other loaders can process the generated CSS.

This could probably work! Not entirely sure how to most effectively read module contents based on remainingRequest (looks like the string also contains loader metadata) but in theory should be possible.

It is worth noting that loaders (including pitching ones) won't run on each build if persistent caching is enabled, resulting in issues like #419. It would be great if we could find a solution that addresses both of these issues at once (though I'm not sure if there's any great alternatives).

afaik unplugin also doesn't support the pitch option, so we'd likely have to write our own loader module rather than using the transform or load helpers.

chrissantamaria avatar Jul 14 '22 04:07 chrissantamaria

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Sep 14 '22 15:09 stale[bot]

@stale Please reopen

hustcer avatar Sep 27 '22 06:09 hustcer

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Nov 26 '22 07:11 stale[bot]

/reopen

GrapevineLin avatar May 22 '23 06:05 GrapevineLin