【2020年1月】ラズパイ4のNode-REDにAngular Elements差し込んでHugoからデプロイ

# 昨年作ったDJサービス、スッピン過ぎて辛い

昨年のアドベントカレンダーにて作った、音声認識APIを使った"DJに声でリクエストする"サービスですが・・・

![DjRequestApp (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/414fdeb3-12f1-711b-42ba-1b8c786f3b04.png)

・・・はい、デザインまったくしてないスッピンのHTMLでございます。
せっかくのDJリクエストフォームなのにもうちょとなんとかならないかと…
これに**テーマ的なモノをビシッと嵌め込んで**もうちょいイケてるサイトにならないもんかと考えておりました。

しかしAngularは基本的にSPAを作成するフレームワークなので、"テーマ的なモノ"＝ボイラープレートといえばページやパーツといった"コンポーネント群"を大量に投入することになります。
う〜ん、いや、そう言う大掛かりなことじゃなくて、もっとこうサクッとハメ込めるようにならないもんかな…と。

また別の方面で"テーマをサクッと当てられる"、と言えば静的サイトジェネレーター(Static Site Generator = SSG)ですが良さげなSSGもいろいろ探ってみてました。
Jekyll、Gatsby、Hugo ・・・最近のイケてるサービスはHugoかなぁ、Hugoのサイトの一部にAngualr混ぜるかHugoもPWA対応してくれないかとか…いろいろ探っていると…

- [Hugo のような静的なコンテンツを扱うフレームワークを利用している際に部分的に動的なガジェットが欲しくなることがあります。 
Angular Elements で作ったアプリケーションを Hugo に埋め込む方法をまとめます。 - nananao blog](https://nananao-dev.hatenablog.com/entry/angular-elements-dialog-embed-hudo)

**"静的なコンテンツに部分的にAngularを埋め込む方法"**だとっ？！

**Angular Elements、これじゃないか。**

と言うわけで、"ラズパイ4に乗っているNode-REDにAngular Elementsのコンポーネントが差し込まれたHugoで生成されたHTMLをデプロイする手順"、行ってみたいと思います。~~いつも通りてんこ盛りです。~~

## 対象環境

OS: Raspbian Buster 4.19
Node-RED: 1.0.3
Angular: 8.2.14
Hugo: 0.62.1 linux/arm

Angular や Hugo も全てラズパイ4上にて環境を構築してます。
作業はVSCodeのRemote SSH経由にてラズパイ上で行っております。

## 1. Hugo のインストール

Hugoをインストールするにはいろいろ方法がありますが、今後のことも考えて`snap`からインストールしてみましょう。
と言うわけで、snapから行きます。

### 1-1. snap のインストール

以下のsnap公式サイトの案内に従ってインストールを行います。

- [Installing snap on Raspbian](https://snapcraft.io/docs/installing-snap-on-raspbian)

とは言えコマンドは簡単です。aptから`snapd`をインストールするだけです。

```
$ sudo apt update
$ sudo apt install snapd
```
ラズパイ4に`snap`をインストールするのは以上です。

### 1-2. Hugo のインストール

SnapからHugoのインストールを行います。

- [Install Hugo for Linux using the Snap Store | Snapcraft](https://snapcraft.io/hugo)

```shell
$ sudo snap install hugo
2019-12-30T12:58:33Z INFO Waiting for restart...
hugo 0.61.0 from Hugo Authors installed
```

### 1-3. Hugo の調整？

Hugoのインストールは上記で完了なのですが、`hugo`コマンドを叩くと妙なエラー？が出ます。

```shell
$ hugo new site quickstart
ERROR: ld.so: object '/usr/lib/arm-linux-gnueabihf/libarmmem-${PLATFORM}.so' from /etc/ld.so.preload cannot be preloaded (cannot open shared object file): ignored.
ERROR: ld.so: object '/usr/lib/arm-linux-gnueabihf/libarmmem-${PLATFORM}.so' from /etc/ld.so.preload cannot be preloaded (cannot open shared object file): ignored.
Congratulations! Your new Hugo site is created in /home/pi/quickstart.
...
```
うーん、エラーメッセージが出ています。しかしhugoではちゃんとプロジェクトが生成できている模様です。ちゃんと生成できているのかよくわからないので気になりますね・・・
ちょっと調べてみると１件だけビンゴなPOSTが。

- [Install Hugo on Raspberry Pi 4](https://bick.xyz/archives/1444)

`/etc/ld.so.preload` の `/usr/lib/arm-linux-gnueabihf/libarmmem-${PLATFORM}.so` を `#` でコメントアウトしただけ、の模様です。(そのまんまかい。。。)

```text:/etc/ld.so.preload
#/usr/lib/arm-linux-gnueabihf/libarmmem-${PLATFORM}.so
```

これでとりあえずエラーは出なくなりましたが、これで良いのかな・・・？
と、とりあえずこれで進めたいと思います。

## 2. Hugo でプロジェクト作成

気を取り直してHugoのサンプルプロジェクトを作成してみましょう。

- [Quick Start | Hugo](https://gohugo.io/getting-started/quick-start/)

```
$ hugo new site quickstart
...
$ cd quickstart
$ git init
$ git submodule add https://github.com/budparr/gohugo-theme-ananke.git themes/ananke
```
最初のテーマとして`ananke`を追加してみました。(チュートリアルまんまです。。。)

で、設定ファイルに`ananke`を追記しましょう。

```toml:config.toml
...
theme = "ananke"
```
さらにチュートリアルに従ってブログの記事を追加してみます。

```shell
$ hugo new posts/my-first-post.md
```
ここで一旦、hugoの開発サーバーを起動してみます。

```shell
$ hugo server -D
```
ブラウザで`localhost:1313`を開いてみましょう。

![My New Hugo Site.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/5a6fe610-3b9b-9121-04b8-7bae28e81dc9.png)

はい、hugoの準備は完了です！

## 3. Angular を Angular Element 対応にする

今回のメインエベントのAngular Elementですが、ざっくり言うと以下のような感じです。

> Angular Elements は、 Web Components としてパッケージ化される Angular コンポーネントであり、Web Components は、フレームワークに依存しない形で新たな HTML 要素を定義するウェブ標準技術です

とにかく、Angularで定義したコンポーネントタグがそのまま使えるってことです。

詳しくは以下をご覧ください。

- [angular-elements概要](https://angular.jp/guide/elements)

冒頭で紹介した[Angular Elements で作ったアプリケーションを Hugo に埋め込む方法](https://nananao-dev.hatenablog.com/entry/angular-elements-dialog-embed-hudo)の記事を参考に、[前回までの記事で作成したAngularプロジェクト](https://qiita.com/koinori/items/4ed65834821497ef3658#3-angular-%E3%81%AE%E5%AE%9F%E8%A3%85)を Angular Elementsに対応させたいと思います。

ng コマンドで `@angular/elements`プラグインを追加します。

```shell
$ ng add @angular/elements
...
```
実は、以上で終了です！

今回は`AppComponent`をそのまま使いたいと思いますので、`<app-root></app-root>` をそのまま利用します。
Angularはデフォルトで`app-root`タグを起点とするので設定の変更などはしなくてOKです。設定ファイルはいじらずに、このままjsの生成だけ行ってしまいます。

```
$ ng build --prod --output-hashing=none
...
```
で、

> Angular8 から es5 と es2015 で別ファイルになっています。
なので以下のように package.json のスクリプトで es5 と es2015 それぞれ単一の jsファイルにまとめています。

とのことなので、distに出力された js を単一の js にします。
このAngularプロジェクトはNode-REDのstaticサイトに直接デプロイしておりますので、ちょっとややこしいですがコマンドは以下のようになりました。

```shell
$ cat ~/node-red-static/DjRequestApp/{runtime-es5,polyfills-es5,scripts,main-es5}.js > ~/node-red-static/DjRequestApp/DjRequestApp-es5.js
$ cat ~/node-red-static/DjRequestApp/{runtime-es2015,polyfills-es2015,scripts,main-es2015}.js > ~/node-red-static/DjRequestApp/DjRequestApp-es2015.js
```
ここで単一のjsとなった`DjRequestApp-es5.js`と`DjRequestApp-es2015.js`がhugoの方で使用するjsファイルとなります。

## 4. Hugoへの移植

### 4-1. js,css のコピー

さて、hugoに埋め込む手順に従って、hugoで作成した"quickstart"プロジェクトにAnuglarで生成したjsとstyle.cssをhugoにコピーします。
ディレクトリの構造は以下のようにします。

```
static
├── css
│   └── styles.css
└── js
    ├── DjRequestApp-es2015.js
    └── DjRequestApp-es5.js
```

### 4-2. HTMLでの読み込み、その１

hugoで設定したテーマのテンプレートを上書きして、上記の jsとcss を読み込んでみたいと思います。

まず`quickstart/themes/ananke/layouts/partials/site-header.html` を`quickstart/layouts/partials/site-scripts.html` にコピーします。
これが各ページの<head>タグで読み込まれますので、ここに`style`タグと`script`タグを追加します。
以下のようになるかと思います。

```html:site-scripts.html
{{ $script := .Site.Data.webpack_assets.app }}
{{ with $script.js }}
  <script src="{{ relURL (printf "%s%s" "dist/" .) }}"></script>
{{ end }}
<script type="module" src="{{ .Site.BaseURL }}/js/DjRequestApp-es2015.js"></script>
<script src="{{ .Site.BaseURL }}/js/DjRequestApp-es5.js" nomodule defer></script>

<link rel="stylesheet" href="{{ .Site.BaseURL }}/css/styles.css">
```
最初の３行の`$script.js` でループしているあたりがオリジナルのコードです。それ以下が今回追加したコードとなります。

### 4-3. HTMLでの読み込み、その２

続いていよいよ本体のテンプレートHTMLに`<app-root>`を追加します。ヘッダーの時と同様に、テーマの`index.html`を上書きします。

まず、`quickstart/themes/ananke/layouts/index.html`を`quickstart/layouts/index.html` にコピーします。

続いて、index.htmlに以下のように`<app-root>`タグを追加します。

```html:index.html
...
...
        {{/* As above, Use $section_name to get the section title, and URL. Use "with" to only show it if it exists */}}
        {{ with .Site.GetPage "section" $section_name }}
          <a href="{{ .Permalink }}" class="link db f6 pa2 br3 bg-mid-gray white dim w4 tc">{{ i18n "allTitle" . }}</a>
        {{ end }}
        </section>
      {{ end }}

      </div>
  {{ end }}
  <div class="pa3 pa4-ns w-100 w-70-ns center">
    <app-root></app-root>
  </div>
{{ end }}
```
位置としては、ブログ記事の一覧を出力したブロックの後に続けて同様のスタイルで`div`を追加し、その中に`app-root`タグを追加しています。
作業としては修正作業としては以上ですが、果たしてこれで本当にAngularのコンポーネント=音声認識APIが動くのでしょうか・・・？

### 4-4. Hugo の出力先を変更

hugoはデフォルトで`public`フォルダに生成されたHTMLなどが配置されます。hugoで生成してからpublicからコピーするのはめんどいので、node-redのstaticフォルダに直接、デプロイさせてみましょう。
hugoのtomlに以下のように追加します。

```toml:config.toml
...
publishDir = "../node-red-static/DjReqChannel"
```
`DjReqChannel`は適当です。AngularのAppとぶつからなければOKです。

### 4-5. ベースアドレスの変更とサイトの生成

ついでに、hugoサイトの起点となるアドレスも指定してしまいましょう。
baseURLを変更します。
設定ファイルの config.tomlは以下のようになりました。

```toml:config.toml
baseURL = "https://raspberrypi.local:1880/DjReqChannel"
languageCode = "en-us"
title = "My New Hugo Site"
theme = "ananke"

publishDir = "../node-red-static/DjReqChannel"
```
今回はlocalhost でなくて`raspberrypi.local`を使ってみました。

そしてhugoコマンドでHTMLを生成すれば出来上がりです！

```shell
$ hugo -D
```

## 5. 動作確認

いや、実はここからが長かったのですよ。。。
この記事とは本質的に関係ないのですが、いろいろありまして、お陰様で記事２本[^1][^2]も書けました。笑
まぁ、カクカクシカジカがございましてやっと表示できたのが以下のスクショです。

![My New Hugo Site (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/b2679c98-97c7-9381-9cb5-e8c41c120415.png)

"ハッピーニューイヤー"とマイクに呟いたところです。音声認識APIもちゃんと動いていますよ！
~~(シンプルなテーマのお陰で元のスッピンHTMLとあまり変化がない。。。)~~
で、Node-REDのデバッグログにもちゃんと出てきております。

![Node-RED   raspberrypi local (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/335150/4bc67fda-2230-d3de-6462-0dbacfa13c45.png)

ふぅ。。。動いてよかった。。~~(音声認識APIがHTTPS必須でなければこんな苦労はしなかった。。。)~~

## まとめ

Angular Elementsの埋め込みはとっても簡単ですが、今回は`app-root`そのまま使ったのでほとんど何もしなくてOKでした。
が、通常(?)は複数のコンポーネントをエクスポートしてサイトに埋め込むことになると思います。
複数コンポーネントの出力もできるのですが、app.module.tsでいろいろ追記する必要があるらしいので、現時点では正直、実用的ではないかなぁ・・・とも思います。
今回のように本当にワンポイントで、単体の動的なガジェットを埋め込む用途であれば(Angularのプロジェクトをそのように作れれば)、面白いサービスが作れるのではないかと思いました。

[^1]: [【2020年1月】新年明けたらMacに"Zsh使うからな！"って言われた。](https://qiita.com/koinori/items/c9ade76fa7360c3ac6cc)
[^2]: [【2020年1月】ラズパイ4のSSLがChromeで"ERR_CERT_REVOKED"につきアクセスできなくなりまして](https://qiita.com/koinori/items/c8938f6ef01a3b54ee40)