🚀

Electron + React + TypeScript の開発環境構築

2021/04/29に公開
14

はじめに

ReactTypeScript でつくる Electron アプリのボイラープレートです。
メインプロセス、レンダラープロセスともにホットリロード可能な開発環境の構築を目指します。

https://github.com/sprout2000/electron-react-ts#readme

前提

Node.jsGit Bash(もしくは何らかの UNIX シェル)はインストール済みであることを想定しています。

プロジェクトディレクトリの作成

プロジェクト用のフォルダ(ディレクトリ)を作成し、その中で必要なパッケージのインストールや設定を行います。

⚙️ Node.js のプロジェクトとして初期化

npm コマンドは Node.js に同梱されています。

  • プロジェクトフォルダの作成と移動
bash
$ mkdir myapp
$ cd myapp
  • Node.js プロジェクトとして初期化
bash
npm init --yes

📁 想定するディレクトリ構成

bash
% tree -a -I "node_modules"
.
├── dist
├── package-lock.json
├── package.json
├── src
│    ├── main.ts
│    ├── preload.ts
│    └── web
│        ├── App.css
│        ├── App.tsx
│        ├── index.html
│        └── index.tsx
├── tsconfig.json
└── webpack.config.ts

3 directories, 10 files
  • dist/: webpack の出力先フォルダ
  • src/main.ts: メインプロセスのエントリファイル
  • src/preload.ts: プリロードスクリプト
  • src/web/: レンダラープロセス (= React アプリケーション) ソースコード置き場
  • tsconfig.json: TypeScript 設定ファイル
  • webpack.config.ts: webpack 設定ファイル

TypeScript のインストールと設定

TypeScript や周辺ツールのインストールと設定を行います。

📥 TypeScript のインストール

bash
npm install --save-dev typescript ts-node @types/node

--save-dev オプションを使ってインストールすると、そのパッケージは package.jsondevDependencies エントリへ追加されます。

  • typescript: 本体
  • ts-node: TypeScript のスクリプト (webpack.config.ts など) をそのまま実行するためのツール
  • @types/node: Node.js のための型定義ファイル

⚙️ TypeScript 設定ファイル: tsconfig.json の作成

プロジェクトフォルダ直下に tsconfig.json を作成します。

tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "esModuleInterop": true,
    "moduleResolution": "Bundler",
    "jsx": "react-jsx",
    "strict": true
  },
  "ts-node": {
    "compilerOptions": {
      "module": "CommonJS"
    }
  }
}
  • target: 出力する JavaScript のバージョンを設定します。
  • module: 出力される JavaScript がどの形式のモジュールを読み込むかを指定します。 下の esModuleInteroptrue に設定しておき、ES* を指定しておくのが無難でしょう。
  • esModuleInterop: CommonJS と ES Modules 間で相互運用可能なコードを出力します。
  • moduleResolution: モジュール解決の方法を指定します。webpack を利用するので Bundler を指定します。
  • lib: 必須ではないので設定しません。target が指定されていれば、その target で使われているライブラリは自動的に追加されるからです。逆に lib を指定すると、明示的に target のどの lib を使うかも明記しなければいけなくなります。
  • jsx: JSX 構文がどのように JavaScript ファイルに出力されるかを設定します。 .tsx で終わるファイルの JS 出力にのみ影響します。
    • preserve: JSX を変更せずに .jsx ファイルを出力します。
    • react: JSX を等価な react.createElement に変換して .js ファイルを出力します。
    • react-jsx: JSX を _jsx 呼び出しに変更した .js ファイルを出力します。多くのモジュールで React 本体をインポートする必要がなくなります。
  • strict: TypeScript による幅広い型チェックの挙動を有効化します。これだと厳しすぎる場合には、個別の strict モードファミリーを無効化する必要があります。
  • ts-node: ts-node で Node.js スクリプトを実行する(webpack.config.ts などの TypeScript で書かれた設定ファイルを評価する)には module: CommonJS の指定が必要です。

Electron アプリの作成

必要最低限の機能を持った Electron アプリを作成します。

📥 Electron のインストール

Electron は package.jsondevDependencies へエントリされている必要があります。

  • 本体
bash
npm install --save-dev electron
  • Electron をホットリロードしてくれるツール
bash
npm install --save-dev electronmon

https://github.com/catdad/electronmon#readme

electronmon の設定

package.jsonelectronmon に変更を検知してほしいファイルのパターンを登録します。

package.json
    "author": "",
+   "electronmon": {
+     "patterns": [
+       "dist/**/*"
+     ]
+   },
    "devDependencies": {

⚒️ メインプロセスの作成

src/main.ts
import path from "node:path";
import { BrowserWindow, app } from "electron";

app.whenReady().then(() => {
  // アプリの起動イベント発火で BrowserWindow インスタンスを作成
  const mainWindow = new BrowserWindow({
    webPreferences: {
      // webpack が出力したプリロードスクリプトを読み込み
      preload: path.join(__dirname, "preload.js"),
    },
  });

  // レンダラープロセスをロード
  mainWindow.loadFile("dist/index.html");
});

// すべてのウィンドウが閉じられたらアプリを終了する
app.once("window-all-closed", () => app.quit());

⚒️ プリロードスクリプトの作成

ここではとりあえず空のファイルとします。

src/preload.ts
console.log("preloaded!");

React アプリ(レンダラープロセス)の作成

こちらも最低限の体裁を備えたアプリ(= WebView 部分)を作成します。

📥 React のインストール

  • 本体
bash
npm install react react-dom

--save-dev オプションなし(もしくは --save or -S オプション付き)でインストールすると、そのパッケージは package.jsondependencies エントリへ追加されます。

  • 型定義ファイル
bash
npm install -D @types/react @types/react-dom

-D オプションは --save-dev の省略形です。

⚒️ レンダラープロセスの作成

画面に Hello. と表示するだけのシンプルな React アプリを作成します。

📋 src/web/index.html

src/web/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <!-- CSP の設定 https://developer.mozilla.org/ja/docs/Web/HTTP/CSP -->
    <meta http-equiv="Content-Security-Policy" content="default-src 'self';" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Electron App</title>
  </head>
  <body>
    <!-- React アプリのマウントポイント -->
    <div id="root"></div>
  </body>
</html>

https://www.electronjs.org/ja/docs/latest/tutorial/security

https://developer.mozilla.org/ja/docs/Web/HTTP/CSP

📋 src/web/App.css

src/web/App.css
.container {
  font-family: sans-serif;
  text-align: center;
}

📋 src/web/App.tsx

src/web/App.tsx
import "./App.css";

export const App = () => {
  return (
    <div className="container">
      <h1>Hello.</h1>
    </div>
  );
};

📋 src/web/index.tsx

src/web/index.tsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

import { App } from "./App";

createRoot(document.getElementById("root") as Element).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

バンドラー (webpack) の設定

webpack を使って、メイン、レンダラープロセスともにビルドします。

📥 Webpack のインストール

https://webpack.js.org/

本体

bash
npm i -D webpack webpack-cli

バンドルするための各種ローダー

  • TypeScript と CSS のローダー
bash
npm i -D ts-loader css-loader

https://www.npmjs.com/package/ts-loader
https://www.npmjs.com/package/css-loader

各種プラグイン

bash
npm i -D html-webpack-plugin mini-css-extract-plugin
  • html-webpack-plugin: バンドルされた JavaScirpt ファイルを HTML の <script> ~ </script> タグへ差し込むプラグイン
  • mini-css-extract-plugin: CSS を JS へバンドルせず単独のファイルとして出力するプラグイン

https://www.npmjs.com/package/html-webpack-plugin
https://www.npmjs.com/package/mini-css-extract-plugin

⚙️ 設定ファイル webpack.config.ts の作成

開発時にはウォッチモードを使って、なんらかのファイルが変更されるたびにコンパイル&バンドルします。

その結果が dist ディレクトリへ出力されると、electronmon がその変更を検知して、 メインプロセスへの変更では Electron アプリをリスタートさせ、レンダラープロセスへの変更ではブラウザウィンドウをリロードします。

webpack.config.ts
/** エディタで補完を効かせるために型定義をインポート */
import type { Configuration } from "webpack";

import HtmlWebpackPlugin from "html-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";

// 開発者モードか否かで処理を分岐する
const isDev = process.env.NODE_ENV === "development";

// 共通設定
const common: Configuration = {
  // モード切替
  mode: isDev ? "development" : "production",
  // モジュール解決に参照するファイル拡張子
  resolve: {
    extensions: [".js", ".ts", ".jsx", ".tsx", ".json"],
  },
  /**
   * macOS でビルドに失敗する場合のワークアラウンド
   * https://github.com/yan-foto/electron-reload/issues/71
   */
  externals: ["fsevents"],
  // 出力先:デフォルトは "dist"
  output: {
    // 画像などのアセット類は "dist/assets" フォルダへ配置する
    assetModuleFilename: "assets/[name][ext]",
  },
  module: {
    // ファイル種別ごとのコンパイル & バンドルのルール
    rules: [
      {
        /**
         * 拡張子 ".ts" または ".tsx" (正規表現)のファイルを "ts-loader" で処理
         * ただし node_modules ディレクトリは除外する
         */
        test: /\.tsx?$/,
        exclude: /node_modules/,
        loader: "ts-loader",
      },
      {
        // 拡張子 ".css" (正規表現)のファイル
        test: /\.css$/,
        // use 配列に指定したローダーは *最後尾から* 順に適用される
        // セキュリティ対策のため style-loader は使用しない
        use: [MiniCssExtractPlugin.loader, "css-loader"],
      },
      {
        // 画像やフォントなどのアセット類
        test: /\.(ico|png|svg|eot|woff?2?)$/,
        /**
         * アセット類も同様に asset/inline は使用しない
         * なお、webpack@5.x では file-loader or url-loader は不要になった
         */
        type: "asset/resource",
      },
    ],
  },
  // 開発時には watch モードでファイルの変化を監視する
  watch: isDev,
  /**
   * development モードではソースマップを付ける
   *
   * なお、開発時のレンダラープロセスではソースマップがないと
   * electron のデベロッパーコンソールに "Uncaught EvalError" が
   * 表示されてしまうことに注意
   */
  devtool: isDev ? "source-map" : undefined,
};

// メインプロセス向け設定
const main: Configuration = {
  // 共通設定を読み込み
  ...common,
  target: "electron-main",
  // エントリーファイル(チャンク名の "main.js" として出力される)
  entry: {
    main: "./src/main.ts",
  },
};

// プリロードスクリプト向け設定
const preload: Configuration = {
  ...common,
  target: "electron-preload",
  entry: {
    preload: "./src/preload.ts",
  },
};

// レンダラープロセス向け設定
const renderer: Configuration = {
  ...common,
  // セキュリティ対策として "electron-renderer" ターゲットは使用しない
  target: "web",
  entry: {
    // React アプリのエントリーファイル
    app: "./src/web/index.tsx",
  },
  plugins: [
    // CSS を JS へバンドルせず別ファイルとして出力するプラグイン
    new MiniCssExtractPlugin(),
    /**
     * バンドルしたJSファイルを <script></scrip> タグとして差し込んだ
     * HTMLファイルを出力するプラグイン
     */
    new HtmlWebpackPlugin({
      // テンプレート
      template: "./src/web/index.html",
    }),
  ],
};

// 上記 3 つの設定を配列にしてデフォルト・エクスポート
export default [main, preload, renderer];

https://zenn.dev/sprout2000/articles/9d026d3d9e0e8f

NPM スクリプトの設定

開発時や本番ビルド時に走らせるスクリプトを package.json へ追記します。

📥 各種ユーティリティのインストール

bash
npm i -D rimraf wait-on cross-env npm-run-all
  • rimraf
    どのプラットフォームでも UNIX の rm -rf と同等のコマンドを実現するユーティリティ。
  • wait-on
    指定したスクリプトが完了するのを待って別のスクリプトを実行してくれるユーティリティ。
  • cross-env
    どのプラットフォームでも環境変数の設定が共通となるユーティリティ。
  • npm-run-all
    • run-s: スクリプトを順番に実行。
    • run-p: スクリプトを並列に実行。

⚙️ NPM スクリプトの作成

package.json
{
  "main": "dist/main.js",
  "scripts": {
    "dev": "rimraf dist && run-p dev:webpack dev:electron",
    "dev:webpack": "cross-env NODE_ENV=\"development\" webpack --progress",
    "dev:electron": "wait-on ./dist/index.html ./dist/main.js && electronmon .",
    "build": "cross-env NODE_ENV=\"production\" webpack --progress"
  }
}
  • main エントリには webpack が出力するメインプロセスの JS ファイルを指定します。
  • scripts
    • dev:webpack: webpack のウォッチモードでコンパイル&バンドルします。
    • dev:electron: webpack のコンパイル結果が出力されるのを待って electronmon を起動します。
    • dev: npm run dev で、前回のビルド結果を削除したあと上記 2 つのコマンドを並列に実行します。
    • build: 本番向けビルドを production モードで実行します。

実行テスト

React アプリ(レンダラープロセス)やメインプロセスのコードを編集し、ホットリロードが有効であることを確認します。

bash
npm run dev

renderer
レンダラープロセスのホットリロード (ブラウザウィンドウのリロード)

main
メインプロセスのホットリロード(Electron の再起動)

こちらもよろしくお願いします

https://zenn.dev/sprout2000/books/6f6a0bf2fd301c

https://zenn.dev/sprout2000/books/3691a679478de2

GitHubで編集を提案

Discussion

kentarokentaro

記事を参考にelectronデビュー致しました。 詳細な解説をありがとうございます。

1つ躓いた点がございまして、お答えいただけるとありがたいです。
リリースビルドしたあと exe を実行すると エラーになります。

Error: Cannot find module 'electron-search-devtools'

これはつまり、「devDependenciesのモジュールを参照しようとしたこと」が原因と判断しました。

該当モジュール使用箇所をコメントアウトしたところ エラーは解消されました。
ですが、実際その対策は意図したものではないと考えられます。
同じ場所で躓いている方をお見かけしないので、何か私にミスがあると思うのですがわかりませんでした。

もし何かアドバイスがございましたらよろしくお願い致します。

はっぱはっぱ

@kentaro さん
ご指摘ありがとうございます。

本番向けにビルドする場合には、環境変数 NODE_ENVproduction にセットしてください(記事本文にも追記しました)。

package.json
   "scripts": {
      "predev": "rimraf dist",
      "dev": "run-p dev:*",
      "dev:tsc": "tsc -w -p tsconfig.main.json",
      "dev:webpack": "cross-env NODE_ENV=\"development\" webpack",
      "dev:electron": "wait-on ./dist/index.html && cross-env NODE_ENV=\"development\" electron .",
+     "build": "cross-env NODE_ENV=\"production\" webpack --progress"
   },

記事中で言及した拙作レポジトリでの例で言えば、リリースビルドは以下の手順となります。

zsh
% npm install
% npm run build

https://github.com/sprout2000/electron-react-ts

はっぱはっぱ

補足 Electron アプリケーションにおける devDependencies について

production ビルド用に node_modules をインストールする場合、以下のようなコマンドは利用しないでください。

zsh
% npm install ---production
# or
% NODE_ENV=production npm install

なぜなら、これらは devDependencies に指定されたパッケージをインストールから除外してくれますが、electron 本体や electron-builder 等のパッケージャーは devDependencies に登録されている必要があるからです。

https://docs.npmjs.com/cli/v8/commands/npm-install

https://www.electron.build/

これら以外のパッケージで、本当に production ビルドに必要のないパッケージを除外したいのであれば、それらは optionalDependencies へ移動させ、かつ ifdef-loader 等を用いてソースコード内でそのモジュールを利用している部分を production ビルドでは評価しないようにする必要があります。

zsh
% npm install --no-optional
# or yarn install --ignore-optional

https://www.npmjs.com/package/ifdef-loader

これらは node_modules の減量 (≒ ビルドサイズの減量) とコードの保守・拡張の容易さとのバランスを勘案して利用する必要があるでしょう。

てべすてんてべすてん

記事を参考にとりあえず環境構築ができたものです(^^)

さて、私が移行したいと考えているプロジェクトでは、import ... from "src/○/○" ;のようにsrcフォルダを基点にした絶対パスでインポートする文が多く使われており、これを可能にするために全プロジェクトでは**tsconfig.jsonのcompolerOptions.baseUrlに"."**を設定しました。

本記事ではtsconfig.jsonはレンダラープロセス用、tsconfig.main.jsonはメインプロセス用の設定ファイルという理解をしたため、絶対パスでのインポートを可能にするためtsconfig.jsonのcompolerOptions.baseUrlに"."を設定しましたが、以下のようにパスが解決できないという旨のエラーメッセージが表示されてしまいます。

ERROR in ./src/app.tsx 5:0-37
Module not found: Error: Can't resolve 'src/○○/××' in '(プロジェクトルート)'

webpack compiled with 1 error

tsconfig.jsonのcompolerOptions.baseUrlに"."を設定するだけではなにか足りないのでしょうか?

はっぱはっぱ

そのエラーがレンダラープロセス側で生じているのであれば tsconfig-paths-webpack-plugin などを利用して webpack にモジュール解決をさせれば良いでしょうが(下記参照)、メインプロセス側でのエラーだとするとちょっと根本的に仕組みを考え直すしかないのではないでしょうか。もしくはメインプロセス側のコードだけは相対パスによるインポートを使うとか・・・

https://github.com/dividab/tsconfig-paths-webpack-plugin

webpack.config.ts
+ import TsConfigPathsPlugin from 'tsconfig-paths-webpack-plugin';

   resolve: {
     extensions: ['.js', '.ts', '.jsx', '.tsx', '.json'],
+    plugins: [new TsConfigPathsPlugin({ baseUrl: '.' })],
   },
てべすてんてべすてん

ブラウザ(アプリのウィンドウ)にも同様のエラーメッセージがあったためどうやらレンダラープロセス側で起こっていたみたいです。
@Ke Touge様のおっしゃる通りにwebpack.config.tsに設定を加えると解決できました!

(webpackの勉強不足でした。。。)

ただメインプロセスの方は、上記の対策をしても当然ながら絶対パスが使えなかったので、諦めて相対パスで頑張ることにしました!

ご回答ありがとうございました。

LinlyLinly

はじめまして、この記事を参考にはじめて触った者です。ある程度動作するものできたので、しexeにしてみようと思いnpm run buildとやってみたところ最終的にwebpack compiled successfully と表示されているにもかかわらずどこにもフォルダが生成されていません。ログは以下のようになっていました。お答えいただけると幸いです。
試しにelectron-packagerを使ったところ生成されましたが、そちらでは
Cannot find module 'electron-search-devtools'と出ています
以下がログです。

 npm run build
npm WARN config global `--global`, `--local` are deprecated. Use `--location=global` instead.

> electron-react-ts@1.2.0 build
> cross-env NODE_ENV="production" webpack --progress

asset main.js 611 KiB [emitted] [minimized] [big] (name: main) 1 related asset
runtime modules 786 bytes 4 modules
orphan modules 2.57 KiB [orphan] 2 modules
modules by path ./node_modules/ 1.17 MiB
  javascript modules 1.03 MiB 308 modules
  json modules 150 KiB
    modules by path ./node_modules/conf/ 3.07 KiB
      ./node_modules/conf/node_modules/ajv/dist/refs/json-schema-draft-07.json 2.72 KiB [built] [code generated]
      ./node_modules/conf/node_modules/ajv/dist/refs/data.json 360 bytes [built] [code generated]
    modules by path ./node_modules/ajv-formats/ 3.07 KiB
      ./node_modules/ajv-formats/node_modules/ajv/dist/refs/json-schema-draft-07.json 2.72 KiB [built] [code generated]
      ./node_modules/ajv-formats/node_modules/ajv/dist/refs/data.json 360 bytes [built] [code generated]
    ./node_modules/binary-extensions/binary-extensions.json 1.61 KiB [built] [code generated]
    ./node_modules/mime-db/db.json 142 KiB [built] [code generated]
+ 20 modules
webpack 5.73.0 compiled successfully in 17656 ms

asset preload.js 266 bytes [emitted] [minimized] (name: preload)
orphan modules 147 bytes [orphan] 2 modules
./src/preload.ts + 2 modules 659 bytes [not cacheable] [built] [code generated]
webpack 5.73.0 compiled successfully in 7954 ms

assets by status 344 KiB [cached] 2 assets
assets by path . 140 KiB
  asset app.js 138 KiB [emitted] [minimized] (name: app) 1 related asset
  asset app.css 1.76 KiB [emitted] (name: app)
  asset index.html 406 bytes [emitted]
Entrypoint app 140 KiB (344 KiB) = app.css 1.76 KiB app.js 138 KiB 2 auxiliary assets
orphan modules 7.86 KiB (javascript) 1.83 KiB (runtime) [orphan] 15 modules
runtime modules 29 bytes 1 module
cacheable modules 145 KiB (javascript) 1.76 KiB (css/mini-extract) 344 KiB (asset)
  javascript modules 145 KiB
    modules by path ./node_modules/react-dom/ 131 KiB 3 modules
    modules by path ./node_modules/react/ 6.94 KiB 2 modules
    modules by path ./node_modules/scheduler/ 4.33 KiB 2 modules
    ./src/web/index.tsx + 3 modules 3.07 KiB [built] [code generated]
  modules by path ./src/web/*.css 1.76 KiB
    css ./node_modules/css-loader/dist/cjs.js!./src/web/index.css 49 bytes [built] [code generated]
    css ./node_modules/css-loader/dist/cjs.js!./src/web/App.css 1.71 KiB [built] [code generated]
  asset modules 84 bytes (javascript) 344 KiB (asset)
    ./src/web/assets/defaultIcon.png 42 bytes (javascript) 10.1 KiB (asset) [built] [code generated]
    ./src/web/assets/favicon.ico 42 bytes (javascript) 334 KiB (asset) [built] [code generated]
webpack 5.73.0 compiled successfully in 12479 ms
はっぱはっぱ

コメントありがとうございます。

npm run build コマンドは Electron が起動できるようにコンパイルするだけで、アプリを exe などの形式へパッケージするには別途 electron-packagerelectron-builder が必要です。

electron-builder については私のレポジトリ(↓)の scripts/build.ts などを参考にしてみてください。

https://github.com/sprout2000/leafview#readme

Cannot find module 'electron-search-devtools'と出ています

NPM スクリプトの NODE_ENV (cross-env) の設定は適切ですか?
また、サンプルレポジトリ(↓)の src/main.tsisDev 変数なども試してみてください。

https://github.com/sprout2000/electron-react-ts#readme

LinlyLinly

とても素早い対応ありがとうございます。なんとかexeにすることができました。しかし今度はこれまでは使えていた機能(websocket関係です)が使えなくなっていました。ファイアウォールを切ってみるなどしましたが特に変化はなかったのですが、ビルドした後にコンソールなどを見るには、ログファイルを書き出したりウィンドウに表示するほかないでしょうか。

はっぱはっぱ

webSocket についてよく知らないので何とも言えないのですが…

Development モードで動いている機能がパッケージング後に使えなくなるのであれば、そのモジュールを asarUnpackelectron-builder の場合)に指定してみるのも良いかもしれません。

https://www.electron.build/configuration/configuration#configuration-asarUnpack

あとは CSPwebContents オプションの設定を見直すくらいでしょうか。

https://developer.mozilla.org/ja/docs/Web/HTTP/CSP

https://www.electronjs.org/ja/docs/latest/api/web-contents

ビルドした後にコンソールなどを見るには、ログファイルを書き出したりウィンドウに表示するほかないでしょうか。

ビルド後も(正式リリースまでは)devTools を表示させることは可能ですし、インスペクタの WebSocket URL の公開方法もコマンドラインから設定できるようですよ。

https://www.electronjs.org/ja/docs/latest/api/command-line-switches#--inspect-publish-uidstderrhttp

Discord の Electron サーバー の Help チャネルで質問してみても(※英語)良いかもしれませんね。

https://discord.gg/electronjs

LinlyLinly

ありがとうございます。npm run devnpm run buildで出力した後electron .で起動するという方法で調べてみたところパッケージではなくnpm run buildの時点で正常に動作していないようなのですが、なにかそういったことに関してご存じないでしょうか。package.jsonとビルド時のログ、実行時のエラーはこのようになっていました。また、npm run dev:electronの場合も同じエラーが出ています。
何度もお時間いただいて申し訳ありません。

package.json
  "scripts": {
    "predev": "rimraf dist",
    "dev": "run-p dev:*",
    "dev:tsc": "tsc -w -p tsconfig.main.json",
    "dev:webpack": "cross-env NODE_ENV=\"development\" webpack --progress",
    "dev:electron": "wait-on ./dist/index.html && cross-env NODE_ENV=\"development\" electron .",
    "build": "cross-env NODE_ENV=\"production\" webpack --progress",
    "pack": "electron-packager . --icon=favicon.ico",
    "preview": "electron ."
  },
log
> portal-jump@1.2.0 build
> cross-env NODE_ENV="production" webpack --progress

asset main.js 628 KiB [emitted] [minimized] [big] (name: main) 1 related asset
runtime modules 786 bytes 4 modules
orphan modules 2.57 KiB [orphan] 2 modules
modules by path ./node_modules/ 1.22 MiB
  javascript modules 1.07 MiB 319 modules
  json modules 150 KiB
    modules by path ./node_modules/conf/ 3.07 KiB
      ./node_modules/conf/node_modules/ajv/dist/refs/json-schema-draft-07.json 2.72 KiB [built] [code generated]
      ./node_modules/conf/node_modules/ajv/dist/refs/data.json 360 bytes [built] [code generated]
    modules by path ./node_modules/ajv-formats/ 3.07 KiB
      ./node_modules/ajv-formats/node_modules/ajv/dist/refs/json-schema-draft-07.json 2.72 KiB [built] [code generated]
      ./node_modules/ajv-formats/node_modules/ajv/dist/refs/data.json 360 bytes [built] [code generated]
    ./node_modules/binary-extensions/binary-extensions.json 1.61 KiB [built] [code generated]
    ./node_modules/mime-db/db.json 142 KiB [built] [code generated]
webpack 5.73.0 compiled successfully in 12100 ms

asset preload.js 266 bytes [emitted] [minimized] (name: preload)
orphan modules 147 bytes [orphan] 2 modules
./src/preload.ts + 2 modules 659 bytes [not cacheable] [built] [code generated]
webpack 5.73.0 compiled successfully in 5576 ms

assets by status 344 KiB [cached] 2 assets
assets by path . 140 KiB
  asset app.js 138 KiB [emitted] [minimized] (name: app) 1 related asset
  asset app.css 1.76 KiB [emitted] (name: app)
  asset index.html 406 bytes [emitted]
Entrypoint app 140 KiB (344 KiB) = app.css 1.76 KiB app.js 138 KiB 2 auxiliary assets
orphan modules 7.86 KiB (javascript) 1.83 KiB (runtime) [orphan] 15 modules
runtime modules 29 bytes 1 module
cacheable modules 145 KiB (javascript) 1.76 KiB (css/mini-extract) 344 KiB (asset)
  javascript modules 145 KiB
    modules by path ./node_modules/react-dom/ 131 KiB 3 modules
    modules by path ./node_modules/react/ 6.94 KiB 2 modules
    modules by path ./node_modules/scheduler/ 4.33 KiB 2 modules
    ./src/web/index.tsx + 3 modules 3.07 KiB [built] [code generated]
  modules by path ./src/web/*.css 1.76 KiB
    css ./node_modules/css-loader/dist/cjs.js!./src/web/index.css 49 bytes [built] [code generated]
    css ./node_modules/css-loader/dist/cjs.js!./src/web/App.css 1.71 KiB [built] [code generated]
  asset modules 84 bytes (javascript) 344 KiB (asset)
    ./src/web/assets/defaultIcon.png 42 bytes (javascript) 10.1 KiB (asset) [built] [code generated]
    ./src/web/assets/favicon.ico 42 bytes (javascript) 334 KiB (asset) [built] [code generated]
webpack 5.73.0 compiled successfully in 8573 ms
error
> electron .

$Started
App threw an error during load
TypeError: h.Server is not a constructor
    at 3768 (E:\app\dist\main.js:14:132270)
    at s (E:\app\dist\main.js:14:372679)
    at E:\app\dist\main.js:14:373016
    at Object.<anonymous> (E:\app\dist\main.js:14:373025)
    at Module._compile (node:internal/modules/cjs/loader:1118:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1173:10)
    at Module.load (node:internal/modules/cjs/loader:988:32)
    at Module._load (node:internal/modules/cjs/loader:829:12)
    at c._load (node:electron/js2c/asar_bundle:5:13343)
    at loadApplicationPackage (C:\Users\---\AppData\Roaming\npm\node_modules\electron\dist\resources\default_app.asar\main.js:110:16)
はっぱはっぱ

TypeError: h.Server is not a constructor
at 3768 (E:\app\dist\main.js:14:132270)

とのメッセージなので、メインプロセスのコードに誤りがあるのではないでしょうか。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Errors/Not_a_constructor

メインプロセスのデバッグについては、この記事で作成したプロジェクトをベースにしてつばめさんが↓の記事を執筆しておられます。

https://zenn.dev/tubame/articles/cd5f66c68b46e7

HirotoHiroto

React + Electron + TypeScriptの構成での開発環境の構築に手間取っていたのでものすごく助かりました!素敵な記事とライブラリをありがとうございます!

はっぱはっぱ

@Hiroto さん
コメントとバッジをありがとうございます。とても励みになります。