@rhinostone/swig-django 2.9.13 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/lib/index.js +1 -1
- package/lib/lexer.js +6 -4
- package/lib/parser.js +46 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -33,6 +33,7 @@ Renders real Django templates — everything below was cross-checked against Dja
|
|
|
33
33
|
* **Variable resolution** — Django-faithful attribute / dictionary / index lookup. Callable attributes are auto-called (honoring `alters_data` / `do_not_call_in_templates`), numeric indexing works (`{{ list.0 }}`), and dicts iterate via `.keys` / `.values` / `.items`.
|
|
34
34
|
* **Filter arguments** — Django colon syntax (`{{ value|date:"Y-m-d" }}`).
|
|
35
35
|
* **Filters** — 42 built-ins (`add`, `addslashes`, `capfirst`, `center`, `cut`, `date`, `default`, `default_if_none`, `divisibleby`, `escape` / `e`, `escapejs`, `filesizeformat`, `first`, `floatformat`, `force_escape`, `get_digit`, `iriencode`, `join`, `last`, `length`, `linebreaks`, `linebreaksbr`, `linenumbers`, `ljust`, `lower`, `make_list`, `phone2numeric`, `pluralize`, `rjust`, `safe`, `slice`, `slugify`, `striptags`, `time`, `title`, `truncatechars`, `truncatewords`, `upper`, `urlencode`, `wordcount`, `wordwrap`, `yesno`). See `lib/filters.js` for the full list.
|
|
36
|
+
* **Unknown names** — a filter name the instance does not hold is a compile error wherever it stands, as in Django. Register custom filters before the compile that uses them.
|
|
36
37
|
* **Async** — `renderFile(path, locals, cb)` against a loader with `async === true`, or `compileFile` with `codegenMode: 'async'`; `renderFileAsync` / `compileFileAsync` still work but are deprecated.
|
|
37
38
|
|
|
38
39
|
Explicitly unsupported (parse-time throw or absent)
|
package/lib/index.js
CHANGED
package/lib/lexer.js
CHANGED
|
@@ -243,9 +243,9 @@ exports.types = TYPES;
|
|
|
243
243
|
/**
|
|
244
244
|
* Match the next token at the start of `str`.
|
|
245
245
|
*
|
|
246
|
-
* Throws
|
|
247
|
-
*
|
|
248
|
-
*
|
|
246
|
+
* Throws when no rule matches. This module does not know the line or the
|
|
247
|
+
* file, so the error is flagged `unplaced` and the Django frontend's
|
|
248
|
+
* `exports.parse` names them.
|
|
249
249
|
*
|
|
250
250
|
* @param {string} str Input slice starting at the unconsumed offset.
|
|
251
251
|
* @return {LexerToken} Matched token.
|
|
@@ -277,7 +277,9 @@ function reader(str) {
|
|
|
277
277
|
});
|
|
278
278
|
|
|
279
279
|
if (!matched) {
|
|
280
|
-
|
|
280
|
+
var err = new Error('Unexpected token "' + str.charAt(0) + '" in Django expression.');
|
|
281
|
+
err.unplaced = true;
|
|
282
|
+
throw err;
|
|
281
283
|
}
|
|
282
284
|
|
|
283
285
|
return matched;
|
package/lib/parser.js
CHANGED
|
@@ -5,6 +5,10 @@ var ir = require('@rhinostone/swig-core/lib/ir'),
|
|
|
5
5
|
var lexer = require('./lexer');
|
|
6
6
|
var _t = require('./tokentypes');
|
|
7
7
|
|
|
8
|
+
// A catalog entry is read as an own property: a filter can be registered under
|
|
9
|
+
// the name hasOwnProperty, and a name like toString is not a filter.
|
|
10
|
+
var hasOwn = Object.prototype.hasOwnProperty;
|
|
11
|
+
|
|
8
12
|
/**
|
|
9
13
|
* Make a string safe for embedding into a regular expression.
|
|
10
14
|
* @param {string} str
|
|
@@ -154,9 +158,13 @@ exports.missingAsEmpty = function (expr) {
|
|
|
154
158
|
* @param {object} [_posOut] Optional out-param; final cursor stored on
|
|
155
159
|
* `_posOut.pos` to let callers detect partial
|
|
156
160
|
* consumption.
|
|
161
|
+
* @param {object} [_scope] Set by `exports.parse`: the `line` and the
|
|
162
|
+
* `filename` an error names, and the instance's
|
|
163
|
+
* `extensions`, which make a filter name the
|
|
164
|
+
* catalog does not hold an error.
|
|
157
165
|
* @return {object} IRExpr tree.
|
|
158
166
|
*/
|
|
159
|
-
exports.parseExpr = function (tokens, filters, _posOut) {
|
|
167
|
+
exports.parseExpr = function (tokens, filters, _posOut, _scope) {
|
|
160
168
|
var pos = 0;
|
|
161
169
|
filters = filters || {};
|
|
162
170
|
|
|
@@ -173,7 +181,7 @@ exports.parseExpr = function (tokens, filters, _posOut) {
|
|
|
173
181
|
return t;
|
|
174
182
|
}
|
|
175
183
|
function bail(msg) {
|
|
176
|
-
utils.throwError(msg);
|
|
184
|
+
utils.throwError(msg, _scope && _scope.line, _scope && _scope.filename);
|
|
177
185
|
}
|
|
178
186
|
|
|
179
187
|
function guardSegment(segment) {
|
|
@@ -406,7 +414,7 @@ exports.parseExpr = function (tokens, filters, _posOut) {
|
|
|
406
414
|
} else if (tok.type === _t.FILTEREMPTY || tok.type === _t.FILTER) {
|
|
407
415
|
consume();
|
|
408
416
|
fname = tok.match;
|
|
409
|
-
if (
|
|
417
|
+
if (hasOwn.call(filters, fname) ? typeof filters[fname] !== 'function' : (_scope && _scope.extensions)) {
|
|
410
418
|
bail('Invalid filter "' + fname + '"');
|
|
411
419
|
}
|
|
412
420
|
// Django colon-filter: an optional single positional argument
|
|
@@ -590,6 +598,25 @@ exports.parse = function (swig, source, opts, tags, filters) {
|
|
|
590
598
|
tags = tags || {};
|
|
591
599
|
filters = filters || {};
|
|
592
600
|
|
|
601
|
+
// Where parseExpr is working, so that an expression error can name its line
|
|
602
|
+
// and file, and whether it has a catalog to check a filter name against (only
|
|
603
|
+
// an engine instance has). A tag gets a handle on this module whose parseExpr
|
|
604
|
+
// works in the same scope and on this parse's filter catalog.
|
|
605
|
+
var scope = { line: 0, filename: opts.filename, extensions: swig && swig.extensions };
|
|
606
|
+
var exprParser = Object.create(exports);
|
|
607
|
+
exprParser.parseExpr = function (tokens, _filters, posOut) {
|
|
608
|
+
return exports.parseExpr(tokens, filters, posOut, scope);
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
// The lexer does not know where it is working, so the place of its error is
|
|
612
|
+
// named here. An error from a nested parse has its place already.
|
|
613
|
+
function throwPlaced(err) {
|
|
614
|
+
if (err.unplaced) {
|
|
615
|
+
utils.throwError(err.message.slice(0, -1), scope.line, scope.filename);
|
|
616
|
+
}
|
|
617
|
+
throw err;
|
|
618
|
+
}
|
|
619
|
+
|
|
593
620
|
var varControls = opts.varControls || ['{{', '}}'];
|
|
594
621
|
var tagControls = opts.tagControls || ['{%', '%}'];
|
|
595
622
|
var cmtControls = opts.cmtControls || ['{#', '#}'];
|
|
@@ -642,16 +669,22 @@ exports.parse = function (swig, source, opts, tags, filters) {
|
|
|
642
669
|
* @private
|
|
643
670
|
*/
|
|
644
671
|
function parseVariable(str, _line) {
|
|
645
|
-
|
|
672
|
+
scope.line = _line;
|
|
673
|
+
var lexed;
|
|
674
|
+
try {
|
|
675
|
+
lexed = lexer.read(utils.strip(str));
|
|
676
|
+
} catch (e) {
|
|
677
|
+
throwPlaced(e);
|
|
678
|
+
}
|
|
646
679
|
var sawSafe = false;
|
|
647
680
|
utils.each(lexed, function (tok) {
|
|
648
681
|
if (tok.type === _t.FILTEREMPTY || tok.type === _t.FILTER) {
|
|
649
|
-
if (
|
|
682
|
+
if (hasOwn.call(filters, tok.match) && filters[tok.match].safe === true) {
|
|
650
683
|
sawSafe = true;
|
|
651
684
|
}
|
|
652
685
|
}
|
|
653
686
|
});
|
|
654
|
-
var expr = exports.missingAsEmpty(exports.parseExpr(lexed, filters));
|
|
687
|
+
var expr = exports.missingAsEmpty(exports.parseExpr(lexed, filters, undefined, scope));
|
|
655
688
|
var tail;
|
|
656
689
|
var esc = escapeStack[escapeStack.length - 1];
|
|
657
690
|
if (esc && !sawSafe) {
|
|
@@ -734,7 +767,13 @@ exports.parse = function (swig, source, opts, tags, filters) {
|
|
|
734
767
|
escape: escapeStack[escapeStack.length - 1]
|
|
735
768
|
};
|
|
736
769
|
|
|
737
|
-
|
|
770
|
+
scope.line = _line;
|
|
771
|
+
var ok;
|
|
772
|
+
try {
|
|
773
|
+
ok = tag.parse(tagArgs, _line, exprParser, _t, stack, opts, swig, token);
|
|
774
|
+
} catch (e) {
|
|
775
|
+
throwPlaced(e);
|
|
776
|
+
}
|
|
738
777
|
if (!ok) {
|
|
739
778
|
utils.throwError('Unexpected tag "' + tagName + '"', _line, opts.filename);
|
|
740
779
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rhinostone/swig-django",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"description": "Django-syntax frontend for the @rhinostone/swig-core template engine. Part of the @rhinostone/swig multi-flavor family.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"template",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"node": ">=12"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"@rhinostone/swig-core": "2.
|
|
25
|
+
"@rhinostone/swig-core": "2.10.0"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|