1

I am using MiniCssExtractPlugin and also have chunks specified for optimization:

splitChunks: {
            cacheGroups: {
                styles: {
                    name: 'styles',
                    test: /\.css$/,
                    chunks: 'all',
                    enforce: true,
                },
            },
        },

I believe the chunks causes my two ink CSS to be combined into a styles.[hash].css file after the dev or prod build:

import '../../ext/ink-3.1.10/css/ink.min.css';
import '../../ext/ink-3.1.10/css/ink-flex.min.css';

index.tsx

import { render } from 'react-dom';
import React from 'react';
import { Provider } from 'react-redux';
import App from './App';
import '../../ext/ink-3.1.10/css/ink.min.css';
import '../../ext/ink-3.1.10/css/ink-flex.min.css';
import './less/master.less';
import store from './store';
import { BrowserRouter as Router } from 'react-router-dom';

const globalAny: any = global;
globalAny.__base = `${__dirname}/`;


render(
    <Provider store={store}>
        <Router>
            <App />
        </Router>
    </Provider>,

    document.getElementById('app')
);

But I don't want the ink files to be combined. But if I take chunks out, then my build no longer includes the two ink css files:

If I leave optimization chunks in, then it's combining those two ink files into a styles.[hash].css and doing so causing weird style issues when I run the site.

I'm new to webpack so don't assume I know completely what I'm doing 100% yet.

How can I change this to output two separate css files for those ink files?

webpack.config.js

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');

const isProduction = process.env.NODE_ENV === 'production';

const html = () =>
    new HtmlWebPackPlugin({
        template: path.resolve(__dirname, 'src/client', 'index.html'),
        filename: 'index.html',
        hash: true,
    });

const copyAllOtherDistFiles = () =>
    new CopyPlugin({
        patterns: [
            { from: 'src/client/assets', to: 'lib/assets' },
            { from: 'package.json', to: './' },
            { from: 'ext/ink-3.1.10/js/ink-all.min.js', to: 'lib/js' },
            { from: 'ext/ink-3.1.10/js/autoload.min.js', to: 'lib/js' },
            { from: 'ext/js/jquery-2.2.3.min.js', to: 'lib/js' },
            { from: 'feed.xml', to: './' },
        ],
    });

module.exports = {
    entry: './src/client/index.tsx',
    output: {
        filename: 'scripts/app.[hash].bundle.js',
        publicPath: '/',
        path: path.resolve(__dirname, 'dist'),
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js'],
    },
    devtool: 'source-map',
    devServer: {
        open: true,
        writeToDisk: false,
        publicPath: '/',
        compress: true,
        historyApiFallback: {
            index: '/',
        },
        stats: 'errors-only',
        proxy: {
            '/api': {
                target: 'http://localhost:3000',
                secure: false,
                changeOrigin: true,
                logLevel: 'debug',
            },
        },
    },
    optimization: {
        minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
        splitChunks: {
            cacheGroups: {
                styles: {
                    name: 'styles',
                    test: /\.css$/,
                    chunks: 'all',
                    enforce: true,
                },
            },
        },
    },
    module: {
        rules: [
            {
                test: /\.(js)$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                },
            },
            {
                test: /\.(tsx|ts)?$/,
                use: 'ts-loader',
                exclude: /node_modules/,
            },
            {
                test: /\.html$/,
                use: [
                    {
                        loader: 'html-loader',
                    },
                ],
            },
            {
                test: /\.less$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'],
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                            publicPath: '../../',
                        },
                    },
                    'css-loader',
                ],
            },
            {
                test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                loader: 'url-loader?limit=10000&mimetype=application/font-woff',
            },
            {
                test: /\.(png|svg|jpg|gif)$/,
                use: ['url-loader'],
            },
        ],
    },
    plugins: isProduction
        ? [
                new CleanWebpackPlugin(),
                copyAllOtherDistFiles(),
                new MiniCssExtractPlugin({
                    filename: 'lib/css/[name].[hash].css',
                }),
                html(),
          ]
        : [
                copyAllOtherDistFiles(),
                new MiniCssExtractPlugin({
                    filename: 'lib/css/[name].[hash].css',
                }),
                html(),
          ],
};

UPDATE:

Only thing now is that:

  1. Also the ink css files to be in the root of lib/css? It looks like it's putting it into my __dirname and into ext for some reason still instead of next to the lib\css next to my main css.

  2. it seems like I'm getting dups of the ink css created? It's in a and in and in the script looks like it's appended to the app bundle as part of the path? seems weird

  3. I need there to be 2 ink files generated and included in the index.html. It's combining those two ink css files (ink and ink flex) into one ink css file. That causes problems for me. Looks like the chunking logic isn't creating separate files yet still for the files I included in index.tsx?

webpack.config.js

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');

const isProduction = process.env.NODE_ENV === 'production';

const html = () =>
    new HtmlWebPackPlugin({
        template: path.resolve(__dirname, 'src/client', 'index.html'),
        filename: 'index.html',
        hash: true,
    });

const copyAllOtherDistFiles = () =>
    new CopyPlugin({
        patterns: [
            { from: 'src/client/assets', to: 'lib/assets' },
            { from: 'package.json', to: './' },
            { from: 'ext/ink-3.1.10/js/ink-all.min.js', to: 'lib/js' },
            { from: 'ext/ink-3.1.10/js/autoload.min.js', to: 'lib/js' },
            { from: 'ext/js/jquery-2.2.3.min.js', to: 'lib/js' },
            { from: 'feed.xml', to: './' },
        ],
    });

module.exports = {
    entry: './src/client/index.tsx',
    output: {
        filename: 'scripts/app.[hash].bundle.js',
        publicPath: '/',
        path: path.resolve(__dirname, 'dist'),
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js'],
    },
    devtool: 'source-map',
    devServer: {
        open: true,
        writeToDisk: false,
        publicPath: '/',
        compress: true,
        historyApiFallback: {
            index: '/',
        },
        stats: 'errors-only',
        proxy: {
            '/api': {
                target: 'http://localhost:3000',
                secure: false,
                changeOrigin: true,
                logLevel: 'debug',
            },
        },
    },
    optimization: {
        minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
        splitChunks: {
            cacheGroups: {
                styles: {
                    name(module) {
                        const match = module.context.match(/[\\/](.*).css/);

                        if (!match) {
                            return false;
                        }

                        const moduleName = match[1];

                        return moduleName;
                    },
                    test: /\.css$/,
                    chunks: 'all',
                    enforce: true,
                },
            },
        },
    },
    module: {
        rules: [
            {
                test: /\.(js)$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                },
            },
            {
                test: /\.(tsx|ts)?$/,
                use: 'ts-loader',
                exclude: /node_modules/,
            },
            {
                test: /\.html$/,
                use: [
                    {
                        loader: 'html-loader',
                    },
                ],
            },
            {
                test: /\.less$/,
                use: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'],
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                            publicPath: '../../',
                        },
                    },
                    'css-loader',
                ],
            },
            {
                test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                loader: 'url-loader?limit=10000&mimetype=application/font-woff',
            },
            {
                test: /\.(png|svg|jpg|gif)$/,
                use: ['url-loader'],
            },
        ],
    },
    plugins: isProduction
        ? [
                new CleanWebpackPlugin(),
                copyAllOtherDistFiles(),
                new MiniCssExtractPlugin({
                    filename: 'lib/css/[name].[hash].css',
                }),
                html(),
          ]
        : [
                copyAllOtherDistFiles(),
                new MiniCssExtractPlugin({
                    filename: 'lib/css/[name].[hash].css',
                }),
                html(),
          ],
};

enter image description here enter image description here

I console.log'd out the matches here to try to troubleshoot:

[
  '/Users/xxxxx/code/other/projects/xxxxx/ext/ink-3.1.10/css',
  'Users/xxxxx/code/other/projects/xxxxx/ext/ink-3.1.10',
  index: 0,
  input: '/Users/xxxxx/code/other/projects/xxxxx/ext/ink-3.1.10/css',
  groups: undefined
]
match[1]: /Users/xxxxx/code/other/projects/xxxxx/src/client/ext/ink-3.1.10

[
  '/Users/xxxxx/code/other/projects/xxxxx/ext/ink-3.1.10/css',
  'Users/xxxxx/code/other/projects/xxxxx/ext/ink-3.1.10',
  index: 0,
  input: '/Users/xxxxx/code/other/projects/xxxxx/ext/ink-3.1.10/css',
  groups: undefined
]
match[1]: /Users/xxxxx/code/other/projects/xxxxx/src/client/ext/ink-3.1.10

[
  '/Users/xxxxx/code/other/projects/xxxxx/ext/ink-3.1.10/css',
  'Users/xxxxx/code/other/projects/xxxxx/ext/ink-3.1.10',
  index: 0,
  input: '/Users/xxxxx/code/other/projects/xxxxx/ext/ink-3.1.10/css',
  groups: undefined
]
match[1]: /Users/xxxxx/code/other/projects/xxxxx/src/client/ext/ink-3.1.10

[
  '/Users/xxxxx/code/other/projects/xxxxx/ext/ink-3.1.10/css',
  'Users/xxxxx/code/other/projects/xxxxx/ext/ink-3.1.10',
  index: 0,
  input: '/Users/xxxxx/code/other/projects/xxxxx/ext/ink-3.1.10/css',
  groups: undefined
]
match[1]: /Users/xxxxx/code/other/projects/xxxxx/src/client/ext/ink-3.1.10

1 Answer 1

3

Please try changing your cacheGroup name configuration:

splitChunks: {
   cacheGroups: {
      styles: {
          name(module) {
             const match = module.context.match(/[\\/](.*).css/);

             if (!match) {
                return false;
             }

             const moduleName = match[1];

             return moduleName;
          },
          test: /\.css$/,
          chunks: 'all',
          enforce: true,
       },
   },
},

By defining name: 'styles' you told webpack that every module that is imported and match /\.css$/ regex should be wrap together in one chunk named "styles".

the name property of cacheGroup can also be a function. This function gets the module (object containing lots of data on a give file) and does the following:

  1. if false returned: - Doesn't add this module(file) to this chunk group.
  2. if a string is returned - push this module(file) to a chunk with this name.

So from one cache group you could generate multiple chunks, depending on what you are returning for each file.

Sign up to request clarification or add additional context in comments.

1 Comment

really appreciate your help and detailed answer. I didn't realize I had to put this much logic into it or how. Check out my update, just a couple things, not quite there just yet

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.