Electron アプリのテンプレート 2021
作ったので公開しておくが、こういうテンプレートはメンテしないとすぐ腐ってしまうけどメンテするモチベーションも特にないのでスナップショットという意味合いも込めて2021をつけている。
electron-next
ベースに electron-next というのを使っていて、これがけっこうよくできていた。やっていることはシンプルで、レンダラプロセスに Next.js を使っていて、開発時は Next.js のサーバーを起動し、production build では next export
した HTML をいい感じにナビゲーショできるようになっている。これにより Next.js のいい感じのビルドシステムを使えて、開発時には hot reload も可能になっているし、production build は静的な HTML と bundle されたアセットが利用可能になる。賢い。
Next.js 公式にもこれを使った example がある。
これをを元に作ってはいるんだけど、ディレクトリ構成があまりにも気に食わなかったので、ほぼ作り直した。
before
.
├── electron-src # mainプロセスのソース
├── main # mainプロセスの出力
└── renderer # レンダラプロセスのソース
└── out # レンダラプロセスの出力
after
.
├── out
│ ├── main # mainプロセスの出力
│ └── renderer # レンダラの出力
└── src
├── main # mainプロセスのソース
└── renderer # レンダラプロセスのソース
圧倒的にわかりやすくなった気がする。元のやつはなんでこんなことになってるんだろう。
Next.js の SSR
Next.js はスピード狂なので可能なところは SSR してしまう癖がある。速いのはいいことだ。しかし、SSR するということは Node.js で実行するということであり、当然ブラウザでしか使えない API を使うとエラーになる。Web だったら LCP とか FCP を気にして可能な限り SSR/SSG したほうがいいけど Electron なのでそんなことは一切考えたくない。
Next.js が SSR しないようにするためのハックとして、dynamic import で ssr: false
にするという技が知られているのでこれを使う。
import dynamic from "next/dynamic";
const App = dynamic(async () => (await import("~/components/App")).App, { ssr: false });
const IndexPage = () => {
return <App />;
};
export default IndexPage;
こんな感じにして App.tsx
にメインの処理を書くようにする。これによって気兼ねなく localStorage
を使うもよし document
にさわるもよし。となる。
IPC
IPCするのに preload.js
というのを main プロセスで呼んで ipcRenderer
を global に expose しているコードがある。
Next.js 公式にある with-electron-typescript
だと @types/node
のバージョンが v14 なんだけど、これを v16 にすると上記のコードが
Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature
という TypeScript のエラーを吐くようになったので Node.js の global でなく window を使うようにした。
そもそもなんで元のやつが Node.js の global を使っているのかよくわかってない。
ESLint
Electron アプリとはあんまり関係ないけど eslint の設定もだいたい前回作ったプロジェクトからコピペして現代との diff を埋めることが多いのでこのスナップショットに含めている。基本的な方針はてっぺいさんのを参考にしている。
Next.js の lint を使ってもよかったのかもしれないけど main プロセスもあるので変にはまるよりは自前で書いたほうがいいかと思って自前で書いている。
プラグインの構成はこんな感じ。
extends: [
"eslint:recommended",
"plugin:import/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"prettier",
]
いたってフツーだけど、久しぶりに設定すると全部忘れてるので難しい。いったい人生であと何回 eslint の設定をやればいいんだ。
はまったところとして、path alias を設定したときに import/no-unresolved
のエラーがでて解決できなかった。eslint-import-resolver-typescript とか色々試してみたんですけどね。eslint 難しいですね。そもそも import が resolve できるかって tsc で見てくれるので import/no-unresolved
いらないね?って気づいたので off にした。
結論
Electron で Next.js 便利でした。
Discussion