Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Require using end<tagname> like `endif` and `endfor` for all tags.

This update will not be backwards compatible with old-style {% end %} tags. You will need to update all templates.
closes gh-1
  • Loading branch information...
commit c4795abcc64381a205283f1e384be567239338d2 1 parent a2079a7
@paularmstrong authored
View
6 README.md
@@ -23,9 +23,9 @@ Swig is a template engine inspired by the Django syntax. It has a few extensions
<p>
{{ forloop.counter }}
{# This is a comment #}
- {{ name }}{% if name == "Django" %} Reinhardt{% end %}
+ {{ name }}{% if name == "Django" %} Reinhardt{% endif %}
</p>
- {% end %}
+ {% endfor %}
### node.js code
@@ -48,7 +48,6 @@ Swig is a template engine inspired by the Django syntax. It has a few extensions
3
Louis
</p>
- {% end %}
## How it works
@@ -59,7 +58,6 @@ Swig reads template files and translates them into javascript functions using th
While Swig is inspired by the [Django template syntax][1], there are a few differences:
- Filters have a different syntaxt.
-- Tags like {% for %} and {% if %} are closed with a simple {% end %} tag.
- Some tags are missing or have different syntax.
- Some extra tags are available.
View
18 docs/tags.md
@@ -14,11 +14,11 @@ Comment tags are simply ignored. Comments can't span multitple lines.
## extends / block
-Check django's template inheritance system for more info. Unlike django, the block tags are terminated with {% end %}, not with {% endblock %}
+Check django's template inheritance system for more info.
## include
-Includes a template in it's place. The template is rendered within the current context. Does not requre closing with {% end %}.
+Includes a template in it's place. The template is rendered within the current context. Does not use and {% endinclude %}.
{% include template_path %}
{% include "path/to/template.js" %}
@@ -29,21 +29,21 @@ You can iterate arrays and objects. Access the current iteration index through '
{% for x in y %}
<p>{% forloop.index %}</p>
- {% end %}
+ {% endfor %}
## if
Supports the following expressions. No else tag yet.
- {% if x %}{% end %}
- {% if !x %}{% end %}
+ {% if x %}{% endif %}
+ {% if !x %}{% endif %}
{% if x operator y %}
Operators: ==, !=, <, <=, >, >=, ===, !==, in
The 'in' operator checks for presence in arrays too.
- {% end %}
+ {% endif %}
{% if x == 'five' %}
The operands can be also be string or number literals
- {% end %}
+ {% endif %}
## autoescape
@@ -57,11 +57,11 @@ So the following:
{% autoescape off %}
{{ some_html_output }}
- {% end %}
+ {% endautoescape %}
{% autoescape on %}
{{ some_html_output }}
- {% end %}
+ {% endautoescape %}
Will output:
View
2  examples/basic/page.html
@@ -13,7 +13,7 @@
<ul>
{% for person in people %}
<li>{{ person.name }} age {{ person.age }}</li>
- {% end %}
+ {% endfor %}
</ul>
</body>
View
2  examples/express/views/index.html
@@ -1,3 +1,3 @@
{% extends 'layout.html' %}
-{% block title %}Home Page{% end %}
+{% block title %}Home Page{% endblock %}
View
8 examples/express/views/layout.html
@@ -2,15 +2,15 @@
<html>
<head>
<meta charset="utf-8" />
- <title>{% block title %}{% end %} - Example</title>
+ <title>{% block title %}{% endblock %} - Example</title>
</head>
<body>
<header>
- <h1>{% block title %}{% end %}</h1>
- {% block header %}{% end %}
+ <h1>{% block title %}{% endblock %}</h1>
+ {% block header %}{% endblock %}
<nav>
<ul>
<li><a href="/">Home Page</a></li>
@@ -20,7 +20,7 @@
</header>
<section role="main">
- {% block body %}{% end %}
+ {% block body %}{% endblock %}
</section>
</body>
View
6 examples/express/views/people.html
@@ -1,11 +1,11 @@
{% extends 'layout.html' %}
-{% block title %}People{% end %}
+{% block title %}People{% endblock %}
{% block body %}
<ul>
{% for person in people %}
<li><a href="/people/{{ forloop.index }}">{{ person.name }}</a> age {{ person.age }}</li>
- {% end %}
+ {% endfor %}
</ul>
-{% end %}
+{% endblock %}
View
4 examples/express/views/person.html
@@ -1,7 +1,7 @@
{% extends 'people.html' %}
-{% block title %}{% parent %} - {{ person.name }}{% end %}
+{% block title %}{% parent %} - {{ person.name }}{% endblock %}
{% block body %}
<p>{{ person.name }} is {{ person.age }} years old.</p>
-{% end %}
+{% endblock %}
View
23 lib/parser.js
@@ -26,9 +26,9 @@ exports.parse = function (data, tags, autoescape) {
index = 0,
i = 0, j = rawtokens.length,
filters = [], filter_name,
- varname, token, parts, part, names, matches, tagname;
+ varname, token, parts, part, names, matches, tagname, lastToken;
- for (i, j; i < j; i += 1) {
+ for (; i < j; i += 1) {
token = rawtokens[i];
// Ignore empty strings and comments
@@ -67,17 +67,22 @@ exports.parse = function (data, tags, autoescape) {
parts = token.replace(/^\{% *| *%\}$/g, '').split(' ');
tagname = parts.shift();
- if (tagname === 'end' || (index > 0 && ('end' + stack[index - 1][0].name) === tagname)) {
- if (_.last(stack).name === 'autoescape') {
- escape = last_escape;
+ if (index > 0 && (/^end/).test(tagname)) {
+ lastToken = stack[stack.length - 2];
+ if ('end' + _.last(lastToken).name === tagname) {
+ if (_.last(stack).name === 'autoescape') {
+ escape = last_escape;
+ }
+ stack.pop();
+ index--;
+ continue;
}
- stack.pop();
- index--;
- continue;
+
+ throw new Error('Unbalanced end tag found: ' + tagname);
}
if (!(tagname in tags)) {
- throw new Error("Unknown logic tag: " + tagname);
+ throw new Error('Unknown logic tag: ' + tagname);
}
if (tagname === 'autoescape') {
View
6 lib/tags.js
@@ -56,13 +56,13 @@ exports.include = function (indent) {
* Example 'If' tag syntax:
* {% if x %}
* <p>{{x}}</p>
-* {% end %}
+* {% endif %}
*
* {% if !x %}
* <p>No x found</p>
* {% else %}
* <p>{{x}}</p>
-* {% end %}
+* {% endif %}
*
* {% if x == y %}, {% if x < y %}, {% if x in y %}, {% if x != y %}
*/
@@ -144,7 +144,7 @@ exports['if'].ends = true;
* Example 'For' tag syntax:
* {% for x in y.some.items %}
* <p>{{x}}</p>
-* {% end %}
+* {% endfor %}
*/
exports['for'] = function (indent) {
var operand1 = this.args[0],
View
19 tests/parser.test.js
@@ -22,14 +22,8 @@ exports.Tags = testCase({
test.done();
},
- 'basic tag with named end': function (test) {
- var output = parser.parse('{% foo %}{% endfoo %}', { foo: { ends: true } });
- test.deepEqual([{ type: parser.TOKEN_TYPES.LOGIC, name: 'foo', args: [], compile: { ends: true }, tokens: [] }], output, 'end matches start tag');
- test.done();
- },
-
'basic tag with ends': function (test) {
- var output = parser.parse('{% blah %}{% end %}', { blah: { ends: true } });
+ var output = parser.parse('{% blah %}{% endblah %}', { blah: { ends: true } });
test.deepEqual([{ type: parser.TOKEN_TYPES.LOGIC, name: 'blah', args: [], compile: { ends: true }, tokens: [] }], output);
test.done();
},
@@ -43,13 +37,20 @@ exports.Tags = testCase({
'throws if not end but end found': function (test) {
test.throws(function () {
- parser.parse('{% blah %}{% end %}', { blah: {}});
+ parser.parse('{% blah %}{% endblah %}', { blah: {}});
+ }, Error);
+ test.done();
+ },
+
+ 'throws on unbalanced end tag': function (test) {
+ test.throws(function () {
+ parser.parse('{% blah %}{% endfoo %}', { blah: { ends: true }, foo: { ends: true }});
}, Error);
test.done();
},
'tag with contents': function (test) {
- var output = parser.parse('{% blah %}hello{% end %}', { blah: { ends: true } });
+ var output = parser.parse('{% blah %}hello{% endblah %}', { blah: { ends: true } });
test.deepEqual([{ type: parser.TOKEN_TYPES.LOGIC, name: 'blah', args: [], compile: { ends: true }, tokens: ['hello'] }], output);
test.done();
}
View
8 tests/speed.js
@@ -15,10 +15,10 @@ tplS = template.fromString(
+ "\n{{forloop.index}} {{k}}: "
+ "{% if forloop.index in 'msafas' %}"
+ "<p>Hello World {{k}}{{foo}}{{k}}{{foo}}{{k}}{{foo}}</p>"
- + "{% end %}"
- + "{% end %}"
- + "{% end %}"
- + "{% end %}"
+ + "{% endif %}"
+ + "{% endfor %}"
+ + "{% endif %}"
+ + "{% endfor %}"
);
array = [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], { af: "s", baz: "d", d: "f" }, "zeus"];
View
4 tests/templates.test.js
@@ -49,7 +49,7 @@ exports.Tags = testCase({
Tags: function (test) {
swig.init({});
- var tmpl8 = swig.fromString('{% if foo %}hi!{% end %}{% if bar %}nope{% end %}');
+ var tmpl8 = swig.fromString('{% if foo %}hi!{% endif %}{% if bar %}nope{% endif %}');
test.strictEqual(tmpl8.render({ foo: 1, bar: false }), 'hi!');
test.done();
},
@@ -66,7 +66,7 @@ exports.Tags = testCase({
swig.init({ tags: tags });
- tmpl8 = swig.fromString('{% foo %}{% end %}');
+ tmpl8 = swig.fromString('{% foo %}{% endfoo %}');
test.strictEqual(tmpl8.render({}), 'hi!');
test.done();
},
View
2  tests/templates/extends_1.html
@@ -3,6 +3,6 @@
{% block one %}
This is the "extends_1.html" content in block 'one'
-{% end %}
+{% endblock %}
View
2  tests/templates/extends_2.html
@@ -4,4 +4,4 @@
{% block two %}
This is the "extends_2.html" content in block 'two'
{% include "include_base.html" %}
-{% end %}
+{% endblock %}
View
4 tests/templates/extends_base.html
@@ -2,8 +2,8 @@
{% block one %}
This is the default content in block 'one'
-{% end %}
+{% endblock %}
{% block two %}
This is the default content in block 'two'
-{% end %}
+{% endblock %}
View
8 tests/templates/included.html
@@ -6,7 +6,7 @@
<p>foo: {{foo}}</p>
<p>Hello World {{k}} {{foo}}</p>
<p>array.length: {% include "included_2.html" %}</p>
- {% end %}
- {% end %}
- {% end %}
-{% end %}
+ {% endif %}
+ {% endfor %}
+ {% endif %}
+{% endfor %}
Please sign in to comment.
Something went wrong with that request. Please try again.