How to use Node Modules in Electron React App - javascript

I am currently making a desktop application that is a ReactJS application bundled or built with Electron and I want to use node modules that come with electron in my app, specifically exec or spawn from the child process module. My goal is to have a button press that executes a set of commands from command prompt as a sub/side process and return the stdout.
My app follows the typical app creation process as the tutorials online for first making a react app, get some dependencies and package.json alterations, get electron, make an electron.js or main.js type of file with all the basic window configs in it.
In the main entry point file, for me that is electron.js, I have:
webPreferences: {
nodeIntegration: true
},
However, I cannot use the child process node module in my react code even though I have allowed it in the create window function on electron's side. In my react component, when I do
import { exec } from 'child_process';
react seems to recognize it and displays all the exec function information when hovered over (parameters, overloads, found in child_process module, etc).
However, if I run the this example command from a button click, I get the error:
"TypeError: Object(...) is not a function". Here the error is referring to exec.
import { exec } from 'child_process';
...
const run = () => {
exec('ls -lh', (error, stdout, stderr) => {
if (error) {
console.log(`error123: ${error.message}`);
return;
}
if (stderr) {
console.log(`stderr123: ${stderr}`);
return;
}
console.log(`stdout:\n${stdout}`);
})
};
....
return (
<div>
<button onClick={run}>Run Command!</button>
</div>
)
export ....
Does anybody know how to give the react side access to the node modules within the directory or how to import/access them correctly, either directly from the react side or maybe pass them from electron to react? Or maybe I am just running it incorrectly? I am new to javascript and stuff in general so understanding the issue and debugging is really hard for me.

Thanks to Badal for putting me in the right direction.
The answer I found involves setting contextisolation to false, nodeIntegration to true, and using the ipcMain handler (electron main process) and ipcRenderer handler (rendering, in this case react, process). The first link below shows how to use the handlers (look at the newer answers in the thread) and the second link is more detailed documentation on the inner workings. Note that if you search this 'node modules in react electron app' online, a lot of people are using remote, etc. but electron.remote is for the most deprecated at v12 or 13 and onwards, using it and other deprecated modules WILL NOT tell you its deprecated. Instead it will give the fs.filereadsync or wtvr error (I forgot) because it will not be able to understand it during building. The handlers for ipcMain and ipcRenderer handle all app internal communication and processes now, as the name ipc implies.
How to import ipcRenderer in react?
https://www.electronjs.org/docs/api/ipc-main
(new changes to electron) https://www.electronjs.org/docs/breaking-changes
With this, you can have a run button to execute a certain command and use ipcrenderer to send it to the electron side of things. In the electron side (defaults to sending to the main process file, so electron or main.js), you can import whatever node modules you want and use them. For me, I made a new file, did const {exec} = require('child process'), and then had a function which executed certain command line operations. The function call to exec was placed in the ipcMain handler.
This is insecure. For my purpose, security is not a concern, but for others it might be. Make sure to perform input cleaning and other precautions that prevent people from abusing the app and doing things like making calls without proper handles that can infiltrate the system. If you are using child process, OS subsystem modules, etc. this is especially important. As long as the front end can abstract the nature of how the internal communication is performed, that should be a good start.
Electron, as a framework, is highkey changing very fast. If you want to do well in creating applications with it, I highly recommend staying very attentive with version changes. I am new to it, and through my search on this matter and more, I have seen many small but major changes to how electron's internal process works and a lot of search results yield deprecated/outdated information.

It is preferred to access Node APIs from the Main process. Hence, you need to communicate from the Renderer to the Main process to execute such actions.
To access Node APIs from the Renderer process, you also need to set contextIsolation to false along with nodeIntegration set to true. This is again very insecure, to do because
..it helps prevent the website from accessing Electron internals or the powerful APIs your preload script has access to. [1]
So try to avoid doing in production, as for development you are free to experiment.
Reference:
[1] https://www.electronjs.org/docs/tutorial/context-isolation

Related

Can web workers have access to classes defined in Vue application?

I’m building an application that uses Vue for our front-end, specifically Vue 3 Composition API + Typescript. I need the ability to use web workers for long running processes in the background. I was able to get a vanillajs web worker to run alongside the Vue application no problem. My issue is that I need my web worker to have access to classes and functions that are written inside the Vue app. Both my web worker and my Vue app need to use the same classes and functions and I don’t want to have to write them in two places. Is there a way to share them? Any help is appreciated.
The answer is yes:
There is a way to let the web worker having access to the Vue.js framework through one single JavaScript file.
The library that will help you with this in your Vue.js project is vue-worker. This library uses the simple-web-worker package. (Inline Web Workers approach)
As you may know, a standard web worker requires an extra js file that will contain the code of the worker. For inline web workers it isn't necessary to use a helper method. That will inject a property into Vue (and pass it to every child component), with a default name of $worker. In total you can say it is behaving as an JavaScript promise but without really being a second JavaScript file.
Now your turn:
There is a really good documentation to follow along with this approach I described and additional a second "old-fashion" way. Try it out :)
I was able to figure out a solution using esbuild. I added a process to my npm run serve and npm run build commands so it runs node ./esbuild.js before the vue-cli commands. This compiles my typescript web worker to javascript and puts it in the public folder before compiling the Vue app. Now I can create a web worker like normal using:
const worker = new Worker('/worker.js', { type: 'module' })
And here is my esbuild.js file:
const esbuild = require('esbuild')
const { nodeExternalsPlugin } = require('esbuild-node-externals')
const config = {
entryPoints: ['./src/workers/worker.ts'],
outfile: 'public/worker.js',
bundle: true,
minify: false,
plugins: [nodeExternalsPlugin()]
}
esbuild.build(config).catch(() => process.exit(1))

Sentry with SSR

We have an application on Reactjs
We have an SSR that builds the app on webpack, renders this application and returns HTML
Then on the client-side we hydrate the app
So the app is run on both server and client sides.
Problem:
We'd like to use sentry in react, so that it can be used on both client and server sides, because an error may occur in either place
What we've tried:
There are 2 modules #sentry/browser and #sentry/node.
We tried to make a module that would do:
export default isClientSide ? SentryBrowser : SentryNode
So if it's client, then use SentryBrowser. If server - SentryNode
But #sentry/node can't be run on webpack since it can't resolve node dependencies like fs, path etc.
Question:
How can we use a single Sentry interface on both client and server sides for React like #sentry/nextjs works on both sides?
Our solution was to pass in an errorHandler prop to the app. When rendering on the server we'd pass in #sentry/node's captureException, and on the client we'd pass in the #sentry/browser's one. Seemed to work just fine.
const App = ({errorHandler}) => {
..
return (<ErrorBoundary errorHandler={errorHandler}>
..
</ErrorBoundary>);
Then on server:
import { captureException as serverCaptureEx } from '#sentry/node';
hydrate(..., <App errorHandler={serverCaptureEx} />)
and on client:
import { captureException as clientCaptureEx } from '#sentry/browser';
render(..., <App errorHandler={clientCaptureEx} />)
Short answer - you can't.
Client-side is wrapped in special HOC, called in most cases Error Boundary. React passes up an error to the root component of the project and catches it and reports it to the URL. This is a browser, which is client's "server", can do requests on ethernet and so on.
If you run NextJS server, not html export (extremely important condition!), under the hood it is uses some NodeJS libraries (Express, for example). This server has nothing in common with Webpack or JS compilation and is used as middleware. As you can see, it is just a wrapper upon some route or functionality.
If you got an error on server, it is reported by NodeJS, not Webpack. So the flag somewhere in React or Webpack plugins will not help you.
One more time - client and server are different environments, browser and NodeJS server. The errors are reported by servers, not bundlers. Webpack will not help you here.

Modules are not importing inside render process in electron

I Am trying to create run a python script and show the data in the HTML view.
const {PythonShell} = require('python-shell')
alert('Hello')
I have written this code inside the render process. I have connected this js file with my HTML page
Problem
The alert function is not getting executed, I suppose that python-shell is not getting imported, because it is running when I am deleting the import statement.
It looks like you are attempting to run a node package in a renderer process. By default Electron does not have node enable for renderer processes (browser windows).
You can enable it by modifying your BrowserWindow configuration.
new BrowserWindow({
// your window configuration
webPreferences: {
nodeIntegration: true
}
});
Reference: https://www.electronjs.org/docs/api/browser-window
Please note this feature is not enabled by default for security reasons. I recommend reading the security documentation to see if access to node in the renderer process will be secure for your use case.
Security Reference: https://www.electronjs.org/docs/tutorial/security

Make webpack ignore a certain import

I'm trying to create a desktop app using ElectronJS and ReactJS.
The renderer process is bundled using webpack, since i'm using JSX in it.
When I try to import anything from electron (e.g. import electron from 'electron';, or const electron = require('electron');) in the renderer process I get these 2 errors when I either try to build it with webpack (the web part), or when I use webpack-dev-server and open the localhost URL in electron:
https://pastebin.com/WdkCcPzm
Note that I'm not using create-react-app, that bundle.js is webpack's output, App.jsx is the file I'm trying to import electron from, that I want to import electron to access the ipcRenderer variable and that I'm not attempting to import fs from the renderer process (or from the main process for that matter).
A solution I found was to bypass webpack's packing by adding this line to my index.js
eval('window.Electron = require("electron")');
and accessing electron though the variable Electron (the capital E is because vs code recognizes that as a namespace even though electron isn't imported, and thus I still get code completion)
But that's really ugly and I was hoping there was another solution.
To build bundles for the renderer process of Electron apps, webpack provides a special target parameter.
Add this to your webpack config:
target: 'electron-renderer'
See documentation: https://webpack.js.org/configuration/target/

How to handle window object on nodejs for server-side rendering of reactjs application

I am trying to implement server-side rendering for my reactjs application.
I am using webpack to build reactjs application, enhanced-resolve to handle importing of jsx files in nodejs.
My application depends on third party libraries like enquire.js.
When react application tries to import enquire.js on nodejs, it fails with error
ReferenceError: window is not defined
Since window object is not available nodejs
how to handle libraries that use window for server side rendering ?
I haven't used Webpack yet. However, I had similar issue using browserify and I solved it by creating two builds: one for browser one for node.js with list of modules to ignore. You could achieve the same effect by adding to webpack config:
{
plugins: [
new webpack.IgnorePlugin(/file\.js$/)
]
}
Then you have to make sure that you are not making any calls to enquire.js by checking if require() returned false value or object with module functions.
Well this is quite old question still if some one is looking at it.
Try
if (typeof window === 'undefined') {
global.window = {};
}

Categories

Resources