typescript
+webpack
なelectron-forge
で、preload
スクリプトを読み込もうと思った。
typescript
ではあんまり意味はないです。私の設定が下手なだけかもしれませんが。なので結局、セキュリティを考えつつ、nodeIntegration: true
にすることにした。
一応、preload
の設定方法と使い方のメモを残しておく。
環境
- @electron-forge/cli: 6.0.0-beta.51
- Electron: 9.0.0
- nodejs: v12.4.0
- typescript: 3.9.2
すること
webpack
の設定がelectron-forge
だと、webpack.*.config.js
とpackage.json
のconfig
のところとに分けられている。
preload
の設定は、package.json
の方で行う。こんなふうに:
{
"config": {
"forge": {
"packagerConfig": {},
"makers": [
//.. electron-forgeのmakerの設定
],
"plugins": [
[
"@electron-forge/plugin-webpack",
{
"mainConfig": "./webpack.main.config.js",
"renderer": {
"config": "./webpack.renderer.config.js",
"entryPoints": [
{
"html": "./src/renderer/index.html",
"js": "./src/renderer/renderer.tsx",
"name": "main_window",
// ここにpreloadを指定する
"preload": {
"js": "./src/preload.js"
}
}
]
}
}
]
]
}
}
}
あとは、いつも通りBrowserWindow
の生成時にpreload
スクリプトを指定しておく。
const mainWindow = new BrowserWindow({
...,
webPreferences: {
// ここで指定。パスは適切に指定する
preload: `${__dirname}/preload.js`
}
});
そもそもpreloadスクリプトとは
以下の項であまり使わない理由を述べるが、その前にpreload
スクリプトがどんなものか確認する。
Electron
はプロセスがメインプロセスとレンダラプロセスの2つに分かれていて、最近のバージョンのデフォルトでは、Node API
の機能はメインプロセスでしか使えない。それを橋渡しで利用可能にするのがpreload
スクリプトだと考えておけばいい。橋渡しなので、必要な機能だけをレンダラプロセスに渡すことができる。
以前利用したときはこのように使った:
// preload.js
const _setImmediate = setImmediate;
const _clearImmediate = clearImmediate;
const React = require("react");
const ReactDom = require("react-dom");
const ReactTransitionGroup = require("react-transition-group");
const { ipcRenderer, remote } = require("electron");
process.once('loaded', () => {
global.setImmediate = _setImmediate;
global.clearImmediate = _clearImmediate;
global.React = React;
global.ReactDom = ReactDom;
global.ReactTransitionGroup = ReactTransitionGroup;
global.ipcRenderer = ipcRenderer;
global.remote = remote;
});
console.log("preload.js was loaded.");
React
やipcRenderer
などを渡しておけば、レンダラ側で利用できるという流れになる。
これにより、リモートコンテンツに悪意あるコードが含まれていたときにレンダラ側で実行されても、Node API
の機能は使えず、深刻なダメージは避けられる、といった具合になる。
あまり使わない
上のような設定でpreload
なスクリプトを読み込むことができるが、正直扱いがめんどう。特にtypescript
では。
その理由は、preload
スクリプトは.js
ファイルで渡す(おそらく)ので、型が指定できないこと。もう1つが、利用するときに、私が知る限りは、渡した機能(ipcRenderer
など)はglobal
のプロパティに設定しておくものだが、typescript
ではコンパイル・トランスパイルした後に、global
がundefined
になっているため、機能を取得できないことだ。スコープの問題のためだろう。
typescript
はあまり詳しくはないので、回避方法があるのかもしれないが、解決策は見つからなかった。
Lint
の解決のために、必死にd.ts
とかでNodeJS.Global
にプロパティ(ipcRenderer
)を追加したりしたけど、無理だった。どうすればいいというのか…
という迷宮に陥ってしまったので、preload
スクリプトを使うのはやめた!
nodeIntegration: trueでいいや…
リモートコンテンツを読まなければ、nodeIntegration: true
オプションをBrowserWindow
につけておけばいいじゃんと開き直ることにした。
このオプションはNode API
の機能をpreload
で渡す必要がなくなるもの。
レンダラ側が簡単にNode API
にアクセスできるようになる。
上記の理由により、このオプションを使うときは、セキュリティリスクをよく考える必要がある。今回は、ipcRenderer
を使うBrowserWindow
ではリモートコンテンツを読むことがないので、安全。…のはず。
const mainWindow = new BrowserWindow({
...,
webPreferences: {
// ここに指定する
nodeIntegration: true
}
});
参考
- WebpackPluginEntryPoint | @electron-forge/plugin-webpack
- Webpack - Electron Forge
- BrowserWindow | Electron
あとがき
electron-forge
は楽でいい。React
やVue
などフロントエンドのフレームワークのテンプレートも充実するともっと使いやすくなりそう。
Electron 10
が8月ごろに公開されるようのでアップデート可能ならしていこう。
Electron 9
はつい最近公開だった。四半期ごとのようだ。
以上です。