@rhinostone/swig-jinja2 2.5.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 +56 -0
- package/lib/async/pre-walker.js +267 -0
- package/lib/filters.js +1369 -0
- package/lib/index.js +344 -0
- package/lib/lexer.js +305 -0
- package/lib/parser.js +763 -0
- package/lib/tags/autoescape.js +75 -0
- package/lib/tags/block.js +82 -0
- package/lib/tags/elif.js +33 -0
- package/lib/tags/else.js +27 -0
- package/lib/tags/extends.js +77 -0
- package/lib/tags/filter.js +205 -0
- package/lib/tags/for.js +154 -0
- package/lib/tags/from.js +305 -0
- package/lib/tags/if.js +82 -0
- package/lib/tags/import.js +250 -0
- package/lib/tags/include.js +154 -0
- package/lib/tags/index.js +32 -0
- package/lib/tags/macro.js +174 -0
- package/lib/tags/raw.js +51 -0
- package/lib/tags/set.js +164 -0
- package/lib/tags/with.js +121 -0
- package/lib/tests/index.js +213 -0
- package/lib/tokentypes.js +89 -0
- package/package.json +31 -0
package/lib/tags/with.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Jinja2 `{% with %}` tag.
|
|
3
|
+
*
|
|
4
|
+
* Jinja2 scoped-context region with optional named assignments:
|
|
5
|
+
*
|
|
6
|
+
* {% with %}…{% endwith %} (new scope, shallow copy of _ctx)
|
|
7
|
+
* {% with x = 1 %}…{% endwith %} (x bound in the inner scope)
|
|
8
|
+
* {% with x = 1, y = total + 1 %}…{% endwith %} (multiple assignments)
|
|
9
|
+
*
|
|
10
|
+
* Each assignment is `<name> = <expr>`; the value expression is lowered
|
|
11
|
+
* through `parser.parseExpr`, so object literals, variable references,
|
|
12
|
+
* conditionals, and function calls all route through the same path. The
|
|
13
|
+
* assignments are collected into an object literal that the backend
|
|
14
|
+
* merges over a shallow copy of the outer context — so an assignment's
|
|
15
|
+
* value sees the enclosing scope (not its sibling assignments), and the
|
|
16
|
+
* inner bindings do not leak past `{% endwith %}`.
|
|
17
|
+
*
|
|
18
|
+
* Unlike Twig's `{% with {ctx} only %}`, Jinja2 has no `only` keyword and
|
|
19
|
+
* the region is never isolated — the outer context stays visible inside.
|
|
20
|
+
*
|
|
21
|
+
* Each assignment target is a bare identifier — dotted paths are rejected
|
|
22
|
+
* at parse time, and CVE-2023-25345 prototype-chain names are rejected
|
|
23
|
+
* before the binding lands (a quoted `"__proto__"` object key still sets
|
|
24
|
+
* the prototype in JS).
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
var ir = require('@rhinostone/swig-core/lib/ir');
|
|
28
|
+
var utils = require('@rhinostone/swig-core/lib/utils');
|
|
29
|
+
var _dangerousProps = require('@rhinostone/swig-core/lib/security').dangerousProps;
|
|
30
|
+
|
|
31
|
+
var lexer = require('../lexer');
|
|
32
|
+
|
|
33
|
+
exports.ends = true;
|
|
34
|
+
exports.block = false;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Parse the `{% with %}` tag body. Collects zero or more
|
|
38
|
+
* comma-separated `<name> = <expr>` assignments into an object literal on
|
|
39
|
+
* `token.irExpr`. A bare `{% with %}` leaves `token.irExpr` undefined so
|
|
40
|
+
* the backend emits a shallow copy of `_ctx`.
|
|
41
|
+
*
|
|
42
|
+
* @param {string} str Tag body.
|
|
43
|
+
* @param {number} line Source line of the opening `{%`.
|
|
44
|
+
* @param {object} parser The Jinja2 parser module (exposes `parseExpr`).
|
|
45
|
+
* @param {object} types Jinja2 lexer token-type enum.
|
|
46
|
+
* @param {Array} stack Open-tag stack (parser.js manages the push).
|
|
47
|
+
* @param {object} opts Per-call options (honors `opts.filename`).
|
|
48
|
+
* @param {object} swig Swig instance (unused).
|
|
49
|
+
* @param {object} token In-progress TagToken.
|
|
50
|
+
* @return {boolean} Always `true` on success. Throws otherwise.
|
|
51
|
+
*/
|
|
52
|
+
exports.parse = function (str, line, parser, types, stack, opts, swig, token) {
|
|
53
|
+
var tokens = lexer.read(utils.strip(str));
|
|
54
|
+
var pos = 0;
|
|
55
|
+
|
|
56
|
+
function peek() {
|
|
57
|
+
while (pos < tokens.length && tokens[pos].type === types.WHITESPACE) { pos += 1; }
|
|
58
|
+
return pos < tokens.length ? tokens[pos] : null;
|
|
59
|
+
}
|
|
60
|
+
function consume() {
|
|
61
|
+
var t = peek();
|
|
62
|
+
if (t) { pos += 1; }
|
|
63
|
+
return t;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
var props = [];
|
|
67
|
+
|
|
68
|
+
// Bare `{% with %}` has no assignments — the leading peek() is null and
|
|
69
|
+
// the loop is skipped, leaving token.irExpr undefined.
|
|
70
|
+
if (peek()) {
|
|
71
|
+
while (true) {
|
|
72
|
+
var nameTok = consume();
|
|
73
|
+
if (!nameTok || nameTok.type !== types.VAR) {
|
|
74
|
+
utils.throwError('Expected variable name in "with" tag', line, opts.filename);
|
|
75
|
+
}
|
|
76
|
+
if (nameTok.match.indexOf('.') !== -1) {
|
|
77
|
+
utils.throwError('"with" assignment target "' + nameTok.match + '" must be a bare identifier', line, opts.filename);
|
|
78
|
+
}
|
|
79
|
+
if (_dangerousProps.indexOf(nameTok.match) !== -1) {
|
|
80
|
+
utils.throwError('Unsafe "with" assignment to "' + nameTok.match + '" is not allowed (CVE-2023-25345)', line, opts.filename);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
var eqTok = consume();
|
|
84
|
+
if (!eqTok || eqTok.type !== types.ASSIGNMENT || eqTok.match !== '=') {
|
|
85
|
+
utils.throwError('Expected "=" after "' + nameTok.match + '" in "with" tag', line, opts.filename);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Parse a single value expression from the cursor. parseExpr stops
|
|
89
|
+
// at the next COMMA (which is not part of an expression); the
|
|
90
|
+
// out-param reports how many slice tokens it consumed.
|
|
91
|
+
var posOut = {};
|
|
92
|
+
var valExpr = parser.parseExpr(tokens.slice(pos), {}, posOut);
|
|
93
|
+
pos += posOut.pos;
|
|
94
|
+
props.push(ir.objectProperty(ir.literal('string', nameTok.match), valExpr));
|
|
95
|
+
|
|
96
|
+
var next = peek();
|
|
97
|
+
if (!next) { break; }
|
|
98
|
+
if (next.type !== types.COMMA) {
|
|
99
|
+
utils.throwError('Expected "," between assignments in "with" tag', line, opts.filename);
|
|
100
|
+
}
|
|
101
|
+
consume();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
token.irExpr = props.length ? ir.objectLiteral(props) : undefined;
|
|
106
|
+
return true;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Emit an IRWith node carrying the optional context object literal (the
|
|
111
|
+
* collected assignments) and the recursively-compiled body wrapped in
|
|
112
|
+
* IRLegacyJS. The region is never isolated; the backend's `With` branch
|
|
113
|
+
* merges the context over a shallow copy of `_ctx` for the body's lexical
|
|
114
|
+
* scope.
|
|
115
|
+
*
|
|
116
|
+
* @return {object} IRWith node.
|
|
117
|
+
*/
|
|
118
|
+
exports.compile = function (compiler, args, content, parents, options, blockName, token) {
|
|
119
|
+
var bodyJS = compiler(content, parents, options, blockName);
|
|
120
|
+
return ir.withStmt(token.irExpr, false, [ir.legacyJS(bodyJS)]);
|
|
121
|
+
};
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rhinostone/swig-jinja2 — built-in test runtime helpers.
|
|
3
|
+
*
|
|
4
|
+
* Jinja2 `is <name>` / `is not <name>` expressions lower to
|
|
5
|
+
* `_ext._test_<name>(subject, ...args)` at the IR layer. The Jinja2
|
|
6
|
+
* constructor registers each export here via `self.setExtension('_test_'
|
|
7
|
+
* + name, fn)`, which installs the helper onto the per-instance
|
|
8
|
+
* `_swig.extensions` map — so Path A (`new Jinja2().render(...)`) honors
|
|
9
|
+
* per-instance overrides without leaking cross-instance.
|
|
10
|
+
*
|
|
11
|
+
* Three tests (`defined`, `none`, `undefined`) are additionally
|
|
12
|
+
* special-cased in the parser when the subject is a VarRef with no args:
|
|
13
|
+
* they route through IRVarRefExists to preserve the defined/undefined
|
|
14
|
+
* signal that `emitVarRef` coerces to "". The helpers below still run for
|
|
15
|
+
* non-VarRef subjects (literals, BinaryOp, FnCall) where the coercion is
|
|
16
|
+
* not in play. See parser.js `parseExpression` IS/ISNOT branch.
|
|
17
|
+
*
|
|
18
|
+
* JS / Python impedance notes (documented divergences):
|
|
19
|
+
* - No int/float distinction in JS, so `is integer` / `is float` are not
|
|
20
|
+
* provided; use `is number`.
|
|
21
|
+
* - `is sequence` is array-or-string (ordered, integer-indexed); a dict
|
|
22
|
+
* is `is mapping` (not `is sequence`). `is iterable` covers arrays,
|
|
23
|
+
* strings, and objects, matching the `{% for %}` iterate-by-key rule.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/*!
|
|
27
|
+
* Array detection without depending on the runtime's Array.isArray (kept
|
|
28
|
+
* browser-safe and consistent with the other per-flavor test helpers).
|
|
29
|
+
* @private
|
|
30
|
+
*/
|
|
31
|
+
function isArr(v) {
|
|
32
|
+
return Object.prototype.toString.call(v) === '[object Array]';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/*!
|
|
36
|
+
* Finite-number guard shared by the numeric tests. @private
|
|
37
|
+
*/
|
|
38
|
+
function isNumber(v) {
|
|
39
|
+
return typeof v === 'number' && !isNaN(v);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* `foo is defined` — true when the subject is not `undefined`. The
|
|
44
|
+
* VarRef-subject path bypasses this helper and uses IRVarRefExists.
|
|
45
|
+
*
|
|
46
|
+
* @param {*} v
|
|
47
|
+
* @return {boolean}
|
|
48
|
+
*/
|
|
49
|
+
exports['defined'] = function (v) {
|
|
50
|
+
return typeof v !== 'undefined';
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* `foo is undefined` — true when the subject is `undefined`. The
|
|
55
|
+
* VarRef-subject path bypasses this helper and uses `!IRVarRefExists`.
|
|
56
|
+
*
|
|
57
|
+
* @param {*} v
|
|
58
|
+
* @return {boolean}
|
|
59
|
+
*/
|
|
60
|
+
exports['undefined'] = function (v) {
|
|
61
|
+
return typeof v === 'undefined';
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* `foo is none` — true when the subject is `null` or `undefined` (Python
|
|
66
|
+
* `None`). The VarRef-subject path bypasses this helper.
|
|
67
|
+
*
|
|
68
|
+
* @param {*} v
|
|
69
|
+
* @return {boolean}
|
|
70
|
+
*/
|
|
71
|
+
exports['none'] = function (v) {
|
|
72
|
+
return v === null || typeof v === 'undefined';
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* `foo is boolean` — true for a JS boolean.
|
|
77
|
+
*
|
|
78
|
+
* @param {*} v
|
|
79
|
+
* @return {boolean}
|
|
80
|
+
*/
|
|
81
|
+
exports['boolean'] = function (v) {
|
|
82
|
+
return typeof v === 'boolean';
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* `foo is number` — true for a finite number.
|
|
87
|
+
*
|
|
88
|
+
* @param {*} v
|
|
89
|
+
* @return {boolean}
|
|
90
|
+
*/
|
|
91
|
+
exports['number'] = function (v) {
|
|
92
|
+
return isNumber(v);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* `foo is string` — true for a string.
|
|
97
|
+
*
|
|
98
|
+
* @param {*} v
|
|
99
|
+
* @return {boolean}
|
|
100
|
+
*/
|
|
101
|
+
exports['string'] = function (v) {
|
|
102
|
+
return typeof v === 'string';
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* `foo is mapping` — true for a plain object (a dict), excluding arrays
|
|
107
|
+
* and null.
|
|
108
|
+
*
|
|
109
|
+
* @param {*} v
|
|
110
|
+
* @return {boolean}
|
|
111
|
+
*/
|
|
112
|
+
exports['mapping'] = function (v) {
|
|
113
|
+
return typeof v === 'object' && v !== null && !isArr(v);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* `foo is sequence` — true for an array or string (ordered,
|
|
118
|
+
* integer-indexed). A dict is `mapping`, not `sequence`.
|
|
119
|
+
*
|
|
120
|
+
* @param {*} v
|
|
121
|
+
* @return {boolean}
|
|
122
|
+
*/
|
|
123
|
+
exports['sequence'] = function (v) {
|
|
124
|
+
return isArr(v) || typeof v === 'string';
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* `foo is iterable` — true for arrays, strings, and non-null objects
|
|
129
|
+
* (mirrors the `{% for %}` rule that dicts iterate by key).
|
|
130
|
+
*
|
|
131
|
+
* @param {*} v
|
|
132
|
+
* @return {boolean}
|
|
133
|
+
*/
|
|
134
|
+
exports['iterable'] = function (v) {
|
|
135
|
+
if (v === null || typeof v === 'undefined') { return false; }
|
|
136
|
+
if (isArr(v) || typeof v === 'string') { return true; }
|
|
137
|
+
return typeof v === 'object';
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* `foo is callable` — true for a function.
|
|
142
|
+
*
|
|
143
|
+
* @param {*} v
|
|
144
|
+
* @return {boolean}
|
|
145
|
+
*/
|
|
146
|
+
exports['callable'] = function (v) {
|
|
147
|
+
return typeof v === 'function';
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* `foo is sameas(bar)` — strict identity check (`foo === bar`).
|
|
152
|
+
*
|
|
153
|
+
* @param {*} v
|
|
154
|
+
* @param {*} other
|
|
155
|
+
* @return {boolean}
|
|
156
|
+
*/
|
|
157
|
+
exports['sameas'] = function (v, other) {
|
|
158
|
+
return v === other;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* `foo is lower` — true when the subject is a string equal to its own
|
|
163
|
+
* lowercase form.
|
|
164
|
+
*
|
|
165
|
+
* @param {*} v
|
|
166
|
+
* @return {boolean}
|
|
167
|
+
*/
|
|
168
|
+
exports['lower'] = function (v) {
|
|
169
|
+
return typeof v === 'string' && v === v.toLowerCase();
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* `foo is upper` — true when the subject is a string equal to its own
|
|
174
|
+
* uppercase form.
|
|
175
|
+
*
|
|
176
|
+
* @param {*} v
|
|
177
|
+
* @return {boolean}
|
|
178
|
+
*/
|
|
179
|
+
exports['upper'] = function (v) {
|
|
180
|
+
return typeof v === 'string' && v === v.toUpperCase();
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* `n is even` — true for numbers whose remainder mod 2 is zero.
|
|
185
|
+
*
|
|
186
|
+
* @param {number} v
|
|
187
|
+
* @return {boolean}
|
|
188
|
+
*/
|
|
189
|
+
exports['even'] = function (v) {
|
|
190
|
+
return isNumber(v) && v % 2 === 0;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* `n is odd` — true for numbers whose remainder mod 2 is non-zero.
|
|
195
|
+
*
|
|
196
|
+
* @param {number} v
|
|
197
|
+
* @return {boolean}
|
|
198
|
+
*/
|
|
199
|
+
exports['odd'] = function (v) {
|
|
200
|
+
return isNumber(v) && v % 2 !== 0;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* `n is divisibleby(m)` — true when `m` is a non-zero number and `n % m
|
|
205
|
+
* === 0`.
|
|
206
|
+
*
|
|
207
|
+
* @param {number} v
|
|
208
|
+
* @param {number} n
|
|
209
|
+
* @return {boolean}
|
|
210
|
+
*/
|
|
211
|
+
exports['divisibleby'] = function (v, n) {
|
|
212
|
+
return isNumber(v) && isNumber(n) && n !== 0 && v % n === 0;
|
|
213
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jinja2 lexer token type enum — the contract between the Jinja2 lexer and
|
|
3
|
+
* the Jinja2 parser in @rhinostone/swig-jinja2.
|
|
4
|
+
*
|
|
5
|
+
* Numeric IDs in the shared range (0–25, 100) mirror
|
|
6
|
+
* @rhinostone/swig-core/lib/tokentypes by design: Jinja2 and native Swig
|
|
7
|
+
* lower to the same swig-core IR, and aligning the IDs keeps shared
|
|
8
|
+
* consumers (e.g. CVE-2023-25345 `_dangerousProps` enforcement) flavor-
|
|
9
|
+
* agnostic. The Jinja2 parser is its own module — it does not inherit
|
|
10
|
+
* from swig-core's TokenParser — but the cognitive overhead of re-mapping
|
|
11
|
+
* IDs across flavors is not worth the freedom.
|
|
12
|
+
*
|
|
13
|
+
* Jinja2-only IDs (30+) are reserved here so later commits can add lexer
|
|
14
|
+
* rules without renumbering. Jinja2 has no range (`..`), null-coalescing
|
|
15
|
+
* (`??`), ternary (`?:`), or `#{}` string interpolation operators, so the
|
|
16
|
+
* Jinja2-only block is smaller than Twig's: `~` concat, `**` power, `//`
|
|
17
|
+
* floor-division, and the `is` / `is not` test keywords.
|
|
18
|
+
*
|
|
19
|
+
* @readonly
|
|
20
|
+
* @enum {number}
|
|
21
|
+
*/
|
|
22
|
+
module.exports = {
|
|
23
|
+
/** Whitespace */
|
|
24
|
+
WHITESPACE: 0,
|
|
25
|
+
/** Plain string literal */
|
|
26
|
+
STRING: 1,
|
|
27
|
+
/** Variable filter call with arguments — `|name(...)` */
|
|
28
|
+
FILTER: 2,
|
|
29
|
+
/** Variable filter call with no arguments — `|name` */
|
|
30
|
+
FILTEREMPTY: 3,
|
|
31
|
+
/** Function call with arguments — `name(...)` */
|
|
32
|
+
FUNCTION: 4,
|
|
33
|
+
/** Function call with no arguments — `name()` */
|
|
34
|
+
FUNCTIONEMPTY: 5,
|
|
35
|
+
/** Open parenthesis */
|
|
36
|
+
PARENOPEN: 6,
|
|
37
|
+
/** Close parenthesis */
|
|
38
|
+
PARENCLOSE: 7,
|
|
39
|
+
/** Comma */
|
|
40
|
+
COMMA: 8,
|
|
41
|
+
/** Variable identifier */
|
|
42
|
+
VAR: 9,
|
|
43
|
+
/** Numeric literal */
|
|
44
|
+
NUMBER: 10,
|
|
45
|
+
/** Math operator (+, -, *, /, %) */
|
|
46
|
+
OPERATOR: 11,
|
|
47
|
+
/** Open square bracket */
|
|
48
|
+
BRACKETOPEN: 12,
|
|
49
|
+
/** Close square bracket */
|
|
50
|
+
BRACKETCLOSE: 13,
|
|
51
|
+
/** Dot-key accessor — `.key` */
|
|
52
|
+
DOTKEY: 14,
|
|
53
|
+
/** Open square bracket at the start of an array literal */
|
|
54
|
+
ARRAYOPEN: 15,
|
|
55
|
+
/** Open curly brace */
|
|
56
|
+
CURLYOPEN: 17,
|
|
57
|
+
/** Close curly brace */
|
|
58
|
+
CURLYCLOSE: 18,
|
|
59
|
+
/** Colon — object-literal key/value separator, and slice subscript */
|
|
60
|
+
COLON: 19,
|
|
61
|
+
/** JavaScript-valid comparator (==, !=, <=, etc.) */
|
|
62
|
+
COMPARATOR: 20,
|
|
63
|
+
/** Boolean logic (`and`, `or`, `&&`, `||`) */
|
|
64
|
+
LOGIC: 21,
|
|
65
|
+
/** Boolean negation (`not`, `!`) */
|
|
66
|
+
NOT: 22,
|
|
67
|
+
/** Boolean literal (`true`, `false`) */
|
|
68
|
+
BOOL: 23,
|
|
69
|
+
/** Variable assignment (`=`, `+=`, `-=`, `*=`, `/=`) */
|
|
70
|
+
ASSIGNMENT: 24,
|
|
71
|
+
/** Method call open — internal */
|
|
72
|
+
METHODOPEN: 25,
|
|
73
|
+
|
|
74
|
+
/* ---- Jinja2-only token IDs (reserved; rules land in later commits) ---- */
|
|
75
|
+
|
|
76
|
+
/** Jinja2 string-concatenation operator — `~` */
|
|
77
|
+
TILDE: 30,
|
|
78
|
+
/** Jinja2 exponentiation operator — `**` */
|
|
79
|
+
POWER: 31,
|
|
80
|
+
/** Jinja2 floor-division operator — `//` */
|
|
81
|
+
FLOORDIV: 32,
|
|
82
|
+
/** Jinja2 test operator — `is` */
|
|
83
|
+
IS: 33,
|
|
84
|
+
/** Jinja2 negated test operator — `is not` */
|
|
85
|
+
ISNOT: 34,
|
|
86
|
+
|
|
87
|
+
/** Unknown token */
|
|
88
|
+
UNKNOWN: 100
|
|
89
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rhinostone/swig-jinja2",
|
|
3
|
+
"version": "2.5.0",
|
|
4
|
+
"description": "Jinja2-syntax frontend for the @rhinostone/swig-core template engine. Part of the @rhinostone/swig multi-flavor family.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"template",
|
|
7
|
+
"templating",
|
|
8
|
+
"jinja2",
|
|
9
|
+
"jinja",
|
|
10
|
+
"swig",
|
|
11
|
+
"swig-jinja2",
|
|
12
|
+
"frontend"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/gina-io/swig.git",
|
|
17
|
+
"directory": "packages/swig-jinja2"
|
|
18
|
+
},
|
|
19
|
+
"author": "Rhinostone <contact@gina.io>",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"main": "lib/index.js",
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=12"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@rhinostone/swig-core": "2.5.0"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
}
|
|
31
|
+
}
|