Elm Development Environment

First off, my apologies for the deep silence of late. I’m pretty deep in the trenches when my Master’s semesters are running and I simply had to take a personal break during my summer. I’m hoping this post can help me pick up my cadence again!

I recently dove back into Elm development, only to find that the community has evolved rapidly between versions 0.16 and 0.18 (current as of this writing). I thought I’d share my notes on how I have my new development environment set up, now that we have more options beyond simple text editors (or vim).

Right now, my basic environment looks like this:

That’s all I’m using to get a nice rapid development environment up and running.

Project Structure

I’m currently structuring my projects like this:

project_dir/
|-- elm_stuff/
|-- src/
|   |-- Main.elm
|   |-- Module1.elm
|   |-- Module2.elm
|   \-- ...
|-- static/
|   |-- styles.css
|   |-- whatever.js
|   \-- ...
|-- elm-package.json
|-- elm.js
|-- index.html
|-- reactor_debug_index.html
\-- reactor_compiled_index.html

Let’s break it down!

elm_stuff/ is just the auto-generated stuff built by Elm.

I’ve defined my elm-package.json to keep all my .elm source files under the src/ directory, like so:

{
...
"source-directories": ["src"],
...
}

I drop all of my non-Elm static files (stylesheets, JavaScript, whatever) into the static/ directory.

elm.js is the compiled app generated by elm-make, as usual.

index.html is my actual production index page. This will likely be custom to you.

Now, for those two funny reactor_ pages.

Getting VS Code to play nice with elm-reactor and elm-make

elm-reactor gives us nice instant refresh behavior for code changes. VS Code gives us a nice editor and auto-save behavior. Getting the two to play nicely together (and elm-make) isn’t too difficult, but does take a couple tricks. Further, debug (with elm-reactor) vs production (with elm-make) require different tricks to get them to work.

For debugging (using elm-reactor), I started with this recommendation by Denis Kolodin and made some modifications. This one can reference your entry point .elm module file directly and elm-reactor will do the rest. I did a tiny bit of cleanup and am currently using the definition from this gist.

For development debugging from there, my development process looks like this:

  • Starting up: Cmd+Shift+P (on Mac), type Elm: Reactor Start, then open the link in your browser (this will be clickable from your VS Code console output).
  • Iterative development: Make your edits, refresh the browser page!
  • Shutting down: Type Elm: Reactor Stop before exiting VS Code. (Check the Troubleshooting section below about stopping elm-reactor!)

[Update: There’s some talk about ports not working with the debug version, but this no longer appears to be a problem with Elm 0.18. Just capture the app created at startup: var app = runElmProgram();]

For testing the compiled version (using elm-make), I started with this recommendation by Denis Kolodin. This one needs to reference your compiled elm.js, but there’s a catch from VS Code: You must run elm-make with the entry point .elm module as the active window! If you don’t do this, it will compile a different module and you’ll get Elm startup errors. I ran into this problem multiple times, each time wondering why nothing was showing up until I checked the JS console output for errors. To help streamline this (and prevent confusion), I modified the HTML to spit out a visible error message if my Main module isn’t defined – I’m currently using the implementation from this gist.

From production debugging from there, my development process looks like this:

  • Starting up: Cmd+Shift+P (on Mac), type Elm: Reactor Start, then open the link in your browser (this will be clickable from your VS Code console output).
  • Iterative development: Make edits, make Main.elm the active window (check the Troubleshooting section below), type Elm: Make, refresh the browser page.
  • Shutting down: Type Elm: Reactor Stop before exiting VS Code. (Check the Troubleshooting section below about stopping elm-reactor!)

That’s all I needed to get up and running in a fast, iterative way with VS Code, elm-reactor, and elm-make, plus pulling in external styling.

Troubleshooting

I’ve run into a few gotchas with this:

  • Tip: Always make sure you stop an elm-reactor process before exiting VS Code.
    • If I start elm-reactor from VS Code, but close VS Code without stopping elm-reactor, it will create a dangling process. I’m unable to start another elm-reactor from the command line or VS Code, or stop the current process. I have to kill the process manually.
  • Tip: Always have your Main.elm as the active window in VS Code when you elm-make.
    • Running elm-make will generate elm.js based on whatever the currently active window module is. If you compile from a different module, it’s likely to break your reactor_compiled_index.html (which is the reason for the extra error message conditional – I found this to be more obvious then investigating the JS console).
  • Tip: elm-reactor has some pre-defined path references you can access from your HTML.
    • Prefixing /_compile/ will point to the root of your project directory.
    • Prefixing /_reactor/ will give you access to things in the reactor space, such as the fancy waiting.gif.
    • Referencing /elm.js seems to work the same as /_compile/elm.js, so I’m guessing elm-reactor copies the elm.js file into its root directory – but other references, like for /_compile/static/styles.css, don’t appear to work that way. This may be behavior special to elm.js.