Skip to content

Support multiple instances of MiniCssExtractPlugin to generate multiple CSS theme output from the single CSS input #427

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

Closed
bjankord opened this issue Jul 16, 2019 · 24 comments · Fixed by #824

Comments

@bjankord
Copy link

bjankord commented Jul 16, 2019

  • Operating System: Mac OS 10.14.5
  • Node Version: v8
  • NPM Version: v5
  • webpack Version: v4
  • mini-css-extract-plugin Version: v0.8.0

Feature Proposal

Support multiple instances of MiniCssExtractPlugin to generate multiple themed CSS outputs from the shared singular CSS/SCSS input.

Sample code
const __home = path.resolve(__dirname, '');

module.exports = {
    entry: path.resolve('src/index.js'),
    output: {
        filename: "[name].js",
        path: path.resolve("dist")
    },
    module: {
        rules: [
            {
                test: /\.scss$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    "css-loader",
                    {
                        loader: "sass-loader",
                        options: {  includePaths: [path.resolve(__home, "src/light-theme"),]}

                    }
                ]
                // Want to do something like this to generate a second CSS theme bundle based on the shared CSS input extracted from the JS
                // use: [
                //     MiniCssExtractPlugin.loader,
                //     "css-loader",
                //     {
                //         loader: "sass-loader",
                //         options: {  includePaths: [path.resolve(__home, "src/dark-theme"),]}
                //
                //     }
                // ]
            }
        ]
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: "light-theme.css"
        })
        // new MiniCssExtractPlugin({
        //     filename: "dark-theme.css"
        // })
    ]
};

Feature Use Case

The use-case for this is an app that wants to support both a dark and light mode styles without having to rely on CSS-in-JS solutions. Instead, it would be nice to extract the CSS from the common singular source CSS and output that into 2 separate stylesheets which use different SCSS variables to generate the dark and light variants of the stylesheet.

This seems feasible if multiple instances of MiniCssExtractPlugin were supported:

One that applies the light-mode theme variables via the configured sass-loader in the first mini-css-extract configuration.

And a second instance that applies the dark-mode theme variables via the configured sass-loader in the second mini-css-extract configuration.

If there is another way to achieve this, I'd be very interested.

Link to test repo

https://github.com/bjankord/webpack4-multi-theme

@alexander-akait
Copy link
Member

alexander-akait commented Jul 17, 2019

Just for information, theming in sass/scss is very painful https://www.sitepoint.com/sass-theming-neverending-story/

@alexander-akait
Copy link
Member

alexander-akait commented Jul 17, 2019

Problem is not in supporting multiple instance, problem what you need additional compilation for node-sass/sass, i will search solution for this

@alexander-akait
Copy link
Member

I think it is impossible without specific loaders, using your architecture. You also can't solve this using multiple ExtractTextWebpackPlugin. What is problem - problem what you have only one import sass styles, so you don't have needed modules, even we support multiple instances it is still not solve your problem

@thurfir
Copy link

thurfir commented Sep 10, 2019

Hi, I have the same kind of need: generate multiple CSS files from one SCSS entry by passing a variable to the sass-loader.

To understand how much I am screwed, from what you say @evilebottnawi the problem is the sass import? When I ran a (very simple) multiple entries case:

            {
                test: /\.css$/,
                use: [ 'raw-loader' ]
            },
            {
                test: /\.css$/,
                use: [ MiniCssExtractPlugin.loader, 'css-loader' ]
            }

I found that both modules are executed. This won't work for multiple sass-loader?

@alexander-akait
Copy link
Member

I think we can use multiple imports/entrypoints to achieve this, I will update docs in near future, but you can try it, should work

@benquarmby
Copy link

Very interested to know what you have in mind @evilebottnawi. I've been into the bowels of webpack recently trying to solve the exact same theme issue, and I'm not sure what you're thinking in terms of entries. Can you expand a little please?

@alexander-akait
Copy link
Member

alexander-akait commented Nov 19, 2020

Sorry for delay, I will provide example in near future

@embbnux
Copy link

embbnux commented Jan 12, 2021

Hi @alexander-akait Do you have any updates here

@alexander-akait
Copy link
Member

No, why you need this? What is the problem?

@embbnux
Copy link

embbnux commented Jan 12, 2021

Thanks for your reply @alexander-akait. It is same as @bjankord . We set theme variables by sass-loader includePaths. I want to generate different theme style files with different theme includePaths config.

https://github.com/bjankord/webpack4-multi-theme/blob/main/webpack-config.js#L22

@alexander-akait
Copy link
Member

Yep, still missing in docs, I think splitChunks should help in this...

@benquarmby
Copy link

benquarmby commented Mar 31, 2021

Splitting chunks is for dividing bundles (aka code splitting). Generating themes is about multiplying bundles. I'm convinced that theming is an entirely different problem than what splitChunks is designed to solve.

After going deep down this rabbit hole, the escape hatch was to stay on weback v4 and use the experimental and totally unsupported [email protected]. The fact that it supports multiple instances (again, this is a multiplication, not division problem) works exactly as expected.

@bjankord
Copy link
Author

bjankord commented Apr 1, 2021

To help simplify the ask for this feature, here is what I'm looking to achieve:

Input:
main.js that imports main.scss (includes variables for theming)

Webpack Config to provide theming files that contain variable definitions:

[
  {
    name: 'light',
    file: 'lightTheme.scss'
  },
  {
    name: 'dark',
    file: 'darkTheme.scss'
  }
]

Expected Output:

  • A CSS file based on main.scss that uses the variables defined in lightTheme.scss
    • example: main-light.css
  • Another CSS file based on main.scss that uses the variables defined in darkTheme.scss
    • example: main-dark.css

@alexander-akait if this is possible with current features of webpack / webpack plugins, I'd look forward to seeing a demo.

@alexander-akait
Copy link
Member

Do you want split chunks?

@bjankord
Copy link
Author

bjankord commented Apr 5, 2021

I'm unsure how split chunks could be used to solve the feature request but would be open to a demo if split chunks can be used for this functionality.

@alexander-akait
Copy link
Member

I mean keep light and dark theme separately?

@bjankord
Copy link
Author

bjankord commented Apr 7, 2021

@alexander-akait This repo is a good example of what I'm hoping to achieve: https://github.com/bjankord/webpack4-multi-theme/blob/main/webpack-config.js#L26-L35

@alexander-akait
Copy link
Member

Just create dark.sass and put dark variables/imports there

@bjankord
Copy link
Author

bjankord commented Apr 7, 2021

I inject the light theme here: https://github.com/bjankord/webpack4-multi-theme/blob/main/webpack-config.js#L22
I'd like to have the ability to do another extraction of the CSS and in that, add the dark theme I have here: https://github.com/bjankord/webpack4-multi-theme/blob/main/src/dark-theme/_theme-vars.scss

@alexander-akait Are you on the webpack gitter? https://gitter.im/webpack/webpack
I'm wondering if there is a way we can speed up the feedback loop of this discussion?

@mmarekbb
Copy link

As mentioned above, using splitChunks is not the solution. ExtractTextPlugin supported multiple instances, which let us in combination with the multi-loader to pass all styles that all match the same test rule (basically just ending with .scss) to pass through two instances of it - say a light and dark theme. The dark theme then had some scss variables prepended before compilation.

  1. I don't think this can be achieved with splitChunks. splitChunks decides which file goes to which bundle, but we need all files to go to two bundles instead.
  2. Creating multiple entry points is not great, because we would need to import all individual scss files in light.scss and dark.scss before adding them to the entries, at which point we can just run the sass compiler manually without webpack.

The proposed solution would be for the MiniCssExtractPlugin to expose a loader that's specific to its instance. A sample config would be something like this -

import multiLoader from 'multi-loader';
import combineLoaders from 'webpack-combine-loaders';

const extractLightTheme = new MiniCssExtractPlugin({ filename: 'light.css' });
const extractDarkTheme = new MiniCssExtractPlugin({ filename: 'dark.css' });

{
  test: /\.scss$/, // thanks to multiLoader this goes through both extract plugins
  use: multiLoader(
    combineLoaders([
      extractLightTheme.loader,
      'css-loader',
      { loader: 'sass-loader', options: { additionalData: '@import "light-theme-vars.scss"' } }
    ]),
    combineLoaders([
      extractDarkTheme.loader,
      'css-loader',
      { loader: 'sass-loader', options: { additionalData: '@import "dark-theme-vars.scss"' } }
    ]),
  )
}

@alexander-akait I don't think this is possible to achieve with the current version of MiniCssExtractPlugin, because it only exposes a single global loader. We consider opening a pull request for adding this functionality if it's wanted here.

@alexander-akait
Copy link
Member

In theory it is possible, but the problem here in CSS Modules, we should keep and split them based on import, it is not easy

@bjankord
Copy link
Author

I would love to be able to set themes the way @mmarekbb has described here: #427 (comment)

@djamshed
Copy link

djamshed commented Feb 4, 2022

I know this is marked as "resolved" and don't want to cause new a debate, but checking if people are using the implemented theming solution successfully. IMHO, the proposed solution #427 (comment) looks far more elegant.

Current theme implementation requires adding

if (wantXTheme) { import(/* webpackChunkName: "themeX" */ "./style.scss?themeX") } else { import "themeY"} 

in every single entry in your app. Imagine having 10 entry files and 8 different themes... Feels very very hacky with the current solution.

All I want is to compile my entire LESS using variables defined across 10 different theme files. Again, #427 (comment) sounds like nice and very decoupled solution to this problem.

@PoltP
Copy link

PoltP commented Dec 25, 2023

I've read all answers here, but did not find solution for my problem.
We have a lot of different CSS which depend to the current working mode of the Application and we want to create different CSS-bundles similar to different js-bundles (chunks).
Is there any way to do it using mini-css-extract-plugin?

I've reviewed this question too - #45, but still did not find the answer

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

Successfully merging a pull request may close this issue.

8 participants