ReScriptでEmailJSからメールを出す

# EmailJSの準備
以下を参考にアカウントを作成し、mail serviceとmail templateを設定します。

https://www.emailjs.com/docs/

今回はemail_templateを
```SH:email_template
{{message}}
{{user_name}} ({{user_email}})
```
としています。
# npmパッケージのインストール
```SH:ReScriptのインストール
npm create rescript-app@latest
```
```SH:EmailJSのインストール
cd emailjs-test
npm install --save-dev @emailjs/browser
```
ReScriptをインストールするときのプロジェクト名を`emailjs-test`にしたので、そのディレクトリに移動してからEmailJSをインストールします。
# 作成するファイル
```html:emailjs_test.html
<!DOCTYPE html>
<html>
<head>
    <title>Contact Form</title>
</head>
<body>
    <form id="contact-form">
        <label>　名　前　</label>
        <input id="user_name" type="text" name="user_name">
        <br><br>
        <label>　メール　</label>
        <input id="user_email" type="email" name="user_email">
        <br><br>
        <label>メッセージ</label>
        <textarea id="message" name="message"></textarea>
        <br><br>
        <input type="submit" value="　送　信　">
    </form>
    <script type="module" src="EmailJsTest.res.mjs"></script>
</body>
</html>
```
```ReScript:EmailJsTest.res
type publicKey = { publicKey: string }
type sendObject = {
  user_name: string,
  user_email: string,
  message: string
}
@module("@emailjs/browser") external init: publicKey => unit = "init"
@module("@emailjs/browser") external send: (string, string, sendObject) => promise<'a> = "send"

@send external getElementById: (Dom.document, string) => Dom.element = "getElementById"
@send external addEventListener: (Dom.element, string, Dom.event => unit) => unit = "addEventListener"
@get external getValue: Dom.element => string = "value"
@send external preventDefault: Dom.event => unit = "preventDefault"

let onBtnClick = (event) => {
  event->preventDefault

  let user_name = document->getElementById("user_name")->getValue
  let user_email = document->getElementById("user_email")->getValue
  let message = document->getElementById("message")->getValue
  let send_object = {
    user_name: user_name,
    user_email: user_email,
    message: message
  }

  // these IDs from the previous steps
  send("service_id", "template_id", send_object)
    ->Promise.thenResolve(() => { Console.log("SUCCESS!") })
    ->Promise.catch((error) => {
      Console.log2("FAILED...", error)
      Promise.resolve()
    }
  )->ignore
}

// https://dashboard.emailjs.com/admin/account
init({ publicKey: "YOUR_PUBLIC_KEY" });
document->getElementById("contact-form")->addEventListener("submit", onBtnClick)
```
```console:ディレクトリ構成
emailjs-test/
└── src/
    ├── emailjs_test.html
    └── EmailJsTest.res
```
# ビルドして実行
```SH:ビルド
npm run res:build
```
ブラウザで確認するとエラーが出ます
```consle:
TypeError: Failed to resolve module specifier "@rescript/core/src/Core__Promise.res.mjs". Relative references must start with either "/", "./", or "../".Understand this error
```
モジュールが見つけられないようです。
# Parcelでバンドル
```SH:Parcelのインストール
npm install --save-dev parcel
```
```SH:Parcelでバンドル
npx parcel build src/EmailJsTest.res.mjs
```
```console:ディレクトリ構成
emailjs-test/
├── dist/
│   ├── EmailJsTest.res.mjs
│   └── EmailJsTest.res.mjs.map
└── src/
    ├── emailjs_test.html
    ├── EmailJsTest.res
    └── EmailJsTest.res.mjs
```
# 再実行
読み込むJSファイルを変更します。
```html:emailjs_test.html
<script type="module" src="../dist/EmailJsTest.res.js"></script>
```
下のようなページが表示され、入力して「送信」を押すとメールが送られます。
<p class="codepen" data-height="300" data-slug-hash="PovgdXe" data-pen-title="Untitled" data-user="sgigagaeru" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/sgigagaeru/pen/PovgdXe">
  Untitled</a> by sgigagaeru (<a href="https://codepen.io/sgigagaeru">@sgigagaeru</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

# `EmailJsTest.res`でやっていること
## `@emailjs/browswer`のバインド
`@emailjs/browser`の`init`と`send`を用いたので、それをバインドします。
`init`は`Object`を引数に取って、`unit`を返します。
ここでは`Object`は`{ publicKey: "YOUR_PUBLIC_KEY" }`に決まっているので、それを型にしました。
```ReScript:EmailJsTest.res
type publicKey = { publicKey: string }
@module("@emailjs/browser") external init: publicKey => unit = "init"
```
`send`は`(string, string, Object)`を引数にとって、`promise`を返します。
`Object`はここでは固定なので型にしました。
```ReScript:EmailTestJs.res
type sendObject = {
  user_name: string,
  user_email: string,
  message: string
}
@module("@emailjs/browser") external send: (string, string, sendObject) => promise<'a> = "send"
```
# `onBtnClick`の処理
「送信」ボタンが押されると`onBtnClick`が呼び出されます。
まずデフォルトの処理を無効化します。これでページの遷移も防がれます。
```ReScript:EmailTestJs.res
event->preventDefault
```
インプットフォームの値を読み取って、送信するオブジェクトを作ります。
```ReScript:EmailTestJs.res
let user_name = document->getElementById("user_name")->getValue
let user_email = document->getElementById("user_email")->getValue
let message = document->getElementById("message")->getValue
let send_object = {
  user_name: user_name,
  user_email: user_email,
  message: message
}
```
`send`で送ります。
```ReScript:EmailTestJS.res
send("service_id", "template_id", send_object)
  ->Promise.thenResolve(() => { Console.log("SUCCESS!") })
  ->Promise.catch((error) => {
    Console.log2("FAILED...", error)
    Promise.resolve()
  }
)->ignore
```
ReScriptの`Promise.catch`は`catch(promise, errorCallback)`の形になります。そして`errorCallback`は`catch`の第一引数と同じ型の`promise`を返さないといけません。なので最後に`Promise.resolve()`を付けて`promise`を返しています。
`Promise.then`も同様で、`then(promise, callback)`の形で、`callback`は`promise`を返します。なので`callback`の最後に`Promise.resolve()`を付けます。
`thenResolve`を用いると余計な`Promise.resolve()`を書かなくて良いので、そちらを用いています。