Electron + Nuxt.js + TypeScriptの環境構築

## はじめに

Electron + Nuxt.js + TypeScriptの環境を作る必要があったのですが、様々な問題から1から構築した方が良さそうと思ったので、自分で構築してみました。
もし、同じような状況にある人がいればその助けになればいいかなと思い、記事にしてみました！

完成品のリポジトリは[こちら](https://github.com/282Haniwa/nuxt-electron-example)ですので、見ながらもしくは、これをcloneして使っていただいても大丈夫です！

## Nuxt.js + TypeScriptのプロジェクトを作る

`create-nuxt-app`を利用して、プロジェクトを作ります。

```shell-session
$ yarn create nuxt-app nuxt-electron-example
...
create-nuxt-app v3.3.0
✨  Generating Nuxt.js project in nuxt-electron-test
? Project name: nuxt-electron-test
? Programming language: TypeScript
? Package manager: Yarn
? UI framework: Vuetify.js
? Nuxt.js modules: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Linting tools: ESLint, Prettier, Lint staged files, StyleLint
? Testing framework: Jest
? Rendering mode: Single Page App
? Deployment target: Static (Static/JAMStack hosting)
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Version control system: Git
```

今回は、上の通りにプロジェクトを構築している前提で進めていきますが、自分の環境でお好みの設定にしながら読み進めてください。

## Electronの対応をする

Electronを導入して、ElectronのrendererでNuxt.jsが動くように設定していきます。

### ディレクトリ構成を変更する

最終的に以下のようなディレクトリ構成になります。

```
.
├── README.md
├── build
├── dist
├── electron-builder.yml
├── package.json
├── src
│   ├── main
│   └── renderer
└── yarn.lock
```

先ほど生成したNuxt.jsの必要なコードを`src/renderer`のなかに移動します。
移動した後は、以下のようになります。

```
src/renderer
├── assets
├── components
├── layouts
├── middleware
├── node_modules
├── nuxt.config.js
├── pages
├── plugins
├── static
├── store
├── test
└── tsconfig.json
```

### コンフィグを書き換える

この状態でNuxt.jsのプロジェクトを普通に実行できるようにコンフィグを書き換えていきます。

このあと使っていくのはこの二つのスクリプトのみなので、nuxtのbuildなどに関するscriptはこの二つだけにします。

```json:package.json
...
"dev:renderer": "nuxt-ts src/renderer",
"build:renderer": "nuxt-ts generate src/renderer",
...
```

この状態で`yarn dev:renderer`を実行して、Nuxt.jsのプロジェクトが正しく実行できることを確認します。

ついでに、jestの設定も書き換えておきましょう。

スクリプトを貼ると長くなるのでリンクを置いておきます。
[jest.config.js](https://github.com/282Haniwa/nuxt-electron-example/blob/master/jest.config.js)

### Electronで動かす

次に実際にElectronで動かせるようにしていきましょう。

#### Electronとそれに関連する必要なパッケージをインストールする

```shell
$ yarn add --dev electron electron-builder electron-devtools-installer
```

ElectronのmainプロセスのスクリプトもTypeScriptで書きたいのでtypescriptをインストールします。

```shell
$ yarn add --dev typescript
```

build時のコンフィグを変えたいのでcross-envをインストールします。

```shell
$ yarn add --dev cross-env
```

#### Electronの実行に必要な設定をする

Electronを実行できるように、`src/main/index.ts`を書きます。
後々必要になってくると思うので、`src/main/preload.ts`も空ファイルのままで生成して設定しておきます。

スクリプトを貼ると長くなるのでリンクを置いておきます。
[src/main/index.ts](https://github.com/282Haniwa/nuxt-electron-example/blob/master/src/main/index.ts)

※ preload.tsには、electronのmainプロセスからrendererプロセスにオブジェクトを渡したりするために使ったりします。
例) [ElectronでcontextBridgeによる安全なIPC通信](https://qiita.com/pochman/items/64b34e9827866664d436)

TypeSciprtで書いたのでコンパイル用に`tsconfig.json`を生成しておきます。
[src/main/tsconfig.json](https://github.com/282Haniwa/nuxt-electron-example/blob/master/src/main/tsconfig.json)

ポイントとしては、以下の3点です。

```
"target": "es5"
"module": "commonjs"
"outDir": "../../dist"
```

Electronで実行するので、`target`に`es5`と`module`に`commonjs`を設定しています。
また、`outDir`を`../../dist`にしておくことで、ルートの`dist`ディレクトリにコンパイルされたものが出力されるようになっています。

#### ElectronでNuxt.jsを使うための設定をする

次に、Nuxt.jsの設定を書き換えていきます。
Nuxt.jsをアプリに向けてビルドするためにいくつかの設定を書き足します。

```js:nuxt.config.js
const isProduction = process.env.NODE_ENV === 'production'
const isDev = process.env.NODE_ENV === 'development'

export default {
  ...
  dev: isDev,
  router: {
    mode: 'hash',
  },
  build: {
    extend(config) {
      if (!isDev) {
        // absolute path to files on production (default value: '/_nuxt/')
        config.output.publicPath = '_nuxt/'
      }
      config.node = {
        __dirname: !isProduction,
        __filename: !isProduction,
      }
    },
  },
  generate: {
    dir: '../../dist/nuxt-build',
  },
  telemetry: false,
}
```

上のように記述を足してください。
特に重要な部分は
`publicPath`とrouterの`mode: 'hash'`です。
Electronではrouterのモードが`hash`でないと動かないのと、publicPathを相対パスにしておかないと、ビルドしたjsなどのファイルを読み込むことができません。
後の設定はお好みですが、今回は全ての出力をルートディレクトリの`dist`に出力するために設定しています。

細かい設定は[src/renderer/nuxt.config.js](https://github.com/282Haniwa/nuxt-electron-example/blob/master/src/renderer/nuxt.config.js)を参考にしてください。

**※ この時に`nuxt.config.js`でimportしているパッケージがあれば注意してください。**
例) `import colors from 'vuetify/es5/util/colors'`など

Electronがそれらのパッケージをバインドできるように、`dependencies`に追加しておく必要があります。
例) `yarn add vuetify`

その他にいい方法があれば、コメントで教えてください。

#### Electronのビルド用コンフィグを作成する

下のリンクを参考にして作成してください。
適宜必要な部分を書き換えてください。
[electron-builder.yml](https://github.com/282Haniwa/nuxt-electron-example/blob/master/electron-builder.yml)

#### package.jsonのscriptsを整理する

先に、Electronを実行するために、package.jsonの`main`を設定しなければいけないので、`dist/main/index.js`を実行するようにしておきましょう。

```json:package.json
{
  "name": "nuxt-electron-example",
  "version": "1.0.0",
  "private": true,
  "main": "dist/main/index.js",
  ...
}
```

下のリンクを参考にして作成してください。
[package.json](https://github.com/282Haniwa/nuxt-electron-example/blob/master/package.json)

一部パッケージを追加しています。

```shell-session
$ yarn add --dev npm-run-all rimraf
```

### 動くか確認する

`yarn dev`や`yarn build`などのコマンドを実行して、きちんと意図した通りに動くか確認します。

## まとめ

最後まで読んでいただき、ありがとうございました!

かなり試行錯誤しながら環境構築を行ったので、その備忘録として残しておこうと思いました。

もっといい方法があるよ！って方は、コメント欄で教えていただけると嬉しいです！

## 参考

- 参考にさせていただいたリポジトリ → [electron-nuxt](https://github.com/michalzaq12/electron-nuxt)
- [roure modeをhashに設定する理由](https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/commonIssues.html#blank-screen-on-builds-but-works-fine-on-serve)
- [ElectronでcontextBridgeによる安全なIPC通信](https://qiita.com/pochman/items/64b34e9827866664d436)