How do I mark a filter as being safe?

1k Views Asked by At

I am trying to set up a syntax highlighting filter for nunjucks using highlight.js. It seems pretty easy to do. In my elevnety.js file I included:

    const hljs = require('highlight.js');
    eleventyConfig.addFilter('highlight', function(txt) {
        return hljs.highlightAuto(txt).value;
    });

It appears that highlight.js is a safe filter and will properly escape it's contents and add markup to control the highlighting, so there is nothing else to do.

In my njk page I try to use this with

{% filter highlight %}
  <xmlstuff>
    <myelements attr1="foo" />
  </xmlsfuff>
{% endfilter %}

The highlighting markup is being generated correctly, but the whole result is being escaped (by nunjucks perhaps) so the resulting page renders all the markup code. Here is what is getting added to the output html page:

  &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;xmlstuff&lt;/span&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;myelements&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;attr1&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&quot;foo&quot;&lt;/span&gt; /&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;xmlsfuff&lt;/span&gt;&amp;gt;&lt;/span&gt;

I know that nunjucks has a safe filter to prevent this from happening, but I don't know how to apply that to say that my filter block doesn't need escaping, and I could not find anything in the documentation. I tried a number of approaches, but they all failed:

{% filter highlight | safe %}
  <xmlstuff>
    <myelements attr1="foo" />
  </xmlsfuff>
{% endfilter %}
{% filter highlight %}
  <xmlstuff>
    <myelements attr1="foo" />
  </xmlsfuff>
{% endfilter | safe %}
{{ {% filter highlight %}
  <xmlstuff>
    <myelements attr1="foo" />
  </xmlsfuff>
{% endfilter %} | safe }}

Is there any way to mark this filter block as safe?

3

There are 3 best solutions below

0
Aikon Mogwai On BEST ANSWER

Try to apply env.filters.safe before output.

var nunjucks  = require('nunjucks');
var env = nunjucks.configure();    

env.addFilter('safeFilter', str => env.filters.safe(str));
env.addFilter('unsafeFilter', str => str);

var html = env.renderString(
    `{{ str | safeFilter }}\n{{ str | unsafeFilter }}`, 
    {str: '<h1>Hello</h1>'}
);

console.log(html);
0
opeongo On

@aikon-mogwai posted the correct answer. However for eleventy a bit more is required because the filter needs to be set up in the eleventy.js file and we need to get access to the nunjucks environment. I'll add my complete solution here for posterity.

I does not look like eleventy provides access to the nunjucks environment, so we need to create one and set it to override the existing environment. After that it is pretty well what he said:

module.exports = function(eleventyConfig) {
  eleventyConfig.addPassthroughCopy('src/images')

    /*
     * Create and register a Nunjucks environment, just so we
     * can get access to the safe filter.
     */
    let Nunjucks = require("nunjucks");
    let nunjucksEnvironment = new Nunjucks.Environment(
      new Nunjucks.FileSystemLoader("src/_includes"), { }
    );
    eleventyConfig.setLibrary("njk", nunjucksEnvironment);

    /*
     * Set up a syntax highlighting filter for code blocks
     */
    const hljs = require('highlight.js');
    eleventyConfig.addNunjucksFilter('highlight', function(txt, lang) {
        var txt2;
        if (lang == undefined)
            txt2 = hljs.highlightAuto(txt).value;
        else
            txt2 = hljs.highlight(lang, txt).value;
        return nunjucksEnvironment.filters.safe(txt2);
    });

  return {
    dir: { input: 'src', output: 'dist', data: '_data' },
    passthroughFileCopy: true,
    templateFormats: ['njk', 'md', 'css', 'html', 'yml'],
    htmlTemplateEngine: 'njk'
  }
}
0
Morgan Kelsie McGuire On

I achieved this with [email protected] like this:

eleventyConfig.addNunjucksFilter('doThing', function (value) {
  return this.env.filters.safe(doThing(value));
})