Evaluate conditionals at compile time in babel / webpack - reactjs

I have a config file where I have a variable that can have the value X or Y depending if it's prod or dev.
I want to evaluate it at compile time.
I was able to get the NODE_ENV value from babel-plugin-transform-node-env-inline but can't figure out how to evaluate the expression. I tried babel-plugin-transform-conditionals but it throws strange code errors. Errors that don't exist.
I can evaluate at runtime since in that case both prod and dev values will be available in app.js, which I can't have.
No idea where to go from here!!
Any totally different solution would work too (like somehow compile a different file), but not sure how to go about it.

Not sure about your question, you can just write the if statement like this
if (process.env.NODE_ENV === 'development') {
// ...
}
It will be transform to
// or true
if (false) {
// ...
}
if it is false, then that's a kind of unreachable code, which will be stripped by UglifyJs plugin automatically (use new webpack.optimize.UglifyJsPlugin() in your webpack config)
also don't forget to use DefinePlugin (and cross-env if you are using windows) to declare the process.env.NODE_ENV variable or it may not work
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
})

I have never use the plugin you mention. But default webpack setup should work like what I have done in the following:
webpack.config.js
var config = {
entry: {
...
},
output: {
...
},
module: {
...
},
plugins: [
new webpack.DefinePlugin({
"process.env": {
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
}
}),
// some other plugins
]
};
So the NODE_ENV is coming from your environment when you run webpack.
package.json
{
"scripts": {
"start": "NODE_ENV=development webpack-dev-server --progress --hot --inline --colors --host 0.0.0.0 --port 9999",
"production": "NODE_ENV=production webpack",
}
}
In your package.json run the webpack in environment you specify.
Your config.js
export default {
isDebug: process.env.NODE_ENV === "development",
logLevel: process.env.NODE_ENV === "development" ? "error" : "info"
};
Another example I use in my application for redux. I only wanted to have redux-logger during development.
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import logger from "redux-logger";
import promise from "redux-promise-middleware";
import reducer from "./reducers";
const middleware = process.env.NODE_ENV === "production" ? [promise(), thunk] : [promise(), thunk, logger()];
export default createStore(
reducer,
applyMiddleware(...middleware)
);
After the compile if you look at the generated code from the above redux example.
var middleware = true ? [(0, _reduxPromiseMiddleware2.default)(), _reduxThunk2.default] : [(0, _reduxPromiseMiddleware2.default)(), _reduxThunk2.default, (0, _reduxLogger2.default)()];
process.env.NODE_ENV === "production" have been evaluate to true.

Make one Webpack config for development, and one config for production, which you need anyway. Specify your variable in each config using the DefinePlugin, then you can reference that global variable anywhere in your bundle.

Related

How to configure the start script in webpack to use the api I want?

How to configure the start script in webpack to use the api I want?
For example when I run "npm start" my webpack source will use the productuion api like 'https://abc_loginapi.com, and when I run "npm run dev" the webpack source Mine will use local apis like http://localhost:9500/login.
My current way of doing it is quite manual, when I want to run one, I will comment the other one
export const API_HOST_LIST =
{
HostBaseURL: 'https://abc_loginapi.com'
// HostBaseURL: 'http://localhost:9500/login'
}
Is there any way to handle this problem, my source is webpack4 + reactJS
You can use environment variables to handle it, add env to script with --env argument, like this:
webpack --env prod // result { prod: true }
And call it in webpack config file
const path = require('path');
module.exports = env => {
// Use env.<YOUR VARIABLE> here:
console.log('Production: ', env.prod); // true
return {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
};
For more detail, check Webpack docs here
Like Phr0gggg mentions, using environment variables is one option.
Another option is to use the "NODE_ENV" variable.
In dev mode, process.env.NODE_ENV is equal to 'development' and in production, it's equal to 'productoin'
And you can use something like this:
const isProduction = process.env.NODE_ENV === "production"
const hostBaseUrl = () => {
if (isProduction) {
return 'http://localhost:9500';
}
return 'https://abc_loginapi.com';
}
export const API_HOST_LIST =
{
HostBaseURL: hostBaseUrl()
}
You can use process.env api to judge in webpack,
The code in webpack file like.
export const API_HOST_LIST =
{
HostBaseURL: process.env.NODE_ENV ==='dev'? 'http://localhost:9500/login':'https://abc_loginapi.com'
}
The code in package.json file like.
"scripts": {
"dev": "NODE_ENV=dev npm run start",
"start": "npm start"
}

React Native 0.60.3 babel-plugin-transform-remove-console not working

I am trying to remove console.log outputs from my react-native application's output, but when I run
ENVFILE=.env.production react-native run-android --variant=release
and
adb logcat
I still see my app's data being logged to the console.
I used the following documentation: https://facebook.github.io/react-native/docs/performance.html#using-consolelog-statements.
Here is my .babelrc file:
{
"presets": ["react-native"],
"env": {
"production": {
"plugins": ["transform-remove-console"]
}
}
}
What am I missing ?
Im on react-native 0.60.3
and using "babel-plugin-transform-remove-console": "^6.9.4",
I have "#babel/core": "^7.5.5" and "react-native": "^0.60.5"
The approach descibed in React Native Documentation was not working for me.After many try and error and exploring issues on GitHub I got it working :
In babel.config.js add this -
module.exports = api => {
const babelEnv = api.env();
const plugins = [];
//change to 'production' to check if this is working in 'development' mode
if (babelEnv !== 'development') {
plugins.push(['transform-remove-console', {exclude: ['error', 'warn']}]);
}
return {
presets: ['module:metro-react-native-babel-preset'],
plugins,
};
};
To see changes run using npm start -- --reset-cache
More Info at
https://github.com/babel/minify/issues/934
https://github.com/babel/minify/issues/950#issuecomment-539590159
Using babel.config.js instead of .babelrc, it seems that process.env.BABEL_ENV is used to determine whether to include configs listed under env.production. However, process.env.BABEL_ENV is set to undefined during build.
To get around this, I'm returning a different object depending on if process.env.BABEL_ENV OR process.env.NODE_ENV indicate production build.
YMMV.
module.exports = function(api) {
api.cache(true); // necessary
if (process.env.NODE_ENV === 'production' || process.env.BABEL_ENV === 'production') {
return {
"presets": ["module:metro-react-native-babel-preset"],
"plugins": ["react-native-paper/babel", "transform-remove-console"]
}
} else {
return {
"presets": ["module:metro-react-native-babel-preset"],
}
}
}
install babel-plugin-transform-remove-console
yarn add babel-plugin-transform-remove-console -D
then add follow code in babel.config.js like this
module.exports = function (api) {
const babelEnv = api.env();
api.cache(true);
const plugins = [
[];
if (babelEnv === 'production') {
plugins.push(['transform-remove-console', {exclude: ['error', 'warn']}]);
}
return {
presets: ['babel-preset-expo'],
plugins,
};
};
then run this command
yarn start --reset-cache
NOTE:
this will remove console.log in the production build. if you wanna a test in development you can pass development instead of production

Webpack: process.env undefined using DefinePlugin and DotEnv

I would like to get my variable from my .env file but I always get undefined
This is my js code :
require('dotenv').config();
class Header extends React.Component{
constructor(props){...}
render(){
console.log("NODE ENV", process.env.NODE_ENV);
console.log("REACT_APP_MYAPP", process.env.REACT_APP_MYAPP);
...
}
}
This prints :
NODE_ENV development
REACT_APP_MYAPP undefined
In my package.json there is :
"scripts":{
"start" : "webpack-dev-server --config webpack.dev.js",
"build" : "webpack --config webpack.prod.js"
}
And in my webpack.dev.js:
const webpack = require("webpack");
const merge = require("webpack-merge");
const path = require("path");
const common = require("./webpack.common.js");
module.exports = merge.smart(common, {
devServer: {
contentBase: path.resolve(__dirname, "dist"),
hot: true,
overlay: {
warnings: true,
errors: true
},
inline :true,
historyApiFallback: true,
port: 8085
},
devtool: "inline-sourcemap",
optimization: {
namedModules: true
},
plugins: [
new webpack.HotModulReplacementPlugin(),
new webpack.DefinePlugin({
"process.env.NODE_ENV": JSON.stringify("development"),
"process.env.REACT_APP_MYAPP": JSON.stringify(process.env.REACT_APP_MYAPP)
})
],
mode: "development"
});
And I placed my .env file at the root of my project, next to webpack.dev.js and package.json:
REACT_APP_MYAPP=http://localhost:8080/
So I think, it doesn't success to get the variable in the file.
How can I get the REACT_APP_MYAPP value in the code please ?
Journeying through the deep, dark rabbit hole will lead you to the following facts:
Webpack 5 no longer provides process or other Node.js variables.
The role of DefinePlugin requires redefining.
Values passed to DefinePlugin must be stringified, even if they're strings.
EnvironmentPlugin makes things more confusing, given Webpack 5.
process.env isn't really something you should be using in the frontend.
You can use polyfills to re-add process and others back in, but there is a reason it's no longer available.
dotenv-wepack is neither necessary nor the way to go.
The reason for using process.env is to access variables globally. But it's a passenger on the wrong flight. We can drop it and instead just access the intended variable directly:
plugins: [
new DefinePlugin({
// With dotenv (values must be stringified)
...Object.entries(dotenv.config().parsed).reduce((acc, curr) => ({...acc, [`${curr[0]}`]: JSON.stringify(curr[1]) }), {}),
// Without dotenv
'myString': JSON.stringify('IAmAString')
})
]
In the frontend:
declare var myString: string;
console.log(myString); // 'IAmAString'
If you have several objects with variables it makes sense to abstract the stringification:
// Create a function to stringify values
function stringifyValues(object: {[key: string]: any;}){
return Object.entries(object).reduce((acc, curr) => ({...acc, [`${curr[0]}`]: JSON.stringify(curr[1]) }), {} as { [key: string]: string; });
}
// use with DefinePlugin
plugins: [
new DefinePlugin({
...stringifyValues(dotenv.config().parsed),
...stringifyValues(dotenv.config({ path: '/.env.special' }).parsed),
'myObject': stringifyValues({
name: 'Object',
description: 'to be an object'
})
})
]
If you really want access to process.env:
plugins: [
new DefinePlugin({
// this might expose confidential data about your environment
'process.env': JSON.stringify(process.env),
// the correct way
'process.env.USERNAME': JSON.stringify('Donald Hump')
})
]
In the frontend:
declare var process: any;
console.log(process) // will NOT work, because process hasn't been injected
console.log(process.env); // will work but risky
console.log(process.env.USERNAME); // correct: 'Donald Hump'
First solution by adding REACT_APP_MYAPP in start didn't worked.
But second solution worked.
SOLUTION:
Adding require('dotenv').config() file inside my webpack.dev.js and replacing :
new webpack.DefinePlugin({
"process.env.NODE_ENV": JSON.stringify("development"),
"process.env.REACT_APP_MYAPP": JSON.stringify(process.env.REACT_APP_MYAPP)
})
with
new webpack.EnvironmentPlugin(['NODE_ENV', 'REACT_APP_MYAPP']);
Thank you!
There's a few ways you could make this work.
The easiest to test is to change your "start" : "webpack-dev-server --config webpack.dev.js", to "start" : "REACT_APP_MYAPP=http://localhost:8080/ node webpack-dev-server --config webpack.dev.js",
This will inject the environment variable and it will be available during the webpack build process. You can use this technique whenever using npm or node to run a command. For instance, NODE_ENV='development REACT_MY_APP=http://localhost:8080/ node myapp.js and both will be available on process.env.
You could also call your require('dotenv').config() file inside your webpack.dev.js. Then it would be set during your usage of the DefinePlugin.
Generally, you don't use the npm start command to run your development server.
As the application grows you should look at the Webpack Environment Plugin. Then you can use the .env for the "production" build settings, requiring it in the production webpack.prod.js, while setting your default/fallback environment variables using the plugin.
It works for me in my custom React app model, only if I do not include 'REACT_APP_' in variable names:
in webpack.config.js:
// Node import
const path = require('path');
const webpack = require('webpack');
require('dotenv').config();
module.exports = [
plugins: [
new webpack.EnvironmentPlugin(['NODE_ENV', 'API'])
],
]
in .env:
NODE_ENV=development
API=http://localhost:5000
When I deploy the app though (on Netlify for example), I exclude NODE_ENV from the production environment variables.

Use Webpack's DefinePlugin vars in Jest tests

I'm pretty new in React, coming from Angular. I'm writing some tests for the code that's going to send request to an external endpoint. Obviously, I don't want to hardcode a real host in production code so I thought I could use webpack's DefinePlugin to keep this in a constant.
How to configure Webpack and Jest together to use Webpack's capabilities?
It's just like that in your package.json or jest.config.js :
"jest": {
"globals": {
"__DEV__": true
}
}
If you still have any problems, please check the jest offical docs here:
globals-object
As explained in comments:
/globals.js
module.exports = {
__DEV__: true
}
/webpack.config.js
const globals = require('./globals.js')
// ...
plugins: [
new webpack.DefinePlugin(globals)
]
/jest.config.js
const globals = require('./globals.js')
module.exports = {
// ...
globals,
}

Webpack2 keep adding dev dependancy file

I have this kind of configuration like below. However, it seems to load development script even though the if statement go into only "production"
if (process.env.NODE_ENV === 'production') {
module.exports = require('./configureStoreProd')
} else {
module.exports = require('./configureStoreDev')
}
If I delete the "import logger from 'redux-logoer", it does not show on analyzer.
I am guessing when webpack building vendor file, NODE_ENV is undefine or null. How do i set it properly ?
Workaround I used:
Your config file (that you will import in the js files you wish to use it in) that determines what file you will use depending on the environment you are in.
config.js
var config = null;
if (process.env.NODE_ENV !== 'production') {
config = require('./config.development');
}else {
config = require('./config.production');
}
export default config;
Your config production .json file that will be selected if you are in the production environment. Same goes for development.
config.production.json
{
"graphQlEndpoint": {
"uri": "YourUriHere"
},
}
In the store, you can import the config.js file and then choose what variable you want. Example: config -> graphQlEndpoint -> uri
store.js
import config from '../../configs/config';
networkInterface = createNetworkInterface({
uri: config.graphQlEndpoint.uri
});
const store = createStore(rootReducer, defaultState, networkInterface);
I found it easier to use require() and to have my config files in son. You can then use your config file with the correct environment everywhere you import it.
I suggest you test this with npm run build.
Hope this helps

Resources