samvincent / jekyll-haml

HAML html converter for Jekyll
MIT License
88 stars 40 forks source link

Guard::Jekyll failed: Illegal nesting: nesting within plain text is illegal. #8

Open volkanunsal opened 10 years ago

volkanunsal commented 10 years ago

I am using this plugin with guard-jekyll. I get this error whenever I try to use the partial syntax from a page template.

# index.haml
{% haml navbar.haml %}

It works fine when I use it from a layout template

# _layouts/default.haml
{% haml navbar.haml %}
rstacruz commented 9 years ago

Can confirm this issue still persists. It happens even without guard.

JangoSteve commented 8 years ago

Having the same issue. Doesn't seem to be just this gem either. I switched to an explicit ruby file in the _plugins folder from https://gist.github.com/dtjm/517556, and it's doing the same thing. Works when including from a layout, not from a page.

trusche commented 8 years ago

Just ran into this myself. Here's the issue: When you call {% haml xyz.haml %}, it first converts that source file into HTML and inserts that into the calling HAML file. So if you have this partial:

# includes/excerpt.haml
.excerpt
  %h2
    {{ post.title }}
  %p
    {{ post.excerpt }}

and include it like this:

    %h1 Posts
    .posts
      {% for posts in site.posts %}
      {% haml excerpt.haml %}
      {%endfor %}

It will result in this unholy mess of HAML/HTML:

    %h1 Posts
    .posts
      <div class="excerpt">
  <h2>
    Title post #1
  </h2>
</div>
      <div class='excerpt'>
  <h2>
    Post #2 Title
  </h2>
</div>
...

That's the cause of the illegal nesting complaint.

One way to solve it is to strip all newlines from the partial when including it - here's one way to do that:

    %h1 Posts
    .posts
      {% for posts in site.posts %}
      {% capture excerpt %}{% haml excerpt.haml %}{% endcapture %}{{ excerpt | strip_newlines }}
      {%endfor %}

This captures the compiled HAML (a.k.a. HTML) into a variable and then runs it through a filter to strip line breaks. Voila - the resulting intermediary HAML file looks like this:

    %h1 Posts
    .posts
      <div class="excerpt"><h2>Title post #1</h2></div><div class='excerpt'><h2>Post #2 Title</h2></div>

which is not very readable but correct HAML syntax.

Hope this helps someone.

quantenschaum commented 5 years ago

IMHO it should process the haml files through liquid first (preserving indentation) and then through haml. That way you could just {% include header.haml %} from inside a haml file and there would be no need to escape the liquid statements in haml.

edit Or probably not, because this messes up indentation, but haml should be instructed to recognize liquid statements and pass them through unmodified (not html escaped).

A problem is this for example

%html(lang="{{ page.lang | default: site.lang | default: 'en' }}")

because it gets processed by haml to

<html lang="{{ page.lang | default: site.lang | default: &#39;en&#39; }}">

and then liquid complains with

Liquid Warning: Liquid syntax error (line 2): Unexpected character & in "{{ page.lang | default: site.lang | default: &#39;en&#39; }}" in /_layouts/default.haml

For plain text you could partly work around it with something like

-foo='some string with "quoted parts" and <bar>'
=foo
!=foo
%div(title="#{foo}")

resulting in

some string with "quoted parts" and <bar>
some string with "quoted parts" and <bar>
<div title='some string with &quot;quoted parts&quot; and &lt;bar&gt;'></div>

Why doesn't =foo get html escaped? How to set an attribute w/o html escaping?

With slim and jekyll-slim you can do

doctype html
-foo='some string with "quoted parts" and <bar>'
=foo
==foo
div title="#{foo}"
div title="#{{foo}}"

resulting in

some string with &quot;quoted parts&quot; and &lt;bar&gt;
some string with "quoted parts" and <bar>
<div title="some string with &quot;quoted parts&quot; and &lt;bar&gt;">
</div><div title="some string with "quoted parts" and <bar>"></div>

Which is correct.

So, there is a way to preserve the liquid statements (ugly, but correct).

div title="#{{"{ page.foo | default: 'bar' }"}}"