究極にTypesafeなElectron開発方法
勉強会の録画(社員のみ閲覧可)
サンプルレポジトリ https://github.com/ArakiTakaki/example-typesafe-electron
今回Electronの詳細なセキュリティ対策などは省きます。
概要
タイプセーフにする際に、Electron開発を行う再様々な課題があります
1. MainProsess/PreloadProcessがTypeScript使用困難
2. Processが複数存在し、順番通りに叩く必要がある。
3. webpackの設定が複数必要
4. preloadの型情報が、window要素に入るため、window要素以後が自分で定義をしないとanyになり勝ち
上記を解決できないことにより、Electron特有のアプリであることを生かした命令などを使用する際に、IPCの情報がタイプセーフじゃないため(window要素などが問題)非常に開発がし辛くなります。
詰まる話ビルドプロセスがJavaScriptのほうがやりやすいがための問題です。
TODO: よくよく考えると、require('ts-node/register') でも解決できる気がした。
1. webpackに関してはTypeScriptで設定を記述し、複数プロセス版webpack-cliを再実装する
2. Electronのタスクをyarn start内部に隠蔽させる
3. preloadの情報も型が定義できるため逆算が可能
こんな感じになった!
メインプロセス
https://github.com/ArakiTakaki/example-typesafe-electron/tree/main/src/electron
HTMLなどのレンダラープロセス
https://github.com/ArakiTakaki/example-typesafe-electron/tree/main/src/client
プリロードプロセス
https://github.com/ArakiTakaki/example-typesafe-electron/tree/main/src/preload
ref なぜpreloadをする必要があるのか
セキュアなIPC通信
https://qiita.com/pochman/items/64b34e9827866664d436
ビルドタスクのプロセス
https://github.com/ArakiTakaki/example-typesafe-electron/tree/main/scripts/webpack
ディレクトリ構成
code:text
├── package.json
├── scripts
│   └── webpack // ウェブパックのwrapper
│   ├── index.ts // Webpackのエントリーポイント
│   ├── config // 各種コンフィグ
│   │   ├── main.ts
│   │   ├── preload.ts
│   │   └── renderer.ts
│   └── process
│      ├── buildProcess.ts // release時
│      └── devServerProsses.ts // HMR環境
│
├── src
│   ├── @types
│   │   └── global.d.ts // preloadタスクで搬入された型情報をwindowオブジェクトに付与
│ │
│   ├── client // レンダラープロセス
│   │   ├── index.html
│   │   └── index.tsx // レンダラーのエントリーポイント
│ │
│   ├── electron // メインプロセス
│   │   ├── index.ts
│   │   └── pages
│   │      └── main.ts
│ │
│   └── preload // プリロードプロセス
│   └── index.ts
│
├── tsconfig.json
└── yarn.lock
electron/index.ts
code:typescript
import { app } from 'electron';
import { mainWindow } from './pages/main';
app.on("window-all-closed", () => {
app.quit();
});
app.on("ready", () => {
mainWindow();
});
electron/pags/main.ts
code:typescript
import { BrowserWindow, BrowserWindowConstructorOptions } from 'electron';
import { preloadFile, htmlFile, isDev } from '../constants';
export const mainWindow = () => {
const config: BrowserWindowConstructorOptions = {
width: 600,
height: 600,
x: 0,
y: 15,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: preloadFile,
}
};
const mainWindow = new BrowserWindow(config);
if (isDev) { // DEV環境
const url = 'http://localhost:3000';
mainWindow.loadURL(url);
mainWindow.webContents.openDevTools();
} else { // PRD環境
mainWindow.loadFile(htmlFile);
}
return mainWindow;
}
preload/index.ts
code:ts
import { contextBridge } from 'electron';
import { httpLikeIPC } from '../global/endpoints';
process.env.ELECTRON_PROCESS = 'preload';
export const injectionAPI = {
TokenGetByName: httpLikeIPC.TokenGetByName.fetch,
TokenGetByName2: httpLikeIPC.TokenGetByName.fetch,
} as const;
const injectionObject = {
api: injectionAPI
};
contextBridge.exposeInMainWorld('api', injectionObject.api);
サンプルのAPIインジェクト
所感
windowオブジェクトに入ってくるものが、タイプセーフになるので、非常に快適な開発ができそうでした。
何かしらアプリを作りたいと思います。
#Electron #typescript #勉強会