Buy

During development, we are not minifying our assets: yep, they're full of comments and spaces. And that's perfect for development! I like spaces! And those comments might be useful! Also... minification takes extra time, so we don't want to slow down our builds unnecessarily.

In other words, while developing, no minification! But for production, yes minification! That means that - somehow - we need to be able to pass a flag to webpack.config.js that tells it to compile in dev or production mode.

Using NODE_ENV

In Node applications, there's a standard way to signal the environment to your apps: by setting an environment variable called NODE_ENV. To read this, you can say process.env.NODE_ENV:

135 lines webpack.config.js
... lines 1 - 8
console.log(process.env.NODE_ENV);
... lines 10 - 135

Let's log that. Run webpack like normal:

./node_modules/.bin/webpack --watch

Ok! It prints undefined. So... how do we set that? On a UNIX system, prefix the command:

NODE_ENV=production ./node_modules/.bin/webpack --watch

Yes! It prints production. If you're on Windows, there's a library called cross-env that can help do this.

The point is: we can now send a flag into Webpack to tell it the environment.

Minify JavaScript

Awesome! Let's use this flag to minify our JavaScript first, via a plugin.

Start by replacing module.exports with a new variable: const webpackConfig =:

140 lines webpack.config.js
... lines 1 - 33
const webpackConfig = {
... lines 35 - 131
};
... lines 133 - 140

Then, all the way at the bottom, export this: module.exports = webpackConfig:

140 lines webpack.config.js
... lines 1 - 33
const webpackConfig = {
... lines 35 - 131
};
... lines 133 - 139
module.exports = webpackConfig;

Before that, add an if statement: if process.env.NODE_ENV === 'production'), then we will add a new plugin. So, webpackConfig.plugins.push() then new webpack.optimize.UglifyJsPlugin:

140 lines webpack.config.js
... lines 1 - 33
const webpackConfig = {
... lines 35 - 131
};
if (process.env.NODE_ENV === 'production') {
webpackConfig.plugins.push(
new webpack.optimize.UglifyJsPlugin()
);
}
module.exports = webpackConfig;

And... that's it!

Try it! Run webpack without the NODE_ENV flag first:

./node_modules/.bin/webpack --watch

Ok cool. The un-uglified layout.js file is 1.62 megabytes. Stop and re-run in production:

NODE_ENV=production ./node_modules/.bin/webpack --watch

Ahh... this takes longer to run! But, the JavaScript files are way, way smaller!

Open up the built login.js. Ah, yes, one beautiful, single line.

Tip

License comments from outside libraries are not removed from the Uglified files for legal reasons. To remove them, see the extractComments option.

Adding package.json scripts

But, remembering this long command is a bummer. Heck, the command was already long, before adding the NODE_ENV stuff! My fingers are so tired...

There's a great way to improve this. Open package.json. Add a new key called scripts set to a hash. Inside, you can put something like dev set to NODE_ENV=dev webpack:

36 lines package.json
{
... lines 2 - 7
"scripts": {
"dev": "NODE_ENV=dev webpack",
... lines 10 - 11
},
... lines 13 - 34
}

Thanks to that, we have a shortcut! Just run:

yarn dev

Yep, it runs NODE_ENV=dev webpack! And we don't even need to say node_module/.bin/webpack: the scripts know to look there already for webpack.

Let's add two more: watch set to the same thing with --watch on the end. And finally, production, with NODE_ENV=production:

36 lines package.json
{
... lines 2 - 7
"scripts": {
"dev": "NODE_ENV=dev webpack",
"watch": "NODE_ENV=dev webpack --watch",
"production": "NODE_ENV=production webpack"
},
... lines 13 - 34
}

I love it! Try them out:

yarn watch

Nice! Stop that, and try:

yarn production

The command looks right... and the final JavaScript files are super small.

But! Our work is not done yet: we still need to minify the CSS files... and handle a few other things.

Leave a comment!

  • 2017-10-05 Victor Bocharsky

    Hey Jose,

    Haha, because "const" keyword when you declare variables in JS means a bit different than in PHP ;) When you declare variable via "const", i.e. assign an object, it means that you cannot reassign to that variable another object (or any other value). But you can use and even modify the object you assigned when declare it. This is a new feature of ES6, check our screencast "const vs let" for more details: https://knpuniversity.com/s...

    Cheers!

  • 2017-10-05 Jose Diaz

    Hi Ryan,

    On 2:04 you change `module.exports` to `const webpackConfig`, and then before exporting it you push to it, ... but it was const! Why does this work?
    Coming from the PHP world this _sounds_ wrong.

    Thanks.

  • 2017-09-14 weaverryan

    Yo Thomas Talbot!

    If you have some specific things that weren't flexible enough, we would love to hear from them. Please open an issue - real-world feedback is really helpful! https://github.com/symfony/... Mostly, my philosophy was to create a library that made 80% of the use-cases VERY easy out-of-the-box. Now, when people have real-world requirements that aren't possible (or at least aren't easy), we're adding more configuration methods. For example, this PR (which I'll probably merge today) https://github.com/symfony/... will add about 5 new methods to allow you to control the options of 5 plugins used internally.

    Cheers!

  • 2017-09-13 Thomas Talbot

    Thanks for your answer.

    I think I'll keep the --env. :D

    For Webpack Encore, I've tried it but there is lesser "granularity" IMHO.
    We can't configure all that we want.... or it is not yet mature enough.

    P.S. sorry, i'm pretty bad in english :(

  • 2017-09-13 weaverryan

    Yo Thomas Talbot!

    Great questions! And actually, I don't have a good answer for you! I've looked at the --env flag once before... and I thought I remembered there being some issue or shortcoming with it. But now, I can't find any issue with it: it seems like a perfectly fine way to pass this "flag" into your code! Of course, you need to return a callback now.. but that's not really an issue :).

    Actually, I *may* have had a problem with it in the context of Webpack Encore: where we (in that library) need to know what environment you're in (with --env, the environment is passed to your webpack.config.js callback, but wouldn't be automatically accessible internally by Encore). That may have been why I completely neglected --env here.

    Obviously, both work, but --env is obviously a bit more "official" :).

    Cheers!

  • 2017-09-13 Thomas Talbot

    Hi Ryan,

    What about the option "--env.production" for example ? It seems to be easier than NODE_ENV.