11ty / eleventy

A simpler site generator. Transforms a directory of templates (of varying types) into HTML.
https://www.11ty.dev/
MIT License
17.27k stars 495 forks source link

Support for raw handlebars output #436

Closed andeersg closed 5 years ago

andeersg commented 5 years ago

I have a problem with Handlebars and raw output.

I use handlebars as a template engine, and encountered a problem when I wanted to display some handlebars code in <pre> tags.

If I don't do anything extra the handlebars code is parsed.

This:

<section>
  {{#if isFront}}
    <p>This paragraph is only written to the front page.</p>
  {{/if}}
  <p>This paragraph is rendered on all pages.</p>
</section>

Becomes:

<section>

    <p>This paragraph is only written to the front page.</p>

  <p>This paragraph is rendered on all pages.</p>
</section>

Then I tried to create a raw helper:

{{{{raw}}}}
<section>
  {{#if isFront}}
    <p>This paragraph is only written to the front page.</p>
  {{/if}}
  <p>This paragraph is rendered on all pages.</p>
</section>
{{{{/raw}}}}

But it just ends up like this:

}}
<section>

    <p>This paragraph is only written to the front page.</p>

  <p>This paragraph is rendered on all pages.</p>
</section>
}}

A small test with the handlebars library directly works:

const Handlebars = require('handlebars');

var source = `{{{{raw}}}}
{{#if ready}}
<p>Ready</p>
{{/if}}
<p>Something else!</p>
{{{{/raw}}}}`;

Handlebars.registerHelper('raw', function(content) {
  return content.fn();
});

var template = Handlebars.compile(source);
var result = template();
console.log(result);

This will render it like:

{{#if ready}}
<p>Ready</p>
{{/if}}
<p>Something else!</p>

Is raw tags not supported? Or am I doing something wrong? I have tried both with addHandlebarsHelperand:

eleventyConfig.addFilter("raw", function(options) {
  return options.fn();
});
kleinfreund commented 5 years ago

Why is it {{{{raw}}}}, not {{raw}}?

andeersg commented 5 years ago

With regular handlebars blocks, the content is parsed:

const Handlebars = require('handlebars');
const source2 = `
{{#raw}}
{{#if ready}}
<p>Ready</p>
{{/if}}
<p>Something else!</p>
{{/raw}}
`;
var template2 = Handlebars.compile(source2);

var result2 = template2();
console.log(result2);

This will output:

<p>Something else!</p>
andeersg commented 5 years ago

Added a gist of my test script: https://gist.github.com/andeersg/5dc6a6f794470c30f348df4e79a5b321

andeersg commented 5 years ago

I did some more testing and figured out it's a problem that only happens in markdown files.

I created a small repo that shows the issue: https://github.com/andeersg/elventy_hb_issue In index.html it works as expected, the code inside {{{{raw}}}} is not parsed. The same inside markdown ends up parsed with }} from the raw tag still there.

zachleat commented 5 years ago

I added a few tests based on your code and the sample in the handlebars documentation and this seems to be working fine (as you stated) in handlebars files.

Just as a note for future me, I did run into this issue while added the tests: https://github.com/wycats/handlebars.js/issues/1156 Specifically, {{{{/ raw }}}}, a space after / and before raw caused an infinite loop scenario?? 😱 Anyway.

I’m guessing this is the classic Markdown indented code blocks issue. I just added this to the common pitfalls yesterday. You can read about it here: https://www.11ty.io/docs/languages/markdown/#there-are-extra-%3Cpre%3E-and-%3Ccode%3E-in-my-output

Related: #402

zachleat commented 5 years ago

Tests at https://github.com/11ty/eleventy/blob/0812197b614432668f66ce9b62a524af49aef581/test/TemplateRenderHandlebarsTest.js#L178 and https://github.com/11ty/eleventy/blob/0812197b614432668f66ce9b62a524af49aef581/test/TemplateRenderHandlebarsTest.js#L192

andeersg commented 5 years ago

Thank you for your reply :)

But is there a solution to this? Or is it tricky to solve?

I tested another thing:

Just text with handlebars: {{{{raw}}}}{{#if no}}The thing is no{{/if}} {{{{/raw}}}}

and it's also evaluated to:

<p>Just text with handlebars:  }}The thing is no }}</p>

so it seems like the indentation is not the problem.

zachleat commented 5 years ago

Hmm that’s strange—your result directly contradicts the results of the tests I’ve added, which seem to be almost identical to https://github.com/11ty/eleventy/blob/0812197b614432668f66ce9b62a524af49aef581/test/TemplateRenderHandlebarsTest.js#L192

OH haha I think I’ve just realized what was happening. What template engine are you using to process markdown files? Surely it’s not handlebars. Your addFilter call to add a raw filter is actually a universal filter, which adds to Handlebars, Liquid, and Nunjucks. The default pre-processing template engine for markdown files is Liquid. So in Liquid, it would find {{raw}} inside of {{{{raw}}}} and parse it as valid Liquid syntax (which it is, using the Liquid raw filter you added).

See the info block on https://www.11ty.io/docs/languages/markdown/

zachleat commented 5 years ago

This is an automated message to let you know that a helpful response was posted to your issue and for the health of the repository issue tracker the issue will be closed. This is to help alleviate issues hanging open waiting for a response from the original poster.

If the response works to solve your problem—great! But if you’re still having problems, do not let the issue’s closing deter you if you have additional questions! Post another comment and I will reopen the issue. Thanks!

andeersg commented 5 years ago

Thank you again for your reply, changing the markdown engine to hbs made it work :)