This document describes the approach for managing Node.js binaries and their execution within the Creator Hub Electron application. It outlines the challenges of distributing Node.js-based tools within an ASAR archive and presents a solution for cross-platform binary execution.
The Creator Hub needs to run various Node.js-based tools and binaries (like
@dcl/sdk-commands
) to function properly. This presents several challenges in the
context of an Electron application:
ASAR (Atom Shell Archive) is crucial for our distribution process, particularly for macOS:
The $PATH
environment variable is fundamental to how operating systems locate
executable programs:
$PATH
to ensure our forked processes can
find:
We've implemented a multi-layered approach to handle binary execution:
Instead of bundling a separate Node.js binary, we utilize Electron's built-in Node.js runtime. Electron is built on Node.js, making its binary capable of running Node.js code. To make this work:
node
pointing to the Electron binary.cmd
file that redirects to the Electron binaryTo handle NPM and package management:
package.json
and NPM binaries are kept outside the ASAR archive$PATH
environment variable is modified in forked processes to include:
$PATH
The installation process follows these steps:
$PATH
environment variablepackage.json
To run binaries like sdk-commands
:
utilityProcess
$PATH
containing Node.js and NPM locationsThe forked processes are augmented with monitoring capabilities through a wrapper that provides:
Pattern-based Output Monitoring
on(pattern, handler)
: Subscribe to output matching a RegExp patternonce(pattern, handler)
: Listen for a single pattern matchoff(index)
: Unsubscribe from a patternProcess Control
wait()
: Promise that resolves with complete output or rejects on error
waitFor(resolvePattern, rejectPattern)
: Promise that resolves/rejects based
on output patterns
kill()
: Graceful process termination with force-kill fallbackalive()
: Check if process is still runningLogging and Debugging
Example monitoring usage:
const process = run("@dcl/sdk-commands", "sdk-commands", {
args: ["start"]
})
// Wait for specific output
await process.waitFor(/Server is listening/)
// Monitor errors
process.on(/Error:/, (error) => {
console.error("SDK error:", error)
})
// Graceful shutdown
await process.kill()
// Example of PATH setup
PATH = joinEnvPaths(
path.dirname(nodeCmdPath), // Node.js location
path.dirname(npmBinPath), // NPM location
process.env.PATH // System PATH
)
// Example of binary execution run(package, bin, args)
const child = run("@dcl/sdk-commands", "sdk-commands", {
args: ["start"]
})