Reading a Webpack config and pulling out your hair trying to figure out WTF it does? You’re not alone. That’s pretty much everyone that has ever read a Webpack config.
The most complicated part is the loaders section. Part of the reason it is so complicated: there are many many different ways to write the same config.
Webpack 2 was just released. It’s a great product with tons of awesome features - but unfortunately it introduced even more ways to configure loaders.
But there’s hope - once you understand that many options are the same as others, you’ll realize there is a lot less to learn than you thought. So let’s dive in. 💦
Two important terms before we get started:
rule: A rule specifies how files that meet certain criteria should be treated. For instance you might have a rule that says ”.js files should be run through the babel-loader”. You can read more about rules in the official docs.
loader: A loader is basically a webpack plugin. A loader processes files. Babel-loader is the most famous loader - it converts ES6+ into ES5. You can read more about loaders in the official docs.
Every rule has one or more loaders.
Before getting into all the different ways that loader configs can be written - you need to know about four key ways in which loader configuration has changed in Webpack 2:
rules
is the new & recommended keyword but loaders
will still be accepted.
Using it will NOT generate a warning unfortunately.
These are equivalent:
// rules way
module: {
rules: [
...
]
}
// loaders way
module: {
loaders: [
...
]
}
Within a rule, use
or loaders
can specify the list of loaders. use
is the
new keyword but loaders
can still be used and will not generate a warning.
These are equivalent:
{ test: /\.js$/, use: ['babel-loader'] }
{ test: /\.js$/, loaders: ['babel-loader'] }
This is very helpful for configuring loaders. In Webpack 1 CSS modules were configured like this:
'css-loader?modules&importLoaders=1&localIdentName=[name]_[local]_[hash:base64:5]',
But now they can be configured like this:
{
loader: 'css-loader',
options: {
modules: true,
importLoaders: 1,
localIdentName: "[name]_[local]_[hash:base64:5]",
},
...
}
All loader names must use the full package name.
In Webpack 1 babel-loader
could be written as babel
. Webpack 2 will fail and
show an error you try to use a loader without writing its full name. This is
awesome, but it’s the only place where Webpack 2 actually made an old style of
config obsolete.
BTW - to see more about what changed in Webpack 2, see the official migration guide. It is very good.
Now that we’ve covered some Webpack 2 updates, let’s dig in to a bunch of equivalent configurations. Each of these sections discusses several ways of writing the same list of loaders for a rule.
There are at least 7 equivalent ways to specify a single loader without a configuration. I’ve placed emoji next to each one to let you know how they make me feel. 🤓
loader: "babel-loader";
Very succinct. Same syntax as Webpack 1.
use: ["babel-loader"];
New array syntax, still pretty readable and succinct.
use: [{ loader: "babel-loader" }];
This is starting to get a little verbose. This object syntax is new in Webpack 2. It is most useful when you want to add options to your loader (see below).
use: "babel-loader";
The docs specifically say NOT to do this one. I was surprised that it works!
loader: { loader: 'babel-loader' }
This might be a good way to go if you anticipate adding some options to that object.
loaders: ["babel-loader"];
Remember that loaders
is a Webpack 1 property and is no longer recommended by
the maintainers.
loaders: [{ loader: "babel-loader" }];
Again, loaders
is deprecated so please do not use.
There are at least 3 equivalent ways to specify a single loader with a configuration.
loader: {
loader: 'babel-loader',
options: { presets: ['es2016'] },
}
This is the format that the documentation recommends, so I use it despite the
confusing bit where there are two properties named loader
. The options
property is new in Webpack 2 and lets us write our loader options in a much more
readable way then the next ones:
loader: "babel-loader?presets[]=es2015";
Leftover Webpack 1 syntax. I can’t think of any reason to use this old format.
loader: 'babel-loader',
query: {
presets: ['es2016']
}
This is another leftover Webpack 1 syntax. It’s not great because if you add
additional loaders it is not clear which loader the query
options apply to.
There are many more ways because any of the above could be changed into the
array format with either loaders
or use
.
There are at least 3 ways to specify multiple loaders when none of them require configuration.
use: ["style-loader", "css-loader"];
This is the most succinct way.
use: [{ loader: "style-loader" }, { loader: "css-loader" }];
You can mix and match this one with the strings approach above.
loader: "style-loader!css-loader";
This is the Webpack 1 way, and it is still supported. I find it less readable than either of the above ways.
Again, there are more variations where the deprecated loaders
property is
written in place of use
.
Here are two ways to configure rules with multiple loaders, in which some of the loaders need to be configured.
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: true,
importLoaders: 1,
localIdentName: '[name]_[local]_[hash:base64:5]',
},
]
The options
property is new in Webpack 2 and allows you to specify loader
options in a more readable way.
use: [
"style-loader",
"css-loader?modules&importLoaders=1&localIdentName=[name]_[local]_[hash:base64:5]"
];
This is another Webpack 1-style config. It’s harder to read than the object version.
For more variations you could imagine that style-loader
might be replaced with
the object format, or use
could be replaced with loaders
. Note that some
loader options might contain functions, in which case the string configuration
is not an option.
Webpack 2 is an awesome product with a great feature set. But I believe there are far too many ways to write the config.
The worst part is that Webpack 2 doesn't show any errors/warnings when you use deprecated properties and config formats. I'm sure this will be added eventually, but it can be a pain in the meantime.
When you read configs written by others, you can expect to find any of the above odd/weird formats. Try to use the recommended properties yourself, but be aware that others may not do so. Keep this in mind when you read configs in starter projects, open source projects, etc.
Hopefully once Webpack 2 has been out for a while, some of the older config options will no longer be supported.