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はつい最近公開だった。四半期ごとのようだ。
以上です。




