The basic steps to publish a package with Elm 0.19
2018-10-02

I was gearing up to publish my first package, and was a bit surprised that I was unable to find an explanation of the basic steps involved. There are a couple of posts for Elm 0.18 but they are out of date now, so I decided to write down all the things I had to do as I went. Granted, the steps are quite straightforward, as you will see, but it’s still a bit of a barrier when they aren’t spelled out (or at least a great excuse to procrastinate for me)!

You can get started the same way as you would with an application: by running elm init. This will create an application elm.json file, which you’ll need to update later.

Local development and testing

You can compile your package by running elm make in its directory.

You can add tests to your package, but you might also want to try using it from an application. How do you do it before the package is published? Obviously you can’t add it to your application elm.json before it’s published!

Instead, you can add the source of the package to your application directly, by adding the relative path to the package source to “source-directories” in the application’s elm.json, something like:

{
    "type": "application",
    "source-directories": [
        "src",
        "../my-new-package/src"
    ],
    ...

Note that you will need to add all the dependencies of the package to your application elm.json manually to make things compile.

Requirements for publishing

When you are ready to publish, you can update elm.json in your package directory with "type": "package" and all the other required package fields.

The rest of the fields for a package are as follows:

  • name: Name of the package’s GitHub repo, eg alexkorban/my-package (note that packages must be published via GitHub)
  • summary: Summary for package.elm-lang.org, max 80 characters
  • license: License identifer from the OSI approved licence list
  • version: Package version - see details below
  • exposed-modules: Array of exposed module names
  • elm-version: Range of compatible compiler versions
  • dependencies: A list of dependencies (with version ranges rather than specific versions)
  • test-dependencies: A list of dependencies that are only used in the tests/ directory by elm test.

Here is a sample elm.json to put these into context:

{
    "type": "package",
    "name": "alexkorban/monoids",
    "summary": "A pack of monoids in the category of endofunctors",
    "license": "BSD-3-Clause",
    "version": "1.0.1",
    "exposed-modules": [
        "Monoids"
    ],
    "elm-version": "0.19.0 <= v < 0.20.0",
    "dependencies": {
        "elm/browser": "1.0.0 <= v < 2.0.0",
        "elm/core": "1.0.0 <= v < 2.0.0",
        "elm/html": "1.0.0 <= v < 2.0.0",
        "elm/json": "1.0.0 <= v < 2.0.0",
        "elm/time": "1.0.0 <= v < 2.0.0",
        "elm/url": "1.0.0 <= v < 2.0.0",
        "elm/virtual-dom": "1.0.2 <= v < 2.0.0"
    },
    "test-dependencies": {}
}

With regards to versioning, let me quote the rules spelled out by the elm publish command:

  - Versions all have exactly three parts: MAJOR.MINOR.PATCH

  - All packages start with initial version 1.0.0

  - Versions are incremented based on how the API changes:

        PATCH = the API is the same, no risk of breaking code
        MINOR = values have been added, existing values are unchanged
        MAJOR = existing values have been changed or removed

The incrementing of the version is done by running elm bump before elm publish. elm bump changes the version of the package according to the rules above based on the changes it finds in the package code.

Documentation is required for packages. You will need to provide a README.md in the root of your project, as well as inline documentation in the code which has to be in a specific format.

You can preview the documentation using an online previewer. To preview the inline documentation, you need to collate it into a JSON file, which can be done by running elm make --docs=docs.json.

A license is also required. In addition to specifying a license identifier from the OSI approved licence list in the "license" field of elm.json, you need to include the text of the licence in a file called LICENSE in the root of your project.

Initial release

Publishing the package to the package index is done via GitHub (it’s the only option available).

$ git init
$ git add elm.json src/
$ git commit -m "Initial commit"
$ git tag 1.0.0
$ git push --tags origin master
$ elm publish

Subsequent releases

After you’ve made changes to the code, you can run elm diff to find out whether the changes result in a patch, minor or major version change. The output will detail the API changes.

Then you will need to run elm bump to update the package version in elm.json accordingly.

The last steps are the same as for the initial release: tag the commit, push to GitHub, and run elm publish to publish the new version of your package.

And that’s it! Now you know how to publish a package and how to update it later.

(Thanks to Ian Mackenzie for pointing out a couple of things I missed.)

Further reading

elm.json format details

Documentation format

Higher-level design considerations for packages

Would you like to dive further into Elm?
📢 My book
Practical Elm
skips the basics and gets straight into the nuts-and-bolts of building non-trivial apps.
🛠 Things like building out the UI, communicating with servers, parsing JSON, structuring the application as it grows, testing, and so on.
Practical Elm