Angular-CLI6でElectronしたいのに・・・

# はじめに
以前に「[@angular/cliでElectron](https://qiita.com/lightstaff/items/2542e49f9d2d889a6ce0)」というのを書いたりしたんですが、久しぶりにAngualrとElectronで書くかぁって思ったら、最新のAngular-CLI6には`ng eject`がなくてwebpackがいじれないじゃないですか。`angular.json`にも設定できないし・・・。

そいで調べてみたところ基本的にCLIの`builder`を拡張しなきゃいけないみたいじゃないですか。
ただ`target: 'electron-renderer`の一文を入れたいだけなのに・・・。

というわけでとりあえずやってみます。

## 参考
- https://medium.com/dailyjs/angular-cli-6-under-the-hood-builders-demystified-f0690ebcf01
- https://stackoverflow.com/questions/51069290/angular-cli-6-custom-builder

ちなみにすでにnpm上に[ng-electron-devkit](https://www.npmjs.com/package/ng-electron-devkit)なんていうのがあったりします。
あと[maximegris/angular-electron](https://github.com/maximegris/angular-electron)という実行時にbuilderを書き換えるという力業で解決しているテンプレートもあります（ぶっちゃけこっちのが楽なのでおすすめです）。

## 環境

- @angular-devkit/architect: 0.7.4
- @angular-devkit/build-angular: 0.7.4
- @angular-devkit/core: 0.7.4
- rxjs: 6.2.2
- typescript: 3.0.1

## 超省略的説明

- devkit（CLIの中身）内のBuilderを拡張してElectron用Buidlerを定義します。
- npmパッケージ化します。
- 使いたいプロジェクトにそのパッケージをインストールします。
- `angular.json`に作ったBuilderを使うように定義します。

# Builderを拡張して定義する

## パッケージの準備

まずはBuilder用パッケージをつくります。

```console
$: mk パッケージ名
$: cd パッケージ名
$: npm init
$: npm install --save @angular-devkit/architect @angular-devkit/build-angular @angular-devkit/core @types/node rxjs typescript
```

以下の作業は全てルートディレクトリで行います。

## Schemaを準備
Buidlerにはスキーマ定義が必要です。これは`angular.json`上のoption等を解決するためのスキーマです。
今回は一応色々な`target`を指定できるように`angular.json`でのオプションとして準備します。
devkit内に`schema.json`ファイル（ https://github.com/angular/angular-cli/blob/master/packages/angular_devkit/build_angular/src/dev-server/schema.json ）があるのでコピーするなりして持ってきて、idを変更したり、オプションを追加します（`webpackCompileTarget`としています）。

```json:schema.json
{
  "id": "ElectronBrowserSchema",
  "title": "Electron browser schema for Build Facade",
  "description": "Electron browser target options",
  "properties": {
    ====省略====
    "webpackCompileTarget": {
      "type": "string",
      "description": "webpack compile target",
      "default": "browser"
    }
  },
  ====省略====
}
```

上記で設定したスキーマを元にInterfaceを用意します。

```typescript:schema.d.ts
import { NormalizedBrowserBuilderSchema } from '@angular-devkit/build-angular';

export interface ElectronBrowserSchema extends NormalizedBrowserBuilderSchema   {
  webpackCompileTarget: string;
}
```

## Buidlerを拡張
devkit内の`DevServerBuilder`を拡張します。

```typescript:electron-dev-server.ts
import { BuilderConfiguration, BuilderContext, BuildEvent } from '@angular-devkit/architect';
import { BrowserBuilder, BrowserBuilderSchema, NormalizedBrowserBuilderSchema } from '@angular-devkit/build-angular';
import { Path, virtualFs } from '@angular-devkit/core';
import { Observable } from 'rxjs';
import { Stats } from 'fs';
import { ElectronBrowserSchema } from './schema';

export class ElectronBrowserBuilder extends BrowserBuilder {

  constructor(context: BuilderContext) {
    super(context);
  }

  run(builderConfig: BuilderConfiguration<BrowserBuilderSchema>): Observable<BuildEvent> {
    return super.run(builderConfig);
  }

  buildWebpackConfig(
    root: Path,
    projectRoot: Path,
    host: virtualFs.Host<Stats>,
    options: ElectronBrowserSchema
  ) {
    const browserConfig = super.buildWebpackConfig(root, projectRoot, host, options);
    return Object.assign({}, browserConfig, {
      target: options.webpackCompileTarget
    });
  }
}

export default ElectronBrowserBuilder;
```

まじでwebpackの設定の`target`を書き換えてるだけです。

## buidlers.json
ビルダーの設定ファイルを書きます（ファイル名は何でもいいみたいです）。

```json:builders.json
{
  "$schema": "@angular-devkit/architect/src/builders-schema.json",
  "builders": {
    "dev-server": {
      "class": "./electron-browser.js",
      "schema": "./schema.json",
      "description": "Electron browser builder"
    }
}
```

`"$schema": "@angular-devkit/architect/src/builders-schema.json"`はおまじないだと思えばいいじゃないでしょうか。
後はそのまんま各定義にファイルを割り当ててるだけです。

## npm buildとか
`package.json`に`"scripts": {"build": "tsc"}"`と追加してBuild出来るようにしておきます。
あわせて`"builders": "builders.json"`を追加し、呼び出し元がBuidlerを見つかられるようにしておきます。

とりあえずBuilderのパッケージは完成です。

# 使い方
上記で作ったBuilderをローカルパッケージ化するなり、npmに上げるなりして使用するプロジェクトで`npm install --save-dev Builderパッケージ名`します。
それから`angular.json`を編集します。
今回は通常の`build`設定の`builder`を変更し、`webpackCompileTarget`を追加します。

```json:angular.json
{
=====省略=====
  "projects": {
    "プロジェクト名": {
      =====省略=====
      "architect": {
        "build": {
          "builder": "Builderパッケージ名:browser",
          "options": {
            "outputPath": "dist/プロジェクト名",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.app.json",
            "assets": [
              "src/favicon.ico",
              "src/assets"
            ],
            "styles": [
              "src/styles.scss"
            ],
            "scripts": [],
            "webpackCompileTarget": "electron-renderer"
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true
            }
          }
        }
      }
      =====省略=====
    }
  }
=====省略=====
}

```

# おわりに
DevServerが欲しい場合は同じ要領で`DevServerBuilder`を拡張してやればよいです。
CLIのBuilder自体よく分かってないのでほんとにこれでいいのか感があったりなかったり・・・。
いやほんと`target: electron-renderer`したいだけなのにこの手間はどうかと思う。