11ty / eleventy

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

markdown with handlerbars code block using `@` fails to build #2434

Closed mehulkar closed 2 years ago

mehulkar commented 2 years ago

Describe the bug The following content throws an error during parsing:

<div {{@foo "default"}}>this</div>

To Reproduce Steps to reproduce the behavior:

  1. Clone https://github.com/mehulkar/11ty-hbs-repro
  2. Follow readme instructions

Expected behavior

Environment:

Additional context

Build works in 0.12, and based on the stack trace, I can see that the liquidjs upgrade (from v6 to v9) seems to be the cause. Reported the issue there as well: https://github.com/harttle/liquidjs/issues/510

pdehaan commented 2 years ago

Not sure if this worked/works in 0.12. It seems to compile in 0.12, but it outputs this (which seems to be ignoring the custom @foo helper):

<div >this</div>

If I upgrade to 1.0.1, I get this error:

npm run build

> build
> npx eleventy

[11ty] Problem writing Eleventy templates: (more in DEBUG output)
[11ty] 1. Having trouble rendering liquid template ./does-not-work.md (via TemplateContentRenderError)
[11ty] 2. unexpected token at "@foo \"default\"", file:./does-not-work.md, line:1, col:6 (via ParseError)
[11ty] 3. unexpected token at "@foo \"default\"" (via AssertionError)
[11ty]
[11ty] Original error stack trace: AssertionError: unexpected token at "@foo \"default\""
[11ty]     ...
[11ty] Wrote 0 files in 0.03 seconds (v1.0.1)

The default template language for .md files is liquidjs, so that template isn't exactly valid. Not sure if your https://github.com/mehulkar/11ty-hbs-repro repo is missing an .eleventy.js config file, but this seemed to work for me and builds the template successfully using 1.0.1 and the custom HBS helper:

// .eleventy.js
module.exports = function (eleventyConfig) {
  eleventyConfig.addHandlebarsHelper("@foo", function(value) {
    return value.toUpperCase();
  });

  return {
    markdownTemplateEngine: "hbs"
  }
};
npm run build

> build
> npx eleventy

[11ty] Writing _site/README/index.html from ./README.md (hbs)
[11ty] Writing _site/does-not-work/index.html from ./does-not-work.md (hbs)
[11ty] Wrote 2 files in 0.04 seconds (v1.0.1)

And now the output seems to be displaying the result from our custom Handlebars helper:

<div DEFAULT>this</div>
mehulkar commented 2 years ago

Not sure if this worked/works in 0.12. It seems to compile in 0.12

The default template language for .md files is liquidjs, so that template isn't exactly valid.

I left out one detail since it was reproducible without, by my template is actually a fenced code block: In other words like this:

```hbs
<div {{@foo "default"}}>this</div>
\``` (ignore the leading backslash on this line and this explanatory text)

This should be valid markdown. I didn't have markdownTemplateEngine set in my real project, so I didn't add any config into the repro either.

The liquidjs preprocessing breaks this markdown seems like a real issue to me, but it turns out I'm not really using any preprocessing capabilities on markdown files, so I can set markdownTemplateEngine: false to fix my build failure.

pdehaan commented 2 years ago

Ah... OK, maybe I'm confused.

If you're using it in a fenced code block but do NOT want Liquid to parse a block of code, you'd need to wrap it in {% raw %}...{% endraw %} tags to avoid LiquidJS trying to parse some handlebars-esque code:

{%- raw -%}
<div {{@foo "default"}}>this</div>
{% endraw -%}

This would still let you use Liquid elsewhere in your Markdown template, if that's something you need/want.

If you don't want Liquid to preprocess your Markdown, you can turn if off globally in the config, or on a per-template basis using something like templateEngineOverride:

---
templateEngineOverride: md
---

```hbs
<div {{@foo "default"}}>this</div>
mehulkar commented 2 years ago

to avoid LiquidJS trying to parse some handlebars-esque code:

It makes sense to use the {% raw %}, and that indeed does work! But to be clear, I'm not attempting to use handlebars templating as part of 11ty's build, my markdown just happens to have a code snippet that is handlebars syntax.

My initial thought is that all templating should skip fenced code blocks by default!

@pdehaan not sure if you're a maintainer, but will leave it up to you to determine whether or not this is a bug! Feel free to close and thank you for the help!

pdehaan commented 2 years ago

I think I'd argue that this isn't a bug. By default Eleventy will preprocess Markdown files (and HTML files and global data files) with your engine of choice.

If you want to opt out of the default preprocessing behavior, you can:

zachleat commented 2 years ago

The reason we don’t automatically raw the contents of fenced code blocks allows you the full power of the parent template’s syntax inside of the code blocks. I’ve used this to great effect by putting code snippets in the _includes folder and then {% include %}-ing them in the markdown file. Then you could run automated tests on those stub files, if you wanted!

So, while requiring raw is a little confusing, there is a lot of power to be gained (with less magic) by this default

zachleat commented 2 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 we will reopen the issue. Thanks!