Open9

electron-trpc の preload.mjs の件の調査

Kaiichiro OtaKaiichiro Ota

https://kigh-memo.hatenablog.com/entry/2024/05/18/160040 に書いた件。
sandbox: falseな preload.mjs から exposeElectronTRPC() を叩くとエラーが出る件。
sandbox: true の場合、そもそもESMインポートが使えないのでエラーが出る。https://www.electronjs.org/docs/latest/tutorial/esm#sandboxed-preload-scripts-cant-use-esm-imports)

正確には import { exposeElectronTRPC } from 'electron-trpc/main'; でエラー

file:/my/workspace/try-electron-trpc/node_modules/electron-trpc/dist/main.mjs:11
import { ipcMain as ee, contextBridge as re, ipcRenderer as U } from "electron";
         ^^^^^^^
SyntaxError: The requested module 'electron' does not provide an export named 'ipcMain'
    at ModuleJob._instantiate (node:internal/modules/esm/module_job:132:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:214:5)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async node:electron/js2c/renderer_init:2:33310
    at async loadESM (node:internal/process/esm_loader:28:7)
(anonymous) @ VM111 renderer_init:2
Kaiichiro OtaKaiichiro Ota

preload script で electron-trpc/main を import することは、公式READMEに記載がある
https://github.com/jsonnull/electron-trpc/blob/cfdb2e58126deb5867d57e21b5aac1d96ef49b44/README.md#L58-L66

上記のエラーが出ること自体は、「Preload Scriptでは、sandboxの有効/無効を問わず、electron.ipcMainが参照不可能(※)」であれば、以下の理由から自然である。

  • electron-trpc/mainのbundleは名前通り、メインプロセス向けのcreateIPCHandler()などの処理も含んでいる。このためipcMainを参照している
  • bundleの中身を見てみると
    • cjsのbundleでは、require("electron")している
    • mjsのbundleでは、import { ipcMain as ee } from "electron"している
  • いずれのケースも、インポートされたelectronオブジェクトには、ipcMainが生えていない
    • つまり Preload Script から ipcMain は参照できない。cjs では参照するメソッド(e.g., createIPCHandler())を呼ばなければエラーが発生しないが、mjs では named import で参照しているのでエラーが常に起こる
// Object.keys(electron)
[
    "nativeImage",
    "shell",
    "clipboard",
    "contextBridge",
    "crashReporter",
    "ipcRenderer",
    "webFrame",
    "webUtils"
]

(※)は、Preload Script が、Node.js へのアクセス権を与えられつつ Renderer Process で実行されるものであることからおそらく自明なことと思われる https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts

Kaiichiro OtaKaiichiro Ota

何かの助けになるかもしれないのでIssueを作ってみるか検討する。

  • Preload Script が ESM の場合、exposeElectronTRPC() を叩こうとするとエラーが発生する
    • electron-trpc/mainが、Preload Scriptでは不要かつ参照不能なipcMainをnamed importしているため
      • ipcMaincreateIPCHandler()が必要としているが、createIPCHandler()はPreload Scriptでは使わないので本来ipcMain含め不要
      • これは Issue #180 と類似。contextBridgeexposeElectronTRPC()が必要としているが、exposeElectronTRPC()はMain Processでは使わないのでcontextBridge含めて不要
  • 回避策は、exposeElectronTRPC()の実装をPreload Scriptにsnippetとしてcopy/pasteすること(Issue #116 に示されているものとほぼ同じ)
    • ELECTRON_TRPC_CHANNELの定義が変わると、snippetの修正が必要な点は注意が必要
  • 解決案としては、exposeElectronTRPC()はPreload Script専用のロジックなので、mainのbundleから分離する、具体的にはpackages/electron-trpc/src/preload/ディレクトリを切ってそこに定義を移してはどうか