究極に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の情報も型が定義できるため逆算が可能
こんな感じになった!
メインプロセス
HTMLなどのレンダラープロセス
プリロードプロセス
ref なぜpreloadをする必要があるのか
ビルドタスクのプロセス
ディレクトリ構成
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環境
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オブジェクトに入ってくるものが、タイプセーフになるので、非常に快適な開発ができそうでした。
何かしらアプリを作りたいと思います。