Electron+Vue.js+NestJS+TypeScriptでデスクトップアプリのひな型を作る。

# 作りたいもの
画面はVue.js、ロジック処理はNestJSが担当し、それら2つをElectronでデスクトップアプリとして起動できる事をゴールとします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587410/52b011af-750a-da25-1c99-0ceaec0fd9f7.png)


# プロジェクト構造

    project/
    　├ frontend/
      │     └ Vue.jsで構築
    　└ backend/
            └ NestJSで構築

# 1. NestJSの立ち上げ
## 1-1. NestJSのインストール

    $ npm install @nestjs/cli -g

## 1-2. NestJSプロジェクトの立ち上げ
次にproject直下でNestJSプロジェクト作成コマンドを実行。

    $ nest new backend

パッケージマネージャの好みを聞かれるのでyarnを選択。

    ? Which package manager would you ❤️  to use? (Use arrow keys)
      npm
    > yarn
      pnpm

## 1-3. NestJSの起動確認
ファイルが生成されたら起動してみる。

    $ cd backend
    $ yarn run start

立ち上がったらブラウザで「`http://localhost:3000/`」にアクセスして下記画面が出れば成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587410/d03db4ad-6697-7cf9-4662-a58d6cd9848a.png)

# 2. Electronの追加
## 2-1. Electronのインストール
`project/backend`直下で下記コマンドを実行。

    $ yarn add electron

## 2-2. NestJSにElectron実行処理を組み込み
`project/backend/src/main.ts`にElectron起動処理を追記します。

```typescript:main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();

// ========= 以下を追記 =========

// Electronのモジュール
const electron = require('electron')

// アプリケーションをコントロールするモジュール
const app = electron.app

// ウィンドウを作成するモジュール
const BrowserWindow = electron.BrowserWindow

// メインウィンドウはGCされないようにグローバル宣言
let mainWindow

// 全てのウィンドウが閉じたら終了
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

// Electronの初期化完了後に実行
app.on('ready', () => {
  // メイン画面の表示。ウィンドウの幅、高さを指定できる
  mainWindow = new BrowserWindow({ width: 1200, height: 720 })
  mainWindow.loadURL('http://localhost:3000/')

  // ウィンドウが閉じられたらアプリも終了
  mainWindow.on('closed', () => {
    mainWindow = null
  })
})
```

## 2-3. package.jsonの編集

```json:package.json
{
  "name": "backend",
  "version": "0.0.1",
  "main": "./dist/src/main.js", ☆追加 ※1
  "description": "",
  "author": "",
  "private": true,
  "license": "UNLICENSED",
  "scripts": {
    "prebuild": "rimraf dist",
    "build": "nest build",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "start": "tsc && npx electron .", ☆更新 ※2
    "start:dev": "nest start --watch",
```
※1 : TypeScript→JavaScriptへトランスコンパイルされたファイルは/distに出力されているので、Electronの指定も/dist/src配下に出力されるjsを指定します。
※2 : `$ yarn run start`一発でトランスコンパイルとElectronの実行をして欲しいので書き換えてます。

## 2-4. Electronの起動確認

    $ cd backend
    $ yarn run start

下記ウィンドウが表示されれば成功です。(表示されてるHello World!はNestJSのモノです)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587410/6cbec8db-05ef-64e8-7516-e22f2f34abbb.png)

# 3. Vue.jsの立ち上げ
## 3-1. Vue.jsのインストール

    $ npm install -g @vue/cli

## 3-2. Vue.jsプロジェクトの立ち上げ
次にproject直下でVue.jsプロジェクト作成コマンドを実行

    $ vue create frontend

プリセット選べと言われるのでVue3を選択

    ? Please pick a preset:
      Default ([Vue 2] babel, eslint)
    > Default (Vue 3) ([Vue 3] babel, eslint) 
      Manually select features

パッケージマネージャの好みを聞かれるのでyarnを選択。

    ? Pick the package manager to use when installing dependencies: (Use arrow keys)
    > Use Yarn
      Use NPM

## 3-3. Vue.jsの起動確認

ファイルが生成されたら起動してみる。

    $ cd frontend
    $ yarn serve

立ち上がったらブラウザで「`http://localhost:8080/`」にアクセスして下記画面が出れば成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587410/13239dbc-58ce-6faa-aaf5-255e2e4ce075.png)

# 4. Vue.jsの組み込み
## 4-1. NestJSにhtml格納先ディレクトリを追加

    project/
    　└ backend/
          └ public ☆作成

## 4-2. Vue.jsのbuild出力先をNestJSに切り替え
`project/frontend/vue.config.js`を作成し、下記内容を追加します。

```javascript:vue.config.js
module.exports = {
  outputDir:'../backend/public',
}
```
下記コマンドを実行して`project/backend/public`にindex.htmlなどが出力されれば成功です。

    $ cd frontend
    $ yarn build

## 4-3. NestJSにhtml読み込み定義を追加
`project/backend/src/main.ts`に以下処理を追記します。

```typescript:main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import {NestExpressApplication} from '@nestjs/platform-express'; // 追加
import {join} from 'path'; // 追加

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule); // 編集
  app.useStaticAssets(join(__dirname, '../..', 'public')) // 追加 ※1
  await app.listen(3000);
}
bootstrap();
```
※1 トランスコンパイル後の`/dist/src/main.js`から見たpublicへの相対パスを設定します。

## 4-4. Electron + NestJS + Vue.js組み合わせ起動確認

    $ cd backend
    $ yarn run start

下記ウィンドウが表示されれば成功です。
ウィンドウはElectron・表示内容はVue.js・表示内容を返すNestJSとなっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587410/463645f7-f005-2eb0-714f-b0868ea10fe7.png)

# 5. デスクトップアプリとしてビルド
## 5-1. electron-builder のインストール

    $ cd backend
    $ yarn add electron-builder --dev

## 5-2. package.jsonの編集

package.jsonにbuildの定義を追加します。
またdevDependenciesにelectronの定義を移動します。

```json:package.json
{
  "name": "backend",
  "version": "0.0.1",
  "main": "dist/src/main.js",
  "description": "",
  "author": "",
  "private": true,
  "license": "UNLICENSED",
  "scripts": {
    ~~
    "build:exe": "electron-builder" // 追加
    ~~
  "build": { // 追加
    "appId": "com.example.myapp",
    "productName": "MyApp", // ※1
    "win": {
      "target": {
        "target": "zip",
        "arch": [
          "x64"
        ]
      }
    },
    "files": [ // ※2
      "dist/**/*",
      "public/**/*"
    ]
  },
  "devDependencies": {
    "electron": "^16.0.2", // dependenciesから移動
    "electron-builder": "^22.14.5",
```
※1 ビルド後のアプリ名です。(～.exeになります)
※2 NestJSのソースはdist/、Vue.jsのデータはpublic/配下にあるので、この2つを取り込む設定をします。


## 5-3. ビルド実行

    $ cd backend
    $ yarn run build:exe

ビルドが完了すると`project/backend/dist/win-unpacked`に実行ファイルが出力されます。
起動してみるとデスクトップアプリとして下記の様に実行されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587410/0318f52c-4503-762f-2d68-9ff113fc7117.png)


# 参考文献
https://qiita.com/rxoxixyxd/items/7f31ddb715e2ab77f921