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
}
}
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)
})
]