Create a gist now

Instantly share code, notes, and snippets.

@sokra /webpack-2.md Secret
Last active Dec 17, 2017

What would you like to do?
What's new in webpack 2

What's new in webpack 2

Work in progress... This reflects stuff until 2.0.5-beta+

Major changes

ES6 Modules

webpack 2 brings native support ES6 Modules. This means webpack now understands import and export without them being transformed to CommonJS:

import { currentPage, readPage } from "./book";

currentPage === 0;
readPage();
currentPage === 1;
// book.js
export var currentPage = 0;

export function readPage() {
	currentPage++;
}

export default "This is a book";

Code Splitting with ES6

The ES6 Loader spec defines System.import as method to load ES6 Modules dynamically on runtime.

Webpack threads System.import as splitpoint and puts the requested module in a separate chunk.

System.import takes the module name as argument and returns a Promise.

function onClick() {
	System.import("./module").then(module => {
		module.default;
	}).catch(err => {
		console.log("Chunk loading failed");
	});
}

Good news: Failure to load a chunk can be handled now.

Dynamic expressions

It's possible to pass an partial expression to System.import. This is handled similar to expressions in CommonJS (webpack creates a context with all possible files).

System.import creates a separate chunk for each possible module.

function route(path, query) {
	return System.import("./routes/" + path + "/route")
		.then(route => new route.Route(query));
}
// This creates a separate chunk for each possible route

Mixing ES6 with AMD and CommonJS

As for AMD and CommonJS you can freely mix all three module types (even within the same file). Webpack behaves similar to babel in this case:

// CommonJS consuming ES6 Module
var book = require("./book");

book.currentPage;
book.readPage();
book.default === "This is a book";
// ES6 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";

babel and webpack

The es2015 babel preset transforms ES6 Modules to CommonJS by default. To use webpack to process ES6 Modules you should use the es2015-webpack preset instead.

ES6 specific optimizations

The static nature of ES6 Modules allows some new kind of optimizations. In example in many cases it's possible to detect which exports are used and which aren't used.

In cases in which webpack can say for sure that an export isn't used it omits the statement which exposes the export to other modules. Later the minimizer may flag the declaration as unused and omits it.

In the following cases it's possible to detect usage:

  • named import
  • default import
  • reexport

In the following cases it's not possible to detect usage:

  • import * as ... when used indirectly
  • CommonJS or AMD consuming ES6 module
  • System.import

ES6 export mangling

In cases where it's possible to track export usage, webpack can mangle export names to single char properties.

Configuration

In the past environment variables are often used to handle different environments in the configuration file. Webpack 2 brings a new way to pass options to the configuration.

The configuration file can export a function which returns the configuration. The function is called by the CLI and the value passed via --env is passed to the configuration function.

You can pass a string (--env dev => "dev") or a complex options object (--env.minimize --env.server localhost => {minimize: true, server: "localhost"}). I would recommend using an object, because it's more extendable, but it's up to you.

Example

// webpack.config.babel.js
exports default function(options) {
	return {
		// ...
		devtool: options.dev ? "cheap-module-eval-source-map" : "hidden-source-map"
	};
}

Resolving options

There was a major refactoring in the resolver (https://github.com/webpack/enhanced-resolve). This means the resolving option were changed too. Mostly simplification and changes that make it more unlikely to configure it incorrectly.

The new options are:

{
	modules: [path.resolve(__dirname, "app"), "node_modules"]
	// (was split into `root`, `modulesDirectories` and `fallback` in the old options)
	// In which folders the resolver look for modules
	// relative paths are looked up in every parent folder (like node_modules)
	// absolute paths are looked up directly
	// the order is respected

	descriptionFiles: ["package.json", "bower.json"],
	// These JSON files are read in directories

	mainFields: ["main", "browser"],
	// These fields in the description files are looked up when trying to resolve the package directory

	mainFiles: ["index"]
	// These files are tried when trying to resolve a directory

	aliasFields: ["browser"],
	// These fields in the description files offer aliasing in this package
	// The content of these fields is an object where requests to a key are mapped to the corresponding value

	extensions: [".js", ".json"],
	// These extensions are tried when resolving a file

	enforceExtension: false,
	// If false it will also try to use no extension from above

	moduleExtensions: ["-loader"],
	// These extensions are tried when resolving a module

	enforceModuleExtension: false,
	// If false it's also try to use no module extension from above

	alias: {
		jquery: path.resolve(__dirname, "vendor/jquery-2.0.0.js")
	}
	// These aliasing is used when trying to resolve a module
}

Minor breaking changes

Promise polyfill

The chunk loading stuff now relies on Promise being available. This means you need to provide a Promise polyfill for older browsers.

The ES6 spec uses promises and I don't want to include a Promise polyfill in every bundle. So it's up the application developer to provide the polyfill if needed.

Can I use Promises?

Other polyfills

You need a Object.defineProperty polyfill for ES6 Module or if using the module object in other ways than module.exports, module.id, module.loaded or module.hot.

For ES6 Modules you also need a Function.prototype.bind polyfill.

That's not new but anyway: You need an Object.keys polyfill for require.context().keys().

Loaders configuration

The loaders in the configuration now match to the resourcePath instead of the resource. This means the query string is no longer included for matching.

This was an issue with bootstrap which complicates the test for bootstrap fonts and images from /\.svg$/ to /\.svg($|\?)/. Now you can use the simple form.

The loader in the configuration now resolves relative to the configuration file (or the context option in the configuration file if specified). This should fix some issues with npm linked modules that are outside of the current package.

Another change allows the following syntax to configure loaders:

loaders: [
	{
		test: /\.css$/,
		loaders: [
			"style-loader",
			{ loader: "css-loader", query: { modules: true } },
			{
				loader: "sass-loader",
				query: {
					includePaths: [
						path.resolve(__dirname, "some-folder")
					]
				}
			}
		]
	}
]

Loader options & minimize

The UglifyJsPlugin no longer puts loaders into minimize mode. The debug option has been removed. Loaders should no longer read their options from the webpack configuration. Instead you need to provide these options with the LoaderOptionsPlugin.

new webpack.LoaderOptionsPlugin({
	test: /\.css$/, // optionally pass test, include and exclude, default affects all loaders
	minimize: true,
	debug: false,
	options: {
		// pass stuff to the loader
	}
})

This happens for separation of concern reasons. I want to disallow arbitrary keys in the configuration, to enable configuration validation.

Plugins

Many plugins now take option objects instead of multiple arguments. This happens because it is easier to extend. They throw an Error when the old argument style is passed.

HMR communication

In webpack 1 the update signal used the Web Messaging API (postMessage). Webpack 2 uses a standard event emitter to receive the event. This means WebSocket must be inline in the bundle.

webpack-dev-server has inlined mode as default now.

This should allow to use the webpack-dev-server to update code in WebWorkers.

Occurrence order

The plugin is no longer needed and occurrence order is on by default.

Code Splitting

require.ensure and AMD require is now always async, even if the chunk was already loaded.

born2net commented Feb 2, 2016

is typescript lazyloading supported?

born2net commented Feb 2, 2016

here is an example of what I am after http://blog.mgechev.com/2015/09/30/lazy-loading-components-routes-services-router-angular-2/

congrats on the beta release

deteect => detect

xabikos commented Feb 2, 2016

Is there a plan to support the exclude configuration in loaders through CLI argument as pointed here in comments

Bartekus commented Feb 2, 2016

fyi es2015-native-modules has been available for the tree-shaking purpose for over a month now too, it deals specifically with the CommonJS problem and is done as per Dr. Axel Rauschmayer specifications.

Also, there is this little gem as per @ getify

(async x => someUtil(...))()
  .then(...)

that calls someUtil(..) which may or may not return a promise, and absorbs any sync error it may or may not throw.

the equiv alternative is much uglier and slower since it takes an extra tick:

Promise.resolve().then(x => someUtil())
  .then(...)

dallonf commented Feb 3, 2016

Regarding the "Loaders configuration" section, I'd like to see some other way to parameterize loaders. Right now I'm taking advantage of the querystring, for example, to use a different loader configuration for *.css and *.css?global, the former using CSS Modules by default. Will there be another way to accomplish something like this?

Still too much configuration and no conventions. Switch to http://brunch.io folks.

chiplay commented Feb 3, 2016

Just wanted give a HUGE thanks for all of your tireless work on this amazing open source project!!!

gajus commented Feb 3, 2016

One more breaking change:

  • OccurenceOrderPlugin plugin alias has been removed (This was an alias for a previously misspelled OccurrenceOrderPlugin plugin). OccurrenceOrderPlugin remains.

samccone commented Feb 3, 2016

@paulmillr really? Let's try and treat people with respect and not resort to outright trolling... We wonder why people get burnt out on OSS... The fact that you work on quite a few OSS projects and still think that what you wrote was ok is extra rough 😒


Awesome work everyone on webpack2 πŸ‘

kittens commented Feb 3, 2016

@paulmillr wow, have some respect dude. not cool.

dtinth commented Feb 4, 2016

@dallonf Put it in file name e.g. .global.css?

configurate => configure

And thanks for the amazing work!

tnrich commented Feb 4, 2016

Awesome work @sokra and other contributors, very neat stuff! I was wondering if webpack had plans in the works to support so called "tree-shaking"?

gajus commented Feb 4, 2016

Awesome work @sokra and other contributors, very neat stuff! I was wondering if webpack had plans in the works to support so called "tree-shaking"?

webpack 2 does this. It is enabled by supporting ES6 modules, as described in this document. You can read more about it in @rauschma blog post http://www.2ality.com/2015/12/webpack-tree-shaking.html

@samccone @kittens no trolling. Just presenting some sane alternatives here for folks who are not interested in spending time configuring a build tool instead of actually creating new apps and services.

@paulmillr its true.

@paulmillr While it shows no class putting such messages on the webpack beta release notes. It did make me curious to checkout the Brunch site. And to my surprise you provide comparisons with grunt and gulp but completely avoid the word Webpack throughout your website.

@schippie yeah, good point, we should add it. The reason for that is Brunch being around since 2011, when there was no Grunt, Gulp or webpack. Just using the old data.

enviroment β†’ environment
configurate β†’ configure (?)
unlikly β†’ unlikely

grgur commented Feb 16, 2016

Big kudos and thanks to the team for the work you've put into this release! β™₯️
I gave it a try with 2.0.7-beta and worked beautifully. Here' my writeup on Webpack 2 with a React+Redux sample app

sohkai commented Feb 18, 2016

Will there be any migration notes for loaders and plugins that were built for 1.x? There's a number of well used add-ons (eg. sass-loader, extract-text-webpack-plugin) that are still requiring webpack@1.x, but as far as I can tell, they already work well on 2.x.

djeeg commented Feb 23, 2016

@born2net looks like typescript1.8 + webpack2.0.7 supports lazy loading (at least with react-router2.0.0 + babel6.5 thrown into the mix)

My test here: https://github.com/djeeg/example-react-router-universal-lazyroutes-typescript

amobiz commented Feb 25, 2016

Regarding the conditional configurations, I would shamelessly recommend my json-regulator.

With json-regulator, you can write configurations this way:

// webpack.config.babel.js
import regulate from 'json-regulator';

let config = {
    dev: {
        // ...
        devtool: "cheap-module-eval-source-map"
    },
    prod: {
        // ...
        devtool: "hidden-source-map"
    }
};

exports default function(options) {
    return options.dev ? regulate(config, 'dev', 'prod') : regulate(config, 'prod', 'dev');
}

xgrommx commented Feb 26, 2016

@paulmillr your brunch doesn't have a hot swap code approach.

rogovdm commented Mar 1, 2016

Just amazing, @sokra. Thanks.

bstst commented Mar 9, 2016

Has the concept of resolve.root been made redundant? We prefer to use absolute (and sometimes relative) paths for imports, e.g. in a situation where the source root is ./src, all components may use a relative or an absolute path to retrieve components. E.g. the component ./src/router.jsx can simply do import Inbox from 'app/inbox.jsx' (the physical path being ./src/app/inbox.jsx). Previously we were using resolve.root to achieve this. This does not seem to work with the current beta. Is this intentional?

EDIT:

Sorry, my bad -- my config was bad. I was defining the options for the resolver in the wrong place. This works like a charm:

resolve: { extensions: ['.jsx', '.js'], modules: [ path.resolve(__dirname, 'src'), 'node_modules' ] },

@paulmillr perhaps we want Webpack and its massive array of features, loaders, plugins, and all the configuration that goes with them? It's a lot to compete with and to sell Webpack users on an alternative, you'll going to have to go a lot deeper than harping on its configuration over convention model.

grrowl commented Mar 15, 2016

It would be great if someone could cover a migration path for plugins passed to ResolverPlugin. The plugins: option is missing from this document but exists in the code

@ghost

ghost commented Mar 30, 2016

What is the summary of changes from 2.0 to 2.1?

Congratulations on making more people not want to use Brunch @paulmillr.

Thanks for all the work Webpack team! No other build tool can compare to Webpack.

Is this my birthday gift in advance? Dynamic expressions? More ES6 support? @sokra, thanks, keep up the great work. This is pure awesomeness. πŸš€

fzero commented Apr 1, 2016

Most important question ever: is anyone working on documenting those things and making a decent "getting started" section on the website? This is the most important feature lacking on Webpack.

jtenner commented Apr 1, 2016

@fzero has the nail right on the head! I'm ready to switch but I need some guidance on how to set up an es2015 project with the new loaders!

Thank you so much @sokra! Keep up your amazing work!

This is great stuff. Thank you so much @sokra & co.!

I don't know why everyone is jumping on @paulmillr his comments have some merit. webpack already has some sensible defaults adding some more to shrink the majority of webpack config files would be an excellent addition to webpack 2. For example look at the output block in many webpack config files. I imagine most of us have something similar to this one below;

output: {
        path: __dirname + '/public',
        filename: 'bundle.js',
        publicPath: '/public/'
},

Why require people to specify filename: 'bundle.js', why not make that the default if one isn't specified? Same with all the other values in output. public seems a reasonable default folder name, if you are not happy with it then configure your own value, but if webpack had defaults for these values I'm sure most people would skip that config block.

Here is a rough sketch of an API that relies on defaults.

import {configBabelLoader} from 'babel-loader';
import {configCSSLoader} from 'css-loader';
import {webpackConfig} from 'webpack';

configBabelLoader(webpackConfig);
configCSSLoader(webpackConfig);

export default webpackConfig;

Let webpack provide a default config object and let loaders configure a reasonable default value for themselves (same with Plugins) when provided with the default webpack config. It reduces the need for people to repeat the same config blocks over and over again. If they need their own config it's easy for them to apply it in the fashion they are used to now or as second argument in the configBabelLoader function. It also reduces the loader resolution issues as loaders are required and would provide a direct reference to themselves.

Owner

sokra commented Apr 4, 2016

There are defaults for every key in the output object. output.filename defaults to [name].js, output.path defaults to the current directory, publicPath defaults "".

Interesting, I didn't know that. Are those defaults reasonable ones for a production app though? I guess [name].js means the entry point js module name? What about the pattern of having loaders/plugins configure themselves given a webpack config object or webpack providing a correctly set up config object?

jaetask commented Apr 5, 2016

Great work. thank you

@sokra we have setup a framework that is dependent on webpack2. But since it is in its beta we are not able to push to production. Can I know when will the stable version of webpack 2 release? Thanks in advance for your quick response :)

@sokra Fabulous feature especially the tree shaking part. Looking forward for webpack 2.

con-ssc commented May 6, 2016

How is it possible to make tree shaking work when I use TypeScript?

Bnaya commented May 8, 2016

@con-ssc, set the compilerOptions target to ES6, and then use babel with the webpack preset

onpaws commented May 27, 2016

thanks for your awesome work @sokra

Can we finally work with npm linked modules ?

where is webpack.ResolverPlugin?

I'm excited for the RTM of webpack 2!

Great work, just one question, is the webpack.LoaderOptionsPlugin supposed to work already because I couldn't make it work and the only way to put loaders in to minifying mode was to in fact include uglify plugin for the prod build.

We need some better docs for this.

prasannavl commented Jun 19, 2016

Great stuff. What exactly is the difference between, loader query, and the options sent through LoaderOptionsPlugin?

I agree that plugins should no longer read from arbitrary properties in the configuration. One of the longest curves I had to get past was that webpack just too much and there was no one way to do them. So too many ways to do simple things that got very confusing. I think simplifying the loader options is a great in that aspect.

With that in mind, I understand that loader query were called so, because since in Webpack 1 it was passed as a query string. Now, since its no longer the case, wouldn't it make sense to call it more appropriately as options, perhaps?

Why is this gist secret and how am I able to access it (especially via Google results)? How to GitHub Gist?

Also, what about http/2?

I've created a boilerplate that tries to use as much of webpack 2's radness as possible. Might be a good reference for some. It includes hot reloading of the webpack configuration too.
https://github.com/ctrlplusb/react-universally

stevegibbings commented Jul 12, 2016

EDIT: Corrected in Fork https://gist.github.com/stevegibbings/6394e32c034f183634b384379f5e4510

Typo

exports default function(options) {

should be

export default function(options) {

KeKs0r commented Jul 19, 2016

Is there a way to tell webpack that 2 different System.import calls should be bundled together into a single chunk?
As it was possible with the 3rd argument to require.ensure?

donaldpipowitch commented Jul 25, 2016

Can someone explain me the module field as a value of resolve.mainFields in webpack 2? When is it used? Does it replace the webpack field of resolve.packageMains in webpack 1? Can I assume that others use webpack when I use module so I can safely require other assets from dependencies of dependencies (given the loader config is correct). E.g. if my application requires a component A which requires a component B which requires an image, if want to have that image required in my application, too. (Component A and component B are standalone node modules.)

UPDATE

module is defined here: https://github.com/dherman/defense-of-dot-js/blob/master/proposal.md
But I probably would use webpack in the future, too, to solve my use case.

One thing tripped me. Order matters folks!

Instead of mainFields: ["main", "browser"], use mainFields: ["browser", "main"],. Otherwise you might see some errors related to native node modules being required.

Kovensky commented Aug 5, 2016

As of babel-core@^6.13.2 + babel-preset-es2015@^6.13.1 you should be able to use "presets": [ [ "es2015", { "modules": false } ] ] (note that it's a [presetName, options] tuple) instead of the es2015-webpack preset to preserve the ES6 modules.

btnwtn commented Aug 5, 2016

Nice find @Kovensky

joeshub commented Aug 11, 2016

This is great. Is there a larger effort to write or re-write the webpack documentation for Webpack 2?

dandv commented Aug 15, 2016

Why is this gist secret and how am I able to access it (especially via Google results)?

Even worse, GitHub doesn't send notifications for gist comments.

Tobias, next time you're reading comments here, can you please consider moving this to a better venue, like a README on GitHub? Unless, of course, it's supposed to be secret.

@joeshub the writer of SurviveJS: Webpack has been assigned the task of writing the Webpack 2 docs. If you haven't checked out his book I suggest you do πŸ‘

jahed commented Aug 21, 2016

@dandv I think the reason why it's secret is to avoid misunderstandings that Webpack 2 is released and/or finalised. This is more of a sneak peak and the feature list may change in the future.

babel and webpack

The es2015 babel preset transforms ES6 Modules to CommonJS by default. To use webpack to process ES6 Modules you should use the es2015-webpack preset instead.

This is no longer quite correct, You should still use babel-preset-es2015 with babel-core >= 6.13.0 you can use the preset options in your .babelrc to disable modules like so:

{
    "presets": [
        [
            "es2015",
            {
                "modules": false
            }
        ]
    ]
}

cycold commented Aug 31, 2016

cool +1

jaydenseric commented Sep 1, 2016

Not mentioned anywhere are the changes to UglifyJsPlugin config defaults.

The sourceMap option of is now false by default, even if a devtool sourcemap option has been set. If you want devtool and UglifyJsPlugin to work at the same time you will need to do this:

new webpack.optimize.UglifyJsPlugin({
  sourceMap: true
})

compress.warnings now defaults to false, making this previously common config redundant:

new webpack.optimize.UglifyJsPlugin({
  compress: {
    warnings: false
  }
})

boo1ean commented Sep 22, 2016

How to migrate existing preloaders?

etiennejcharles commented Sep 22, 2016

To migrate loaders
I think the following commit can indicate what needs to be done.

webpack/webpack@b2e8f96

In every case, you might want to see the full commit list.
https://github.com/webpack/webpack/commits/master

@boo1ean ... regarding preloaders, try this:-

// webpack 1.x
module: {
    preLoaders: [
      {
        test: /\.js?$/,
        loader: 'eslint',
        exclude: /node_modules/
      }
    ]
}

... becomes ...

// webpack 2.x
module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.js?$/,
        loader: 'eslint',
        exclude: /node_modules/
      }
    ]
}

faller commented Sep 23, 2016

Nice job! How's about i18n-webpack-plugin?

@ghost

ghost commented Oct 4, 2016

just got my Angular 2.0 stuff moved over to webpack 2. Got tripped up on CSS/SASS sourcemaps pretty good however. Anybody have any idea how to get that working with beta.25??

{ test : /\.scss$/, include : [ helpers.root('src/app/sass-config.scss'), helpers.root('src/app/styles') ], loader : ExtractTextPlugin.extract({ loader : [ { loader : 'css', query : { sourceMap : false // set to true... } }, 'postcss', { loader : 'sass', query : { sourceMap : false // set to true... } } ], fallbackLoader : 'style-loader' }) }

pretty sure I nailed the config...and by "pretty sure" I mean "I have no idea" >.<

jasan-s commented Oct 29, 2016

I can't seem to figure out how to convert this to webpack 2 :

{ test: /\.css$/, loader: ExtractTextPlugin.extract('style', 'css?sourceMap&modules&localIdentName=name]__[local]___[hash:base64:5]&importLoader=1!postcss') }, with

postcss: [ autoprefixer({ browsers: ['last 2 versions'] }) ]

Is ES2015 in webpack.config.js still supported? I used to append a .babel before the extension, but it seems it's not supported anymore.

rxgx commented Nov 16, 2016

@rbartoli Are you referring to the Babel preset es2015? There's been some recent improvements to Babel and that preset for working with ES2015+ features.

This is great and all, but locking the config to a schema just completely broke our deployment procedures.
Where we were appending a metadata key to the config, it is now invalid, which means I can't merge metadata (env-specific data) across configs, making my common config much less "common". Now I have to abstract that stuff out into some common-vars file, or something?
Do you have a solution for this? I understand wanting a validatable schema, but at least give us a key on the config object for misc other config stuff, it's a pretty common scenario. Because the loaderConfigPlugin seems like a very hacky way to try to somehow access commonconfig settings that aren't part of the base schema.
Thoughts?

augbog commented Nov 28, 2016

I like how this gist is marked Secret but its one of the top Google search results for what's new in webpack 2 lol

drewdeal commented Jan 9, 2017

Looking forward to using WP2 official tree-shaking, but shouldn't 1.current already only put functions in the bundle that I have import or at least only the ones I export? I have been experimenting with this thinking that it should work if I do something a certain way, as I see references to rxjs and other libraries constantly saying something along the lines of, "only import the methods you intend to use to reduce bundle size".

AngeIII commented Jan 19, 2017

People, is there any ready to use forks or repos where used workable babel-polyfills for IE11? Chunked with vendor and bundle with CommonChunksPlugin and of course work with react? I have tried many different solutions but it looks like it doesn't work

joefraley commented Jan 26, 2017

@rxgx, @rbartoli wants to know if this has changed:

// webpack.config.babel.js <--- special naming convention makes ES2015 features magically work inside the webpack.config file
// don't need to run $ babel-node node_modules/bin/webpack 
// to make this file work.
// just run $ webpack
import webpack from 'webpack' 

export default {
  modules:{},
  plugins: ...require('./plugins'), // <--- spread operator works
  // ... other stuff
}

adyz commented Feb 24, 2017

What if I want to run webpack from my node_modules local folder? it seems like it cannot resolve my required modules. Here is's how my resolve webpack looks:

resolve: {
    modules: [
        path.resolve(__dirname, ""),
        path.join(__dirname, "node_modules/")
    ],
    descriptionFiles: ['package.json'],
    extensions : ['*','.js', '.hbs', '.vue'],
    aliasFields: ["browser"],
    alias: {
        'handlebars': 'handlebars/runtime.js',
        'vue$': 'vue/dist/vue.common.js'
    },
}

jinhduong commented Apr 17, 2017

Big thanks to Webpack team ;).

ChrisKramer2020 commented Aug 6, 2017

Can someone help me? I am having trouble starting my localhost server here is a link to my problem on stack overflow here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment