11ty / eleventy

A simpler static site generator. An alternative to Jekyll. Transforms a directory of templates (of varying types) into HTML.

Home Page:https://www.11ty.dev/

Github PK Tool:Github PK Tool

Repository from Github https://github.com/11ty/eleventyRepository from Github https://github.com/11ty/eleventy

Pug templates need better filter support

kaceo opened this issue · 4 comments

Is your feature request related to a problem? Please describe.
One of the best feature of Eleventy is the ability to mix and match different template languages to produce a static site. This advantage falls short when some template languages are more empowered than others.

Currently Pug does not support Eleventy universal filters. This makes it difficult for Pug users to adapt to the Eleventy environment, or for a template in one language to be modified into a different language.

Describe the solution you'd like
In njk/liquid/md a data is evaluated like this: {{ myData | myFilter }}

In Pug a data is used like this: #{ myData }. It does not understand this expression #{ myData | myFilter } as only one variable name should be inside the parenthesis.

My solution is for Eleventy to run one pre-processing step in front of the Pug processing.

During this pre-processing step, Eleventy searches for these expressions #{ *** } and check if they need evaluation (whether there is a | inside the string):

  • if there is no filter, just skip this variable. It will be Pug's responsibility to perform substitution during run.

  • if there is a filter expression, Eleventy evaluates the filter and generate a temporary unique variable, and place the variable/value into the data stack, and replace the Pug template variable name.

As Eleventy is a compiler, data values can be determined without side effects during template evaluation. So the invention of temporary variables to store the evaluated eexpressions is safe.

Describe alternatives you've considered
Similar requests but that suggests a solution that requires extending Pug with user-customisation. In my opinion, that is a strategy that will lead to incompatibility between Pug and other template languages.

Additional context
I come into Eleventy because it is described as a "Jekyll in Nodejs". I tried to convert a complete site from Jekyll into Eleventy. Filters are the most problematic issues, as not all Jekyll filters have equivalent (eg. absolute_url, relative_url etc). But this is solveable by plugins.

Pug as my preferred template language further complicate the development, as it has only some of the expressive power of the other languages. Although I can rewrite the logic, I want to keep the Eleventy idioms as much as possible, and that means filters should be retained inside Pug.

Why shouldn't filters be added as functions visible to pug, with globals?
https://pugjs.org/api/reference.html#options
and an example pugjs/pug#3198 (comment)

For example, we can have global.filters with all the registered filters from e11y, and they will be usable as #{filters.toc(content)} in pug compared to {{content | toc}}. Yes, it is global object pollution, but who cares if your project is a self-contained and self-sufficient site generator?

.eleventy.js

// taking https://www.npmjs.com/package/eleventy-plugin-toc as an example
const pluginTOC = require('eleventy-plugin-toc');
const markdownIt = require('markdown-it')
const markdownItAnchor = require('markdown-it-anchor');

module.exports = function(eleventyConfig) {
    eleventyConfig.setLibrary('md', markdownIt().use(markdownItAnchor));
    eleventyConfig.addPlugin(pluginTOC);
    global.filters = eleventyConfig.javascriptFunctions; // magic happens here
    eleventyConfig.setPugOptions({ // and here
        globals: ['filters']
    });
};

anyPugFile.pug

aside.aPageNavigation!=filters.toc(content)

or

aside.aPageNavigation !{filters.toc(content)}

And it works! dab noises


Another example:

{% set previousPost = collections.posts | getPreviousCollectionItem(page) %}
{% set nextPost = collections.posts | getNextCollectionItem(page) %}
{% if previousPost %}Previous Blog Post: <a href="{{ previousPost.url }}">{{ previousPost.data.title }}</a>{% endif %}
{% if nextPost %}Next Blog Post: <a href="{{ nextPost.url }}">{{ nextPost.data.title }}</a>{% endif %}

becomes

- var prev = filters.getPreviousCollectionItem(collections.posts, page)
- var next = filters.getNextCollectionItem(collections.posts, page)
if prev
    a(href=prev.url)=prev.data.title
if next
    a(href=next.url)=next.data.title

I think it's worth mentioning that the above solution solves the problem just fine. Additionally, the built in eleventy filters that you see in so many of the docs' examples like: {{ myThing | url }} will all "just work" if you do the above. In pug it's like:

a(href=filters.url('/my-unsafe-url')) My Link That Will Be Correct

@wolfejw86 @CosmoMyzrailGorynych Amazing!! The above should be in docs!!