I have a macOS electron app that is based on this electron-forge Webpack + Typescript boilerplate, integrated with React, as documented here.
I'm able to spawn a binary node module in dev mode (yarn start
) but not able to in production mode (yarn package
).
A binary node module that is being spawn
ed but not imported is not being packed by webpack
.
In my code, I use the NodeJS spawn module to run a child process in the background. This child process is an installed dependency node module (btw it's @loadmill/agent npm package, but the problem could be applied to any package that is being called by a binary file, instead of a js file).
spawn('loadmill-agent', ['start', '-t', token])
But I don't explicitly import this package into the code. (i.e there is no
import '@loadmill/agent'
line anywhere in the code)
It works well in development mode. When I run yarn start
, the child process is spawned and I can communicate with it and all is well under the sun.
However, when I package the app and run the same line of code, I get an error.
Uncaught Exception:
Error: spawn loadmill-agent ENOENT
at Process.ChildProcess._handle.onexit (node:internal/child_process:282:19)
at onErrorNT (node:internal/child_process:477:16)
at processTicksAndRejections (node:internal/process/task_queues:83:21)
I searched a bit and found that I can debug spawned process errors in NodeJS like so:
spawn('loadmill-agent', ['start', '-t', token], {
env: { NODE_DEBUG: 'child_process', },
}
);
Now instead of the error popup dialog, I get the actual error output:
/bin/sh: loadmill-agent: command not found
Which means the command is either not installed, or not on the PATH, or not executable without a shell.
Furthermore, the @loadmill/agent
node module was not even packed by webpack as a dependency. I know this because I don't see it in the dependencies of the packaged electron app contents/resources.
loadmill-agent
node module is not being packed by webpack
.spawn
ed process outputs
/bin/sh: loadmill-agent: command not found
@loadmill/agent
.@loadmill/agent
with-the-right-path-to-binary-file.
This issue can probably be resolved by configuring the PATH env var or by using the fix-path npm package.To those who are interested, I solved my problem. Here is how:
Inspired by this answer to a similar question, I changed a few things in my code for it to work.
const { start } = require('@loadmill/agent');
const stop = start({
token: 'INSERT_TOKEN_HERE'
});
// Stop the agent at a later time
in a file I named loadmill-agent.ts
.
spawn
, I used NodeJS's fork
. fork
requires a path to file, instead of a terminal command. So I gave fork
the path to my new file. Something like this (in main process code):const childProcess = fork('loadmill-agent', // args & options ...
This obviously still didn't work because of the relative problems in electron apps and in webpack bundled apps
The problem was that after bundling with webpack, I had only 1 index.js
file and it was calling a non-existing loadmill-agent.js
file in the fork
statement above.
loadmill-agent.ts
file, in webpack.main.config.js
file:module.exports = {
...
entry: {
index: './src/index.ts',
'loadmill-agent': './src/loadmill-agent.ts',
},
...
}
Now the output of webpack had 2 files, index.js
and loadmill-agent.js
. Nice.
But still the path was wrong for some reason
app.getAppPath() + '/.webpack/main/' + 'loadmill-agent'
WHY?
Well,
app.getAppPath()
simplyReturns string - The current application directory.
'/.webpack/main/'
is the output path of this built-in webpack configuration.'loadmill-agent'
is the file name (.js
) is implicit here.const childProcess = fork(app.getAppPath() + '/.webpack/main/' + 'loadmill-agent', // args & options ...
Hope this helps someone in the future, Cheers