2

I have a basic electron app (wrapping a Reactjs app) - everything works fine both in dev and when packaged.

Now I want to introduce a dependency, another basic nodejs express file server. I have written this file server and it works.

This file server is a nodejs module (so that it can be managed seperately). It is then required and spawn within the electron app as a seperate "child process".

Problem: When I run "yarn start:dev", everything works perfectly but once I package this app(yarn pack:dev), everything works EXCEPT the file server(child process).

I believe this has something to do with the path and the fact that electron will bundle the entire app as "asar". I have tried different approach and exhausted of ideas.

I think the "asar" file path is not accessible or something. I have also disabled the asar in electron-builder.yml but still having same issue.

Please I will really appreciate any help. Thanks.

4
  • I believe it's related to path configuration. Commented Nov 23, 2021 at 14:11
  • Have you been able to find a solution for that? I'm struggling on how to run nodejs server from reactjs electron app Commented Mar 18, 2023 at 0:24
  • @HarounDarjaj so sorry for the late response. My issue was not passing the env variables mainly the PORT in the child process. I will post an answer shortly for future reference. Commented Apr 29, 2023 at 7:46
  • @HarounDarjaj I have added an answer to this. Commented Apr 29, 2023 at 8:38

1 Answer 1

0

My solution to this was to wrap the node express app (which should be started on electron app launch) as an npm package say offline-file-server . I then installed this package in my electron app as shown below:

package.json - for electron app

{
  "name": "my-react-app-desktop",
  "version": "1.0.0",
  "private": true,
  "main": "./main.js",
  "homepage": "./",
  "scripts": {
    "start:dev": "electron .",
    "clean": "rm -rf ./dist",
    "build": "rollup -c",
    "pack": "electron-builder"
  },
  "devDependencies": {
    "electron": "^11.5.0",
    "electron-builder": "^23.6.0",
    "rollup": "^2.60.0",
    "rollup-plugin-peer-deps-external": "^2.2.4"
  },
  "dependencies": {
    "electron-log": "^4.4.1",
    "fix-path": "^4.0.0",
    "offline-file-server": "^1.0.0"
  }
}

I then imported the package into electron app main.js file as follows:

const serverPath = path.join(__dirname,'node_modules/offline-file-server/dist/server.js');
const { fork } = require("child_process");

let startOfflineServer = () => {
    const child = fork(serverPath, [], {
      silent: true,
      detached: true, 
      stdio: 'ignore',
      env:{
        ...process.env,
        PORT:3001
      }
    });
}

My actual issue was not including this section in the code above:

 env:{
    ...process.env,
    PORT:3001
  }

this is because for some weird reason, while nodejs main process have access to process.env variables, the child process created using the fork method does not, even when the doc says that the default behavior is that child process inherits the parent process.env. At least it did not work in my case, hence, the need to explicitly provide it using the env option in the fork method.

My full electron main.js code now look like the following:

main.js

const { app, BrowserWindow } = require('electron');
const log = require('electron-log');
const path = require('path');
const serverPath = path.join(__dirname,'node_modules/offline-file-server/dist/server.js');
const { fork } = require("child_process");

let startOfflineServer = () => {
    const child = fork(serverPath, [], {
      silent: true,
      detached: true, 
      stdio: 'ignore',
      env:{
        ...process.env,
        PORT:3001
      }
    });

  child.on('error', (err) => {
    log.info("\n\t\tERROR: spawn failed! (" + err + ")");
  });
 
  child.on('data', function(data) {
    log.info('stdout: ' +data);
  });
 
  child.on('exit', (code, signal) => {
    log.info('exit code : ',code);
    log.info('exit signal : ',signal);
    
  });
 
  child.unref();

  //on parent process exit, terminate child process too.
  process.on('exit',function(){
    child.kill()
  })

}


// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow = null;

function createWindow() {
   
  //start offline server on a separate process(child process).
   startOfflineServer();

    mainWindow = new BrowserWindow({
        alwaysOnTop: true, 
        webPreferences: { 
            allowRunningInsecureContent: true,
            backgroundThrottling: false,
            nodeIntegration: true 
        }, 
        kiosk: false, 
        frame: false
    });

    mainWindow.loadURL('your_web_app_url');

    mainWindow.on('closed', function () {
        mainWindow = null;
    });

    mainWindow.on('page-title-updated', function (e) {
        e.preventDefault();
    });
    mainWindow.once('ready-to-show', () => {
        mainWindow.show()
    });
}

// Start the app automatically when the system started.
app.setLoginItemSettings({ openAtLogin: true, openAsHidden: false });

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);

// Quit when all windows are closed.
app.on('window-all-closed', function () {
    // On macOS it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== 'darwin') {
        app.quit();
    }
});
app.on('activate', function () {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (mainWindow === null) {
        createWindow();
    }
});

I hope this helps.

Sign up to request clarification or add additional context in comments.

2 Comments

how did you wrap your node express app into npm package ?
can you provide me your package.json content to see how did you configure electron there?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.