react native web, __DEV__ not defined - reactjs

If you want to share code between RN and RN-web, __DEV__ should also be provided in the both platform.
However I can't add DEV using const __DEV__ = process.env.NODE_ENV !== 'production'; new webpack.DefinePlugin({__DEV__})
I can set window.__DEV__ fine, but RN code uses __DEV__
I've also tried adding module:metro-react-native-babel-preset
I've seen React Native - __DEV__ is not defined
/* global __DEV__ */ works, but hope there's a way to fix it without modifying all source which uses __DEV__

in your webpack.config.js, add this:
plugins: [
// `process.env.NODE_ENV === 'production'` must be `true` for production
// builds to eliminate development checks and reduce build size. You may
// wish to include additional optimizations.
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
__DEV__: process.env.NODE_ENV !== 'production' || true,
}),
],
see https://medium.com/#alexander.forselius/experiment-combining-native-web-ios-android-and-macos-development-in-rectnative-part-1-ecd5887e9cfc

I solved it by making it depend on the webpack mode input.
In webpack.config.js:
const config = {
...
plugins: [],
...
}
module.exports = (env, argv) => {
config.plugins.push(new webpack.DefinePlugin({
__DEV__: JSON.stringify(argv.mode !== 'production'),
}));
return config;
};

Related

Invalid hook call. When using external folders with webpack

I have divided my app into folders to be able to reuse code. My project consists of two folder common and common-web that I need to use in my customer-web-app project.
This is the directory structure
The customer-web-app is using razzle and its webpack config is
module.exports = {
modifyWebpackConfig({
env: {
target, // the target 'node' or 'web'
dev, // is this a development build? true or false
},
webpackConfig, // the created webpack config
webpackObject, // the imported webpack node module
options: {
razzleOptions, // the modified options passed to Razzle in the `options` key in `razzle.config.js` (options: { key: 'value'})
webpackOptions, // the modified options that will be used to configure webpack/ webpack loaders and plugins
},
paths, // the modified paths that will be used by Razzle.
}) {
console.log("TT:", webpackConfig, paths);
const copy = Object.assign({}, webpackConfig);
copy.resolve.modules = (copy.resolve.modules || []).concat([
path.resolve(__dirname, "../common-web/src"),
path.resolve(__dirname, "../common/src"),
path.resolve(__dirname, "../admin-common/src"),
]);
copy.resolve.alias = {
...(copy.resolve.alias || {}),
"#material-ui/core": path.resolve(
__dirname,
"./node_modules/#material-ui/core"
),
react: path.resolve("./node_modules/react"),
"react-dom": path.resolve("./node_modules/react-dom"),
"react-router-dom": path.resolve(
__dirname,
"./node_modules/react-router-dom"
),
"#common": path.resolve(__dirname, "../common/src"),
"#customer": path.resolve(__dirname, "../customer-common/src"),
"#web": path.resolve(__dirname, "../common-web/src"),
};
// Do some stuff...
return copy;
},
modifyWebpackOptions({
env: {
target, // the target 'node' or 'web'
dev, // is this a development build? true or false
},
options: {
webpackOptions, // the default options that will be used to configure webpack/ webpack loaders and plugins
},
}) {
webpackOptions.notNodeExternalResMatch = (request, context) => {
return /\#web|\#common|\#customer/.test(request);
};
webpackOptions.babelRule.include = webpackOptions.babelRule.include.concat([
path.resolve("../common-web/src"),
path.resolve("../common/src"),
path.resolve("../customer-common/src"),
]);
return webpackOptions;
},
};
But when I run the app its giving me
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app

Webpack obfuscator not working with craco, maps disabled

today I have a very large problem using react & craco, I can't seem to get my webpack-obfuscator to do anything. I have disabled source maps, but to no avail.
This is my craco config:
const path = require("path");
const WebpackObfuscator = require('webpack-obfuscator');
module.exports = {
webpack: {
configure: (webpackConfig) => {
// Because CEF has issues with loading source maps properly atm,
// lets use the best we can get in line with `eval-source-map`
if (webpackConfig.mode === 'development' && process.env.IN_GAME_DEV) {
webpackConfig.devtool = 'eval-source-map'
webpackConfig.output.path = path.join(__dirname, 'build')
}
return webpackConfig
},
plugins: {
add: [
new WebpackObfuscator ({
rotateStringArray: true
}),
],
},
},
devServer: (devServerConfig) => {
if (process.env.IN_GAME_DEV) {
// Used for in-game dev mode
devServerConfig.writeToDisk = true
}
return devServerConfig
}
}
I get no visible maps files when building, and I've put "GENERATE_SOURCEMAP=false" in my .env file that's located where the package.json is.
Hopefully someone has the answer as to why this is happening.
Kind regards, and thanks for reading.
To upgrade a short config, you can use a construct that, if the condition is met, updates the configuration without using WebpackObfuscator:
module.exports = {
webpack: {
configure: {
...(process.env.IN_GAME_DEV && process.env.NODE_ENV === 'development' && {devtool: 'eval-source-map'})
}
}
}
Also, if you need additional properties for the configuration, in addition to the dvttool, you can add them

How to set buildId to config (publicRuntimeConfig) or environment variable?

We're using nextjs (9.5.3) and next-i18next (6.0.3) for translations. To implement a special caching, I need to access the build ID outside of next.config.js in i18n.js in order to set it to the locale path:
localePath: path.resolve(`./public/static/cache/${config.buildId}/locales`)
In next.config.js I can access the build ID pretty easily:
withPWA({
webpack(config, { buildId }) {
[...]
config.plugins.push(
new CopyWebpackPlugin({
patterns: [
{
context: path.join(__dirname, 'public/static/locales'),
from: '**/*',
to: path.join(__dirname, `public/static/cache/${buildId}/locales`)
}
]
})
);
// not working:-(
process.env.CONFIG_BUILD_ID = buildId;
return config;
},
publicRuntimeConfig: {
buildId: process.env.CONFIG_BUILD_ID
}
});
However setting it to the environment variable process.env.CONFIG_BUILD_ID is not working, so publicRuntimeConfig.buildId will still be undefined.
Is there any way to access the build ID outside the next config?
Instead of
// not working:-(
process.env.CONFIG_BUILD_ID = buildId;
You should use Webpack DefinePlugin (https://webpack.js.org/plugins/define-plugin/). No extra imports or installs are needed: this plugin is already a part of the next.js package. Just replace those lines with:
config.plugins.push(
new webpack.DefinePlugin({
'process.env.CONFIG_BUILD_ID': JSON.stringify(buildId)
})
);
Full next.js.config may look like this:
/** #type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
config.plugins.push(
new webpack.DefinePlugin({
'process.env.CONFIG_BUILD_ID': JSON.stringify(buildId)
})
);
return config;
}
}
module.exports = nextConfig;

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.

Evaluate conditionals at compile time in babel / webpack

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.

Resources