Open TitaniteSlab opened 6 years ago
Not really, nunjucks is a template library made to output a string. What's the use-case for this, why use nunjucks instead of eval
here?
For use case, consider Redhat Ansible which utilizes the expression engine from Jinja2 but not the templating features.
https://docs.ansible.com/ansible/2.5/user_guide/playbooks_templating.html
To be clear I'm not necessarily looking for an existing API - I'm willing to fork and tear up the source, just hoping for guidance where to start.
What I see in Ansible docs, it doesn't care that Nunjucks returns a string, since it is later used in YAML configs, which are a string, which later serialized.
For instance, even if you will do this:
---
{% set aString = '123' %}
aNumber: {{ aString }}
aString: '{{ aString }}
You will get following results, because it depends not on Nunjucks output, but YAML syntax parsing.
typeof yaml.aNumber === 'number' // true, because numbers without strings will be read by YAML parser as numbers
typeof yaml.aString === 'string' // true, because quoted numbers will be read by YAML parser as strings
Ansible parses pure YAML first, and evaluates Jinja2 expressions property-by-property:
https://github.com/ansible/ansible/blob/devel/lib/ansible/template/__init__.py#L682
It does this by calling Jinja2's root_render_func()
directly. This is a supported feature in Jinja2 (though perhaps uncommonly used in this manner), as they have a generate()
function that can evaluate one statement at a time:
https://github.com/pallets/jinja/blob/master/jinja2/environment.py#L1031
I am essentially asking if nunjucks can be hacked to support the same thing. Evaluate 1 expression by itself.
Well, that's strange, because if they parse YAML before rendering, any YAML file with such content will throw an error:
aNumber: {{ aString }}
because it will treat {
and }
as an inline object, and because it isn't valid, it should throw.
Meanwhile, using such construction is the only way to declare numbers (because addition of any other character will make YAML treat it as a string).
Anyway, answering your question — no, right now Nunjucks can output only strings.
You're correct, which is why Ansible requires you wrap bare expressions in quotes ;)
aNumber: "{{ foo }}" # don't remove the quotes or Ansible will get angry!
Any concerns with the first snippet I suggested?
JSON.parse(nunjucks.renderString(`{{ ${expr} | dump | safe }}`, ctx));
It seems to work. I'll report back if I figure out another way.
I implemented an Environment.evaluateExpression()
that does this:
https://github.com/TitaniteSlab/nunjucks/commit/865cb61a9d8ee6f1a8cb0316c718e83d35755117
This is a quick hack that creates a separate path through the compiler, as the string coercion was happening in the generated code. I'd like some feedback and hopefully we can turn this into a proper pull request.
Note that Jinja2 actually does support this feature directly:
https://github.com/pallets/jinja/blob/master/jinja2/environment.py#L595
I notice that the design principles include Jinja2 parity. This would be a great feature to add to nunjucks. Jinja2's compile_expression()
works slightly differently (it returns the compiled function to be called from a little helper class called ExpressionTemplate
). I'd be happy to continue working on this to make it more similar to Jinja2.
Usage example:
let nunjucks = require('nunjucks');
let Environment = nunjucks.Environment;
let expr = `{{ foo + 1 + 2 | string | int }}`;
let ctx = {foo: 3};
let environment = new Environment();
let result = environment.evaluateExpression(expr, ctx);
console.info(`${expr} -> ${result} [${typeof result}]`);
Updated version with Environment.compileExpression()
and without requiring {{ }}
on the input to be more like Jinja2:
https://github.com/TitaniteSlab/nunjucks/commit/7e4c9252bcc70fefc332ba76f940aaf86cd3353d
Example:
new Environment().compileExpression('foo')({foo: 3}); => 3
I want to make a pull request but I'm having trouble building the docs. I posted the problem over on the google group:
https://groups.google.com/forum/#!topic/nunjucks/JWCGUOUEK3k
I can't seem to replicate your doc build error. Even so, I don't think it should be necessary to build the docs as part of the pull request. I'll build and push the docs to the gh-pages branch when I create a release, but generally not before. If you open the PR as-is I can check whether I encounter a similar doc-build issue.
I would like to use nunjucks to evaluate a single expression enclosed by
{{ }}
. nunjucks always returns a String, but I would like to keep the expression result data type.So, one solution is to dump the expression and then use
JSON.parse()
on the result:Is there another/better way to accomplish this?