WebPack production environment is NOT the NODE_ENV or BABEL_ENV environment variable

webpack Apr 12, 2019

A revelation came to me the other day when I was reviewing some of our bundling processes at Eventbrite.

We currently run production bundles like this...

node --max_old_space_size=4096\
 ./node_modules/.bin/webpack \
 --bail \
 --config-name node \
 --env.production \
 --config ./config/webpack.production.config.js        

Notice the --env.production in there.

We also use the babel-loader along with the babel-preset-env plugin as any good citizen would.

Here's what's interesting.

--env.production does NOT set NODE_ENV=production

I proceeded to hover over the environment key on my .babelrc in VSCode, and got this little nugget.

I then found the exact quote from the old 62.6 docs (we're still on Babel 6 for now).

The env key will be taken from process.env.BABEL_ENV, when this is not available then it uses process.env.NODE_ENV if even that is not available then it defaults to "development".

Alright so basically that means we've been running Babel as dev mode!

What does that --env.production thing even do?

Well, according to https://webpack.js.org/guides/environment-variables, all it does is make it so when you setup your webpack config, you actually export a function that gives you an env, and evidently you can also set your actual NODE_ENV like...

webpack --env.NODE_ENV=local --env.production --progress

Then you you can get that env in the config callback.

module.exports = env => {
  // Use env.<YOUR VARIABLE> here:
  console.log('NODE_ENV: ', env.NODE_ENV); // 'local'
  console.log('Production: ', env.production); // true

DefinePlugin

Here's the thing though, just running NODE_ENV=production webpack doesn't necessarily give you production bundles either.

NODE_ENV=production just tells node in what mode to actually run webpack.

Meaning, if you have code in your app which does...

if (process.env.NODE_ENV === 'production') {
  // do production things
}

It will simply leave those in place.

In order to make your output code reflect the proper NODE_ENV you have to use either the EnvironmentPlugin or the DefinePlugin. (The EnvironmentPlugin uses Define under the covers).

I personally prefer the explicitness of the DefinePlugin...

new webpack.DefinePlugin({
  'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
  'process.env.DEBUG': JSON.stringify(process.env.DEBUG)
});

Or if you had passed in the --env.production thing, you could do like...

new webpack.DefinePlugin({
  'process.env.NODE_ENV': env.production ?
    JSON.stringify('production'),
    JSON.stringify('development'),
  'process.env.DEBUG': JSON.stringify(process.env.DEBUG)
});

p.s. You have to do the JSON.stringify thing so that in your code it'll do...

if (process.env.NODE_ENV === 'production')

// Converts the above to

if ('production' === 'production')

webpack 4

In webpack 4, what's nice is, there is a webpack configuration setting called mode. And when you set mode: "production", it goes ahead and sets up the DefinePlugin for you.

However, you still may run into a scenario where you need to make sure that the babel-loader knows your NODE_ENV=production. So, just keep a close eye on it.

tldr;

Make sure when you're doing production webpack builds involving babel, particularly in webpack 3 where you don't have the mode option, make sure to set NODE_ENV=production when you run webpack.

Tags