Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shortcodes, white space, and encoding #402

Closed
Heydon opened this issue Feb 7, 2019 · 10 comments
Closed

Shortcodes, white space, and encoding #402

Heydon opened this issue Feb 7, 2019 · 10 comments
Labels
education: template language Learning about a template language. waiting-to-close Issue that is probably resolved, waiting on OP confirmation.

Comments

@Heydon
Copy link

Heydon commented Feb 7, 2019

Hi,

I'm having fun with shortcodes and (if I'm not missing something obvious) there are issues with the way white space is handled.

Here is my shortcode for creating a little aside thingy (note the wrapping for readability inside the template literal):

  eleventyConfig.addPairedShortcode('note', function (content, title) {
    const slug = slugify(title).toLowerCase();
    return `
      <aside aria-labelledby="${slug}">
        <h4 id="${slug}">${title}</h4>
        ${content}
      </aside>
    `;
  });

Using

{% note 'Title' %}

Some text.

Some other text

{% endnote %}

I get

<pre><code>  &lt;aside aria-labelledby=&quot;title&quot;&gt;
    &lt;h4 id=&quot;title&quot;&gt;Title&lt;/h4&gt;
</code></pre>
<p>Some text.</p>
<p>Some other text</p>
<pre><code>  &lt;/aside&gt;
</code></pre>

Weird, because it encodes and renders as a <pre>. So I tried removing the white space with the hyphens:

{%- note 'Title' -%}

Some text.

Some other text

{%- endnote -%}

The result is... different:

<aside aria-labelledby="title">
<h4 id="title">Title</h4>
Some text.</p>
<p>Some other text
</aside>

Looks like all the white space is collapsed, so my two markdown paragraphs are broken. The only way I seem to be able to fix it is by putting the template on one line (and not using the nunjucks white space removal):

  eleventyConfig.addPairedShortcode('note', function (content, title) {
    const slug = slugify(title).toLowerCase();
    return `<aside aria-labelledby="${slug}"><h4 id="${slug}">${title}</h4>${content}</aside>`;
  });

Is there something else to manage this? Perhaps I need to switch to a different template language?

@kleinfreund
Copy link
Contributor

kleinfreund commented Feb 7, 2019

Just as a note: The reason why it is rendered inside a <pre><code> tag is because during the Markdown rendering, the indentation of the template literal is interpreted as the syntax for code blocks.

So, one admittedly uncomfortable workaround would be a de-indent function that’s called like return deIndent(indentedString);.

Something along these lines:

  1. Find the first line that has non-whitespace content (e.g. <). Remove all lines before this line (in your case, one newline character).
  2. Measure the amount of whitespace on this line.
  3. For each line, if it starts with the measured amount of whitespace, remove that much leading whitespace.
  4. Trim the result for fun and giggles

@Heydon
Copy link
Author

Heydon commented Feb 7, 2019

Thanks for the swift reply @kleinfreund! That is indeed a bit uncomfortable, but I can't think of anything better at this stage either.

@zachleat
Copy link
Member

zachleat commented Mar 6, 2019

Hmm, yeah. I’m not sure what the best path forward is here either. I will say the “indent to code block” feature of markdown has been a source of confusion for quite a few other people too, kinda wish we could opt-out of that altogether in markdown-it somehow in a major version release, hmm.

https://spec.commonmark.org/0.28/#indented-code-blocks

That said, there are two problems at play here:

  1. Correct, expected output content (not whitespace related)
  2. Correct indentation and whitespace

Number one can be solved by removing the whitespace from your shortcode, like so:

  eleventyConfig.addPairedShortcode('note', function (content, title) {
    const slug = slugify(title).toLowerCase();
    return `
<aside aria-labelledby="${slug}">
    <h4 id="${slug}">${title}</h4>
    ${content}
</aside>
`;
  });

Number 2 is a much harder problem, as the shortcodes have no notion of the contextual indention in which they are used.

@zachleat
Copy link
Member

zachleat commented Mar 6, 2019

Does this answer your question? If you’d like to think deeper about a bigger solution to problem two we should open a new enhancement issue for this, but that’s likely a templating engine issue.

One thing you could do is maybe use Prettier to indent the output HTML in an Eleventy transform? I haven’t tried it on HTML yet but it’s advertised on the tin:

https://www.npmjs.com/package/prettier
https://www.11ty.io/docs/config/#transforms

@zachleat zachleat added the waiting-to-close Issue that is probably resolved, waiting on OP confirmation. label Mar 6, 2019
@Heydon
Copy link
Author

Heydon commented Mar 6, 2019

@zachleat Yeah, I guess it's not so much an 11ty issue and am happy to lay this to rest. Thanks for the discussion.

@zachleat
Copy link
Member

zachleat commented Mar 6, 2019

Well I wouldn't necessarily say it's not an Eleventy problem. I think at the very least I should add the indentation caveat to the common pitfalls page.

@Heydon
Copy link
Author

Heydon commented Mar 6, 2019

Ah yeah, good shout.

zachleat added a commit to 11ty/11ty-website that referenced this issue Mar 7, 2019
@zachleat
Copy link
Member

zachleat commented Mar 7, 2019

Docs added here: https://www.11ty.io/docs/languages/markdown/#there-are-extra-%3Cpre%3E-and-%3Ccode%3E-in-my-output

Closing this for now, though I would not be surprised to see continued confusion surrounding this issue again

@zachleat zachleat closed this as completed Mar 7, 2019
@zachleat zachleat added the education: template language Learning about a template language. label Mar 7, 2019
@jeremenichelli
Copy link

jeremenichelli commented May 31, 2020

Hey yo, I know this is already old and cover in dust, but got here trying to see if there was a better solution to what I was doing. Leaving this here in case is useful for somebody.

Option 1, don't judge me but if we want non-indented strings while indenting I once tried this.

const shortcode = (something) => (
  ([
    '<p>',
        {something ? 'something' : 'nothing'},
    '</p>'
  ]).join('')
)

Option 2, create a helper function trimming and cleaning tabs and line breaks:

const deindent = (str) => str.replace(/\n|\t/g, '').trim()

const shortcode = (something) => deindent(
  `<p>
        ${something ? 'something' : 'nothing'}
    </p>
  `
)

Both work, I don't like neither 😆

@oliverjam
Copy link

(Sorry for adding to a closed issue! I couldn't find any others mentioning my specific problem here so I thought I'd try and help future me when I inevitably hit this again and google it)

The previous solution worked great for me, except for paired shortcodes that can have markdown content inside. E.g.

{% box %}

This _is_ processed as **markdown**

{% endbox %}

De-indenting the entire thing removes the line breaks around the markdown, which means markdown-it doesn't process it. You just get the raw text output in the HTML.

My best attempt at fixing it is to use a tagged template literal, since that lets you process the string parts and interpolations separately.

// removes all new lines, tabs and pairs of spaces
function deindent(str) {
  return str.replace(/\n|\t|\s{2}/g, "").trim();
}

// deindent HTML strings
// but NOT interpolated content
function html(strings, ...vars) {
  return strings
    .map((str, index) => {
      const nextVar = vars[i] || "";
      return deindent(str) + nextVar;
    })
    .join("");
}

Usage:

function box(content) {
  return html`
    <div class="box">
      ${content}
    </div>
  `
}

All the indentation in your HTML string will be removed, but it'll preserve any inside the content variable.

Bonus: If you're using a plugin like es6-template-html you also get syntax-highlighting for free since the function is named html 🎉 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
education: template language Learning about a template language. waiting-to-close Issue that is probably resolved, waiting on OP confirmation.
Projects
None yet
Development

No branches or pull requests

5 participants