0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ReScriptでとりあえずParcelでバンドル

Last updated at Posted at 2024-06-19

準備

ReScriptのインストール
npm create rescript-app@latest
src/SousekiForm.res
src/SousekiForm.res
module Document = {
  @send external getElementById: (Dom.document, string) => Dom.element = "getElementById"
  @send external getElementsByName: (Dom.document, string) => Dom.nodeList = "getElementsByName"
  @get external getBody: Dom.document => Dom.element = "body"
}
module Element = {
  @send external addEventListener: (Dom.element, string, () => unit, bool) => unit = "addEventListener"
  @get external getDataset: Dom.element => Dom.domStringMap = "dataset"
  @get external getValue: Dom.element => string = "value"
  @get external getChecked: Dom.element => bool = "checked"
  @set external setInnerHTML: (Dom.element, string) => unit = "innerHTML"
}
module NodeList = {
  @get external getLength: Dom.nodeList => int = "length"
  @send external item: (Dom.nodeList, int) => Dom.element = "item"
}
module DomStringMap = {
  @get external getCorrectChoice: Dom.domStringMap => string = "correctChoice"
}

// 目的:設問部のinputのNodeをReScriptのlistに格納する
// getListedNodeList : Dom.nodeList -> Dom.element list
let getListedNodeList = nodeList => {
  let length = nodeList->NodeList.getLength

  // 目的: nodeList->item(0)からnodeList->item(i)までのDom.elementをReScriptのlistに格納する
  // loop : int -> Dom.element list
  let rec loop = i =>
    if i < 0 {list{}}
    else {list{nodeList->NodeList.item(i), ...loop(i - 1)}}

  loop(length - 1)
}

// 目的: 指定された選択肢のvalueを返す
// getAnswer : (Dom.element -> bool) -> Dom.element list -> string
let rec getAnswer = (lst, f) => switch lst {
| list{} => ""
| list{first, ...rest} => if f(first) {first->Element.getValue} else {getAnswer(rest, f)}
}

// 目的: ボタンがクリックされたときの処理
// onBtnClick : unit -> unit
let onBtnClick = () => {
  let listedNodeList = document->Document.getElementsByName("Q1")->getListedNodeList
  let correctAnswer = listedNodeList->getAnswer(e => e->Element.getDataset->DomStringMap.getCorrectChoice == "true")
  let selectedAnswer = listedNodeList->getAnswer(Element.getChecked)
  let judgement = correctAnswer == selectedAnswer ? "正解です!" : "不正解です。"
  let message = "正解は" ++ correctAnswer ++ "。あなたの回答は" ++ selectedAnswer ++ "でした。"
  document->Document.getBody->Element.setInnerHTML(judgement ++ "<br>" ++ message)
}

document->Document.getElementById("btn")->Element.addEventListener("click", onBtnClick, false)
src/souseki_form.html
souseki_form.html
<!doctype HTML>
<html lang="ja">
<head>
  <title>漱石先生クイズ</title>
</head>
<body>
  <form>
    <dl>
      <dt>Q1. 漱石先生のデビュー作は?</dt>
      <dd><p>漱石先生のデビュー作は次のうちどれでしょう?</p>
        <p><input type="radio" name="Q1" value="坊っちゃん" checked>坊っちゃん<br>
          <input type="radio" name="Q1" value="吾輩は猫である" data-correct-choice="true">吾輩は猫である<br>
          <input type="radio" name="Q1" value="明暗">明暗</p>
      </dd>
    </dl>
    <button id="btn" type="button">答え合わせ</button>
  </form>
  <script type="module" src="SousekiForm.res.js"></script>
</body>
</html>
ディレクトリ構成
parcel-test/
├── rescript.json
├── package.json
└── src/
    ├── SousekiForm.res
    └── souseki_form.html
ビルド
npm run res:build

Live Serverなどのローカルのサーバで試すと、以下のWebページが表示されます。

See the Pen 漱石先生クイズ #3 by sgigagaeru (@sgigagaeru) on CodePen.

改修して動作確認

SousekiForm.resgetAnswerを再帰を使って書いてます。

修正前:SousekiForm.res
let rec getAnswer = (lst, f) => switch lst {
| list{} => ""
| list{first, ...rest} => if f(first) {first->Element.getValue} else {getAnswer(rest, f)}
}

findを使う方が良いかと思ったので修正します。

修正後:SousekiForm.res
let getAnswer = (lst, f) => switch List.find(lst, f) {
| None => ""
| Some(e) => e->Element.getValue
}

ローカルサーバにアクセスしてページの動作を確認すると、動作しません。

Uncaught TypeError: Failed to resolve module specifier "@rescript/core/src/Core__List.res.js". Relative references must start with either "/", "./", or "../".

モジュールが見つけられないようです。

Parcelでバンドル

バンドルして一つのファイルにしてしまえば良いのではと思ったので、parcelでバインドします。
以下のコマンドをプロジェクトのトップディレクトリ(rescript.jsonpackage.json等が置かれているディレクトリ)で実行します。

Parcelのインストール
npm install --save-dev parcel
SousekiForm.resをエントリーポイントにしてバンドル
npx parcel build src/SousekiForm.res.js

distディレクトリが作成され、SousekiForm.res.jsSousekiForm.res.js.mapが置かれました。

ディレクトリ構成
parcel-test/
├── rescript.json
├── package.json
├── src/
│   ├── SousekiForm.res
│   └── souseki_form.html
└── dist/
    ├── SousekiForm.res.js
    └── SousekiForm.res.js.map

確認

dist配下のSousekiForm.res.jsを読み込むようにsouseki_form.htmlscriptタグを修正します。

souseki_form.html
<script type="module" src="../dist/SousekiForm.res.js"></script>

ブラウザで確認すると動きます。
バンドラが作成したJavaScriptファイルはモジュール形式ではないようなので、読み込みからtype="module"を外してみます。

souseki_form.html
<script src="../dist/SousekiForm.res.js"></script>

これでも動きました。1

  1. type="module"があってもなくても動くのは不思議です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?