SmartIndentationEngine — Clean indentation for EEx templates

Over the last few days I built a small custom EEx engine that tackles a frustration I’ve had for a while trying to get proper indentation with EEx templates. I thought some of you might find it useful too.

I’ve been doing a lot of Codegen lately and when writing EEx templates with loops and conditionals, I often end up facing a choice between:

  • Messy template code to get properly formatted output
  • Clean template code but with weird (or even invalid) spacing in the output

This library attempts to fix this by extending EEx.SmartEngine with a special <%| tag that injects some context-aware string manipulation into the AST to ensure that the output is indented in a way that makes sense (at least to me :sweat_smile:).

It also provides a ~TT sigil for convenience and supports including (and re-indenting) “partial” templates.

Quick Example

defmodule Template.DockerCompose do
  import SmartIndentationEngine.Template

  def render(assigns) do
    ~TT"""
    services:
      <%| for {name, config} <- @services do %>
        <%= name %>:
          <%= include :service, config: config %>
      <% end %>
    """
  end
  
  def service(assigns) do
    ~TT"""
    image: <%= @config.image %>
    ports:
      <%| for port <- @config.ports do %>
        - <%= port %>
      <% end %>
    # ...
    """
  end
end

Template.DockerCompose.render(
  services: %{
    livebook: %{
      image: "ghcr.io/livebook-dev/livebook:0.15.5",
      ports: [8080, 8081]
    }
  }
)

This will produce the following output:

services:
  livebook:
    image: ghcr.io/livebook-dev/livebook:0.15.5
    ports:
      - 8080
      - 8081
    # ...

Give It a Try!

I just published it on Hex: smart_indentation_engine.

If you’re working with EEx templates, I’d love for you to try it out. Let me know what you think, if you encounter any issues, edge cases, or if you have ideas for improvements!

12 Likes