Eleventy: Access to `_includes`, and `filters` inside Eleventyconfig

Created on 25 Dec 2018  Â·  11Comments  Â·  Source: 11ty/eleventy

This is a wild idea, but what if its possible to add Nunjucks (or other template engine tags) right inside Eleventy Config? This will make it easier to write shortcodes (whilst still using filters).

Example: Say I want to create a testimonial block based on a given name.

  1. I've included a markdown file for the testimonial
  2. I also need to convert the "name" into title case.
eleventyConfig.addShortcode('testimonial', name => {
    return `<div class="c-testimonial o-text">

  // Including a file... 
  {% include "testimonial/" + name + ".md" %}

  // Using filters
  <img src="/images/quotes/${name}.jpg" alt="Picture of {{name | replace('-', ' ') | title }}">
  <p>{{name | replace('-', ' ') | title }}</p>
</div>`
  })

Currently I'm getting the markdown by manually grabbing files with fs.readFileSync, then converting it into markdown with markdown-it.
I'm also writing the title filter manually in eleventyConfig file.

duplicate open-question

Most helpful comment

@zachleat It might be, but you kinda have to implement it twice, yeah? Filters is a small thing. I'm more interested in having the _includes work. But if _includes work... it doesn't make sense that filters don't.

All 11 comments

If you don't mind not using shortcodes, you could look at doing this with nunjucks macros instead.

Have you tried playing with Nunjucks inside eleventy.js?

const nunjucks = require('nunjucks');

module.exports = (eleventyConfig) => {
    ...
    eleventyConfig.addShortcode('testimonial', (name) => {
        const template = `
            <p>Using variables: "{{ name | replace(' ', '-')}}" and "${name}"</p>
            <P>And using includes: {% include 'layouts/partials/my-layout.njk' %}</p>
            `;
        const env = nunjucks.configure('./site/_includes');  <-- Path to 'layouts/partials/...'
        const result = env.renderString(template, {name});

        return result;
    });
 };

@edwardhorsford I don't want half of my code lying in Eleventy as shortcodes, while the other half are import statements from Nunjucks... Very inconsistent that way. That's why I opened this issue.

@pamtbaau This is interesting. I tried it, but I get an error when I try to render anything in .eleventy.js.

TypeError: Cannot read property 'dev' of undefined (Template render error)

    at ChildProcess.exithandler (child_process.js:294:12)
    at ChildProcess.emit (events.js:182:13)
    at ChildProcess.EventEmitter.emit (domain.js:459:23)
    at maybeClose (internal/child_process.js:978:16)
    at Process.ChildProcess._handle.onexit (internal/child_process.js:265:5)

renderString works perfectly outside of .eleventy.js. @zachleat any idea? 🤔

@zellwk A few suggestions:

  • Be advised that renderString() fails silently by default, which might cause issues when 'piping' the result into other filters. You might try adding 'throwOnUndefined' to nunjucks.configure().
  • Stepping through the definition of addShortcode() inside the debugger might reveal more information on the exception and where it occurs.

I tested it further. I had to use nunjucks.Template with the correct env to render without the error message above.

const renderString = (template, ctx) => {
  const tmpl = new nunjucks.Template(template, env)
  return tmpl.render(ctx)
}

But in this case, I only have access to Nunjucks built-in filters. I don't have access to the filters I in Eleventy :(

@zellwk You are using bare 'nunjucks' so you'll have to add the filters yourself:

module.exports = (eleventyConfig) => {
    ...
    // Add hash to filePath. eg '/style.fa9cbfcdf604101ef3a58cde7d2aba08.min.css'
    eleventyConfig.addFilter('addVersion', (filePath) => {
        ...
    });

    eleventyConfig.addShortcode('testimonial', (name) => {
        const template = `
            ...
            <p>Custom filter: {{ '/assets/css/style.min.css' | addVersion }}</p>
            ...
            `;
        const env = nunjucks.configure('./site/_includes');

        // Add single filter or loop through config.nunjucksFilters array to add all filters
        env.addFilter('addVersion', eleventyConfig.nunjucksFilters.addVersion);

        const result = env.renderString(template, {name});

        return result;
    });
};

Hmm!

My first reaction here is that if you want to use filters in your shortcodes it’s just a pure JavaScript problem, no?

function uppercase(str) {
  // …
}
eleventyConfig.addFilter('uppercase', uppercase);
eleventyConfig.addShortcode('testimonial', (name) => {
  return uppercase(name);
});

Can you be more specific about what this feature would buy you over the above example?

@zachleat It might be, but you kinda have to implement it twice, yeah? Filters is a small thing. I'm more interested in having the _includes work. But if _includes work... it doesn't make sense that filters don't.

This might be tangentially related to #148 if we want a JS API to do these template renders inside of configuration files too.

Note that we did ship a getFilter configuration API method in 0.11.0: https://www.11ty.dev/docs/filters/#access-existing-filters

For the rest of it, I’d consider that a duplicate of #148 to render templates in the configuration file. Going to close this old one as a duplicate—thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

DirtyF picture DirtyF  Â·  3Comments

zachleat picture zachleat  Â·  3Comments

veleek picture veleek  Â·  3Comments

michrome picture michrome  Â·  3Comments

ndaidong picture ndaidong  Â·  4Comments