How to compile Elm files on save

When writing JavaScript I am used to have a tool like Webpack or Browserify that automatically compiles the code whenever I save my changes. Therefore one of the first things I wanted to do in Elm is to set up the same build process.

Elm’s compiler has no watch option

Unfortunately Elm’s compiler does not support watching files right out of the box. elm make only compiles once when executed and does not have a --watch option.

There is elm reactor, that does watch for file changes and recompiles your code, but it starts a server, which serves its own HTML file. This is fine for small experiments or while learning the language. As soon as you want to add custom CSS or integrate Elm into an existing code base you have to use something else though.

Why I don’t use webpack or grunt with Elm

I know that there is elm-webpack-loader and elm-grunt, but starting an Elm project with one of these big JS build tools pulls in a lot of code. In the end one reason I chose Elm is to worry less about JavaScript.

Since Elm has its own compiler that imports Elm packages and bundles your code into one file, webpack is too heavy just for file watching. When I write JavaScript I also avoid grunt/gulp. The more code is written to build your project, the more can go wrong and newcomers have to learn when they want to make changes to the build process.

This is why I try to keep it as simple as possible and mainly use npm’s scripts field in package.json instead. It does not require pulling in another dependency and is what I recommend for Elm as well.

File watching with chokidar

The most popular JS build tools, e.g. webpack, browserify and gulp, use the same package to watch for file changes: chokidar. It deals with problems each operating system has when it comes to watching files and provides one interface that abstracts away all the workarounds needed to make it work across different environments.

Chokidar does not offer a CLI interface out of the box, but there is chokidar-cli, which wraps it and offers exactly what I need: executing a command on each file change.

This is what the command looks like:

chokidar <file pattern> -c <command>

My Elm build setup

Whenever I start a new Elm project I can now set it up by adding chokidar-cli and one line to my package.json:

  1. Install Elm with: npm install elm --save-dev

  2. Install chokidar-cli with: npm install chokidar-cli --save-dev

  3. Open package.json and add the following line:

{
  …
  "scripts": {
    "watch": "chokidar '**/*.elm' -c 'elm make Main.elm --output elm.compiled.js' --initial"
  }
  …
}

This line tells chokidar to watch for all files ending with .elm in the root and subfolders. It then runs elm make Main.elm --output elm-compiled.js for each change. If your Elm entry file is not called Main.elm or your output JavaScript file should be called differently change this line.

Now you can execute npm run watch to get Elm’s awesome compiler messages when saving your changes and the updated compiled JavaScript after refreshing the browser.