How to Modify Heex with Igniter

I’ve been having fun getting familiar with Igniter for code generation and modification. Thanks for building and releasing it @zachdaniel !

Is there a suggested approach for modifying Heex code?

I have a scenario where I’d like to modify some of the Heex code that mix phx.new generates into core_components.ex. I’m working with Phoenix v1.8-rc.0, which makes notable changes to the core components compared with previous versions. See Chris McCord’s post for more details.

This default core_components.ex includes component markup inline with sigil_H heredocs, like this:

  def input(%{type: "select"} = assigns) do
    ~H"""
    <fieldset class="fieldset mb-2">
      <label>
        <span :if={@label} class="fieldset-label mb-1">{@label}</span>
        <select
          id={@id}
          name={@name}
          class={["w-full select", @errors != [] && "select-error"]}
          multiple={@multiple}
          {@rest}
        >
          <option :if={@prompt} value="">{@prompt}</option>
          {Phoenix.HTML.Form.options_for_select(@options, @value)}
        </select>
      </label>
      <.error :for={msg <- @errors}>{msg}</.error>
    </fieldset>
    """
  end

The AST represents the code inside the heredoc as a bitstring. I’ve tried several approaches, including simply replacing everything in the Heex block with what I want to be there, but Sourceror.parse_string/1 chokes on the first < in the string I pass to it. Does the existence of characters like < necessitate using a template to overwrite the file?

Thanks!

Sourceror can parse elixir code. Text within sigils is not elixir code. It could be arbitrary text. Only additional tools we have allow us making sense of the text within sigils like for heex the (eex) engines and the tag parsing it can do as well as the related formatter plugin. That’s all provided by the phoenix_live_view package and therefore would need integration with that to be used. There’s nothing to be known about sigil contents from an elixir AST level.

@shahryarjb is doing this in mischka chelekom library I believe

1 Like

Yeah, we’d need to have a separate parser that creates an AST for the contents of ~H sigils, and then you’d have to update that, and then you can replace the arguments to the ~H sigil with the new resulting string. I haven’t looked into it myself, but I’m positive something like this exists as Phoenix must itself be using it.

Thanks all. That makes sense to me. I looked a little at what Phoenix is doing for non-umbrella apps, and have so far seen that it’s copying template files instead of modifying code. I’ll look further, and check out what’s going on in mischka chelekom.

Hi! At Mishka Chelekom, we don’t modify Phoenix components using AST directly. Instead, we have around 90 Phoenix components that are generated by Igniter and placed into your project.

For example you want to have Combobox, you just need to run mix mishka.ui.gen.component combobox

Source: mishka_chelekom/priv/components/combobox.eex at master · mishka-group/mishka_chelekom · GitHub

Igniter CLI: mishka_chelekom/lib/mix/tasks/mishka.ui.gen.component.ex at master · mishka-group/mishka_chelekom · GitHub

Igniter also includes parsers for modifying JavaScript and CSS, but when it comes to changing Phoenix-related HTML code (Heex), it’s best to ask @zachdaniel, who has already responded about this.

Thanks @shahryarjb. I was just checking out the code in Mishka Chelekom.

Looking forward to trying it out with Phoenix 1.8 :slight_smile:

1 Like

You could potentially take a look at Phoenix.LiveView.Tokenizer, using it to turn the text into a data structure you can manipulate, and then see if there is some way to turn it back into a string? That is the general pattern for all of igniter, i.e source -> AST -> manipulate -> source.

Thank you; after releasing the Phoenix 1.8, we will test and update the lib for this version :pray:t2:

Oh of course! I apparently had blinders on and was only thinking about Phoenix generators and not LiveView’s more core functionality of diffing markup changes. Thanks @zachdaniel