@rhinostone/swig-core 2.8.4 → 2.9.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 -1
- package/lib/backend.js +15 -0
- package/lib/index.js +1 -1
- package/lib/ir.js +2 -0
- package/lib/utils.js +92 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
@rhinostone/swig-core
|
|
2
2
|
=====================
|
|
3
3
|
|
|
4
|
-
[](https://www.npmjs.com/package/@rhinostone/swig-core) [](https://socket.dev/npm/package/@rhinostone/swig-core)
|
|
4
|
+
[](https://www.npmjs.com/package/@rhinostone/swig-core) [](https://socket.dev/npm/package/@rhinostone/swig-core) [](https://scorecard.dev/viewer/?uri=github.com/gina-io/swig)
|
|
5
5
|
|
|
6
6
|
> **Shared runtime** for the `@rhinostone/swig` family of template engines. Not intended for direct consumption unless you are building a custom frontend. Install [@rhinostone/swig](https://www.npmjs.com/package/@rhinostone/swig) for the default Swig (Jinja2/Django-inspired) flavor, [@rhinostone/swig-twig](https://www.npmjs.com/package/@rhinostone/swig-twig) for the Twig flavor, [@rhinostone/swig-jinja2](https://www.npmjs.com/package/@rhinostone/swig-jinja2) for the Python Jinja2 flavor, or [@rhinostone/swig-django](https://www.npmjs.com/package/@rhinostone/swig-django) for the Django flavor — they all pull this package in pinned to the matching version.
|
|
7
7
|
|
package/lib/backend.js
CHANGED
|
@@ -910,6 +910,14 @@ function groupOperand(node, js, unary) {
|
|
|
910
910
|
return js;
|
|
911
911
|
}
|
|
912
912
|
|
|
913
|
+
/*!
|
|
914
|
+
* The flavor name `emitBinaryOp` writes into an `_utils.isIn` call: one of
|
|
915
|
+
* the three names `isIn` knows, never the flag's own text. @private
|
|
916
|
+
*/
|
|
917
|
+
function membershipMode(flag) {
|
|
918
|
+
return (flag === 'twig' || flag === 'django') ? flag : 'jinja2';
|
|
919
|
+
}
|
|
920
|
+
|
|
913
921
|
function emitBinaryOp(node, d) {
|
|
914
922
|
var left = groupOperand(node.left, emitExpr(node.left, d)),
|
|
915
923
|
right = groupOperand(node.right, emitExpr(node.right, d));
|
|
@@ -917,6 +925,13 @@ function emitBinaryOp(node, d) {
|
|
|
917
925
|
return left + ' ' + node.op + ' ' + right;
|
|
918
926
|
}
|
|
919
927
|
if (node.op === 'in') {
|
|
928
|
+
// A Twig, Jinja2 or Django `in` carries its flavor name in
|
|
929
|
+
// `membership` and calls that engine's membership test, which `negate`
|
|
930
|
+
// turns into `not in`; native swig keeps the JavaScript `in` operator,
|
|
931
|
+
// which tests keys.
|
|
932
|
+
if (node.membership) {
|
|
933
|
+
return '_utils.isIn(' + left + ', ' + right + ', "' + membershipMode(node.membership) + '"' + (node.negate ? ', true' : '') + ')';
|
|
934
|
+
}
|
|
920
935
|
return left + ' in ' + right;
|
|
921
936
|
}
|
|
922
937
|
// Twig/Jinja2 `~` is explicit string-concat: both sides coerce to
|
package/lib/index.js
CHANGED
package/lib/ir.js
CHANGED
|
@@ -446,6 +446,8 @@
|
|
|
446
446
|
* @property {string} op
|
|
447
447
|
* @property {IRExpr} left
|
|
448
448
|
* @property {IRExpr} right
|
|
449
|
+
* @property {string} [membership] Opt-in for `in`: "twig", "jinja2" or "django" emits `_utils.isIn(left, right, mode)`, that engine's membership test; without it, the JavaScript `in` operator (native swig).
|
|
450
|
+
* @property {boolean} [negate] With `membership`: the `not in` form, negated inside `_utils.isIn` (Django reads a test it cannot run as false either way).
|
|
449
451
|
* @property {IRLoc} [loc]
|
|
450
452
|
*/
|
|
451
453
|
|
package/lib/utils.js
CHANGED
|
@@ -388,3 +388,95 @@ exports.resolve = function (obj, path) {
|
|
|
388
388
|
}
|
|
389
389
|
return cur;
|
|
390
390
|
};
|
|
391
|
+
|
|
392
|
+
/*!
|
|
393
|
+
* Item equality for `isIn`. Jinja2 and Django compare with `===` and treat
|
|
394
|
+
* null and a missing value as equal (Django reads both as None). Twig
|
|
395
|
+
* compares two scalars with `==`, as swig-twig's `==` does, and anything
|
|
396
|
+
* else by identity. @private
|
|
397
|
+
*/
|
|
398
|
+
function sameItem(a, b, twig) {
|
|
399
|
+
if (a === b) {
|
|
400
|
+
return true;
|
|
401
|
+
}
|
|
402
|
+
if (!twig) {
|
|
403
|
+
return (a === null || a === undefined) && (b === null || b === undefined);
|
|
404
|
+
}
|
|
405
|
+
if ((a !== null && typeof a === 'object') || (b !== null && typeof b === 'object') ||
|
|
406
|
+
typeof a === 'function' || typeof b === 'function') {
|
|
407
|
+
return false;
|
|
408
|
+
}
|
|
409
|
+
return a == b;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/*!
|
|
413
|
+
* The membership test of `isIn`: true or false, or null when Jinja2 and
|
|
414
|
+
* Django cannot run it, where Python raises a TypeError: a container that is
|
|
415
|
+
* not a list, a string or a mapping, a needle that is not a string tested
|
|
416
|
+
* against a string, or a list or an object tested against a mapping's keys.
|
|
417
|
+
* Twig runs every test. @private
|
|
418
|
+
*/
|
|
419
|
+
function membership(needle, haystack, twig) {
|
|
420
|
+
var keys, i;
|
|
421
|
+
if (typeof haystack === 'string') {
|
|
422
|
+
if (typeof needle === 'string' || (twig && typeof needle === 'number')) {
|
|
423
|
+
return haystack.indexOf(String(needle)) !== -1;
|
|
424
|
+
}
|
|
425
|
+
return twig ? false : null;
|
|
426
|
+
}
|
|
427
|
+
if (haystack === null || typeof haystack !== 'object') {
|
|
428
|
+
return twig ? false : null;
|
|
429
|
+
}
|
|
430
|
+
if (exports.isArray(haystack)) {
|
|
431
|
+
for (i = 0; i < haystack.length; i += 1) {
|
|
432
|
+
if (sameItem(needle, haystack[i], twig)) {
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return false;
|
|
437
|
+
}
|
|
438
|
+
if (twig) {
|
|
439
|
+
keys = Object.keys(haystack);
|
|
440
|
+
for (i = 0; i < keys.length; i += 1) {
|
|
441
|
+
if (sameItem(needle, haystack[keys[i]], true)) {
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
if (typeof needle === 'string' || typeof needle === 'number') {
|
|
448
|
+
return Object.prototype.hasOwnProperty.call(haystack, String(needle));
|
|
449
|
+
}
|
|
450
|
+
return (needle !== null && typeof needle === 'object') ? null : false;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Membership test behind the `in` operator of the Twig, Jinja2 and Django
|
|
455
|
+
* frontends, which set `IRBinaryOp.membership` to their flavor name; native
|
|
456
|
+
* swig keeps the JavaScript `in` operator, which tests keys. Each flavor
|
|
457
|
+
* follows its reference engine:
|
|
458
|
+
*
|
|
459
|
+
* - Jinja2 and Django (Python's `in`): a list holds an equal item, a string
|
|
460
|
+
* a substring (the needle must be a string), a mapping a key (a number
|
|
461
|
+
* needle tests the key with the same digits).
|
|
462
|
+
* - Twig: a list or a mapping holds an equal value, compared loosely; a
|
|
463
|
+
* string holds a string or number needle as a substring.
|
|
464
|
+
*
|
|
465
|
+
* A test the engine cannot run (Jinja2 raises; Django's if tag reads it as
|
|
466
|
+
* false) gives false, as does any other container, rather than an error.
|
|
467
|
+
* With `negate` (the `not in` operator) the result is negated, except that
|
|
468
|
+
* Django reads a test it cannot run as false for `not in` too.
|
|
469
|
+
*
|
|
470
|
+
* @param {*} needle
|
|
471
|
+
* @param {*} haystack
|
|
472
|
+
* @param {string} mode "twig", "jinja2" or "django".
|
|
473
|
+
* @param {boolean} [negate] The `not in` form.
|
|
474
|
+
* @return {boolean}
|
|
475
|
+
*/
|
|
476
|
+
exports.isIn = function (needle, haystack, mode, negate) {
|
|
477
|
+
var found = membership(needle, haystack, mode === 'twig');
|
|
478
|
+
if (found === null) {
|
|
479
|
+
return negate === true && mode !== 'django';
|
|
480
|
+
}
|
|
481
|
+
return negate === true ? !found : found;
|
|
482
|
+
};
|