back to index

Electron / Node / TS / Vue Gotchas

A handful of things I tripped on while building UpgradeE.

published Apr 20, 2017 tags #javascript #electron #node

~/posts/electron-pit $ cat post.md

/ LANG EN / 中文
/ THEME / /

Electron

  • When you reach into the main process via remote to grab a global, you can’t pass a custom object. If you force it, prototype methods are dropped and any “private” boundary is gone.
  • Setting an icon won’t accept a NativeImage or a raw path — you have to build a Path object.
  • If a Menu doesn’t include copy/paste items, copy/paste sometimes stops working entirely.
  • On macOS, the first parent menu in a Menu is forced to the app name.
  • Packaging: on Windows the icon file has to be at least 256×256 and can’t be naively converted; the easy fix is to re-save it through the built-in Paint app.
  • macOS packages dmg only; Windows packages msi and exe.
  • When Electron exits it doesn’t kill the Node processes it started. Stash the pids and clean up on exit.
const platform = process.platform;

function killTask() {
    try {
        if (platform === "win32") {
            for (let pid of pids) {
                childProcess.exec(`taskkill /pid ${pid} /T /F`);
            }
            pids = [];
        } else {
            for (let pid of pids) {
                process.kill(pid);
            }
            pids = [];
        }
    } catch (e) {
        showInfo("pid not found");
    }
    clearInterval(timerId);
}

Node

  • In TypeScript, calling require or some library globals directly fails the compiler. The workaround is declare:
declare function require(name: string);
declare var Vue: any;
  • Arrow-function this behavior is confusing. (Rewritten on 2017-12-27.)

What this really does: when a Function is invoked, it implicitly calls Function.call(<this>). With a normal function X, JS binds this to the current execution context — some class, or the global. Take this:

function a() {
    this.d = 1;
    function b() {
        this.f = 2;
        function c() {
            this.g = 3;
        }
    }
}

When c is a regular function, this inside it is its own execution context (the b layer), so this snippet is equivalent to:

function a() {
    this.d = 1;
    function b() {
        this.f = 2;
        function c() {
            b.g = 3;
        }
    }
}

Make c an arrow function and the picture shifts:

function a() {
    this.d = 1;
    function b() {
        this.f = 2;
        let c = () => {
            this.g = 3;
        };
    }
}

Now c’s this is the same as b’s this — meaning this.g = 3 sets g on the a layer, not on c’s own object.

  • TypeScript won’t let you assign to a non-existent key with obj.key, but obj["key"] lets the string assignment slide past the type checker.

Vue

  • Inside a component, pass callbacks as wrapper functions, not as bare method references — otherwise this ends up somewhere unhelpful.

API

  • LoL’s API occasionally returns a wrong 404.
  • The current-game API sometimes returns an incorrect timestamp; validate the time first and fall back to the real game time when it’s off.
back to index