New Project with Elm and Vite

    TL;DR

    1. A quick rundown on how to setup a new project that will use Elm and Vite.
    2. Implementing a skeleton Elm app to ensure that everything works.

    Project Setup

    Initialize the project

    This command will initialize a vite project.

    npm init vite
    

    Select vanilla (and vanilla variant) since Elm is not one of the supported frameworks.

    Configure vite and elm

    cd <your new project>
    npm i --save-dev vite-plugin-elm
    

    Create a new file called vite.config.js:

    import elmPlugin from "vite-plugin-elm"
    
    export default {
        // identify what plugins we want to use
    plugins: [elmPlugin()],
             // configure our build
             build: {
                 // file path for the build output directory
    outDir: "build",
            // esbuild target
            target: "es2020"
             }
    }
    

    Setup the Elm application

    npm i --save-dev elm-tooling
    npx elm-tooling init
    npx elm-tooling install
    

    This creates a new file, elm-tooling.json and installs the selected tools to ~/.elm/elm-tooling (and symlinking them in ./node_modules/.bin).

    You can check what tools are installed with:

    npx elm-tooling tools
    

    To track elm dependencies and manage its project settings you can create a new file elm.json with:

    npx elm-json new
    

    Change around the file structure

    In the end we want the following folder structure:

    src/Main.elm
    js/index.js
    public/assets/{images,stylesheets}/
    

    We need to connect index.html with index.js:

    <script type="module" src="./js/index.js"></script>
    

    And index.js need to load the Elm App:

    import { Elm } from "/src/Main.elm"
    const app = Elm.Main.init({});
    

    main.js and styles.css can be deleted.

    Adding Elm dependencies

    Here is an example on how to add Elm packages:

    npx elm-json install elm/browser elm/url mdgriffith/elm-ui
    

    Project Setup Simplified

    One can also just use a repo template instead of all the steps above:

    npx degit lindsaykwardell/vite-elm-template my-elm-app
    npm install
    npm run dev
    

    But it doesn’t hurt to know the steps to get to the same result, the template might get out of sync or parts of the toolchain might change.

    Skeleton Elm App

    Basic Structure

    In src/Main.elm

    -- MAIN
    -- MODEL
    -- UPDATE
    -- SUBSCRIPTIONS
    -- VIEW
    

    If you have a helpful LSP running, then the imports can be automatically handled for you.

    main

    Our main is relatively straightforward:

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

    We still need to implement all those functions.

    Model and Msg

    But first, a quick draft of our Model and Msg:

    type Model
        = Loading
        | Failure
        | Success String
    
    type Msg
        = GotText (Result Http.Error String)
    

    init and update

    And how data is being initialized and changed:

    init : () -> ( Model, Cmd Msg )
    init _ =
        ( Loading
        , Http.get
            { url = "<...>"
            , expect = Http.expectString GotText
            }
        )
    
    
    update : Msg -> Model -> ( Model, Cmd Msg )
    update msg _ =
        case msg of
            GotText result ->
                case result of
                    Ok fullText ->
                        ( Success fullText, Cmd.none )
    
                    Err _ ->
                        ( Failure, Cmd.none )
    

    Subscriptions

    Only two to go, the subscriptions are very simple:

    subscriptions : Model -> Sub Msg
    subscriptions _ =
        Sub.none
    

    view

    And a simple view to show the request states and finally the response :

    view : Model -> Html Msg
    view model =
        case model of
            Loading ->
                text "Loading..."
    
            Failure ->
                text "Failed to load."
    
            Success fullText ->
                pre [] [ text fullText ]
    

    imports

    And if you didn’t get any nice auto-imports, you will need to put this at the top of the file:

    module Main exposing (Model(..), Msg(..), init, main, subscriptions, update, view)
    
    import Browser
    import Html exposing (Html, pre, text)
    import Http
    

    You don’t need to expose all of those functions of course. There you go, a simple Elm Program that you can build upon, using vite as its build system.