Search code examples
node.jsimagemagickelectron

bundling precompiled binary into electron app


Is there a good solution on how to include third party pre compiled binaries like imagemagick into an electron app? there are node.js modules but they are all wrappers or native binding to the system wide installed libraries. I wonder if it's possible to bundle precompiled binaries within the distribution.


Solution

  • Update 2024

    Building on the provided answers,

    Here's a more concise solution that does not require any external dependencies,

    1. Create a folder at the root of your project (/resources/$os) where $os could be mac, linux or win and copy the required binary file there. Lets assume we have a ffmpeg binary and would like to package it with electron.
    2. Update package.json and put extraFiles option in your build config as follows:
     "build": {
        "extraFiles": [
          {
            "from": "resources/${os}", // $os => "mac/linux/win"
            "to": "Resources/bin",     // for Linux => "resources/bin"
            "filter": ["**/*"]
          }
        ],
      }
    
    1. Create a file (for example: utils.ts) with a few helper functions
    // utils.ts
    
    import path from 'path';
    import { platform } from 'os';
    import { app } from 'electron';
    
    export function getPlatform() {
      switch (platform()) {
        case 'aix':
        case 'freebsd':
        case 'linux':
        case 'openbsd':
        case 'android':
          return 'linux';
        case 'darwin':
        case 'sunos':
          return 'mac';
        case 'win32':
          return 'win';
        default:
          return null;
      }
    }
    
    export function getBinariesPath() {
      const IS_PROD = process.env.NODE_ENV === 'production';
      const { isPackaged } = app;
    
      const binariesPath =
        IS_PROD && isPackaged
          ? path.join(process.resourcesPath, './bin')
          : path.join(app.getAppPath(), 'resources', getPlatform()!);
    
      return binariesPath;
    }
    
    // "ffmpeg" is the binary that we want to package
    export const ffmpegPath = path.resolve(path.join(getBinariesPath(), './ffmpeg'));
    
    1. Now consume the binary wherever you need to. (For example: main.ts)
    // main.ts
    
    import { exec, spawn } from 'child_process';
    import { ffmpegPath } from './utils';
    
    // option 1: using exec
    exec(`"${ffmpegPath}" -version`, (err, stdout, stderr) => {
        console.log('err, stdout, stderr :>> ', err, stdout, stderr);
    });
    
    // option 2: using spawn
    const ffmpeg = spawn(ffmpegPath, ['-version']);
    ffmpeg.stdout.on('data', (data) => {
      console.log(`spawn stdout: ${data}`);
    });
    
    ffmpeg.stderr.on('data', (data) => {
      console.error(`spawn stderr: ${data}`);
    });
    
    ffmpeg.on('close', (code) => {
      console.log(`spawn child process exited with code ${code}`);
    });