Mocking external imports in development environment using Webpack and React - javascript

In my React Application I am using an API which is provided at runtime as a global variable by the Browser in which the application runs.
To make the Webpack compilation process work I have added this into the webpack config:
externals: {
overwolf : 'overwolf'
}
It is then imported like this
import overwolf from 'overwolf'
This works fine when I built my production application and run it inside the Browser.
However for the webpack development server, as well as my tests I want to be able to run them from a standard browser where the external will not be available. I am not quite sure how to make this work as the dev server will always complain about the import and my attempts to make conditional imports did not work out so far.
What I would like to achieve is to mock the overwolf variable, so that webpack dev server will compile and let me run my code with the mocked version.
My attempt was like this
import overwolf from 'overwolf'
export function overwolfWrapper() {
if(process.env.NODE_ENV !== 'production') {
return false;
}
else {
return overwolf;
}
}
Which results in the following error on the webpack development server
ReferenceError: overwolf is not defined
overwolf
C:/Users/jakob/Documents/private/projects/koreanbuilds-overwolf2/external "overwolf":1

One possible solution is to keep using the overwolf defined as an external (read more here), and use a polyfill for other browsers:
In your index.html include an overwolf.js script which will provide the mock object to use.
Example using HtmlWebpackPlugin and html-webpack-template to generate the index.html as part of the build process. Include in your plugins array:
new HtmlWebpackPlugin({
template: './node_modules/html-webpack-template/index.ejs',
inject: false,
scripts: ['/overwolf.js']
})
And this is an example for the included overwolf.js previously:
if (!window.overwolf) {
window.overwolf = {
foo() {
console.info('overwolf now has foo function!');
}
};
}
Hope this helps!
Check also this webpack-demo project. I think it would help you with some configurations.

I also found a rather simple solution on my own.
Instead of importing the external this also works:
const overwolf = process.env.NODE_ENV === 'production' ? require('overwolf') : new MockedOverwolf();
Webpack will not complain about this in the dev environment and in production require will still give me the real API.

Related

requestAnimationFrame is not defined nest.js/vue [duplicate]

I'm working on Next.js and React-Native-Web. I managed to run them together following the official Next.js example but when I'm trying to use the Animated package from the react-native it fails with Error that the requestAnimationFrame isn't defined. Basically this functionality does the node_modules package but I set the alias in webpack to translate all react-native requires to the react-native-web so even the node_modules package should use the react-native-web.
Any suggestions on how to solve it?
ReferenceError: requestAnimationFrame is not defined
at start (...node_modules\react-native-web\
dist\cjs\vendor\react-native\Animated\animations\TimingAnimation.js:104:11)
enter code here
Thanks for any help!
The problem is in the missed RequestAnimationFrame functionality at the server. This error happens when Next.js tries to render the component during SSR.
Unfortunately, there is no polyfill, etc. for such purpose so I just decided to use the Next.js dynamic imports for a Component that has animation functionality.
Next.js Official documentation
My own case оust to show how code looks:
import dynamic from 'next/dynamic';
const AutocompleteDropdown = dynamic(
() => import(
'myAwesomeLib/components/dropdown/autocomplete/AutocompleteDropdown'
),
{
ssr: false,
}
);
Now you can use the AutocompleteDropdown as the standard JSX component
I'm coding an App with React Native Web and NextJS 12, and in 2021 I encounter this problem and I fixed it, but now I know my fix was only for Next Dev, because it returned for Next Production Build.
Solution details:
No Dynamic import (which is useful too, but can be annoying when having lot of components using it)
Using RAF polyfill and Webpack ProvidePlugin.
Main thing to have in mind is that next.config.js with webpack 5 is going to check the codes first before even reach next entry points _documents.js and _app.js. It means that, you can put polyfill in those entry point files, it will still raise error of RAF undefined. You have to make requestAnimationFrame ready for config check.
DEV approach that will work on Next DEV only. Install RAF package https://www.npmjs.com/package/raf and In next.config.js add codes:
const raf = require('raf');
raf.polyfill();
This will add requestAnimationFrame and cancelAnimationFrame function to global and window object if they don't have it. In our case, it would add it in global for NodeJS.
But this solution won't work when executing npm run dev. I don't know why, if anyone knows why Next or Webpack 5 act differently from DEV to PRODUCTION, let me know.
Complete Solution:
Use ProvidePlugin config of webpack 5 https://webpack.js.org/plugins/provide-plugin/ . Create a file to use as modules, let's say: raf.js in root project or anywhere you want:
const raf = require('raf');
const polys = {};
raf.polyfill(polys);
module.exports = polys.requestAnimationFrame;
And in next.config.js use it inside webpack: () = {} like:
webpack: (config, options) => {
// console.log('fallback', config.resolve.fallback);
if (options.isServer) {
// provide plugin
config.plugins.push(
new options.webpack.ProvidePlugin({
requestAnimationFrame: path.resolve(__dirname, './raf.js'),
}),
);
}
And now, it's up to you to adapt to your existing config logic. By doing this, in Production Build, NextJS is injecting the requestAnimationFrame function in Server Side everywhere a module is using it.

Include JS module/file only in development-mode

How can I conditionally import a module only in development mode (in my case the axios-mock-adapter package). Also the code should not even be present in the production bundle.
Example code I only want to be included during development:
export const mockUpClient = (api: AxiosInstance): void => {
// full api mocking, containing lots and lots of data
}
Now I am importing the module based on the following condition:
if (process.env.NODE_ENV === 'development') {
import("./apiMockAdapter").then((module) => {
module.mockUpClient(api)
})
}
The code is still included in the build, however it is not executed in production mode. How is it possible to completely exlude the code from the production bundle (of course without commenting out the code before every build)?
Update
The above example works fine. Before asking the question, I also imported the file from somewhere else, which led to this behaviour.
The accepted answer explains in detail how webpack will bundle the code & modules.
Basically:
Eject from create-react-app with npm run eject. You may be worried about the maintenance burden but it you look at the create-react-app repo you'll see there are very few meaningful changes in CRA and the upkeep with it is actually higher. If you are insistent on CRA then use craco.
Go to webpack.config.js (or craco.config.js if using craco)
Add an externals field if the app is running in production mode
Should look something like this. In this object add an externals part:
externals: isEnvProduction ? {
'myApiAdapter' : 'window' // or something else global
} : undefined,
This will map import('myApiAdapter') to window in production builds and not include it in the bundle.
That said, webpack should see the dynamic import as a point to break the bundle down into chunks, so it's unclear without seeing your actual code why it is included. Making that file external should bypass any such issues.

How to include JavaScript libraries only in production build files of create-react-app?

I'm trying to add a JavaScript library that is only included in the production build files generated by create-react-app.
What's the best way to do this?
Specifically, I'm trying to include the Rollbar.js client-side error monitoring library. I don't want it triggering every time I get an error in development/testing environments, so I only want it included in the files that are generated while running npm run build.
The answer provided by tpdietz is a very good one and would probably work well for many libraries.
However, this specific library I'm trying to use (Rollbar) needs to be loaded before any other JavaScript (e.g. React and ReactDOM), or else it can't capture all errors. Since those other libraries are loaded via import statements, I can't use a require statement in front of them at the top of index.js.
The solution I found (thanks to GitHub user rokob with the Rollbar.js project) is to reference the NODE_ENV variable from within the index.html file, as described here: https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#referencing-environment-variables-in-the-html
At the top of my index.html file, in a <script> tag, I can set a variable to %NODE_ENV%. The build process for create-react-app will automatically parse that variable and insert the name of the environment, e.g. "production" or "development", in the final index.html file. Then, I can check to see if that variable is equal to "production" before executing any inline JavaScript.
Example index.html:
<!doctype html>
<html lang="en">
<head>
<script>
var environment = "%NODE_ENV%";
if (environment === "production") {
// code here will only run when index.html is built in production environment
// e.g. > $ NODE_ENV="production" npm run build
}
</script>
In the specific case of Rollbar.js, I just needed to pass a config parameter with the environment name, so no if statement is necessary:
var _rollbarConfig = {
...
enabled: ('%NODE_ENV%' === 'production') //returns true if 'production'
...
};
Here is the issue on Github where rokob gave me this solution: https://github.com/rollbar/rollbar.js/issues/583
You can use environment variables. For example:
if (process.env.NODE_ENV === 'production') {
const Rollbar = require('rollbar');
}
You can invoke the build command like with a NODE_ENV var like so:
NODE_ENV="production" npm run build
Its worth noting you cannot conditionally use import syntax.

Define globals when bundling to umd or commonjs

I have a client-side application which makes use of some browser global properties like Element or document.
I'd like to run my application in node.js as well and currently I am overriding those globals with the domino dom implementation in my server like so:
const domino = require("domino");
const domimpl = domino.createDOMImplementation();
const document = domimpl.createHTMLDocument();
Object.assign(global, Element: domino.impl.Element, document};
const myApp = require('my-app');
I am currently using rollup to bundle different versions of my-app, how can I have rollup do this for me automatically for the _server version of my-app so consumers of my-app don't have to do that?
I was thinking of writing my own rollup plugin but I feel like overriding globals seems like a common practice.
TLDR; Use separate entry file instead of a rollup plugin.
Simply add the following instead of a rollup plugin
if (typeof window ==== "undefined") {
const domino = require("domino");
const domimpl = domino.createDOMImplementation();
const document = domimpl.createHTMLDocument();
Object.assign(global, Element: domino.impl.Element, document};
}
// my-app code
You might be worried about domino entering client side code. To fix this, use separate bundles for server and client, wrap the above mocking code in a separate file and use the following in your my-app’s main file meant for the server bundle, an approach similar to how React ships production and development bundles - conditional imports.
Server main file
require(‘./globals-mocking’);
// my-app client code
Client main file
// my-app client code only
package’s main file
if (SERVER_ONLY) {
module.exports = require('./my-app-server.js');
} else {
module.exports = require('./my-app-client.js');
}
Use rollup's replace plugin and define SERVER_ONLY (https://github.com/rollup/rollup-plugin-replace#usage) for server entry only. If you use UglifyJS or simlilar tool that eliminates dead code, you wont have domino and duplicated server code.
EDIT: Noticed a minor issue. Condition should be if (SERVER_ONLY) {. Use the following definition along with it for the server entry file.
plugins: [
replace({
SERVER_ONLY: JSON.stringify(true)
})
]

Uncaught ReferenceError: process is not defined

I am using node.js to create a web application. When I run the application (either by opening index.html on the browser or using the command "npm start" on the terminal) I get two errors:
Uncaught ReferenceError: process is not defined
Uncaught ReferenceError: require is not defined
I solved the "require is not defined" error by specifically including in my index.html head tag the link to this script, where the require function is defined.
However, I cannot find something similar for the process function.
My question is doublefold:
Why do built-in node.js modules need to be re-defined? Why are they not recognized as they are, that is "built-in modules"? Doesn't the term "built-in module" mean that a module need not be redefined externaly/second-handedly?
Is there a way to solve this problem? My script is very simple, I am just trying to use a basic function of node.js, so I cannot figure out what errors I might have done.
If anyone has come about this problem and has found a way around it or a reason this happens, you would be of great help.
Node.js code must be run by the node process, not the browser (the code must run in the server).
To run the code, you must run the command:
node server.js
And then you can access your server from a browser by typing "http://localhost:8080", for example. You must have a file server.js (or whatever) with the server code you want (in this case, creating a web server in port 8080).
You can follow this easy example, using express as http server module: http://expressjs.com/starter/hello-world.html
Webpack can inject environment variables into the "client side" .js code (very useful in case of SPA/PWA). You should define them as plugins in webpack.config.js
webpack.config.js
module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.MY_ENV': JSON.stringify(process.env.MY_ENV),
... and so on ...
})
],
}
Now you can access it on client side:
app.js
// Something like that
if(process.env.NODE_ENV === 'debug'){
setDebugLevel(1)
}
If you are facing this problem and you are using webpack, you can get the desired process data injected into the client bundle by using DefinePlugin within your webpack.config.js.
In the example below, I show how to add several things to process.env object to make available within the browser:
all the environment variables inside .env using the library
dotenv
the value of NODE_ENV, which is either 'development' or 'production'
Working example
# .env
API_KEY=taco-tues-123
API_SECRET=secret_tacos
// webpack.config.js
const dotenv = require('dotenv').config({ path: __dirname + '/.env' })
const isDevelopment = process.env.NODE_ENV !== 'production'
module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env': JSON.stringify(dotenv.parsed),
'process.env.NODE_ENV': JSON.stringify(isDevelopment ? 'development' : 'production'),
}),
].filter(Boolean),
}
// Within client side bundle (React)
// src/App.jsx
console.log(process.env) // {API_KEY: "taco-tues-123", API_SECRET: "secret_tacos"}
console.log(process.env.NODE_ENV) // development
Notice that console.log(process.env) only has the values from the .env file, and that NODE_ENV is not a part of the process.env object.
In the example below, I show how I was trying to inject the process.env object which led me to this stack overflow. I also include a highlight from the webpack documentation on why the code below was not working.
Broken example
module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env': {
...dotenv.parsed,
'NODE_ENV': JSON.stringify(isDevelopment ? 'development' : 'production')
}
}),
].filter(Boolean),
}
// Within client side bundle (React)
// src/App.jsx
console.log(process.env) // Uncaught ReferenceError: taco is not defined
console.log(process.env.NODE_ENV) // development
From the webpack DefinePlugin docs:
Warning When defining values for process prefer
'process.env.NODE_ENV': JSON.stringify('production')
over
process: { env: { NODE_ENV: JSON.stringify('production') } }
Using the latter
will overwrite the process object which can break compatibility with
some modules that expect other values on the process object to be
defined.
!Warning!
Injecting dotenv.parsed into the client bundle as described will expose these secrets to the client. For development purposes, not a big deal, but in a deployed production environment, any passwords or private api keys will be visible to anyone that goes looking for them.
I had the same problem solved it by going into my .eslintrc.js file
to configure my globals variables, adding require and process to the globals variable and setting the corresponding value equal to "writable". Hope it works for you.
this link really helped
https://eslint.org/docs/user-guide/configuring#specifying-globals
I had same problem when I tried to do this node js app: https://www.youtube.com/watch?v=mr9Mtm_TRpw
The require in html was reached from a < script> and was undefined, like
<script> require('./renderer.js');</script>
I changed it to:
<script src="./renderer.js"></script>
The process in html script was also undefined. I included the webPreferences: nodeIntegration in the js file:
win = new BrowserWindow({
width: 800,
height:600,
icon: __dirname+'/img/sysinfo.png',
webPreferences: {
nodeIntegration: true
}
});
I hope it helped.
You can add the following to your package.json file
{
"name": "mypackage",//default
"version": "0.0.1", //default
"eslintConfig": {
"env": {
"browser": true,
"node": true
}
}}
More Explanation
I was just getting this error (Uncaught ReferenceError: process is not defined) in a local hot-reloading Quasar (Vue) app when calling a particular endpoint. The fix for me ended up being to just restart the hot-reloading server (I hadn't reloaded it since adding the process.env.MY_VARIABLE code).
If you are using the npm module dotenv-webpack with Webpack 3, it might be because you are using destructuring, like so:
const { ENV1, ENV2 } = process.env;
This is a known issue.
Ugly workaround is:
const { ENV1 } = process.env;
const { ENV2 } = process.env;
If you followed all answers here but still have same error then try this.
The error is caused by the react-error-overlay package which has dependencies that were updated to support webpack v5 and are not compatible with react-scripts v4.
So to solve the Uncaught ReferenceError: process is not defined in React, open your terminal in your project's root directory and update the version of your react-scripts package by running npm install react-scripts#latest and re-install your dependencies if necessary.
You can find more detailed answers in here
I had this issue, and for some reason no amount of fiddling around with the webpack.config.js using the other suggestions would resolve it. I suspect some packages I'm using are written incorrectly for the browser, or there might be something wrong with my environment.
I ended up "fixing" it by adding the following code to my HTML file above all other <script> file references. .
<script>
// Required by some npm packages
window.process = { browser: true, env: { ENVIRONMENT: 'BROWSER' } };
</script>

Categories