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 #13

Closed
Heydon opened this issue Dec 29, 2017 · 26 comments
Closed

Shortcodes #13

Heydon opened this issue Dec 29, 2017 · 26 comments

Comments

@Heydon
Copy link

Heydon commented Dec 29, 2017

I'm wondering how shortcodes might be handled. I really like how Hugo uses shortcodes (https://gohugo.io/content-management/shortcodes/) but they would be even better if they could be run through node scripts (Hugo is written in Golang and has no support for this currently).

Essentially, I'd expect to be able to write little snippets that take arguments, referencing a script that outputs markup and injects it back as part of the processed template. E.g.

{{shortcodeName arg1="foo" arg2="bar"}}

would run a script called shortcodeName.js that takes "foo" and "bar" then exports/returns markup with those values processed somehow.

@zachleat
Copy link
Member

Very interesting! I like this idea. Are you tied to any specific templating engine? Looks like it’d be pretty easy to make some minor modifications here to get something working in Nunjucks using function calls and/or macros. I could get data imports working with .js files in addition to .json to facilitate this.

https://mozilla.github.io/nunjucks/templating.html#macro

{{ field('user') }}
{{ field('pass', type='password') }}

which works well with import for namespacing:
https://mozilla.github.io/nunjucks/templating.html#import

{% import "forms.html" as forms %}

{{ forms.label('Username') }}
{{ forms.field('user') }}

Lemme think about how to do this a little bit more.

@Heydon
Copy link
Author

Heydon commented Dec 30, 2017

No, not tied to anything really! Just like the concept. The basic function call/macro mechanism sounds like the right base.

NB Hugo also allows this kind of form, where the inline content can be processed as a further argument to the shortcode function/template:

{{% shortcodeName %}}
   Inner content
{{% /shortcodeName %}}

@zachleat
Copy link
Member

zachleat commented Jan 4, 2018

Also considering something more future-compatible with some form of (no JS at runtime) web components. I think it’s very important to maintain a 0-JS output by default here. I don’t want this to turn into a JS framework.

Discussion here https://twitter.com/zachleat/status/947206049228083201

Highlights
https://w3c.github.io/webcomponents/spec/custom/#
https://developers.google.com/web/fundamentals/web-components/customelements
https://github.com/skatejs/skatejs/blob/master/packages/ssr/README.md

@zachleat
Copy link
Member

Sorry to bikeshed in public but this looks very interesting. I’m leaning towards postHTML here, especially if I can post-process templates and keep this layer rendering engine agnostic:

https://github.com/posthtml/posthtml-custom-elements

A combination of custom elements and this would be quite interesting, I think:
https://github.com/island205/posthtml-web-component

@Heydon
Copy link
Author

Heydon commented Jan 10, 2018

Boo to private bikesheds. Bike storage for the people!

So, if I get the implication correctly, one could use this to create custom elements, that could be inserted into the markdown (or HTML) and expanded as a shortcode might be. Sounds good to me.

I think it’s very important to maintain a 0-JS output by default here. I don’t want this to turn into a JS framework.

Right, I was only interested in JS running on the server (a node script) per shortcode / custom element, as required. Nothing on the client.

@cferdinandi
Copy link

One area where Hugo falls flat that I think would be a big win if you can swing it: Hugo has partials and shortcodes, and they're two separate things.

You can reference a partial in a shortcode but not vice-versa, and partials can't accept arguments or variables.

Being able to use repeatable code (with some conditional information) in both content and templates would be huge.

I have a lot of content on my site that's similar but with small variations, and shortcodes help me keep the whole thing a lot more DRY and manageable.

@Heydon
Copy link
Author

Heydon commented Jan 20, 2018

@cferdinandi
Right! I actually wrote a special shortcode for pulling in partials by filename to get round this: https://thepaciellogroup.github.io/cupper/patterns/writing/snippets/

@cferdinandi
Copy link

@Heydon That looks awesome! Do you mind sharing the code behind that shortcode? (If its on that page, I'm sorry, but I can't find it.)

@Heydon
Copy link
Author

Heydon commented Jan 20, 2018

@cferdinandi
Copy link

Thanks @Heydon !

@zachleat
Copy link
Member

zachleat commented Mar 9, 2018

Starting to look at this closer @cferdinandi, can you elaborate on a use case where you’d want to pass data from the shortcode to the partial? Trying to wrap my head around that.

@cferdinandi
Copy link

@zachleat For me, personally, it's less about passing info from a shortcode to a partial and more that I don't understand why Hugo treats them as two separate entities.

This is perhaps a bias from my WordPress background, but I use repeatable content in both theme/template files and content. For example, I use an encoded version of my email address to minimize spammer harvesting. Sometimes that's embedded in content itself (like on my about page), while other times I use it in a template or theme file (hypothetically, like my footer).

Today in Hugo, that's a shortcode and a partial, because you need both for both use cases. They each return a link element with the encoded email address as both the href and the link text.

The shortcode version also allows me to pass in an optional query string to autopopulate a subject and body to the mailto: link—something I can't do with the partial.

In an ideal world, there would be just one shortcode/partial type that would work everywhere and allow variables to get passed in.

Did I explain that well? If I did a bad job explaining anything, please ask away!

@eeeps
Copy link

eeeps commented Apr 6, 2018

Just wanted to pop in here and say I had a brief discussion with @philhawksworth about this issue at VueConf a week+ ago and he opined that this kind of functionality might be best left to the templating engines. I think the example he used was Nunjucks macros? https://mozilla.github.io/nunjucks/templating.html#macro

EDIT: doh Zach said the same thing above back in December.

@chrisdmacrae
Copy link
Contributor

chrisdmacrae commented Apr 12, 2018

@zachleat @cferdinandi I've done this in Hugo, for Cloudinary support.

I created a series of Hugo partials that generated the Cloudinary URLs and handled transforms. The shortcodes then just specified their shortcode logic (params, etc) and then passed the logic to the partials.

This is necessary in Hugo, because you need to have a template file that encapsulates the shortcode logic. I don't think making shortcodes === partials would ever make sense in Hugo, as it would create a really ugly interface.

However, in Eleventy you could have that all encapsulated in the plugin/config file, and just have a template that takes the standard data.

@chrisdmacrae
Copy link
Contributor

Anyways, this should already be handled by Engine & Universal Filters, provided we build wider support for all engines.

@dillonbheadley
Copy link

This all sounds great! FYI to consider: an alternative to postHTML is Reshape. I too am a fan of Hugo snippets but feel like they missed an opportunity to combine them with partials. You have to have code in 2 places if you want a partial and a snippet that outputs the same thing, or as mentioned a snippet that just then uses a partial internally and passes along the props.

@zachleat zachleat added the needs-votes A feature request on the backlog that needs upvotes or downvotes. Remove this label when resolved. label May 3, 2018
@zachleat
Copy link
Member

image

Making progress! 😎

@zachleat
Copy link
Member

zachleat commented Jun 22, 2018

After reading all of the feedback about what y’all would like shortcodes to do (and making a working demo in Liquid), I think the best implementation here is going to wrap a nicer API around template engine Custom Tags. This should give you full access to template engine features (local variables, full tag access inside the shortcode, scoping, etc).

While it isn’t template engine agnostic, I think it’ll be far more useful.

@zachleat zachleat added this to the v0.5.0 milestone Jun 26, 2018
zachleat added a commit that referenced this issue Jun 26, 2018
Adds support for Shortcodes (supported in Nunjucks and LiquidJS). Fixes #13
@zachleat zachleat removed the needs-votes A feature request on the backlog that needs upvotes or downvotes. Remove this label when resolved. label Jun 26, 2018
zachleat added a commit that referenced this issue Jun 26, 2018
zachleat added a commit that referenced this issue Jun 26, 2018
@zachleat
Copy link
Member

zachleat commented Jul 5, 2018

v0.5.0 is out, with Shortcodes!

https://v0-5-0.11ty.io/docs/shortcodes/

@kleinfreund
Copy link
Contributor

I’m currently writing a shortcode for post excerpts/summaries:

{%- for post in posts -%}
<article>
  {% excerpt post %}
</article>
{%- endfor -%}

Something along the lines of this:

module.exports = function (eleventyConfig) {
  eleventyConfig.addLiquidShortcode('excerpt', post => {
    if (!post.templateContent) {
      return;
    }

    const content = post.templateContent;
    const excerptEnd = content.includes('<!--more-->')
      ? content.indexOf('<!--more-->')
      : content.indexOf('\n\n');

    return content.substring(0, excerptEnd).trim();
  });

  // …
};

Now, it seems like post.templateContent doesn’t always contain what I want. Sometimes, this property doesn’t exist. How would I go about this? Do I use post.inputContent and strip any front matter myself? Is that safe? Seems odd.

@zachleat
Copy link
Member

@kleinfreund can you open a new issue with this comment? Thanks!

@Heydon
Copy link
Author

Heydon commented Dec 20, 2018

@zachleat Thanks, now using them in anger!

@zachleat
Copy link
Member

@Heydon 👍 but oh no, why anger??

@Heydon
Copy link
Author

Heydon commented Dec 20, 2018

@zachleat LOL

Actually, I'm having a bit of trouble with strings. In the following, just 'foo' would work, but 'foo bar' makes the template renderer crash?

{% note 'Foo bar' %}

Hello world!

Bye.

{% endnote %}

Here's the shortcode def (I had to put all the code on one line or it gets escaped):

  eleventyConfig.addPairedShortcode('note', function (content, title) {
    return `<aside><h4>${title}</h4>${content}</aside>`;
  });

@zachleat
Copy link
Member

@Heydon ugh yeah—my bad. You’re using Liquid right? That’s filed and fixed for 0.7.0 at issue #347.

@Heydon
Copy link
Author

Heydon commented Dec 20, 2018

@zachleat Fixed already? How can I complain? Cheers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants