sjabloon 0.3.0 → 0.4.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 +9 -1
- package/dist/index.cjs +1 -1
- package/dist/index.module.js +1 -1
- package/dist/index.umd.js +1 -1
- package/index.d.ts +10 -2
- package/package.json +2 -2
- package/src/index.js +15 -4
package/README.md
CHANGED
|
@@ -37,7 +37,15 @@ render(
|
|
|
37
37
|
|
|
38
38
|
### `template(str, functions?)`
|
|
39
39
|
|
|
40
|
-
Compiles the template and returns a renderer `(values?) => string`. Malformed tags, unclosed blocks, and invalid expressions throw a `SyntaxError` at compile time.
|
|
40
|
+
Compiles the template and returns a renderer `(values?, scope?) => string`. Malformed tags, unclosed blocks, and invalid expressions throw a `SyntaxError` at compile time.
|
|
41
|
+
|
|
42
|
+
The anchors `$` (root) and `@` (current `{{#each}}` item) work as [described below](#syntax) with no extra arguments — at the root, before any loop, both point at `values`. If you're embedding sjabloon under an engine with its own scope model, pass `{ root, item }` as the second argument to seed the two root anchors from distinct objects: `$` becomes `root` and `@` becomes `item`. Omit `item` and `@` stays unbound at the root, so reading `@.x` throws where there is no current item. Either way, `{{#each}}` still re-points `@` to the current item inside its body.
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
const tpl = template('{{ $.report }} — {{ @.row }}');
|
|
46
|
+
tpl(base, { root: reportRoot, item: currentRow }); // $ = reportRoot, @ = currentRow
|
|
47
|
+
tpl(base, { root: reportRoot }); // no item → @.x throws
|
|
48
|
+
```
|
|
41
49
|
|
|
42
50
|
The renderer also carries `names` (every variable the template reads from your values, loop variables excluded) and `functions` (the registry functions it calls, methods excluded), both deduplicated. Check a stored template against your data model and its allowed functions before you render it, or fetch only the fields it needs.
|
|
43
51
|
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var t=require("xprsn");function r(t,r){(null==r||r>t.length)&&(r=t.length);for(var e=0,n=Array(r);e<r;e++)n[e]=t[e];return n}function e(t,e){var n="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(n)return(n=n.call(t)).next.bind(n);if(Array.isArray(t)||(n=function(t,e){if(t){if("string"==typeof t)return r(t,e);var n={}.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?r(t,e):void 0}}(t))||e&&t&&"number"==typeof t.length){n&&(t=n);var a=0;return function(){return a>=t.length?{done:!0}:{done:!1,value:t[a++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var n,a,u,
|
|
1
|
+
var t=require("xprsn");function r(t,r){(null==r||r>t.length)&&(r=t.length);for(var e=0,n=Array(r);e<r;e++)n[e]=t[e];return n}function e(t,e){var n="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(n)return(n=n.call(t)).next.bind(n);if(Array.isArray(t)||(n=function(t,e){if(t){if("string"==typeof t)return r(t,e);var n={}.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?r(t,e):void 0}}(t))||e&&t&&"number"==typeof t.length){n&&(t=n);var a=0;return function(){return a>=t.length?{done:!0}:{done:!1,value:t[a++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var n,a,i,u,o,l,c,f={"&":"&","<":"<",">":">",'"':""","'":"'"},s=function(t){return String(t).replace(/[&<>"']/g,function(t){return f[t]})},h=/\{\{\{(-)?\s*([\s\S]*?)\s*(-)?\}\}\}|\{\{(-)?\s*([\s\S]*?)\s*(-)?\}\}/,g=function(t){throw SyntaxError(t)},p=function(t,r){return t.map(function(t){return t(r)}).join("")},v=function(t,r){return e=d(t),function(t){var n;return r(null!=(n=e(t))?n:"")};var e},d=function(r){for(var n,a=t.compile(r,i),u=e(a.names);!(n=u()).done;){var f=n.value;o.has(f)||l.add(f)}for(var s,h=e(a.functions);!(s=h()).done;)c.add(s.value);return a},m=function(t){var r=y(["#elif","#else","/if"]),e=u.startsWith("#elif ")?[m(d(u.slice(6)))]:"#else"===u?y(["/if"]):[];return function(n){return p(t(n)?r:e,n)}},y=function(t){for(var r,e,i=[],l=function(){if(null!=e.text)i.push((x=e.text,function(){return x}));else if(null!=e.raw)i.push(v(e.raw,String));else{if(t.includes(e.tag.split(" ")[0]))return u=e.tag,{v:i};if("!"===e.tag[0]);else if(e.tag.startsWith("#if "))i.push(m(d(e.tag.slice(4))));else if(e.tag.startsWith("#each ")){var r=/^#each ([\s\S]+) as (\w+)(?:\s*,\s*(\w+))?$/.exec(e.tag)||g("Bad {{"+e.tag+"}}"),n=d(r[1]),a=r[2],l=r[3],c=function(){var t=[].slice.call(arguments).filter(function(t){return t&&!o.has(t)});return t.forEach(function(t){return o.add(t)}),function(){return t.forEach(function(t){return o.delete(t)})}}(a,l,"loop"),f=y(["#else","/each"]);c();var h="#else"===u?y(["/each"]):[];i.push(function(t){var r,e=(r=n(t),Array.isArray(r)?r.map(function(t,r){return[t,r]}):r&&"object"==typeof r?Object.keys(r).map(function(t){return[r[t],t]}):[]);return e.length?e.map(function(r,n){var i=r[0],u=r[1],o=Object.create(t);return o[a]=i,l&&(o[l]=u),o["@"]=i,o.loop={index:n+1,index0:n,first:!n,last:n===e.length-1,length:e.length},p(f,o)}).join(""):p(h,t)})}else"#"===e.tag[0]||"/"===e.tag[0]?g("Unexpected {{"+e.tag+"}}"):i.push(v(e.tag,s))}var x};e=n[a++];)if(r=l())return r.v;return t.length&&g("Missing {{"+t[t.length-1]+"}}"),i};function x(t,r){i=r,o=new Set(["$","@"]),l=new Set,c=new Set,n=[];for(var e=String(t).split(h),u=0;u<e.length&&(e[u]&&n.push({text:e[u]}),!(u+1>=e.length));u+=7){var f=null!=e[u+2],s=f?{raw:e[u+2]}:{tag:e[u+5]};s.l="-"===e[u+(f?1:4)],s.r="-"===e[u+(f?3:6)],n.push(s)}n.forEach(function(t,r){var e,a;t.l&&null!=(e=n[r-1])&&e.text&&(n[r-1].text=n[r-1].text.replace(/\s+$/,"")),t.r&&null!=(a=n[r+1])&&a.text&&(n[r+1].text=n[r+1].text.replace(/^\s+/,""))}),a=0;var g=y([]),v=function(t,r){t=t||{};var e=Object.create(t);return e.$=r?r.root:t,r?"item"in r&&(e["@"]=r.item):e["@"]=t,p(g,e)};return v.names=Array.from(l),v.functions=Array.from(c),v}exports.render=function(t,r,e){return x(t,e)(r)},exports.template=x;
|
package/dist/index.module.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{compile as t}from"xprsn";function r(t,r){(null==r||r>t.length)&&(r=t.length);for(var n=0,e=Array(r);n<r;n++)e[n]=t[n];return e}function n(t,n){var e="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(e)return(e=e.call(t)).next.bind(e);if(Array.isArray(t)||(e=function(t,n){if(t){if("string"==typeof t)return r(t,n);var e={}.toString.call(t).slice(8,-1);return"Object"===e&&t.constructor&&(e=t.constructor.name),"Map"===e||"Set"===e?Array.from(t):"Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e)?r(t,n):void 0}}(t))||n&&t&&"number"==typeof t.length){e&&(t=e);var a=0;return function(){return a>=t.length?{done:!0}:{done:!1,value:t[a++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var e,a,u,
|
|
1
|
+
import{compile as t}from"xprsn";function r(t,r){(null==r||r>t.length)&&(r=t.length);for(var n=0,e=Array(r);n<r;n++)e[n]=t[n];return e}function n(t,n){var e="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(e)return(e=e.call(t)).next.bind(e);if(Array.isArray(t)||(e=function(t,n){if(t){if("string"==typeof t)return r(t,n);var e={}.toString.call(t).slice(8,-1);return"Object"===e&&t.constructor&&(e=t.constructor.name),"Map"===e||"Set"===e?Array.from(t):"Arguments"===e||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(e)?r(t,n):void 0}}(t))||n&&t&&"number"==typeof t.length){e&&(t=e);var a=0;return function(){return a>=t.length?{done:!0}:{done:!1,value:t[a++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var e,a,i,u,o,l,f,c={"&":"&","<":"<",">":">",'"':""","'":"'"},s=function(t){return String(t).replace(/[&<>"']/g,function(t){return c[t]})},h=/\{\{\{(-)?\s*([\s\S]*?)\s*(-)?\}\}\}|\{\{(-)?\s*([\s\S]*?)\s*(-)?\}\}/,g=function(t){throw SyntaxError(t)},p=function(t,r){return t.map(function(t){return t(r)}).join("")},v=function(t,r){return n=d(t),function(t){var e;return r(null!=(e=n(t))?e:"")};var n},d=function(r){for(var e,a=t(r,i),u=n(a.names);!(e=u()).done;){var c=e.value;o.has(c)||l.add(c)}for(var s,h=n(a.functions);!(s=h()).done;)f.add(s.value);return a},m=function(t){var r=y(["#elif","#else","/if"]),n=u.startsWith("#elif ")?[m(d(u.slice(6)))]:"#else"===u?y(["/if"]):[];return function(e){return p(t(e)?r:n,e)}},y=function(t){for(var r,n,i=[],l=function(){if(null!=n.text)i.push((x=n.text,function(){return x}));else if(null!=n.raw)i.push(v(n.raw,String));else{if(t.includes(n.tag.split(" ")[0]))return u=n.tag,{v:i};if("!"===n.tag[0]);else if(n.tag.startsWith("#if "))i.push(m(d(n.tag.slice(4))));else if(n.tag.startsWith("#each ")){var r=/^#each ([\s\S]+) as (\w+)(?:\s*,\s*(\w+))?$/.exec(n.tag)||g("Bad {{"+n.tag+"}}"),e=d(r[1]),a=r[2],l=r[3],f=function(){var t=[].slice.call(arguments).filter(function(t){return t&&!o.has(t)});return t.forEach(function(t){return o.add(t)}),function(){return t.forEach(function(t){return o.delete(t)})}}(a,l,"loop"),c=y(["#else","/each"]);f();var h="#else"===u?y(["/each"]):[];i.push(function(t){var r,n=(r=e(t),Array.isArray(r)?r.map(function(t,r){return[t,r]}):r&&"object"==typeof r?Object.keys(r).map(function(t){return[r[t],t]}):[]);return n.length?n.map(function(r,e){var i=r[0],u=r[1],o=Object.create(t);return o[a]=i,l&&(o[l]=u),o["@"]=i,o.loop={index:e+1,index0:e,first:!e,last:e===n.length-1,length:n.length},p(c,o)}).join(""):p(h,t)})}else"#"===n.tag[0]||"/"===n.tag[0]?g("Unexpected {{"+n.tag+"}}"):i.push(v(n.tag,s))}var x};n=e[a++];)if(r=l())return r.v;return t.length&&g("Missing {{"+t[t.length-1]+"}}"),i};function x(t,r){i=r,o=new Set(["$","@"]),l=new Set,f=new Set,e=[];for(var n=String(t).split(h),u=0;u<n.length&&(n[u]&&e.push({text:n[u]}),!(u+1>=n.length));u+=7){var c=null!=n[u+2],s=c?{raw:n[u+2]}:{tag:n[u+5]};s.l="-"===n[u+(c?1:4)],s.r="-"===n[u+(c?3:6)],e.push(s)}e.forEach(function(t,r){var n,a;t.l&&null!=(n=e[r-1])&&n.text&&(e[r-1].text=e[r-1].text.replace(/\s+$/,"")),t.r&&null!=(a=e[r+1])&&a.text&&(e[r+1].text=e[r+1].text.replace(/^\s+/,""))}),a=0;var g=y([]),v=function(t,r){t=t||{};var n=Object.create(t);return n.$=r?r.root:t,r?"item"in r&&(n["@"]=r.item):n["@"]=t,p(g,n)};return v.names=Array.from(l),v.functions=Array.from(f),v}function S(t,r,n){return x(t,n)(r)}export{S as render,x as template};
|
package/dist/index.umd.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("xprsn")):"function"==typeof define&&define.amd?define(["exports","xprsn"],e):e((t||self).sjabloon={},t.xprsn)}(this,function(t,e){function n(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=Array(e);n<e;n++)r[n]=t[n];return r}function r(t,e){var r="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(r)return(r=r.call(t)).next.bind(r);if(Array.isArray(t)||(r=function(t,e){if(t){if("string"==typeof t)return n(t,e);var r={}.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?n(t,e):void 0}}(t))||e&&t&&"number"==typeof t.length){r&&(t=r);var a=0;return function(){return a>=t.length?{done:!0}:{done:!1,value:t[a++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,o,
|
|
1
|
+
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("xprsn")):"function"==typeof define&&define.amd?define(["exports","xprsn"],e):e((t||self).sjabloon={},t.xprsn)}(this,function(t,e){function n(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=Array(e);n<e;n++)r[n]=t[n];return r}function r(t,e){var r="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(r)return(r=r.call(t)).next.bind(r);if(Array.isArray(t)||(r=function(t,e){if(t){if("string"==typeof t)return n(t,e);var r={}.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?n(t,e):void 0}}(t))||e&&t&&"number"==typeof t.length){r&&(t=r);var a=0;return function(){return a>=t.length?{done:!0}:{done:!1,value:t[a++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,i,o,u,l,f,s,c={"&":"&","<":"<",">":">",'"':""","'":"'"},p=function(t){return String(t).replace(/[&<>"']/g,function(t){return c[t]})},h=/\{\{\{(-)?\s*([\s\S]*?)\s*(-)?\}\}\}|\{\{(-)?\s*([\s\S]*?)\s*(-)?\}\}/,g=function(t){throw SyntaxError(t)},d=function(t,e){return t.map(function(t){return t(e)}).join("")},v=function(t,e){return n=m(t),function(t){var r;return e(null!=(r=n(t))?r:"")};var n},m=function(t){for(var n,a=e.compile(t,o),i=r(a.names);!(n=i()).done;){var u=n.value;l.has(u)||f.add(u)}for(var c,p=r(a.functions);!(c=p()).done;)s.add(c.value);return a},y=function(t){var e=x(["#elif","#else","/if"]),n=u.startsWith("#elif ")?[y(m(u.slice(6)))]:"#else"===u?x(["/if"]):[];return function(r){return d(t(r)?e:n,r)}},x=function(t){for(var e,n,r=[],o=function(){if(null!=n.text)r.push((h=n.text,function(){return h}));else if(null!=n.raw)r.push(v(n.raw,String));else{if(t.includes(n.tag.split(" ")[0]))return u=n.tag,{v:r};if("!"===n.tag[0]);else if(n.tag.startsWith("#if "))r.push(y(m(n.tag.slice(4))));else if(n.tag.startsWith("#each ")){var e=/^#each ([\s\S]+) as (\w+)(?:\s*,\s*(\w+))?$/.exec(n.tag)||g("Bad {{"+n.tag+"}}"),a=m(e[1]),i=e[2],o=e[3],f=function(){var t=[].slice.call(arguments).filter(function(t){return t&&!l.has(t)});return t.forEach(function(t){return l.add(t)}),function(){return t.forEach(function(t){return l.delete(t)})}}(i,o,"loop"),s=x(["#else","/each"]);f();var c="#else"===u?x(["/each"]):[];r.push(function(t){var e,n=(e=a(t),Array.isArray(e)?e.map(function(t,e){return[t,e]}):e&&"object"==typeof e?Object.keys(e).map(function(t){return[e[t],t]}):[]);return n.length?n.map(function(e,r){var a=e[0],u=e[1],l=Object.create(t);return l[i]=a,o&&(l[o]=u),l["@"]=a,l.loop={index:r+1,index0:r,first:!r,last:r===n.length-1,length:n.length},d(s,l)}).join(""):d(c,t)})}else"#"===n.tag[0]||"/"===n.tag[0]?g("Unexpected {{"+n.tag+"}}"):r.push(v(n.tag,p))}var h};n=a[i++];)if(e=o())return e.v;return t.length&&g("Missing {{"+t[t.length-1]+"}}"),r};function b(t,e){o=e,l=new Set(["$","@"]),f=new Set,s=new Set,a=[];for(var n=String(t).split(h),r=0;r<n.length&&(n[r]&&a.push({text:n[r]}),!(r+1>=n.length));r+=7){var u=null!=n[r+2],c=u?{raw:n[r+2]}:{tag:n[r+5]};c.l="-"===n[r+(u?1:4)],c.r="-"===n[r+(u?3:6)],a.push(c)}a.forEach(function(t,e){var n,r;t.l&&null!=(n=a[e-1])&&n.text&&(a[e-1].text=a[e-1].text.replace(/\s+$/,"")),t.r&&null!=(r=a[e+1])&&r.text&&(a[e+1].text=a[e+1].text.replace(/^\s+/,""))}),i=0;var p=x([]),g=function(t,e){t=t||{};var n=Object.create(t);return n.$=e?e.root:t,e?"item"in e&&(n["@"]=e.item):n["@"]=t,d(p,n)};return g.names=Array.from(f),g.functions=Array.from(s),g}t.render=function(t,e,n){return b(t,n)(e)},t.template=b});
|
package/index.d.ts
CHANGED
|
@@ -11,13 +11,21 @@
|
|
|
11
11
|
* reach the root (`$.company`) or the current item (`@.total`) explicitly,
|
|
12
12
|
* past any shadowing. Neither counts as a `name`.
|
|
13
13
|
*
|
|
14
|
+
* An embedder with its own scope model can override the anchors per render by
|
|
15
|
+
* passing `{ root, item }` as the renderer's second argument: `$` becomes
|
|
16
|
+
* `root` and `@` becomes `item` (two distinct objects). Omit `item` to leave
|
|
17
|
+
* `@` unbound, so reading `@.x` throws through xprsn's guard.
|
|
18
|
+
*
|
|
14
19
|
* @param {string} str The template, e.g. `'Hello {{ user.name }}!'`.
|
|
15
20
|
* @param {Record<string, Function>} [funcs] Functions callable inside expressions.
|
|
16
|
-
* @returns {{(values?: Record<string, any
|
|
21
|
+
* @returns {{(values?: Record<string, any>, scope?: { root?: any, item?: any }): string, names: string[], functions: string[]}} Renderer for the compiled template.
|
|
17
22
|
* @throws {SyntaxError} On malformed tags, unclosed blocks, or bad expressions.
|
|
18
23
|
*/
|
|
19
24
|
export function template(str: string, funcs?: Record<string, Function>): {
|
|
20
|
-
(values?: Record<string, any
|
|
25
|
+
(values?: Record<string, any>, scope?: {
|
|
26
|
+
root?: any;
|
|
27
|
+
item?: any;
|
|
28
|
+
}): string;
|
|
21
29
|
names: string[];
|
|
22
30
|
functions: string[];
|
|
23
31
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sjabloon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Tiny, CSP-safe template engine for JavaScript, powered by xprsn expressions. No eval, no new Function.",
|
|
5
5
|
"repository": "robinvdvleuten/sjabloon",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"handlebars"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"xprsn": "^0.
|
|
45
|
+
"xprsn": "^0.4.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"microbundle": "^0.15.1",
|
package/src/index.js
CHANGED
|
@@ -120,9 +120,14 @@ let parse = stops => {
|
|
|
120
120
|
* reach the root (`$.company`) or the current item (`@.total`) explicitly,
|
|
121
121
|
* past any shadowing. Neither counts as a `name`.
|
|
122
122
|
*
|
|
123
|
+
* An embedder with its own scope model can override the anchors per render by
|
|
124
|
+
* passing `{ root, item }` as the renderer's second argument: `$` becomes
|
|
125
|
+
* `root` and `@` becomes `item` (two distinct objects). Omit `item` to leave
|
|
126
|
+
* `@` unbound, so reading `@.x` throws through xprsn's guard.
|
|
127
|
+
*
|
|
123
128
|
* @param {string} str The template, e.g. `'Hello {{ user.name }}!'`.
|
|
124
129
|
* @param {Record<string, Function>} [funcs] Functions callable inside expressions.
|
|
125
|
-
* @returns {{(values?: Record<string, any
|
|
130
|
+
* @returns {{(values?: Record<string, any>, scope?: { root?: any, item?: any }): string, names: string[], functions: string[]}} Renderer for the compiled template.
|
|
126
131
|
* @throws {SyntaxError} On malformed tags, unclosed blocks, or bad expressions.
|
|
127
132
|
*/
|
|
128
133
|
export function template(str, funcs) {
|
|
@@ -151,11 +156,17 @@ export function template(str, funcs) {
|
|
|
151
156
|
i = 0;
|
|
152
157
|
const nodes = parse([]);
|
|
153
158
|
// Wrap the values in a root scope carrying the anchors, without mutating
|
|
154
|
-
// what the caller passed: `$` and `@` both point at the root
|
|
155
|
-
|
|
159
|
+
// what the caller passed: by default `$` and `@` both point at the root.
|
|
160
|
+
// An embedder can override the anchors with a `{ root, item }` second arg:
|
|
161
|
+
// `$` = root, `@` = item (distinct objects). Omitting `item` leaves `@`
|
|
162
|
+
// unbound, so `@.x` throws through xprsn's guard — a group-header band that
|
|
163
|
+
// has no current row wants exactly that.
|
|
164
|
+
const f = (v, o) => {
|
|
156
165
|
v = v || {};
|
|
157
166
|
const r = Object.create(v);
|
|
158
|
-
r['$'] =
|
|
167
|
+
r['$'] = o ? o.root : v;
|
|
168
|
+
if (!o) r['@'] = v;
|
|
169
|
+
else if ('item' in o) r['@'] = o.item;
|
|
159
170
|
return run(nodes, r);
|
|
160
171
|
};
|
|
161
172
|
// Array.from, not a spread: the bundler's transpile turns `[...set]` into
|