フロントエンドにElmを使ったアプリをAmplify Hostingで配信する

AmplifyのおかげでWebアプリを爆速でデプロイ＆公開できるようになりましたが、フロントエンドにElmを使った例が意外と少ないような気がします。
というわけで設定の仕方を共有したいと思います。
# ざっくりとした手順
1. ローカルの開発環境で作業：npmやyarnでビルドできる環境を作る
1. [AmplifyのWebコンソール](https://ap-northeast-1.console.aws.amazon.com/amplify/home)で作業：「アプリの設定: ビルドの設定」→「アプリケーション構築の仕様」でAmplify上でのデプロイを設定
1. AmplifyのWebコンソールで作業：「アプリの設定: リライトとリダイレクト」でSPA（Single Page Application）向けの設定を追加


# ローカルの開発環境で作業：npmやyarnでビルドできる環境を作る
ここではyarnを使う想定で書きます。
フロントエンドをElmで構築したアプリをビルドするにはparcelがすごく便利です。


``` yarn add -D parcel ```

parcelを使うと、HTMLから直接Elmファイルを参照する書き方ができます。

```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <title>フロントエンドにElmを使ったアプリをAmplify Hostingで配信する</title>
</head>
<body>
    <script src="./Index.elm"></script>
</body>
</html>
```

ただ、これだとElmプログラムにaws-exports.jsの内容を渡すことが出来ません。
なので、次のようにスクリプトを書きます。

```html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>フロントエンドにElmを使ったアプリをAmplify Hostingで配信する</title>
</head>
<body>
    <main id="main">
        <p>loading...</p>
    </main>
    <script type="module">
        import awsExports from "./aws-exports";
        import { Elm } from "./Index.elm";
        Elm.Index.init({
            node: document.getElementById("main"),
            flags: {
                awsExports
            }
        });
    </script>
</body>
</html>
```

HTMLファイルに直接スクリプトを書くのはカッコ悪い気もしますが、配信には有利かもしれません。

Index.elmのサンプルも載せておきます。

```elm
module Index exposing (..)

import Browser
import Html exposing (..)
import Html.Events exposing (..)


main : Program () Model Msg
main =
    Browser.sandbox { init = 0, update = update, view = view }


type alias Model =
    Int


type Msg
    = Increment
    | Decrement


update : Msg -> Model -> Model
update msg model =
    case msg of
        Increment ->
            model + 1

        Decrement ->
            model - 1


view : Model -> Html Msg
view model =
    div []
        [ button [ onClick Decrement ] [ text "-" ]
        , div [] [ text (String.fromInt model) ]
        , button [ onClick Increment ] [ text "+" ]
        ]
```

これで最低限のアプリが出来ました。
起動するには以下のコマンドです。

```npx parcel src/index.html```

起動するとparcelは参照をたどって適切にビルドして、成果物をdistディレクトリに格納してくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10225/6b3a84a7-7bea-2350-3c0c-1e5e67c41e25.png)

また、ビルドは以下のコマンドです。

```npx parcel build src/index.html```

ビルドの成果物は起動時と同じくdistディレクトリに格納してくれます。

package.jsonにbuildというスクリプトで上記コマンドを登録しておきましょう。

```json
  "scripts": {
    "build": "npx parcel build src/index.html"
  },
```

# AmplifyのWebコンソールで作業：「アプリの設定: ビルドの設定」→「アプリケーション構築の仕様」でAmplify上でのデプロイを設定

さて、Hostingの設定は終わっている前提で、Amplify上でのビルドを設定します。

```yarn run build```でビルドを実行して、distディレクトリからコンテンツを配信するように設定することになります。
以下の場所で編集します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10225/44ca36eb-1334-244f-8bc8-b4ab6cb55477.png)

編集後のYAMLファイルは以下のようになります。

```yaml
version: 1
backend:
  phases:
    build:
      commands:
        - '# Execute Amplify CLI with the helper script'
        - amplifyPush --simple
frontend:
  phases:
    preBuild:
      commands:
        - yarn install
    build:
      commands:
        - yarn run build
  artifacts:
    # IMPORTANT - Please verify your build output directory
    baseDirectory: dist
    files:
      - '**/*'
  cache:
    paths:
      - node_modules/**/*
```

以下３つの設定値を書き換えました。

```frontend > phases > preBuild > commands```
```frontend > phases > build > commands```
```frontend > artifacts > baseDirectory```

これでビルドの設定は完了です。

# AmplifyのWebコンソールで作業：「アプリの設定: リライトとリダイレクト」でSPA向けの設定を追加

Elmの構成によってはこの手順は不要かもしれませんが、SPAとして動作させることを念頭に、あらゆるURLへのリクエストをindex.htmlで受けるような設定を加えます。

以下の場所で設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10225/72325470-6f88-4c88-8ede-38e20f40846b.png)


* 送信元アドレス：```</^[^.]+$|\.(?!(css|gif|ico|jpg|jpeg|js|png|txt|svg|woff|woff2|ttf|map|json|webp)$)([^.]+$)/>```
* ターゲットアドレス：```/index.html```
* 入力：```200(リライト)```

これでElmで構築したWebアプリをAmplifyで配信することが出来るようになりました！