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

comments powered by Disqus
Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.