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:
$PATHThe installation process follows these steps:
$PATH environment variablepackage.jsonTo 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"]
})