saltyshiomix / nextron

⚡ Next.js + Electron ⚡
https://npm.im/nextron
MIT License
3.88k stars 223 forks source link

child_process.fork cannot access node_modules in prod #365

Open Pandaphobic opened 1 year ago

Pandaphobic commented 1 year ago

I'm using the speedtest-net library inside a nextron app in order to run some network testing before continuing to the next stage of the app.

After much trial and error, it seems that the only way to stop a network test is to kill the process. The build in "cancel" function does not stop the test, it just prepares it to kill the process gracefully. Doing this in electron exits the main process.

My solution was to instead execute the speedtest in a child process, then kill that child process if the test needs to be cancelled. This is working in the dev environment flawlessly however in prod, I can't seem to access the required speedtest-net module from the child process and so the test can't start.

This is the code in my background.js that I'm using to locate and call the child process code:

ipcMain.on("speedtest:start", async (event, arg) => {
  let pathToSpeedtest = "";
  // Detect environment, prod or dev
  if (isProd) {
    pathToSpeedtest = path.join(
      process.resourcesPath,
      "extraResources",
      "speedtest.js"
    );
  } else if (!isProd) {
    pathToSpeedtest = path.join(
      __dirname,
      "../",
      "extraResources",
      "speedtest.js"
    );
  }
  console.log("pathToSpeedtest: ", pathToSpeedtest);
  // send path to front end to be logged
  event.sender.send("speedtest:path", pathToSpeedtest);

  child = fork(pathToSpeedtest);

I used the "extraResources" feature to bundle the child js file. The folder structure at the root looks like this:

extraResources
|___ speedtest.js (child code)
main
|___ background.ts

When I build the app, it copies it to dist/win-unpacked/resources/extraResources/speedtest.js as expected, and in prod the speedtest.js file runs!

BUT it errors out immediately at line 1 : Error: Cannot find module 'speedtest-net'

Here is the content of the speedtest.js file:

const speedTest = require("speedtest-net");

(async () => {
  try {
    console.log(
      await speedTest({
        acceptLicense: true,
        acceptGdpr: true,
        verbosity: 2,
        progress: (info) => {
          switch (info.type) {
            case "testStart":
              process.send({ type: "testStart", info });
              break;

            case "ping":
              process.send({ type: "ping", info });
              break;

            case "download":
              process.send({ type: "download", info });
              break;

            case "upload":
              process.send({ type: "upload", info });
              break;

            case "log":
              process.send({ type: "log", info });
          }
        },
      })
    );
  } catch (err) {
    console.log(err.message);
  } finally {
    process.exit(0);
  }
})();

I've read conflicting information about this - some have said that the child process is entirely separate from the main process and can't "see" the node modules. Others said that's only the case if the child is spawned from the renderer process, while others have said that the child process should have full access to the node_modules by design.

As we are maxed out at Electron 21 for Nextron to work, I'm not able to use the "UtilityProcess" (released in v22) which according to the docs, would have the same interface as fork, and be able to use node_modules.

One solution I tried was copying the entire module into my extraResources folder. That "works" in dev, but in prod it fails because it's not able to use the dependencies that speedtest-net library needs.

It seems that many people have used the child process to run background intensive tasks that utilize node_modules to avoid blocking the main process which is why I'm hopeful there is a solution for this!

Is this a bug? Am I doing something wrong? Are child processes supposed to work this way?

Any help would be greatly appreciated!