Passing environment-dependent variables in webpack

Passing environment-dependent variables in webpack

I’m trying to convert an angular app from gulp to webpack. in gulp I use gulp-preprocess to replace some variables in the html page (e.g. database name) depending on the NODE_ENV. What is the best way of achieving a similar result with webpack?

Solutions/Answers:

Solution 1:

There are two basic ways to achieve this.

DefinePlugin

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

Note that this will just replace the matches “as is”. That’s why the string is in the format it is. You could have a more complex structure, such as an object there but you get the idea.

EnvironmentPlugin

new webpack.EnvironmentPlugin(['NODE_ENV'])

EnvironmentPlugin uses DefinePlugin internally and maps the environment values to code through it. Terser syntax.

Alias

Alternatively you could consume configuration through an aliased module. From consumer side it would look like this:

var config = require('config');

Configuration itself could look like this:

resolve: {
    alias: {
        config: path.join(__dirname, 'config', process.env.NODE_ENV)
    }
}

Let’s say process.env.NODE_ENV is development. It would map into ./config/development.js then. The module it maps to can export configuration like this:

module.exports = {
    testing: 'something',
    ...
};

Solution 2:

Just another option, if you want to use only a cli interface, just use the define option of webpack. I add the following script in my package.json :

"build-production": "webpack -p --define process.env.NODE_ENV='\"production\"' --progress --colors"

So I just have to run npm run build-production.

Solution 3:

I investigated a couple of options on how to set environment-specific variables and ended up with this:

I have 2 webpack configs currently:

webpack.production.config.js

new webpack.DefinePlugin({
  'process.env':{
    'NODE_ENV': JSON.stringify('production'),
    'API_URL': JSON.stringify('http://localhost:8080/bands')
  }
}),

webpack.config.js

new webpack.DefinePlugin({
  'process.env':{
    'NODE_ENV': JSON.stringify('development'),
    'API_URL': JSON.stringify('http://10.10.10.10:8080/bands')
  }
}),

In my code I get the value of API_URL in this (brief) way:

const apiUrl = process.env.API_URL;

EDIT 3rd of Nov, 2016

Webpack docs has an example: https://webpack.js.org/plugins/define-plugin/#usage

new webpack.DefinePlugin({
    PRODUCTION: JSON.stringify(true),
    VERSION: JSON.stringify("5fa3b9"),
    BROWSER_SUPPORTS_HTML5: true,
    TWO: "1+1",
    "typeof window": JSON.stringify("object")
})

With ESLint you need to specifically allow undefined variables in code, if you have no-undef rule on. http://eslint.org/docs/rules/no-undef like this:

/*global TWO*/
console.log('Running App version ' + TWO);

EDIT 7th of Sep, 2017 (Create-React-App specific)

If you’re not into configuring too much, check out Create-React-App: Create-React-App – Adding Custom Environment Variables. Under the hood CRA uses Webpack anyway.

Solution 4:

You can directly use the EnvironmentPlugin available in webpack to have access to any environment variable during the transpilation.

You just have to declare the plugin in your webpack.config.js file:

var webpack = require('webpack');

module.exports = {
    /* ... */
    plugins = [
        new webpack.EnvironmentPlugin(['NODE_ENV'])
    ]
};

Note that you must declare explicitly the name of the environment variables you want to use.

Solution 5:

You can pass any command-line argument without additional plugins using --env since webpack 2:

webpack --config webpack.config.js --env.foo=bar

Using the variable in webpack.config.js:

module.exports = function(env) {
    if (env.foo === 'bar') {
        // do something
    }
}

Source

Solution 6:

Since my Edit on the above post by thevangelist wasn’t approved, posting additional information.

If you want to pick value from package.json like a defined version number and access it through DefinePlugin inside Javascript.

{"version": "0.0.1"}

Then, Import package.json inside respective webpack.config, access the attribute using the import variable, then use the attribute in the DefinePlugin.

const PACKAGE = require('../package.json');
const _version = PACKAGE.version;//Picks the version number from package.json

For example certain configuration on webpack.config is using METADATA for DefinePlugin:

const METADATA = webpackMerge(commonConfig({env: ENV}).metadata, {
  host: HOST,
  port: PORT,
  ENV: ENV,
  HMR: HMR,
  RELEASE_VERSION:_version//Version attribute retrieved from package.json
});

new DefinePlugin({
        'ENV': JSON.stringify(METADATA.ENV),
        'HMR': METADATA.HMR,
        'process.env': {
          'ENV': JSON.stringify(METADATA.ENV),
          'NODE_ENV': JSON.stringify(METADATA.ENV),
          'HMR': METADATA.HMR,
          'VERSION': JSON.stringify(METADATA.RELEASE_VERSION)//Setting it for the Scripts usage.
        }
      }),

Access this inside any typescript file:

this.versionNumber = process.env.VERSION;

The smartest way would be like this:

// webpack.config.js
plugins: [
    new webpack.DefinePlugin({
      VERSION: JSON.stringify(require("./package.json").version)
    })
  ]

Thanks to Ross Allen