I wonder is it possible to get macro from template without actually rendering template?
This might be useful when you need to take particular macro from template, pass to it certain parameters and get output, which later will be appended to DOM node within you application.
This is very essential feature for app, which tries to decompose everything into reusable components, which should be re-rendered and injected in runtime. For example, based on user's input. Or when you want to render with Nunjucks only small widget, which stored in macro.
In form of pseudo-code, it should be something like that:
components/MyMacro.nj
{% macro MyMacro(input) %}
This is {{ input }}
{% endmacro %}
app.js
const template = nunjucks.compile('components/MyMacro.nj')
const macro = template.getMacro('MyMacro')
const renderedHTML = macro('just some random value')
$('#widget').append(renderedHTML)
As turned out, in Jinja you can do this, by using jinja_env.get_template(template_name).module (see example from Flask here.
You can see description of those methods here and here.
I've tried to dig into outputs of nunjucks.compile and nunjucks.precompile, and into other methods, but it seems that Nunjucks doesn't expose nothin like get_template method from Jinja. Though, maybe I've just missed it.
Strangely enough, there seems to be no related issues to my problem. I just wonder — or it's just I'm so dumb that can't figure it out, or everyone else just re-rendering whole app on each change and passing it into DOM?
Since Nunjucks repo is pretty much dead, and some people have better knowledge of internals, I call upon you, masters of Nunjucks, @carljm, @jbmoelker, @vecmezoni. Just let me know, please, does Nunjucks have something for this or no. If doesn't, I will simply go with other solution. Thanks and sorry for bothering!
@ArmorDarks --
Here's a toy demo.
const { parse } = require('nunjucks/src/parser');
const { Compiler } = require('nunjucks/src/compiler');
const { Macro } = require('nunjucks/src/nodes');
const runtime = require('nunjucks/src/runtime');
const wrap = source => {
return `return runtime => {
let frame = new runtime.Frame();
let env = { opts: { autoescape: true } };
return (...args) => {
${ source }
return macro_t_1(...args).toString();
}
}`;
}
const str = `
{% macro MyMacro(input) %}
This is {{ input }}
{% endmacro %}
`;
// parse AST
const root = parse(str);
// get macros
const nodes = root.findAll(Macro);
// create macro container
const macros = {};
// for each macro...
for (const node of nodes) {
// make a new compiler
const c = new Compiler(node.name.value);
// use internal API to compile to fn
c._compileMacro(node);
// dump compiler buffer
const source = c.getCode();
// shim runtime API
const wrapped = wrap(source);
// eval code
const macrofn = new Function(wrapped);
// assemble macro
const macro = macrofn()(runtime);
macros[node.name.value] = macro;
}
console.log(macros.MyMacro('foobar')) // This is foobar;
That being said, I could not honestly recommend doing this - it's kind of a hack and requires a pretty extensive knowledge of Nunjucks' internals to pull off.
I imagine it's bad form to shill forks in the parent repo, but as this one is basically dead — I'm currently working on a ground-up rewrite of Nunjucks with (among other features), async/await, iterables and externally-accessible macros/variables. If you'd like to give it a spin or would be interested in helping out, let me know. Cheers.
@noahlange This repository is not basically dead! I'm slowly working my way through the very large backlog of tickets, but we're in the middle of a fairly large product launch at The Atlantic, which has slowed me down.
@noahlange That said, if you are interested in working on those improvements I would really appreciate your input. Those sound like things we could get onto this project. Email me at [email protected] and we can discuss push / administrative privileges.
Oh shoot, awesome! Glad to hear y'all are working on it. :)
@noahlange Hah, that's magnificent! I've learned some things about Nunjucks internals thanks to you example!
Unfortunately, yeah, I imagine how galaxy explodes when someone will take this into production...
It would be really great to to see your efforts embedded into Nunjucks instead of new templater.
@fdintino This issue was written before The Atlantic picked up project, so it's indeed was almost dead.
I imagine it's bad form to shill forks in the parent repo, but as this one is basically dead — I'm currently working on a ground-up rewrite of Nunjucks with (among other features), async/await, iterables and externally-accessible macros/variables. If you'd like to give it a spin or would be interested in helping out, let me know. Cheers.
@noahlange I wonder, what's up with your rework?
I had corresponded with @noahlange asking him if he could contribute back some of his restructuring ideas, and he was open to it but understandably busy. It looks as though the fork hasn't had much further work since this discussion. I'd love to be able to get proper async and iterable support into this library.
That's the long and the short of it. I got sucked into Reactland and haven't spent much time working on the restructuring - there were some bad decision I made with respect to the compiler that would take a good chunk of time to fix.
I'd be willing to put some work into nunjucks proper if we were able to come up with a concrete list of things that need to be added/removed/fixed/adjusted. The list is pretty big, and I think if one just went ahead and started fixing things they'd get burnt out pretty quickly and would have an unmergable PR even quicker.
So if we want to start another issue and chat about that, I'm game.
Most helpful comment
@noahlange This repository is not basically dead! I'm slowly working my way through the very large backlog of tickets, but we're in the middle of a fairly large product launch at The Atlantic, which has slowed me down.