The following sections describe the major changes from webpack 1 to 2.
Note that there were far fewer changes between 2 and 3, so that migration shouldn't be too bad. If you are running into issues, please see the changelog for details.
This content may be moved to the blog post in the near future as version 2 has been out for a while. On top of that, version 3 was recently released and version 4 is on the horizon. As noted above, folks should instead to refer to the changelog for migrations.
resolve.root
, resolve.fallback
, resolve.modulesDirectories
These options were replaced by a single option resolve.modules
. See resolving for more usage.
resolve: {
- root: path.join(__dirname, "src")
+ modules: [
+ path.join(__dirname, "src"),
+ "node_modules"
+ ]
}
resolve.extensions
This option no longer requires passing an empty string. This behavior was moved to resolve.enforceExtension
. See resolving for more usage.
resolve.*
Several APIs were changed here. Not listed in detail as it's not commonly used. See resolving for details.
module.loaders
is now module.rules
The old loader configuration was superseded by a more powerful rules system, which allows configuration of loaders and more.
For compatibility reasons, the old module.loaders
syntax is still valid and the old names are parsed.
The new naming conventions are easier to understand and are a good reason to upgrade the configuration to using module.rules
.
module: {
- loaders: [
+ rules: [
{
test: /\.css$/,
- loaders: [
- "style-loader",
- "css-loader?modules=true"
+ use: [
+ {
+ loader: "style-loader"
+ },
+ {
+ loader: "css-loader",
+ options: {
+ modules: true
+ }
+ }
]
},
{
test: /\.jsx$/,
loader: "babel-loader", // Do not use "use" here
options: {
// ...
}
}
]
}
Like in webpack 1, loaders can be chained to pass results from loader to loader. Using the rule.use
configuration option, use
can be set to an array of loaders.
In webpack 1, loaders were commonly chained with !
. This style is only supported using the legacy option module.loaders
.
module: {
- loaders: [{
+ rules: [{
test: /\.less$/,
- loader: "style-loader!css-loader!less-loader"
+ use: [
+ "style-loader",
+ "css-loader",
+ "less-loader"
+ ]
}]
}
-loader
module name extension removedIt is not possible anymore to omit the -loader
extension when referencing loaders:
module: {
rules: [
{
use: [
- "style",
+ "style-loader",
- "css",
+ "css-loader",
- "less",
+ "less-loader",
]
}
]
}
You can still opt-in to the old behavior with the resolveLoader.moduleExtensions
configuration option, but this is not recommended.
+ resolveLoader: {
+ moduleExtensions: ["-loader"]
+ }
See #2986 for the reason behind this change.
json-loader
is not required anymoreWhen no loader has been configured for a JSON file, webpack will automatically try to load the JSON
file with the json-loader
.
module: {
rules: [
- {
- test: /\.json/,
- loader: "json-loader"
- }
]
}
We decided to do this in order to iron out environment differences between webpack, node.js and browserify.
In webpack 1, configured loaders resolve relative to the matched file. However, in webpack 2, configured loaders resolve relative to the context
option.
This solves some problems with duplicate modules caused by loaders when using npm link
or referencing modules outside of the context
.
You may remove some hacks to work around this:
module: {
rules: [
{
// ...
- loader: require.resolve("my-loader")
+ loader: "my-loader"
}
]
},
resolveLoader: {
- root: path.resolve(__dirname, "node_modules")
}
module.preLoaders
and module.postLoaders
were removed: module: {
- preLoaders: [
+ rules: [
{
test: /\.js$/,
+ enforce: "pre",
loader: "eslint-loader"
}
]
}
UglifyJsPlugin
sourceMapThe sourceMap
option of the UglifyJsPlugin
now defaults to false
instead of true
. This means that if you are using source maps for minimized code or want correct line numbers for uglifyjs warnings, you need to set sourceMap: true
for UglifyJsPlugin
.
devtool: "source-map",
plugins: [
new UglifyJsPlugin({
+ sourceMap: true
})
]
UglifyJsPlugin
warningsThe compress.warnings
option of the UglifyJsPlugin
now defaults to false
instead of true
.
This means that if you want to see uglifyjs warnings, you need to set compress.warnings
to true
.
devtool: "source-map",
plugins: [
new UglifyJsPlugin({
+ compress: {
+ warnings: true
+ }
})
]
UglifyJsPlugin
minimize loadersUglifyJsPlugin
no longer switches loaders into minimize mode. The minimize: true
setting needs to be passed via loader options in the long-term. See loader documentation for relevant options.
The minimize mode for loaders will be removed in webpack 3 or later.
To keep compatibility with old loaders, loaders can be switched to minimize mode via plugin:
plugins: [
+ new webpack.LoaderOptionsPlugin({
+ minimize: true
+ })
]
DedupePlugin
has been removedwebpack.optimize.DedupePlugin
isn't needed anymore. Remove it from your configuration.
BannerPlugin
- breaking changeBannerPlugin
no longer accepts two parameters, but a single options object.
plugins: [
- new webpack.BannerPlugin('Banner', {raw: true, entryOnly: true});
+ new webpack.BannerPlugin({banner: 'Banner', raw: true, entryOnly: true});
]
OccurrenceOrderPlugin
is now on by defaultThe OccurrenceOrderPlugin
is now enabled by default and has been renamed (OccurenceOrderPlugin
in webpack 1).
Thus make sure to remove the plugin from your configuration:
plugins: [
// webpack 1
- new webpack.optimize.OccurenceOrderPlugin()
// webpack 2
- new webpack.optimize.OccurrenceOrderPlugin()
]
ExtractTextWebpackPlugin
- breaking changeExtractTextPlugin requires version 2 to work with webpack 2.
npm install --save-dev extract-text-webpack-plugin
The configuration changes for this plugin are mainly syntactical.
ExtractTextPlugin.extract
module: {
rules: [
{
test: /.css$/,
- loader: ExtractTextPlugin.extract("style-loader", "css-loader", { publicPath: "/dist" })
+ use: ExtractTextPlugin.extract({
+ fallback: "style-loader",
+ use: "css-loader",
+ publicPath: "/dist"
+ })
}
]
}
new ExtractTextPlugin({options})
plugins: [
- new ExtractTextPlugin("bundle.css", { allChunks: true, disable: false })
+ new ExtractTextPlugin({
+ filename: "bundle.css",
+ disable: false,
+ allChunks: true
+ })
]
A dependency with only an expression (i. e. require(expr)
) will now create an empty context instead of the context of the complete directory.
Code like this should be refactored as it won't work with ES2015 modules. If this is not possible you can use the ContextReplacementPlugin
to hint the compiler towards the correct resolving.
Link to an article about dynamic dependencies.
If you abused the CLI to pass custom arguments to the configuration like so:
webpack --custom-stuff
// webpack.config.js
var customStuff = process.argv.indexOf("--custom-stuff") >= 0;
/* ... */
module.exports = config;
You may notice that this is no longer allowed. The CLI is more strict now.
Instead there is an interface for passing arguments to the configuration. This should be used instead. Future tools may rely on this.
webpack --env.customStuff
module.exports = function(env) {
var customStuff = env.customStuff;
/* ... */
return config;
};
See CLI.
require.ensure
and AMD require
are asynchronousThese functions are now always asynchronous instead of calling their callback synchronously if the chunk is already loaded.
require.ensure
now depends upon native Promise
s. If using require.ensure
in an environment that lacks them then you will need a polyfill.
options
You can no longer configure a loader with a custom property in the webpack.config.js
. It must be done through the options
. The following configuration with the ts
property is no longer valid with webpack 2:
module.exports = {
...
module: {
rules: [{
test: /\.tsx?$/,
loader: 'ts-loader'
}]
},
// does not work with webpack 2
ts: { transpileOnly: false }
}
options
?Good question. Well, strictly speaking it's 2 possible things; both ways to configure a webpack loader. Classically options
was called query
and was a string which could be appended to the name of the loader. Much like a query string but actually with greater powers:
module.exports = {
...
module: {
rules: [{
test: /\.tsx?$/,
loader: 'ts-loader?' + JSON.stringify({ transpileOnly: false })
}]
}
}
But it can also be a separately specified object that's supplied alongside a loader:
module.exports = {
...
module: {
rules: [{
test: /\.tsx?$/,
loader: 'ts-loader',
options: { transpileOnly: false }
}]
}
}
LoaderOptionsPlugin
contextSome loaders need context information and read them from the configuration. This needs to be passed via loader options in the long-term. See loader documentation for relevant options.
To keep compatibility with old loaders, this information can be passed via plugin:
plugins: [
+ new webpack.LoaderOptionsPlugin({
+ options: {
+ context: __dirname
+ }
+ })
]
debug
The debug
option switched loaders to debug mode in webpack 1. This needs to be passed via loader options in long-term. See loader documentation for relevant options.
The debug mode for loaders will be removed in webpack 3 or later.
To keep compatibility with old loaders, loaders can be switched to debug mode via a plugin:
- debug: true,
plugins: [
+ new webpack.LoaderOptionsPlugin({
+ debug: true
+ })
]
In webpack 1, you could use require.ensure()
as a method to lazily-load chunks for your application:
require.ensure([], function(require) {
var foo = require("./module");
});
The ES2015 Loader spec defines import()
as method to load ES2015 Modules dynamically on runtime. webpack treats import()
as a split-point and puts the requested module in a separate chunk. import()
takes the module name as argument and returns a Promise.
function onClick() {
import("./module").then(module => {
return module.default;
}).catch(err => {
console.log("Chunk loading failed");
});
}
Good news: Failure to load a chunk can now be handled because they are Promise
based.
It's possible to pass a partial expression to import()
. This is handled similar to expressions in CommonJS (webpack creates a context with all possible files).
import()
creates a separate chunk for each possible module.
function route(path, query) {
return import(`./routes/${path}/route`)
.then(route => new route.Route(query));
}
// This creates a separate chunk for each possible route
As for AMD and CommonJS you can freely mix all three module types (even within the same file). webpack behaves similar to babel and node-eps in this case:
// CommonJS consuming ES2015 Module
var book = require("./book");
book.currentPage;
book.readPage();
book.default === "This is a book";
// ES2015 Module consuming CommonJS
import fs from "fs"; // module.exports map to default
import { readFileSync } from "fs"; // named exports are read from returned object+
typeof fs.readFileSync === "function";
typeof readFileSync === "function";
It is important to note that you will want to tell Babel to not parse these module symbols so webpack can use them. You can do this by setting the following in your .babelrc
or babel-loader
options.
.babelrc
{
"presets": [
["es2015", { "modules": false }]
]
}
No need to change something, but opportunities
webpack now supports template strings in expressions. This means you can start using them in webpack constructs:
- require("./templates/" + name);
+ require(`./templates/${name}`);
webpack now supports returning a Promise
from the configuration file. This allows async processing in your configuration file.
webpack.config.js
module.exports = function() {
return fetchLangs().then(lang => ({
entry: "...",
// ...
plugins: [
new DefinePlugin({ LANGUAGE: lang })
]
}));
};
webpack now supports more things to match on for loaders.
module: {
rules: [
{
resource: /filename/, // matches "/path/filename.js"
resourceQuery: /^\?querystring$/, // matches "?querystring"
issuer: /filename/, // matches "/path/something.js" if requested from "/path/filename.js"
}
]
}
There are some new CLI options for you to use:
--define process.env.NODE_ENV="production"
See DefinePlugin
.
--display-depth
displays the distance to the entry point for each module.
--display-used-exports
display info about which exports are used in a module.
--display-max-modules
sets the number for modules displayed in the output (defaults to 15).
-p
also defines process.env.NODE_ENV
to "production"
now.
Changes only relevant for loader authors.
Loaders are now cacheable by default. Loaders must opt-out if they are not cacheable.
// Cacheable loader
module.exports = function(source) {
- this.cacheable();
return source;
}
// Not cacheable loader
module.exports = function(source) {
+ this.cacheable(false);
return source;
}
webpack 1 only supports JSON.stringify
-able options for loaders.
webpack 2 now supports any JS object as loader options.
Before webpack 2.2.1 (i.e. from 2.0.0 through 2.2.0), using complex options required using ident
for the options
object to allow its reference from other loaders. This was removed in 2.2.1 and thus current migrations do not require any use of the ident
key.
{
test: /\.ext/
use: {
loader: '...',
options: {
- ident: 'id',
fn: () => require('./foo.js')
}
}
}