This command will initialize a vite project.
npm init vite
Select vanilla (and vanilla variant) since Elm is not one of the supported frameworks.
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"
}
}
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
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.
Here is an example on how to add Elm packages:
npx elm-json install elm/browser elm/url mdgriffith/elm-ui
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.
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.
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.
But first, a quick draft of our Model and Msg:
type Model
= Loading
| Failure
| Success String
type Msg
= GotText (Result Http.Error String)
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 )
Only two to go, the subscriptions are very simple:
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.none
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 ]
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.