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

Rendering excerpt with renderContent and safe filters renders outside container #3474

Closed aarongoldenthal closed 3 weeks ago

aarongoldenthal commented 1 month ago

Operating system

Windows 11 (and Linux container)

Eleventy

3.0.0

Describe the bug

I was trying to work around the issue outlined in https://github.com/11ty/eleventy/issues/1380, but using the built-in renderContent filter instead of a custom filter.

The template has:

<p class="excerpt">{{ post.data.page.excerpt | renderContent("md") | safe }}</p>

Without safe, it renders correctly, but is escaped:

<p class="excerpt">&lt;p&gt;The excerpt, correctly rendered, but escaped...&lt;/p&gt</p>

With the safe filter what's rendered is:

<p class="excerpt"></p><p>The excerpt, correctly rendered...</p><p></p>

Without renderContent, e.g. <p class="excerpt">{{ post.data.page.excerpt | safe }}</p>, the content renders as expected, so doesn't seem specific to safe.

Reproduction steps

  1. Setup excerpt per this
  2. Add eleventyConfig.addPlugin(EleventyRenderPlugin) to .eleventy.js
  3. Create page with excerpt
  4. Create Nunjucks template where the content is within another HTML element, rendered with renderContent, and the safe filter
  5. Content is rendered after the parent element in the template, with an extra <p>

Expected behavior

Content should render as expected in the containing HTML element.

Reproduction URL

No response

Screenshots

No response

zachleat commented 1 month ago

What’s your content template (with the excerpt) look like? Seems okay with this test .md file:

---
key: value
---
# Excerpt
<!-- excerpt -->
Full content
aarongoldenthal commented 1 month ago

After a closer look and some digging, I understand the problem: nested <p> tags are not allowed, and renderContent() is wrapping everything in a <p>, so the browsers are fixing them by auto-closing (per this, and the spec). If I change the containing element to something else (e.g. <div>) then all is as expected. But, is there an equivalent of renderContent() that only renders the inner contents with no container?

Maybe what I really want is this TODO, which is what my current filter does.

zachleat commented 3 weeks ago

Notably, this is a byproduct of using renderContent("md") which renders markdown in block mode. You’re right that you can use a <div> or you could do something with the renderInline method of markdown-it: https://github.com/markdown-it/markdown-it/#simple

I’m not sure if this merits an addition but I’m happy to reconsider if other folks weigh in wanting access to rendering markdown inline instead of block!

aarongoldenthal commented 3 weeks ago

Notably, this is a byproduct of using renderContent("md") which renders markdown in block mode. You’re right that you can use a <div> or you could do something with the renderInline method of markdown-it: https://github.com/markdown-it/markdown-it/#simple

That's basically what I'm doing today, which works:

import markdownIt from 'markdown-it';

export default function (eleventyConfig) {
    ...
    eleventyConfig.addFilter('renderHTML', (content = '') =>
        markdownIt({ html: true, linkify: true }).renderInline(content.trim())
    );
   ...
}

I was just thinking it was one more custom piece of code to remove, so I'd support inline if there's enough interest.