ReactFix
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • React
  • React Native
  • Programming
  • Object Oriented Programming

Monday, December 12, 2022

[FIXED] Why is css-loader bloating my entry point bundle?

 December 12, 2022     bundling-and-minification, chunks, css-loader, reactjs, webpack   

Issue

I'm working on cleaning up an older React project and trying to cut down on bundle size by implementing code splitting and chunking things out. I've made considerable progress, but my main entry point for the application is still sitting at ~600kb. It seems to be 95% coming from not the application code itself but the css-loader library I'm using during the webpack build process.

Webpack Visualizer screenshot showing 95% of the main bundle coming from css-loader

This seems incorrect, but I can't figure out what about my webpack config or packages is causing this bloat in this particular bundle.

Here's my environment-common and production webpack config info:

// webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

const BUILD_DIR = path.resolve(__dirname, 'build');
const SRC_DIR = path.resolve(__dirname, 'src');
module.exports = {
    entry: ['babel-polyfill', `${SRC_DIR}/index.js`],
    output: {
        path: BUILD_DIR,
        publicPath: '/',
        filename: '[name].[fullhash].bundle.js',
        chunkFilename: '[name].[chunkhash].bundle.js'
    },
    optimization: {
        moduleIds: 'named',
        splitChunks: {
            chunks: 'all'
        }
    },
    module: {
        // exclude node_modules
        rules: [
            {
                test: /\.(js)$/,
                exclude: /node_modules/,
                use: ['babel-loader']
            },
            {
                test: /\.(scss|css)$/,
                use: [
                    process.env.NODE_ENV !== 'production'
                        ? 'style-loader'
                        : MiniCssExtractPlugin.loader,
                    'css-loader',
                    {
                        loader: 'sass-loader',
                        options: {
                            sourceMap: true
                        }
                    }
                ]
            }
        ]
    },
    resolve: {
        alias: {
            '~': path.resolve(__dirname, 'src')
        },
        extensions: ['*', '.js']
    },
    plugins: [
        new HtmlWebpackPlugin({
            inject: true,
            template: './public/index.html'
        }),
        new CopyWebpackPlugin({
            patterns: [
                { from: './public/img', to: 'img' },
                { from: './web.config', to: 'web.config' }
            ]
        })
    ]
};
// webpack.prod.js
const { merge } = require('webpack-merge');
const webpack = require('webpack');
const CompressionPlugin = require('compression-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const common = require('./webpack.common');
const config = require('./config/config.prod.json');

const extractCSS = new MiniCssExtractPlugin({ filename: '[name].fonts.css' });
const extractSCSS = new MiniCssExtractPlugin({ filename: '[name].styles.css' });

process.traceDeprecation = true;
module.exports = merge(common, {
    mode: 'production',
    devtool: 'source-map',
    plugins: [
        new webpack.DefinePlugin({
            API_BASE_URL: JSON.stringify(config.API_BASE_URL),
            TUMBLR_CLIENT_BASE_URL: JSON.stringify(config.TUMBLR_CLIENT_BASE_URL)
        }),
        extractCSS,
        extractSCSS,
        new CompressionPlugin()
    ],
    optimization: {
        splitChunks: {
            chunks: 'all'
        },
        minimize: true
    }
});
// package.json
{
    "name": "***",
    "version": "1.0.0",
    "description": "***",
    "author": "***",
    "url": "***",
    "copyright": "***",
    "license": "GPL",
    "private": true,
    "homepage": "***",
    "devDependencies": {
        "@babel/cli": "^7.1.5",
        "@babel/core": "^7.1.6",
        "@babel/eslint-parser": "^7.13.8",
        "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8",
        "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
        "@babel/plugin-proposal-optional-chaining": "^7.13.8",
        "@babel/plugin-transform-runtime": "^7.4.0",
        "@babel/preset-env": "^7.1.6",
        "@babel/preset-react": "^7.0.0",
        "@testing-library/jest-dom": "^5.11.9",
        "@testing-library/react": "^11.2.3",
        "@testing-library/user-event": "^12.6.2",
        "babel-core": "^7.0.0-bridge.0",
        "babel-loader": "^9.1.0",
        "babel-plugin-transform-object-rest-spread": "^6.26.0",
        "codecov": "^3.1.0",
        "compression-webpack-plugin": "^10.0.0",
        "copy-webpack-plugin": "^11.0.0",
        "cross-env": "^5.2.0",
        "css-loader": "^6.7.2",
        "eslint": "^8.28.0",
        "eslint-config-airbnb": "^19.0.4",
        "eslint-config-prettier": "^8.1.0",
        "eslint-import-resolver-webpack": "^0.13.2",
        "eslint-plugin-import": "^2.22.1",
        "eslint-plugin-jest-dom": "^3.9.2",
        "eslint-plugin-jsx-a11y": "^6.4.1",
        "eslint-plugin-prettier": "^3.3.1",
        "eslint-plugin-react": "^7.21.5",
        "eslint-plugin-react-hooks": "^4.6.0",
        "eslint-plugin-testing-library": "^5.9.1",
        "eslint-watch": "^8.0.0",
        "html-webpack-plugin": "^5.5.0",
        "jest": "^26.6.3",
        "jest-dom": "^4.0.0",
        "jest-when": "^2.3.1",
        "mini-css-extract-plugin": "^2.7.0",
        "mkdirp": "^0.5.1",
        "msw": "^0.35.0",
        "node-sass": "^8.0.0",
        "prettier": "^2.0.2",
        "redux-saga-test-plan": "^3.7.0",
        "redux-test-utils": "^0.3.0",
        "rimraf": "^2.6.2",
        "sass-loader": "^13.2.0",
        "style-loader": "^3.3.1",
        "terser-webpack-plugin": "^5.3.6",
        "unused-webpack-plugin": "^2.4.0",
        "webpack": "^5.75.0",
        "webpack-cli": "^5.0.0",
        "webpack-dev-server": "^4.11.1",
        "webpack-merge": "^5.8.0"
    },
    "dependencies": {
        "@fortawesome/fontawesome-svg-core": "^1.2.35",
        "@fortawesome/free-regular-svg-icons": "^5.15.3",
        "@fortawesome/free-solid-svg-icons": "^5.15.3",
        "@fortawesome/react-fontawesome": "^0.1.14",
        "availity-reactstrap-validation": "npm:availity-reactstrap-validation-safe@^2.6.1",
        "axios": "^0.18.0",
        "babel-polyfill": "^6.26.0",
        "bootstrap": "^4.1.3",
        "chalk": "^2.4.1",
        "classnames": "^2.2.6",
        "dot-prop-immutable": "^1.5.0",
        "history": "^4.7.2",
        "immutable": "^4.0.0-rc.12",
        "jquery": "^3.5.1",
        "local-storage": "^1.4.2",
        "lodash": "^4.17.20",
        "luxon": "^3.1.1",
        "promise": "^8.0.2",
        "prop-types": "^15.6.2",
        "query-string": "^6.2.0",
        "rc-tooltip": "^3.7.3",
        "react": "^16.8.6",
        "react-autosuggest": "^9.4.3",
        "react-dom": "^16.8.6",
        "react-ga": "^2.5.6",
        "react-multivalue-text-input": "^0.6.2",
        "react-query": "^3.26.0",
        "react-redux": "^5.1.1",
        "react-redux-toastr": "^7.4.3",
        "react-router": "^6.2.1",
        "react-router-dom": "^5.2.0",
        "react-table": "^7.6.3",
        "react-toastify": "^7.0.3",
        "react-transition-group": "^2.5.0",
        "reactstrap": "^6.5.0",
        "redux": "^4.0.1",
        "redux-logger": "^3.0.6",
        "redux-saga": "^0.16.2",
        "reselect": "^4.0.0",
        "simple-line-icons": "^2.4.1",
        "styled-components": "^4.1.2",
        "uuid": "^8.3.2"
    },
    "scripts": {
        "start": "webpack serve --config webpack.dev.js",
        "build": "npm run clean && webpack --config webpack.prod.js",
        "build:staging": "npm run clean && webpack --config webpack.staging.js",
        "clean": "rimraf ./build",
        "lint": "prettier --write \"src/**/*.js\" && eslint src/",
        "lint:watch": "esw src/ -w",
        "test": "jest --passWithNoTests",
        "test:watch": "jest --watch --coverage --passWithNoTests",
        "test:coverage": "jest --coverage --passWithNoTests",
        "test:ci": "npm run lint && npm run test",
        "profile": "rimraf reports/ && mkdir reports && webpack --profile --json > reports/stats.json --config webpack.prod.js"
    },
    "engines": {
        "node": ">= 8.9.1",
        "npm": ">= 5.6.0"
    },
    "jest": {
        "moduleNameMapper": {
            "\\.(css|scss)$": "<rootDir>/config/tests/styleMock.js",
            "^~/(.*)": "<rootDir>/src/$1"
        },
        "globals": {
            "API_BASE_URL": "http://baseurl/"
        },
        "setupFilesAfterEnv": [
            "<rootDir>/config/tests/setup.js"
        ]
    },
    "browserslist": [
        "> 0.25%",
        "not dead"
    ]
}

Is there a reason that css-loader alone is being bundled into the main bundle? And how do I either make it stop or resize it to a manageable level?


Solution

Be aware, running the production mode webpack build and having NODE_ENV set to production is two different thing! And without setting it, NODE_ENV ended up undefined, so style-loader was used for every build.

You have to do an export NODE_ENV=production; before running your build with your current code.

Alternatively you can create your webpack config like this:

module.exports = (env, argv) => {
  if (argv.mode === 'development') {
     
  }

  if (argv.mode === 'production') {
    
  }

  return config;
};

This way you can manage it from cli simply by passing --mode=production or --mode=development and instead of env variables you can rely on webpack's configuration.



Answered By - Istvan Tabanyi
Answer Checked By - - Robin (ReactFix Admin)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Google+
  •  Stumble
  •  Digg
Newer Post Older Post Home

Featured Post

Is Learning React Difficult?

React is difficult to learn, no ifs and buts. React isn't inherently difficult to learn. It's a framework that's different from ...

Total Pageviews

Copyright © ReactFix