-
Notifications
You must be signed in to change notification settings - Fork 5
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
Allow macro to accept body arguments? #51
Comments
Yes! I want macros to work like shortcodes in Zola so they are also more useful as components. I'm just debating internally if there are other things we want to change for component-ization, like https://jinjax.scaletti.dev/ |
Ok so JinjaX goes all in the HTML way which doesn't really work when you are templating something else. Things I'm thinking of after reading their docs and that would work well here:
Usage would look like that:
The and they would refer to components defined in Everything is up for discussion, any thoughts? |
Yeah, I read the jinx docs when you shared them earlier, and I thought it was very html-y, so probably not a 1:1 roadmap for tera. Thanks for sharing though. My initial thoughts:
I guess what is the difference between a macro and a component? In my mind it's when it's available. A macro is available at compile time and a component is available only at runtime? If you're not planning on changing that aspect then maybe it makes sense to just leave it as macro and that will reserve "components" for something that is more component-y? I'd like to hear your thoughts though about why the name change to begin with.
I think this is how most people use macros already, in Zola at least, and shortcodes already introduced the convention. It would certainly be simpler for the user. The one counter argument that comes to mind is that it's common for people to use a shortcode (in Zola) in conjunction with a macro. The shortcode may literally just call the macro, which gives more code reuse in case you also need the same functionality in a template. For this proposal, the downside of having a dedicated components folder would just be that there can't be a "one file" install, in the case of a shortcode that calls a macro. The next point expands on this a little more.
I think having multiple components/macros in a file is useful. For example, I'm working on a project right now that's like a showcase of various shortcodes and macros. I also plan on accepting user submitted ones too. The default for this project is to strive for at least offering a "one file" install, but some complicated make more sense to be broken up into further macros. It helps to keep them in the same file. These shortcodes/macros are pretty complicated and I reuse them across projects. They provide stuff like HN style comments with prev/next navigation, code windows examples that are organized into a tabbed window, a carousel style image gallery, etc... They're all implemented in pure html+css so they're very portable between projects, but it would help to keep the ability to organize the macros (or components) in the same file.
This makes sense, especially for new users. There just needs to be a way to maintain grep-ability, which for me is the main benefit of the import statement. So even if it is something like {{ macros::foo() }} then that would give me enough to see where a certain macro is used, etc...
Yes!
Yes! One ask is to consider any possibility of allowing the end tags to optionally be named? When I'm visually parsing a file and I see |
I love this idea! I requested macro bodies in Tera 2 Wishlist, it would be great to have them. I like "components" more than "macros". However, for me it still creates extra cognitive load when I try to remember the difference between components and shortcodes. Ideally, I would prefer having only "components", which work both in templates and pages. These can be entirely different things under the hood, but it would be nice to have unified user interface and terminology, because functionally they are so close. It would also be natural then to have strictly one component per file as we have with shortcodes now. I hope I'm making any sense here. |
There's no difference really from the template engine pov, it's the same thing, the macro calls are not replaced in the templates (we could but it just makes error reporting harder). I was thinking of using components as it's much clearer to everyone doing frontend these days than macros.
I hear you, sometimes I do want to have multiple ones in the same file. The main reason I'm torn is that if I keep it at 1 file == 1 component, they can effectively replace shortcodes in Zola without any work. If multiples are allow, shortcodes still need to be their separate thing. The difference between macros and shortcodes is already something that confuses a lot of people when they look a bit different, so when they look the same it's going to be even worse.
Since they would require parenthesis all the time, you should just be able to grep for
Yes definitely, and it would likely be required. Yes definitely, if I end up implementing this it will replace shortcodes. A shortcode would be just a component that gets some extra context from the current rendering page. With one component per file, we would have to define the parameters and their defaults somewhere. Maybe we keep the current {% define %} approach or use a comment like jinjax but I'm not a huge fan of that. Either way it's a breaking change for the shortcodes but in exchange you would get an error if you have a typo somewhere versus potentially rendering something empty. Another thing that was requested and which could be implemented as well is a basic type system for components (eg you can mark parameters as |
To take back the example from https://jinjax.scaletti.dev/guide/motivation/#/: with the proposed changes, it would be: {% card(label="hello") %}
{% my_button(color="blue", shadow_size=2) %}
{{ icon(name="ok") }} Click Me
{% end my_button %}
{% end card %} in Tera, which I think is acceptable considering we are not targeting just HTML so it can't be as clean as JSX from that page. As for definition, if we assume one file == one component, we'll pick the component name from the file like shortcodes. The tricky bit is where to define the arguments. The options that I can think of are:
Option 1 is ok but highlighting might be a problem. What do you think? |
Hey, this looks awesome, thank you. I really like the example above.
If the namespace is optional then I don't think having it can hurt and it can only help in the unlikely case of collisions, like you said.
Do you want to elaborate your thoughts on this a little bit? Are you suggesting to just use a different syntax to indicate a component, instead of an optional namespace? I'd love to hear more about what you're considering. defining the arguments in front matter?if we proceed with the assumption of 1:1 file and component (and component name being the filename) then why don't you define the arguments in a short little frontmatter section? I think this might seem strange at first but it has a lot of benefits. For one thing it would solve the unnecessary nesting issue. It would also make the arguments easily readable and separate from the actual code. People can add comments and it would make documenting how a component works really easy (I do this a lot and it helps whenever I have to revisit a shortcode or macro). I think it could also provide sort of a unified syntax for components and shortcodes. Right now, as you know, macros have kind of a macro keyword whereas shortcodes just have their templating and any variables they need are presumed to exist. The shortcode syntax in Zola now is nice because there's no nesting but it's also hard to debug. (I do defaults just by literally just setting I think it would also create a lot of forward compatibility? In the future new functionality could be added in that shortcode section and it wouldn't really crowd the component definition (like it would with the I don't know, I could go on. I honestly just see mainly benefits this way and not a lot of drawbacks. What do you think @Keats and everyone else? |
Just to summarize my thoughts above about using front matter for the component definition:
Here's an example of how it could look with front matter: +++
[args.title]
default = "Untitled"
type = "string"
[args.count]
default = 10
type = "number"
[args.isVisible]
type = "boolean"
[args.description] # No default or type
+++
<h1>{{ title }}</h1>
<ul>
{% for 1 in count %}
{% if isVisible %}hello!{% endif %}
{% endfor %}
</ul>
<p>{{ description | default(value="no description yet") }}</p> but if someone wanted to do kind of a gradual typing thing they could start with just this <h1>{{ title }}</h1>{# this breaks if no `title` but that's ok, and event preferred, at first #}
<ul>
{% for 1 in count %}
{% if isVisible %}hello!{% endif %}
{% endfor %}
</ul>
<p>{{ description | default(value="no description yet") }}</p> that way they could start without any boiler plate at all, and as their work becomes more clear they could sort of progressively add types and defaults. Thoughts? |
To summarize that frontmatter approach: the idea is to allow defining the arguments, with types and defaults, to a component in TOML frontmatter? I'm not sure if going with frontmatter makes sense here if this would be a built-in capability of the engine, I'd rather it be defined in the syntax of the actual template language.
Highlighting would definitely be a problem for option 1, and I think reasonably so! Comments shouldn't be used for anything but... comments. Option 3 is I think what I would prefer, I don't think a file should only be able to define one component too. |
Front-matter is a no from me, way too verbose and require a TOML parser and you can't provide well integrated errors. Not to mention people will ask for YAML 5s after it lands. I would prefer having it built using Tera syntax.
How would you define additional metadata? |
It's also confusing because other applications of Tera will probably want to use front matter (and do in the case of https://github.com/catppuccin/whiskers).
Additional metadata as in types? Maybe something like this? {% define my_component(products: str[], msg: str ="World!") %}
...
{% end my_component %} |
I meant the CSS/JS paths from JinjaX for example. Types would look like what you wrote. |
Do you mean this example? I would say reuse |
That wouldn't work, we need the data to be retrievable when parsing the
template. set_global would allow variables and would need to render to get
it.
…On Thu, 24 Oct 2024, 21:47 uncenter, ***@***.***> wrote:
{% define css = "something.css" %}
Do you mean this example? I would say reuse set_global to allow macros to
expose variables to the caller, if I'm understanding correctly.
—
Reply to this email directly, view it on GitHub
<#51 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAFGDI4MWRNVDTPJTHVZ4BDZ5FFFHAVCNFSM6AAAAABOWQA7C6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMZWGIYTGNZXGM>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Ah right I see. Sure, |
This feature looks like it would be very useful, the kind of feature that after you use it a couple times you find more and more uses for it! |
Latest thoughts for calling components. Maybe we want it different from functions just to allow namespacing in case of conflicts as in 2 components with the same name? I don't know how common that would be but since the components are global to a project, names would need to be unique. I'll need some feedback/experience on whether that actually happens. I can imagine things like a
Symbol can be
I'm using The syntax for shortcodes in Zola will be changed to match whatever is chosen here so there are no more differences between shortcodes and macros/components. Any feedback? Another thing that I think I will miss: imports. Knowing from which file each components comes from is IMO valuable. It might not be an issue in practice, especially if you force components to be defined in specific directories. You can still grep for it but that would be the only way. |
My vote would be option 1. Call them like a function. I do not know how important it would be to have namespaces. I vote for option 1 because it keep the syntax familiar, but if namespaces solve some problems that people are likely to run into then one of the other options would be fine, but I have no idea if option 2 or 3 would be better in that case. Other thoughts: To give meaningful feedback I have been trying to think of some of the best uses for this feature. So I did a grep on all the themes:
Surprisingly Abridge only uses the {{ body }} feature once for katex. abridge: katex The most common use of {{body}} that I see in most shortcodes is to pass a small snippet of readable text to a shortcode. deepthought has interesting uses of {{body}}, but they are js supported features. Pico has an interesting use of {{body}} to construct a timeline: https://kuznetsov17.github.io/pico/features/#timeline Seagull theme seems to already make use of the feature being discussed here??? (seems they use body by passing it as a parameter) https://git.lacontrevoie.fr/HugoTrentesaux/seagull/src/branch/master/templates/icon_macros_dep.html Serene's codeblock shortcode allows specifying the filename, which seems like an interesting use of the feature. With a better understanding of the practical uses of this feature I would probably be able to give much better feedback. |
If you are familiar with React or component based libraries, it's basically that but in a template engine. Not necessarily only for Zola but to organise things better and make it much easier to handle complex templates. Think of a normal website where you have your inputs/buttons/images etc that you want to always be consistent. You can do some CSS class (or tailwind stuff) but it's easier if there's only one place to change it. Also nice if you are using htmx for example and want to re-render a single piece of the template. For your example in the body: it's not as needed for shortcodes as for general components. To reuse JinjaX as an example, if you go to https://jinjax.scaletti.dev/guide/slots/#/ and scroll down to composability for the modal example it's pretty obvious how much the There are a bunch of ways to handle it with Jinja/Django etc but it's usually handled outside of the template engine. I'm trying to figure out how to include it directly in Tera in a way that is nice to use but also extensible. |
OH! it is good that you pointed that out, not necessarily only for Zola. I actually was working on an actix-web project a few years ago that used Tera as the templating engine, and I am sure on that level this feature would be very useful! I stopped working on that project, and if I needed something more dynamic in the future I would probably use axum with Tera instead. When your creating something like a very dynamic web application that uses Tera as the templating engine then I would probably opt to allow namespacing! so option 2 or 3. Initially I thought I was still in the zola repo when I followed your link to this issue, I should have looked more closely! |
I also lean towards option 1. My intuition is that namespacing is not needed for 99% of the cases, and the rest can rename components when importing them ( |
With option 1 you lump together macros and function so slightly more likely to need namespacing. I personally prefer option 2 just because it's obvious if i |
How about going the Rust way and define components with an exclamation point at the end e.g., Also, now that I'm looking at it more and more, I start liking option 3. It kinda gives you an idea that it's not just some computation/evaluation, but an actual tag-like thingie, which is exactly what component is. |
That's a small issue actually. You can use Tera to template anything, not just HTML. Eg with https://github.com/Keats/kickstart you can template things like Python files or really any kind of text files |
Ok this is missing some error handling/validation and actual type checking but it should be usable: #64 to play with Current syntax:
The end name and the metadata are optional
(The end name is optional) The symbol before the name makes the code quite a bit simpler Another thing missing is probably a JSX spread like feature. |
Thoughts on using |
Yes of course |
Shortcodes that take body arguments are wonderful. It allows me to wrap markdown text and voila. It would be nice if the macro syntax were extended to work the same as the shortcode syntax, with an implicit body argument.
The text was updated successfully, but these errors were encountered: