Top View


Author Daiki Urata

Electron ForgeでReact + Vite + TypeScriptのデスクトップアプリ開発環境構築

2023/05/22

はじめに

Electron Forgeはテンプレートからのプロジェクト作成や、アプリ配布のための便利なツールです。

今回はViteテンプレートを使ってTypeScriptとReact環境でデスクトップアプリを動かせるようになるまでの手順を紹介します。

環境

  • Node 18.7.0
  • TypeScript 5.0.4
  • Electron Forge 6.1.1
  • Electron 24.3.0

プロジェクト作成

まずはElectron Forgeの electron-app コマンドを使ってプロジェクト作成をします。

テンプレートはViteを指定します。

$ npm init electron-app@latest electron-vite-react-app -- --template=vite

TypeScriptに書き換える

生成されたプロジェクトはデフォルトだとJavaScriptで書かれたもののため、TypeScriptに書き換えます。

まず作成された.jsファイルを.tsファイルに書き換えました。以下を書き換えました。

  • vite.main.config.ts
  • vite.preload.config.ts
  • vite.renderer.config.ts
  • src/main.ts
  • src/renderer.ts
  • src/preload.ts

forge.config.jsだけは書き換えてしまうと、動かなくなってしまうのでそのままにしました。

そしてそのforge.config.jsとindex.htmlには各エントリファイルの設定が書かれているので、それも.tsに修正します。

module.exports = {
  // 省略
  plugins: [
    {
      name: '@electron-forge/plugin-vite',
      config: {
        build: [
          {
            entry: 'src/main.ts',
            config: 'vite.main.config.ts',
          },
          {
            entry: 'src/preload.ts',
            config: 'vite.preload.config.ts',
          },
        ],
        renderer: [
          {
            name: 'main_window',
            config: 'vite.renderer.config.ts',
          },
        ],
      },
    },
  ],
};
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello World!</title>
  </head>
  <body>
    <h1>💖 Hello World!</h1>
    <p>Welcome to your Electron application.</p>
    <script type="module" src="/src/renderer.ts"></script>
  </body>
</html>

あとはsrc/main.tsのグローバル変数の型も用意する必要がありました。

declare const MAIN_WINDOW_VITE_DEV_SERVER_URL: string;
declare const MAIN_WINDOW_VITE_NAME: string;

tsconfig.jsonの用意

TypeScriptに書き換えたので、tscコマンドで型チェックを行うためにtsconfig.jsonを用意します。

{
  "compilerOptions": {
    "target": "ESNext",
    "allowJs": true,
    "module": "ESNext",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "noImplicitAny": true,
    "baseUrl": ".",
    "noEmit": true,
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "strict": true
  },
}

最後にpackage.jsonに型チェックコマンドを追加して、CIなどでチェックするようにすればOKです。

$ npm install -D typescript
{
	.
	.
  "scripts": {
		.
		.
    "type:check": "tsc"
  },
	.
	.
}

以下コマンドで型のチェックが走ります。

$ npm run type:check

Reactの導入

基本的に以下ドキュメント通りで問題ありませんでした。

インストール

npm install react react-dom
npm install -D @types/react @types/react-dom

app.tsx作成

import * as ReactDOM from 'react-dom';

function render() {
  ReactDOM.render(<h2>Hello from React!</h2>, document.body);
}

render();

作成したapp.tsxをレンダラープロセスで呼び出すため、renderer.tsでインポートします。


import './app';

最後にtsconfig.jsonのcompilerOptionsに "jsx": "react-jsx" を追加するのを忘れないでください。

これで npm start を再度行うと、Reactが動いているのが確認できるはずです。

IPCまわりの型

メインプロセスとレンダラープロセスの通信ではIPCが使われますが、その際の型定義にも少し触れておきます。

例えばpreload.tsで以下のようなメソッドをレンダラープロセス側に用意するとします。

const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('electronAPI', {
  openFile: () => ipcRenderer.invoke('dialog:openFile')
});

そしてメインプロセスでは

ipcMain.handle('dialog:openFile', handleFileOpen);

のような感じで処理を書くと思います。

その場合はレンダラープロセスrenderer.tsでは

const path = await window.electronAPI.openFile();

のように呼べるようになります。

ただ、本来windowオブジェクトにはelectronAPIなんてものはないので、グローバルに型宣言を追加で定義してあげる必要があります。

src/global.d.tsを用意してelectronAPIをwindowオブジェクトに追加してあげます。

declare global {
  interface Window {
    electronAPI: {
      openFile: () => Promise<string>
    };
  }
}
export {}

これで型エラーにならずに、IPCの処理を書くことができました。

おわりに

今回はElectron Forgeを利用してプロジェクト作成、TypeScriptとReactの導入までを行いました。

IPCまわりは自前で型定義ファイルを用意しなければならなく、イケてないので、次回はelectron-trpcを使って型安全なIPCを実現したいと思います。

Daiki Urata

Daiki Urata

Twitter X

フロントエンド/モバイルアプリなどを主に開発しています。