djipco / webmidi

Tame the Web MIDI API. Send and receive MIDI messages with ease. Control instruments with user-friendly functions (playNote, sendPitchBend, etc.). React to MIDI input with simple event listeners (noteon, pitchbend, controlchange, etc.).
Apache License 2.0
1.53k stars 115 forks source link

TypeError: Cannot set property navigator of #<Window> which has only a getter #221

Closed jjeff closed 2 years ago

jjeff commented 2 years ago

Migrating my Electron-based app from WebMidi 2 to WebMidi 3, I get this error in the renderer console when attempting to boot my app:

TypeError: Cannot set property navigator of #<Window> which has only a getter
    at Object../node_modules/webmidi/dist/cjs/webmidi.cjs.min.js

I'm guessing this is related to this code at the top of WebMidi.js:

if (Utilities.isNode) {
  let jzz;
  eval('jzz = require("jzz")');
  global["navigator"] = jzz;
}

Maybe I'm misunderstanding, but it looks like this code says: "If we're running Node, then we must not be in a browser window, so we can create our own navigator object." However, in the case of Electron, that's a false assumption. Electron can... and often does run Node in the browser window - or the "renderer" process as Electron calls it. So attempting to redefine the navigator property of the global object (aka window in the renderer) produces an error and the renderer fails.

Again, I'm just guessing, but maybe this code should say something more like:

if (Utilities.isNode && !window.navigator) {
  let jzz;
  eval('jzz = require("jzz")');
  global["navigator"] = jzz;
}
djipco commented 2 years ago

With version 3, Node.js support was added to the library. When the library is run within the Node environment, the jzz module is imported because it provides the missing Web MIDI API.

However, as you are pointing out, some environments may look like both Node.js and the browser. Electron is one but it's probably the same for NW.js and React Native.

In any case, your suggestion makes perfect sense, but it would have to be something like that to prevent an error being thrown:

if (Utilities.isNode) {

  try {
    window.navigator;
  } catch (err) {
    let jzz;
    eval('jzz = require("jzz")');
    global["navigator"] = jzz;
  }

}

Would you be able to test the above code in Electron? Or, perhaps you could provide a basic Electron example that I could try? I actually want to add an Electron example in the examples directory.

P.S. I would prefer not to overwrite the whole navigator property but the jzz module does not leave me much choice because it creates and uses many objects in the navigator namespace.

djipco commented 2 years ago

Can you try v3.0.10 and tell me if it helps?

jjeff commented 2 years ago

It helps! No more error. Looks like things are working now.