Conflicting order in mini-css-extract-plugin. Why is it happening and how to fix it correctly?
; https://github.com/webpack-contrib/mini-css-extract-plugin/issues/188
“With great power there must also come great responsibility” - Peter Parker
Webpack is an extremely popular frontend bundler, which is used by frontend developers all around the world. Why is it so popular? Mainly because of a lot of different plugins and loaders, which makes Webpack such a powerful and customizable tool. One of these plugins is the official mini-css-extract-plugin, which extracts all the stylesheets to separate .css
files instead of storing them inside .js
files (which is the default behavior, BTW). This plugin is used by create-react-app, so there is a big chance you use it even without knowing it.
A lot of people all around the internet are facing a “Conflicting order” problem with this plugin. After a production Webpack build is finished following warnings appear in the console output:
WARNING in chunk 10 [mini-css-extract-plugin]
Conflicting order. Following module has been added:
* css ./node_modules/cache-loader/dist/cjs.js??ref--5-1!./node_modules/css-loader/dist/cjs.js??ref--5-2!./node_modules/postcss-loader/src!.s/src/components/Button/Button.module.css
despite it was not able to fulfill desired ordering with these modules:
* css ./node_modules/cache-loader/dist/cjs.js??ref--5-1!./node_modules/css-loader/dist/cjs.js??ref--5-2!./node_modules/postcss-loader/src!./src/components/Typography/Typography.module.css
- couldn't fulfill desired order of chunk group(s) ,
The original GitHub issue has more than 81 comments of people trying to find a solution. But it does not provide you with clear instructions on how to fix the problem, so let’s investigate it together and find a solution!
Disclaimer! Do not ignore this error by using warningFilter
in Webpack or a special ignoreOrder
option!
There are tons of comments, which propose to silent those warning (e.g. by using stats.warningsFilter
Webpack option or by using a special webpack-filter-warnings-plugin or by using an ignoreOrder configuration property). I strongly recommend you not to do it, until you investigate the problem first. By filtering out the warnings:
- You will not solve the problem, you will just stop thinking about it. This order conflict can lead to different CSS order between development and production, which leads to style bugs, that will be found only after the site is deployed.
- Even though you’ve analyzed current problems, and you are sure they will not affect production build, there is always a chance that in the future new order conflicts will appear, which you will not be aware about.
Why is it happening?
Let’s start with the most simplest scenario. We have two pages: RegistrationPage
and LoginPage
, which both uses Typography
and Button
components.
// pages/RegistrationPage/RegistrationPage.tsx
import Button from 'components/Button';
import Typography from 'components/Typography';
// pages/LoginPage/LoginPage.tsx
import Typography from 'components/Typography';
import Button from 'components/Button';
As you can see, those components are imported to two different files in a different order. Each of the components has its own CSS file. During the build. those CSS files should be processed and concatenated to one global CSS file. And mini-css-extract-plugin is not able to determine the order, because it varies between the files.
In this case the Button.module.css
and Typography.module.css
style order is not deterministic and can vary from one build to another, producing different styles on production.
How to fix?
The easy example is pretty easy to fix: you just analyze the warning and look which two components are affected. Search for all the places around this project where one of these components is used and try to reorder imports, so they will appear in the same order always.
So the example from the previous part should look like this:
// pages/RegistrationPage/RegistrationPage.tsx
import Button from 'components/Button';
import Typography from 'components/Typography';
// pages/LoginPage/LoginPage.tsx
import Button from 'components/Button';
import Typography from 'components/Typography';
More complex example
If the things were so easy, we could just use eslint-plugin-import which has a special order/alphabetize rule. By enabling this rule and running eslint
with the --fix
option, you can easily sort imports alphabetically and you will have a consistent import order all around the project. But does it work for every case? Unfortunately, not. Let’s imagine, that our LoginPage
components does not import the Button
component, but it imports the LoginForm
component, which imports the Button
:
// pages/RegistrationPage/RegistrationPage.tsx
import Button from 'components/Button';
import Typography from 'components/Typography';
// pages/LoginPage/LoginPage.tsx
import Typography from 'components/Typography';
import LoginForm from './LoginForm';
// pages/LoginPage/LoginForm/LoginForm.tsx
import Button from 'components/Button';
What happened here and why those alphabetically sorted imports emit a warning? Because in LoginPage
Button
is imported inside LoginForm
, so it is imported after Typography
.
There are two ways to resolve the issue:
- To import Button after Typography in the
RegistrationPage
component - To import
LoginForm
beforeTypography
in theLoginPage
component
There is no automatic way to resolve those issues in the whole project. You need to analyze each of the warnings and to find a solution which will suite best for every use-case around the codebase. Don’t be afraid of the warnings count: usually, one fix can eliminate 10 warnings at the same time.
How to ensure it will never happen again?
So you have analyzed each of the problems, resolved the import order and now the projects compiles without those errors. Nice, it was a difficult journey, and nobody wants to repeat it once again.
But nobody can gurantee that this problem will never happen again in the future. On the contrary, it will happen with a pretty big probability. If it will be found at the same moment, when it appeared, it is extremely easy to fix it. But what if the production build is done on the CI server and nobody is looking at the warnings output?
You can use a special fail-on-errors-webpack-plugin to make webpack fail if any warnings occured. It can be configured as easy as:
// webpack.config.js
const FailOnErrorsPlugin = require('fail-on-errors-webpack-plugin');
module.exports = {
...
plugins: [
...
new FailOnErrorsPlugin({
failOnErrors: true,
failOnWarnings: true
});
]
}
You can also have asset size
and entrypoint size
warnings, which can be configured by using a perfomance webpack configuration property:
// webpack.config.js
module.exports = {
performance: {
maxEntrypointSize: 5 * 1024 * 1024,
maxAssetSize: 1 * 1024 * 1024
},
}
If you have other warnings, which you want to skip, you can always use stats.warningsFilter