Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

question: could I use this module to get a list of substitutions in the template? #176

Closed
andrewrk opened this Issue · 9 comments

2 participants

@andrewrk

Example:

Given this template:

<h1>{{ pagename|title }}</h1>
<ul>
{% for author in authors %}
    <li{% if loop.index <= 0 %} class="first"{% endif %}>{{ author }}</li>
{% else %}
    <li>There are no authors.</li>
{% endfor %}
</ul>

I should be able to run something like getVariables(template) which returns something that looks like:

{
  "pagename": {
    "type": "scalar"
  },
  "authors": {
    "type": "array"
  }
}
  1. Is this functionality already built?
  2. Would you accept a pull request adding this functionality?
  3. Could I add this functionality via some extension?
  4. If the answer is "no" to all of these questions, how do you suggest that I solve the problem I present in the use case below?

Justification for the use case:

Creating an email preview tool. Given an email template, I should be able to render a preview with editable dummy data. In order to get editable dummy data, I need to figure out what context/substitutions/variables a template might need in order to generate content, and then present that in the UI.

@paularmstrong
  1. Nope, but sort of...
  2. maybe. It seems like a pretty localized use-case, so I'm not certain the addition is necessary.
  3. Yes, assuming you're using node.js and not doing this in-browser.

When a template is compiled, a tree is built that is later used for consumption during parsing. Try this as a test:

var swig = require('./lib/swig');
console.dir(swig.compileFile('./swig/tests/node/templates/included.html').tokens);
[ { type: 1,
    line: 1,
    name: 'for',
    compile: { [Function] ends: true },
    parent: [],
    strip: { before: false, after: false, start: false, end: false },
    args: [ 'v', 'in', 'array' ],
    tokens: [ '\n ', [Object], '\n' ],
    blocks: {} } ]

What you'll want to do is recursively loop over each object and its tokens, looking for type: 2 (2 is VAR_TOKEN). The name property will give you the variable name.

VAR_TOKENs also have two key properties, args, and filters. The args are the arguments that are mapped to the filters provided.

There is no type, as there is no way of knowing what type a variable will be until it is attempted to be rendered in the fully compiled+parsed template.

Does that answer your questions?

[edit: Also may help to take a look through parser.js, specifically exports.parseVariable]

@andrewrk

Wonderful! This looks like almost exactly what I need. Thank you for getting back so quickly.

2 more questions:

  1. If I use this API, am I depending on internal things that are subject to change with minor (x.x.Z) version increments?
  2. Would you accept a PR that modified parser so that the token object had a field that could specify the context for the variable? In other words, whether the variable was being used as the target of a for..in or an if statement. Of course this PR would include tests for this feature.
@paularmstrong
  1. Nope. At this point, the API endpoints will only change at a major version (this is my call)
  2. I could see that being useful if tags also threw warnings about unexpected types. The problem is, a for tag can iterate [Object], [Array], and [String] just fine. There's no real way of knowing if an Object is expected vs an indexed [Array]. Such is the issue with JavaScript.

I would recommend, instead, that you do this as a custom piece for your integration. Re-reading some code, it's apparent that swig actually doesn't know about the VAR_TOKEN myobj in {% for v in myobj %} until actual template compilation (post creating this token tree). Because of that, you'll need to read the LOGIC_TOKENs (type: 1) and make decisions based on the items in args: [...]. See my previous comment for the example output of a for tag's token.

If you are really trying to enforce types, a JavaScript-based template engine probably isn't the best choice, since JS is not strongly typed. Take, for instance, someone uses the following:

{% if 'a' in something %}...{% endif %}

How will you decide if something needs to be a string, key-value object, or array?

@andrewrk

Thanks, I really appreciate the help! I'm looking forward to using this module; it looks like you've taken good care of it.

@andrewrk andrewrk closed this
@andrewrk

I further extracted out the swig specific stuff to its own module: https://github.com/superjoe30/swig-dummy-context

Do you have any interest in promoting it in swig's README?

@andrewrk

So this is broken in swig 1.x (you bumped the major version as promised). But is it possible to get the same behavior in 1.x?

@andrewrk andrewrk reopened this
@paularmstrong

What's "broken"? Third-party support is up to you to keep up to date.

@andrewrk

The parse tree is no longer exposed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.