11ty / eleventy

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

Expose Markdown renderer as a built-in filter #658

Closed pepelsbey closed 4 years ago

pepelsbey commented 5 years ago

Is your feature request related to a problem? Please describe.

I often want to include Markdown snippets of content into my templates. It’s super convenient to store them in Markdown, not HTML. But inclusion won’t render them, it would just include what’s there untouched, and it’s fare.

{% include 'snippet.md' %}

Describe the solution you'd like

Wouldn’t that be wonderful if Evelenty could expose a default markdown filter (along with url and slug) that does the same thing with Markdown using built-in renderer?

The most convenient solution would be:

{% include 'snippet.md' | markdown %}

But I’m not sure that would work.

Describe alternatives you've considered

I made a custom filter to render snippets after inclusion:

config.addFilter('markdown', function(value) {
    let markdown = require('markdown-it')({
        html: true
    });
    return markdown.render(value);
});

And then:

{% set snippet %}{% include 'snippet.md' %}{% endset %}
{{ snippet | markdown | safe }}`

But in this case I need to have the same markdown-it installed in my package.json and initialized as a filter in my config, though it’s already available and widely used by Eleventy.

coolsoftwaretyler commented 5 years ago

I want this in like... every project I ever build.

Ryuno-Ki commented 5 years ago

Hm, would it be limited to Markdown?

I mean, as a tech blogger I could refer to CSS or JS files and would have them included as well. Or take preprocessors (SASS or CoffeeScript).

pepelsbey commented 5 years ago

It would be great to be able to preprocess any other included formats, not just Markdown. But this might require hacking the way {% include %} works, I’m not sure if it’s possible.

francisbarton commented 5 years ago

I +1d this issue because I have the same desire as the OP, and I like the outcome they recommend.

I am a bit bemused because I don't understand why markdown in my templates is not being included, when the 11ty docs state that You can use any template language in your layout.

I've raised it as an issue here but not had any response yet.

I am a bit out of my depth here and rather confused, but hoping someone will help me see the light, as I would like to start being creative with eleventy templates but as yet I don't understand them well enough. And I am finding that the documentation is not helping me get out of my stuck situation.

francisbarton commented 5 years ago

Hm, would it be limited to Markdown?

I mean, as a tech blogger I could refer to CSS or JS files and would have them included as well. Or take preprocessors (SASS or CoffeeScript).

According to the docs, JS is already supported as a template language (which I think is what @pepelsbey is asking for)?

Ryuno-Ki commented 5 years ago

No, that was not what I head in mind. I'm sorry for not being clear enough.

Look, I use my website as playground. Now, say, that I am trying some JavaScript to try something out. Instead of having to copy its content into a blog post I wondered whether I could have some Nunjucks filter in my Markdown to refer to the file and include its content as within code fences.

Could be stupid down the road (when I iterate over the JS file), but it was an idea.

I'm also fine with focussing on Markdown filter first. Sometimes it helps planning of you keep in mind that it could be more generic. On the other hand YAGNI.

I'm uncertain and thus asked.

francisbarton commented 5 years ago

@Ryuno-Ki I expect it's me who is not clear! I'm very confused... :-) I guess I am just stuck at the point of working out the difference between what you are suggesting and what eleventy already provides in terms of templating functionality, i.e. I don't really understand why it is that what you want to do doesn't already work, when 11ty already supports md/js as template languages.

Ryuno-Ki commented 5 years ago

My guess: Nobody pushed a PR for adding the file to https://github.com/11ty/eleventy/tree/b5613504a9524dfeb8be8c00a3c30d24313f4325/src/Filters

You basically have what you need in the OP.

coolsoftwaretyler commented 5 years ago

I started working on adding this as a universal filter, but then figured it might be more appropriate as an extension, rather than expanding the scope of what universal filters do.

A quick first draft: https://www.npmjs.com/package/eleventy-plugin-markdown-shortcode

Does this help you out @pepelsbey?

mashdot commented 4 years ago

Personally I am trying to include a .njk file which contains both nunjucks and markdown, but only the nunjucks is being rendered.

I had hoped your plugin above would work, but alas it only renders markdown.

coolsoftwaretyler commented 4 years ago

@mashdot - I'm using it in my own nunjucks without issue. I keep the markdown files in the _includes directory, include them through the shortcode, and it sptis out my markdown content inside nunjucks.

What's the part missing for you?

mashdot commented 4 years ago

If put the {% include "test.md" %} directly within a markdown page, it will render the markdown and nunjucks.

(I also have markdownTemplateEngine: "njk", configured in .eleventy.js)

However as soon as I move the include up a level into a .njk layout the nunjucks is rendered but not the markdown. However all existing markdown and nunjucks in the page is rendered correctly. (So the layout templating already works fine)

e.g. # {{ title }} gets rendered as # title of the page rather than <h1>title of the page</h1>

If instead I use {% markdown "/_includes/test.md" %} what gets rendered is <h1>{{ title }}</h1>

Maybe I have my config confused, but basically I want to work with html,md,njk files and that I suppose in order njk, md, html should be rendered. I assumed I could simply include a file with any of that content and it would all "eventually" get rendered on output.

coolsoftwaretyler commented 4 years ago

Ah I see, plugin I wrote works for a more limited use case where you have a .njk file and you want to include a .md file (composed entirely and only of markdown) and have it rendered as HTML for the .njk file to process.

mashdot commented 4 years ago

BTW I discovered how to get it to work! Although not directly related to the topic, adding comments here which may be helpful to others in the future.

In my .eleventy.js I have markdownTemplateEngine: "njk" and htmlTemplateEngine: "njk" set.

I then changed my layout to .md extension. These are composed of html, nunjucks, and markdown.

My include file is now .md and contains html, nunjucks, and markdown, and simply using the standard {% include "partials/footer.md" %} in the parent layout gets included and rendered.

So basically keep the layouts and includes as .md files and set markdownTemplateEngine: "njk".

Ryuno-Ki commented 4 years ago

Another idea (untested): Using set to capture a block - the {% include %} part - and then process the markdown within.

pepelsbey commented 4 years ago

@Ryuno-Ki, that’s exactly what I’m doing right now, mentioned in the first comment.

esharri2 commented 4 years ago

Here is how I'm tackling this. Not too different from @pepelsbey 's solution and perhaps not as robust as @ogdenstudios 's package. Simple to use though.

In .njk file:

{% set snippet %}{% include './content/my-markdown.md' %}{% endset %}
{% markdown snippet %}

In eleventy.config.js:

// Note: You do not need to add markdown-it to package.json. 
let markdown = require("markdown-it")({
  html: true
});

module.exports = config => {
...
  config.addNunjucksShortcode(
    "markdown",
    content => `<div class="md-block">${markdown.render(content)}</div>`
  );
...
};
pdehaan commented 4 years ago

And if you didn't want to use and intermediate {% set %} (Nunjucks) or {% capture %} (Liquid), you could use a paired shortcode:

const markdownIt = require("markdown-it");

module.exports = function (eleventyConfig) {
  const md = new markdownIt({
    html: true
  });

  eleventyConfig.addPairedShortcode("markdown", (content) => {
    return md.render(content);
  });

  return {
    dir: {
      input: "src",
      output: "www"
    }
  };
};
<h1>Markdown in Nunjucks</h1>

{% markdown %}
  {%- include "my-markdown.md" -%}
{% endmarkdown %}
coolsoftwaretyler commented 4 years ago

Yup! That's about right. It's been working for me for months now with no real issue.

stevenmilstein commented 4 years ago

And if you didn't want to use and intermediate {% set %} (Nunjucks) or {% capture %} (Liquid), you could use a paired shortcode:

const markdownIt = require("markdown-it");

module.exports = function (eleventyConfig) {
  const md = new markdownIt({
    html: true
  });

  eleventyConfig.addPairedShortcode("markdown", (content) => {
    return md.render(content);
  });

  return {
    dir: {
      input: "src",
      output: "www"
    }
  };
};
<h1>Markdown in Nunjucks</h1>

{% markdown %}
  {%- include "my-markdown.md" -%}
{% endmarkdown %}

@pdehaan Thanks so much for the solution. It's exactly what I've been stuck on.

However, when I try it, I get "Error: template not found:" for my my-markdown.md. It's as if my eleventy thinks my-markdown.md should be a my-markdown.njk, despite the it's layout front matter points provides the path to its layout. I've tried relative & absolute paths.

I tried some of the other solutions proposed & yield the same response.

my config includes:

    markdownTemplateEngine: "njk",
    htmlTemplateEngine: "njk",
  };

and my dir config is the default.

Any help would be greatly appreciated!

pdehaan commented 4 years ago

@stevenmilstein I posted a simple example at https://github.com/pdehaan/11ty-658

If you want to include a file relative to the current .njk template, you might need to prefix the path with ./:

{% markdown %}
  {%- include "./my-markdown.md" -%}
{% endmarkdown %}

Although this had the unexpected side effect of compiling the included the partial "./my-markdown.md" file into the destination folder's "./www/my-markdown/index.html", which isn't what I wanted.


Or apparently if you're including from your ./src/_includes/ directory, you can include it directly:

{% markdown %}
  {%- include "foo.md" -%}
{% endmarkdown %}
stevenmilstein commented 4 years ago

@pdehaan Thanks so much for the timely reply.

I guess my problem is that my markdowns use different layout njk. I'll submit a PR with my modifications. For some reason, the layouts are not working.

Thanks again for your time!

zachleat commented 4 years ago

Hey sorry y’all—I believe this is a duplicate of #148.

jamest1903 commented 1 year ago

This is an old issue but I wanted to pass on my solution to importing many MD files to one page with 11ty. All my MD content files use the base layout. So effectively the base.njk will populate the main MD content but also import another MD file I use for my navigation links

source: https://www.11ty.dev/docs/plugins/render/#usage

.eleventy.js

const { EleventyRenderPlugin } = require("@11ty/eleventy");
module.exports = config => {
...
  // add 11ty render function to use in njk files
  config.addPlugin(EleventyRenderPlugin);
...

_includes\layouts\nav.njk

{% renderFile "./Navigation/navLinks.md" %}

_includes\layouts\base.njk

...
  <div class="navMenu" >
      {% include './nav.njk' %}
  </div>
...
  <div class='layout-post main-container'>
    <div class='centered'>
      <div class='column'>
        <div class='ui basic segment'>
      {{ content | safe }}
        </div>
      </div>
    </div>
  </div>
  <footer>
...