electron-trpc の preload.mjs の件の調査
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
preload script で electron-trpc/main を import することは、公式READMEに記載がある
上記のエラーが出ること自体は、「Preload Scriptでは、sandboxの有効/無効を問わず、electron.ipcMainが参照不可能(※)」であれば、以下の理由から自然である。
-
electron-trpc/mainのbundleは名前通り、メインプロセス向けのcreateIPCHandler()などの処理も含んでいる。このためipcMainを参照している - bundleの中身を見てみると
- cjsのbundleでは、
require("electron")している - mjsのbundleでは、
import { ipcMain as ee } from "electron"している
- cjsのbundleでは、
- いずれのケースも、インポートされた
electronオブジェクトには、ipcMainが生えていない- つまり Preload Script から
ipcMainは参照できない。cjs では参照するメソッド(e.g.,createIPCHandler())を呼ばなければエラーが発生しないが、mjs では named import で参照しているのでエラーが常に起こる
- つまり Preload Script から
// 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
Preload Scriptっていつからある?→v1で既に存在する https://github.com/electron/electron/blob/v1.8.8/spec/fixtures/api/isolated-preload.js
関連するIssueがelectron-trpcに挙げられている。
main.mjs から、contextBridge を参照しているという話。
contextBridgeはPreload Scriptでしか使わない。今回の裏返しのような話。
sandbox: trueだと、READMEにある electron-trpc のインポートができないよ、という話
何かの助けになるかもしれないのでIssueを作ってみるか検討する。
- Preload Script が ESM の場合、
exposeElectronTRPC()を叩こうとするとエラーが発生する-
electron-trpc/mainが、Preload Scriptでは不要かつ参照不能なipcMainをnamed importしているため-
ipcMainはcreateIPCHandler()が必要としているが、createIPCHandler()はPreload Scriptでは使わないので本来ipcMain含め不要 - これは Issue #180 と類似。
contextBridgeはexposeElectronTRPC()が必要としているが、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/ディレクトリを切ってそこに定義を移してはどうか
Contribution Guide 作ろう、というIssueがある状態なので、Contribution Guideはまだ存在してない。
PRは気が向いたら作るかも。。
昨日気づいてなかったけど、 のようにdefault importに書き換えるだけで回避できるならそれでも良さそう