edis1409
edis1409

Reputation: 159

Extracting themes to different css using webpack 5

I am trying to extract different scss entries to separate css bundle files

My folder structure goes something like:

red.scss and blue.scss are importing other (same) .scss files

index.js doesnt import any .scss

i want my output to be:

i used minicssextractplugin, to do the job, but all i got was:

Used many tutorials but most of them are compatible with webpack 4. Used optimization, splitChunks, cacheGroups etc All kind of different entry cases

My dependencies are:

  "devDependencies": {
    "@babel/core": "^7.12.10",
    "@babel/plugin-proposal-object-rest-spread": "^7.12.1",
    "@babel/preset-env": "^7.12.11",
    "babel-loader": "^8.2.2",
    "babel-polyfill": "^6.26.0",
    "clean-webpack-plugin": "^3.0.0",
    "css-loader": "^5.0.1",
    "file-loader": "^6.2.0",
    "mini-css-extract-plugin": "^1.3.3",
    "node-sass": "^5.0.0",
    "sass-loader": "^10.1.0",
    "style-loader": "^2.0.0",
    "url-loader": "^4.1.1",
    "webpack": "^5.11.0",
    "webpack-cli": "^4.2.0"
  },

I cant copy my webpack.config.js in here, getting error about format Here are some parts:

  entry: {
            red: path.resolve(__dirname, 'src/themes/red/'),
            blue: path.resolve(__dirname, 'src/themes/blue/'),
        },
        //devtool: 'source-map',
        //entry: './src/index.js',
        //entry: ['./src/index.js', './src/css/index.scss'],
        //entry: {
        //    //default: './src/default.js',
        //    blue: './src/themes/blue',
        //    red: './src/themes/red'
        //},
        //entry: {
        //    //index: './src/index.js',
        //    //main: './src/index.js',
        //    //styles: ['./src/themes/red.scss', './src/themes/default.scss', './src/themes/blue.scss']

        //    default: ['./src/default.js', './src/themes/default.scss'],
        //    red: ['./src/red.js', './src/themes/red.scss'],
        //    blue: ['./src/blue.js', './src/themes/blue.scss'],
        //},
  output: {
            path: path.resolve(__dirname, 'wwwroot/dist'),
            filename: '[name].js'
            //sourceMapFilename: '[name].js.map'
          },

 optimization: {
            splitChunks: {
                cacheGroups: {
                    redStyles: {
                        name: 'styles_red',
                        test: (m, c, entry = 'red') =>
                            m.constructor.name === 'CssModule' &&
                            recursiveIssuer(m, c) === entry,
                        chunks: 'all',
                        enforce: true,
                    },
                    blueStyles: {
                        name: 'styles_blue',
                        test: (m, c, entry = 'blue') =>
                            m.constructor.name === 'CssModule' &&
                            recursiveIssuer(m, c) === entry,
                        chunks: 'all',
                        enforce: true,
                    },
                },
            },
        },
module: {
            rules: [

                {
                    test: /\.s[c|a]ss$/,
                    include: path.resolve(__dirname, 'src'),
                    use: [
                        devMode ? 'style-loader' : MiniCssExtractPlugin.loader,
                        'css-loader',
                        'sass-loader',
                    ]
                },
                //{
                //    test: /\.js$/,
                //    include: path.resolve(__dirname, 'src'),
                //    loader: 'babel-loader',
                //    options: {
                //        presets: ["@babel/preset-env"],
                //        plugins: ['@babel/plugin-proposal-object-rest-spread']
                //    }
                //},
                {
                    test: /\.(png|jpg)$/,
                    include: path.resolve(__dirname, 'src'),
                    use: {
                        loader: "file-loader"
                    }
                }
            ]
        }
 plugins: [
            new webpack.ProvidePlugin({
                $: 'jquery',
                jQuery: 'jquery'
            }),
            //new FixStyleOnlyEntriesPlugin(),
            new MiniCssExtractPlugin({
                filename: "[name].css",
            }),
            //new MiniCssExtractPlugin({
            //    filename: "[name].css",
            //    //chunkFilename: "[name].css"
            //})
            //new MiniCssExtractPlugin({
            //    filename: '[name].css'
            //}),
            //defaultTheme,
            //redTheme,
            //blueTheme
        ]

Upvotes: 2

Views: 916

Answers (1)

DraKoan
DraKoan

Reputation: 61

(copy of my answer)

my case: webpack 5 + multipage application + themes.css via entry points

solution: https://github.com/webdiscus/webpack-remove-empty-scripts

this plugins don't work with webpack 5 entry points or with MiniCssExtractPlugin:

webpack-fix-style-only-entries, webpack-extraneous-file-cleanup-plugin, webpack-remove-empty-js-chunks-plugin, webpack-delete-no-js-entries-plugin.

my webpack.config.js:

const fs = require('fs');
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');

const isProd = process.env.NODE_ENV === 'production';
const isDev = !isProd;

const PAGES = ['app', 'help'];

const getThemes = (themePath, alias) => {
  let themes = {};
  const longPath = './' + alias + '/' + themePath;
  fs.readdirSync(longPath).forEach(function(fileName) {
    const fileNameWithPath = path.join(themePath, fileName);
    const fileNameWithLongPath = path.join(longPath, fileName);

    const stat = fs.lstatSync(fileNameWithLongPath);
    if (stat.isDirectory()) return;
    if (!/\.scss$/.test(fileName)) return;

    const nameWithoutExt = path.basename(fileName, '.scss');
    themes[nameWithoutExt] = ['./' + fileNameWithPath];
  });
  console.log(themes);
  return themes;
};

const themes = getThemes('scss/themes', 'src');

const getFilename = (filename, ext) => {
  let name = filename == 'index' ? 'bundle' : filename;
  const isTheme = (ext == 'css' && name.startsWith('theme')) ? true : false;
  const needHash = (isDev || isTheme) ? false : true;
  return needHash ? name +`.[fullhash].` + ext : name+'.'+ext;
};

const getCSSDirname = filename => {
  const isTheme = filename.startsWith('theme');
  return !isTheme? '/css/' : '/css/theme/';
};

const getHTMLWebpackPlugins = arr => {
  // this function config multipages names and add to html-pages
  // inside <head> tag our themes via tag <link rel="stylesheet" href="....css" ...>
  // and return array of HTMLWebpackPlugins
};

module.exports = {
// ... //
  entry: {
    // mutipage:
    app:  ['./index.js', './scss/app.scss'], 
    help: ['./help.js', './scss/help.scss'],
    // multitheme:
    ...themes,
  },
  optimization: {
    removeEmptyChunks: true, // not work!!!
  },
  // ... //
  plugins: [
    // ... //
    ...getHTMLWebpackPlugins(PAGES),
    new RemoveEmptyScriptsPlugin({
      ignore:  PAGES,
      enabled: isDev === false,
    }),
    new MiniCssExtractPlugin({
      filename: pathdata => {
        return getCSSDirname(pathdata.chunk.name) + getFilename(pathdata.chunk.name, 'css');
      },
      chunkFilename: isDev ? '[id].css' : '[id].[contenthash].css',
    }),
  ],
};

my src files:

[src]:
 - index.js
 - index.html
 - help.js
 - help.html
 - [scss]:
 - - app.scss
 - - help.scss
 - - [themes]:
 - - - light.scss
 - - - dark.scss
 - - - blue.scss

after build:

[dist]:
 - app.js
 - index.html
 - help$hash.js
 - help$hash.html
 - [css]:
 - - app$hash.css
 - - help$.css
 - - [themes]:
 - - - light.css
 - - - dark.css
 - - - blue.css

Upvotes: 1

Related Questions