Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Production Build

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

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

Tip

If you are using Webpack 4 or higher, use this to configure the Webpack mode:

// webpack.config.js
//...
const webpackConfig = {
    mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
    //...
}

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;

Tip

This is for Webpack 4 or higher. At first, install terser-webpack-plugin and then use the following configuration

// webpack.config.js
//...
const TerserPlugin = require('terser-webpack-plugin');

//...

if (process.env.NODE_ENV === 'production') {
    webpackConfig.optimization.minimizer = [new TerserPlugin()];
}

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!

13
Login or Register to join the conversation
Rainer-S Avatar
Rainer-S Avatar Rainer-S | posted 3 years ago | edited

In the Windows Powershell the following command works:


$env:NODE_ENV="production"; .\node_modules\.bin\webpack --watch

Or alternatively, as already described as video comment


yarn  add cross-env --dev
cross-env NODE_ENV=production .\node_modules\.bin\webpack --watch
Reply

Hey Sven,

Thank you for this tip for Windows users!

Cheers!

Reply
Anton B. Avatar
Anton B. Avatar Anton B. | posted 3 years ago

Invalid file name here when downloading. (test-upload2).mp4

Reply

Hey Anton,

Thank you one more time for reporting it! This one is fixed as well now.

Cheers!

Reply
Lijana Z. Avatar
Lijana Z. Avatar Lijana Z. | posted 4 years ago

There is one mistake where you say - "If you are on windows" - answer should be - switch to linux based system :)

I myself like windows, things work better, except for development - it causes too much trouble, so I have xubuntu and windows on my computer. I actually still develop my side small projects on windows which do not make any money, but nothing fancy like webpack in the technology stack.

Reply

Haha, yea, it does usually make life more difficult. The new Unix subsystem is helping things - but it's still the "odd" operating system for development :).

Cheers!

Reply
GDIBass Avatar
GDIBass Avatar GDIBass | posted 5 years ago | edited

Looks like webpack 4 is:


if ( process.env.NODE_ENV === 'production' ) {
        webpackConfig.optimization.minimize = true;
}
Reply
Aircury Avatar
Aircury Avatar Aircury | posted 5 years ago

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.

Reply

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!

Reply
Default user avatar
Default user avatar Thomas Talbot | posted 5 years ago

Hi Ryan,

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

Reply

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!

Reply
Default user avatar
Default user avatar Thomas Talbot | weaverryan | posted 5 years ago

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 :(

Reply

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!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial explains the concepts of an old version of Webpack using an old version of Symfony. The most important concepts are still the same, but you should expect significant differences in new versions.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.2.0",
        "symfony/symfony": "3.3.*", // v3.3.16
        "twig/twig": "2.10.*", // v2.10.0
        "doctrine/orm": "^2.5", // v2.7.0
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
        "symfony/swiftmailer-bundle": "^2.3", // v2.6.3
        "symfony/monolog-bundle": "^2.8", // v2.12.1
        "symfony/polyfill-apcu": "^1.0", // v1.4.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.26
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "friendsofsymfony/user-bundle": "^2.0", // v2.1.2
        "doctrine/doctrine-fixtures-bundle": "~2.3", // v2.4.1
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.3.2
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "friendsofsymfony/jsrouting-bundle": "^1.6" // 1.6.0
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.6
        "symfony/phpunit-bridge": "^3.0" // v3.3.5
    }
}

What JavaScript libraries does this tutorial use?

// package.json
{
    "dependencies": [],
    "devDependencies": {
        "babel-core": "^6.25.0", // 6.25.0
        "babel-loader": "^7.1.1", // 7.1.1
        "babel-plugin-syntax-dynamic-import": "^6.18.0", // 6.18.0
        "babel-preset-env": "^1.6.0", // 1.6.0
        "bootstrap-sass": "^3.3.7", // 3.3.7
        "clean-webpack-plugin": "^0.1.16", // 0.1.16
        "copy-webpack-plugin": "^4.0.1", // 4.0.1
        "core-js": "^2.4.1", // 2.4.1
        "css-loader": "^0.28.4", // 0.28.4
        "extract-text-webpack-plugin": "^3.0.0", // 3.0.0
        "file-loader": "^0.11.2", // 0.11.2
        "font-awesome": "^4.7.0", // 4.7.0
        "jquery": "^3.2.1", // 3.2.1
        "lodash": "^4.17.4", // 4.17.4
        "node-sass": "^4.5.3", // 4.5.3
        "resolve-url-loader": "^2.1.0", // 2.1.0
        "sass-loader": "^6.0.6", // 6.0.6
        "style-loader": "^0.18.2", // 0.18.2
        "sweetalert2": "^6.6.6", // 6.6.6
        "webpack": "^3.4.1", // 3.4.1
        "webpack-chunk-hash": "^0.4.0", // 0.4.0
        "webpack-dev-server": "^2.6.1", // 2.6.1
        "webpack-manifest-plugin": "^1.2.1" // 1.2.1
    }
}
userVoice