jdrouet / mrml

Implementation of mjml in rust
MIT License
344 stars 22 forks source link

Root HTML comment fails to parse #409

Closed moomerman closed 3 months ago

moomerman commented 5 months ago

I'm happily using this library via Elixir and have hit an issue rendering the following template:

<!-- comment -->
<mjml>
  <mj-body>
    <mj-section>
      <mj-column>
        <mj-text>Hello World</mj-text>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>

The root comment seems to cause the parser to fail:

{:error, "unexpected token at position 0..12"}

Comments elsewhere in the template parse fine, eg:

<mjml>
  <mj-body>
    <mj-section>
      <!-- comment -->
      <mj-column>
        <mj-text>Hello World</mj-text>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>
<!-- comment -->
{:ok, "<!doctype html><html>...</html>"}
jdrouet commented 5 months ago

👋 Hey @moomerman, thanks for this issue. So the problem here is that we parse against a component in particular. The entry point being Mjml.parse(...). If we accept anything, I think it would require a breaking change. Is it really something you can't live without? Why can't you put it in the <mjml> component itself? 🤔

moomerman commented 5 months ago

👋🏼 @jdrouet thanks for getting back. It isn't something under my control unfortunately, an update to the framework I'm using to generate the MJML is adding these comments in. I can work around it on my side, I just thought perhaps it was a bug in the parser since it handles comments fine otherwise, including outside the </mjml><!-- comment -->. The online MJML live editor also correctly ignores the first comment.

jdrouet commented 5 months ago

I'm curious about that tool you use to generate the mjml. Could you share it with me?

moomerman commented 5 months ago

Yep sure. I'm using the Phoenix web framework for Elixir. Phoenix comes with a way of writing components as functions which gives you formatting, syntax highlighting and compile-time checks for things like mismatched tags and required attributes. It also enables you to compose your components nicely as custom HTML tags.

Here's a real example from our codebase:

Reset Password Template ```elixir defmodule App.Emails.ResetPassword do @moduledoc false use MyApp, :email attr :url, :string, required: true def mjml(assigns) do ~H""" <.simple_layout> <:preview>Reset your password <:title>Reset your password You can reset your password by clicking the button below <.button href={@url}> Reset your password """ end def text(user, url) do """ ============================== Hi #{user.email}, You can reset your password by visiting the URL below: #{url} If you didn't request this change, please ignore this. ============================== """ end end ```

In the example you can see I'm reusing <.simple_layout> and <.button> components and have "slots" for things like preview and title.

The issue I've come across recently is that Phoenix added debug annotations to track where your components were rendered from in development when you look at the source.

When I render my MJML templates I'm getting these annotations which look like this:

<!-- <App.Emails.ResetPassword.mjml> lib/app/emails/reset_password.ex:8 (app) -->
<!-- @caller lib/app/emails/reset_password.ex:9 (app) -->
<!-- <App.Emails.Components.simple_layout> lib/app/emails/components.ex:11 (app) -->
<mjml>...</mjml>

which currently cause it to fail to parse. It is only happening in dev though and I can add a workaround for it since it is predictable.

jdrouet commented 5 months ago

After doing some tests with mjml itself, the comments that are out of mj-body are ignored. I might do it the same way.

jdrouet commented 3 months ago

FYI, the last release embeds the changes 😉