📑

Elmの初期テンプレートファイルを作る elm-initを作った

2021/07/17に公開

elm-initって何?

Elmのプロジェクトを書き始めるぞ!というときに使うCLIツールです。どういったアプリケーションを作るかで、ファイルを吐き出し分けることができます。

elm-initは、こちらからお試しできます。

なぜ、このようなツールを使う必要があるのか?

Elmはフレームワークが内蔵されている言語で、用意すべき関数があらかじめ決められています。そして作りたいアプリケーションによって型が厳密に定められています。実際には以下のようなアプリケーションの種類があります。詳しくはBrowserパッケージをご覧ください。

  1. sandbox — ボタンやチェックボックスのような、ユーザの入力に反応させたいとき
  2. element — HTTPとJavaScript相互呼び出しのような、外の世界と対話させたいとき
  3. document — <title> と <body> をコントロールしたいとき
  4. application — SPAを作りたいとき

開発者はこのアプリケーションの種類から自分の作りたいものに合わせて、main関数中に上記の関数を呼び出し分ける必要がありますが、それに合わせて、view, update, subscription, moduleの書き方などが変化します。これを調整するためにいろんなサンプルを見て回ってコピペをするのは苦痛です。そのためにelm-initを作成しました。

どうやって使うの?

helpを呼び出せば、説明が出てきます。

elm-init --help
elm-init 0.0.1
ABAB↑↓BA <@ababupdownba>
elm-init CLI

USAGE:
    elm-init [FLAGS] <mode>

FLAGS:
    -f, --flags      flags option
    -h, --help       Prints help information
    -p, --ports      ports option
    -V, --version    Prints version information

ARGS:
    <mode>    Elm app mode.
              sandbox — react to user input, like buttons and checkboxes
              element — talk to the outside world, like HTTP and JS interop
              document — control the <title> and <body>
              application — create single-page apps

elm-initコマンドを呼び出すとoutputディレクトリが出力されます。中には、index.htmlMain.elmが吐き出されます。

$ elm-init element -f p
$ tree output
output
├── index.html
└── src
    └── Main.elm

1 directory, 2 files

中身はこのようなファイルが出力されます。

<html>
<head>
  <style>
    /* you can style your program here */
  </style>
</head>
<body>
  <main></main>
  <script>
    
    var storedData = localStorage.getItem('echo history');
    var flags = storedData ? JSON.parse(storedData) : [];
    
    var app = Elm.Main.init({ node: document.querySelector('main') ,flags: flags })

    
    var socket = new WebSocket('wss://echo.websocket.org');

    app.ports.sendMessage.subscribe(function(message) {
         flags.push(message);
         localStorage.setItem('echo history', JSON.stringify(flags));
         socket.send(message);
    });

    socket.addEventListener("message", function(event) {
        app.ports.messageReceiver.send(event.data);
    });
    
  </script>
</body>
</html>
port module Main exposing (..)

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


port sendMessage : String -> Cmd msg


port messageReceiver : (String -> msg) -> Sub msg


type alias Model =
    { draft : String
    , messages : List String
    }


init : List String -> ( Model, Cmd Msg )
init flags =
    ( { draft = "", messages = flags }
    , Cmd.none
    )


type Msg
    = DraftChanged String
    | Send
    | Recv String


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        DraftChanged draft ->
            ( { model | draft = draft }
            , Cmd.none
            )

        Send ->
            ( { model | draft = "" }
            , sendMessage model.draft
            )

        Recv message ->
            ( { model | messages = model.messages ++ [ message ] }
            , Cmd.none
            )


subscriptions : Model -> Sub Msg
subscriptions _ =
    messageReceiver Recv


view : Model -> Html Msg
view model =
    div []
        [ h1 [] [ text "Echo Chat" ]
        , input
            [ type_ "text"
            , placeholder "Draft"
            , onInput DraftChanged
            , value model.draft
            ]
            []
        , button [ onClick Send ] [ text "Send" ]
        , ul []
            (List.map (\msg -> li [] [ text msg ]) model.messages)
        ]


main : Program (List String) Model Msg
main =
    Browser.element
        { init = init
        , view = view
        , update = update
        , subscriptions = subscriptions
        }

おまけ

applicationを選択すると、お茶目です。elm-spaを使うことをおすすめされます。

$ elm-init application
You should use elm-spa( https://www.elm-spa.dev/ ).

まとめ

今回はElmをやる上でいつもちょっと面倒だなあって思う時の便利ツールを作ってみました。よければ使ってみて、要望を出してください。個人的には対話型のアプリにして、もうちょっとフレンドリーなツールにしてみたいです(Rustわからん)。

Discussion