@modulor-js/html
Advanced tools
Comparing version 1.1.3 to 1.2.0
@@ -1,1 +0,1 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(e.MHTML={})}(this,function(e){"use strict";function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document.createTextNode(""),t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:document.createTextNode("");return this.childNodes=[],this.firstChild=null,this.lastChild=null,this.startNode=e,this.stopNode=t,e&&t?e.parentNode!==t.parentNode?this:(this.update(),this):this}t.prototype.appendChild=function(e){this.stopNode.parentNode.insertBefore(e,this.stopNode),this.update()},t.prototype.removeChild=function(e){this.stopNode.parentNode.removeChild(e),this.update()},t.prototype.replaceChild=function(e,t){t.parentNode.replaceChild(e,t),this.update()},t.prototype.extractContents=function(){var e=document.createDocumentFragment();return e.appendChild(this.startNode),this.childNodes.reduce(function(e,t){return e.appendChild(t),e},e),e.appendChild(this.stopNode),e},t.prototype.update=function(){this.childNodes=[];for(var e=this.startNode.nextSibling;e&&e!==this.stopNode;e=e.nextSibling)this.childNodes.push(e);this.firstChild=this.childNodes[0],this.lastChild=this.childNodes[this.childNodes.length-1]};var n=1,r=3,o=8,i=document.body.namespaceURI,u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function a(e){for(;e.firstChild;)e.removeChild(e.firstChild)}function c(e,t){return e.nodeType===t.nodeType&&(e.tagName&&t.tagName&&e.tagName.toLowerCase()===t.tagName.toLowerCase())}function d(e){return void 0!==e}function s(e){return!!e&&("object"===(void 0===e?"undefined":u(e))||f(e))&&f(e.then)}function f(e){return"function"==typeof e}function l(e){return(void 0===e?"undefined":u(e))===u(!0)}function h(e){return e.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,"\\$&")}var p=function(){return function(e,t){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return function(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var u,a=e[Symbol.iterator]();!(r=(u=a.next()).done)&&(n.push(u.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}(e,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();var m={},v=new Map,y=new Map,g=new DOMParser,C="{modulor_html_chunk_"+ +new Date+":",N="}",b="modulor_sanitize_node_"+ +new Date+":",x=new RegExp("<([ /])?("+["table","tr","td","style"].join("|")+")([ ][^]>)?","igm"),w=/<([^\s]+)([ ].+)?\/([ ]+)?>/gim,T=new RegExp(O(),"ig"),j=new RegExp(O(!0),"ig"),A=new RegExp("^"+O(!0)+"$");function E(e){return f(e)?"function":e instanceof Array?"array":e instanceof Node?"element":s(e)?"promise":d(e)?"text":"undefined"}function D(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];return e.replace(j,function(e,n,r){var o=t[r];return d(o)?o:""})}function R(e,t,n){var r=t.name,o=t.value;if(s(o))o.then(function(t){return R(e,{name:r,value:t},n)});else if("style"!==r)if(f(r))r(e,o);else{if(!(n&&""===o)&&r in e)try{return void(e[r]=o)}catch(e){}e.setAttribute(r,o)}else!function(e){return e&&"object"===(void 0===e?"undefined":u(e))&&e.constructor===Object}(o)?e.setAttribute(r,o):Object.assign(e.style,o)}function S(e,t){if(d(e)&&""!==e){var n=E(e);return"promise"===n?e.then(function(e){return S(e,t)}):("array"===n?e:(""+e).split(" ")).forEach(function(e){return t(e)})}}function k(e){var t={nodeType:e.nodeType,namespaceURI:e.namespaceURI,textContent:e.textContent,attributes:[],childNodes:[]};e.tagName&&(t.tagName=e.tagName.toLowerCase().replace(b,"").toUpperCase());for(var n=e.attributes||[],i=function(r){var o=n[r],i=o.name,u=o.value,a=i.match(T),c=u.match(T),d=i.match(A),s=u.match(A);if("class"===i){var f=u.split(" ").reduce(function(e,t){return e[t.match(T)?0:1].push(t),e},[[],[]]),h=p(f,2),m=h[0],v=h[1];return t.attributes.push({name:i,value:v.join(" ")}),m.length&&t.attributes.push(function(e,t){return function(t,n){m.forEach(function(r){var o=r.match(A),i=o?t[o[2]]:D(r,t),u=o?n[o[2]]:D(r,n);u!==i&&(u&&S(u,function(t){return e.classList.remove(t)}),i&&S(i,function(t){return e.classList.add(t)}))})}}),"continue"}a||c?t.attributes.push(function(t){return function(n,r){var o=d?n[d[2]]:D(i,n),a=d?r[d[2]]:D(i,r),c=s?n[s[2]]:D(u,n),f=s?r[s[2]]:D(u,r);if((o!==a||c!==f)&&(o!==a&&t.removeAttribute(a),o))return R(t,{name:o,value:c},l(e[o])),function(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}({},o,c)}}):t.attributes.push({name:i,value:u,isBoolean:l(e[i])})},u=0;u<n.length;u++)i(u);for(var a=e.childNodes||[],c=function(e){var n=a[e];return n.nodeType===r?(n.textContent.split(T).filter(function(e){return!!e}).forEach(function(e){var n=e.match(A);if(n){var o=n[2];t.childNodes.push(function(e){return function(t){return $(t[o],e)}})}else t.childNodes.push({nodeType:r,textContent:e})}),"continue"):n.nodeType===o?(n.textContent.match(T)?t.childNodes.push(function(e){var t=document.createComment(""),r=n.textContent;return e.appendChild(t),function(e){t.textContent=D(r,e)}}):t.childNodes.push({nodeType:o,textContent:n.textContent}),"continue"):void t.childNodes.push(k(n))},d=0;d<a.length;d++)c(d);return t}function F(e){var t,n=(t=e,Array.isArray(t)?t:Array.from(t)),r=n[0];return n.slice(1).reduce(function(e,t,n){var r=""+C+n+N;return e.concat(r).concat(t)},r)}function O(e){var t=(e?"(":"")+"\\d+"+(e?")":"");return"("+h(C)+t+h(N)+")"}function $(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:document.createDocumentFragment(),n=v.get(t)||{},r=E(e),o=n.lastChunk,i=n.lastRenderedChunkType,u=n.update;if(o===e)return t;if("promise"===r)return _.promise(t,e),t;if(i!==r)a(t);else if(u){var c=u(e);return f(c)&&(n.update=c),n.lastChunk=e,t}return v.set(t,{update:_[r](t,e),lastRenderedChunkType:r,lastChunk:e}),t}var _={array:function e(t,n){var r=L({childNodes:[].concat(n).map(function(e,t){return function(e){return function(n){return $(n[t],e)}}})},t,{useDocFragment:!0}),o=p(r,2),i=o[0],u=o[1];return i(n),u(),function(r){if(r.length!==n.length)return e(t,r);i(r)}},undefined:a,text:function(e,t){var n=document.createTextNode(t);return e.appendChild(n),function(e){return n.textContent=e}},element:function(e,t){return e.appendChild(t),function(t){e.childNodes.length>1&&e.childNodes.slice(1).forEach(function(t){return e.removeChild(t)}),e.replaceChild(t,e.childNodes[0])}},promise:function(e,t){t.then(function(t){e.update(),$(t,e)})},function:function(e,t){var n=t(e);return function(t){n=t(e,n)}}};function I(e,t){for(var n=t.attributes,r=e.attributes,o=0;o<r.length;o++)e.removeAttribute(r[o].name);for(var i=[],u={},a=0;a<n.length;a++){var c=n[a];if(f(c))i.push(c(e));else{var d=c.name,s=c.value,l=c.isBoolean;R(e,{name:d,value:s},l),u[d]=s}}if("props"in e){if(i.length)return[function(t,n){var r=i.reduce(function(e,r){return Object.assign(e,r(t,n))},{});Object.keys(r).length&&(e.props=Object.assign(u,r))}];e.props=u}return i}function L(e,u){for(var a,d,s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},l=[],h=s.useDocFragment?document.createDocumentFragment():u,p=e.childNodes,m=function(e){return e?function(t){return u.replaceChild(t,e)}:function(e){return h.appendChild(e)}},v=0,g=0;;v++){var C=p[v],N=u.childNodes[v+g];if(!C&&!N)break;if(C)if(N&&c(C,N))d=N,(r===(a=C).nodeType!==a.nodeType||a.textContent!==d.textContent)&&c(C,N)&&(l=l.concat(L(C,N)[0]).concat(I(N,C)));else{var b=m(N);if(f(C)){var x=y.get(N);if(!x){var w=(x=new t).startNode;b(x.extractContents()),y.set(w,x)}var T=C(x);T&&l.push(T),x.update(),g+=x.childNodes.length+1;continue}switch(C.nodeType){case r:b(document.createTextNode(C.textContent));break;case o:b(document.createComment(C.textContent));break;case n:var j=C.namespaceURI,A=C.tagName.toLowerCase(),E=j===i?document.createElement(A):document.createElementNS(j,A);l=l.concat(L(C,E)[0]).concat(I(E,C)),b(E)}}else u.removeChild(N),v--}var D=[];return[function e(t){return l.forEach(function(e){return e(t,D)}),D=t,e},function(){return s.useDocFragment?u.appendChild(h):void 0}]}e.NodesRange=t,e.emptyNode=a,e.render=$,e.morph=L,e.html=function(){for(var e=arguments.length,t=Array(e>1?e-1:0),n=1;n<e;n++)t[n-1]=arguments[n];var r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];if(!r.length)return this;var o,i=function(e){for(var t=5381,n=e.length;n;)t=33*t^e.charCodeAt(--n);return t>>>0}(r.join(C+N)),u=m[i],a=void 0;if(d(u))a=u;else{var c=F(r);o=function(e){return e.replace(x,"<$1"+b+"$2")}(c.replace(w,"<$1$2></$1>")),a=k(g.parseFromString(o,"text/html").body),m[i]=a}return function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document.createDocumentFragment(),n=arguments[1];if(n&&n.templateId===i)return n(t);var r=L(a,e,{useDocFragment:!0}),o=p(r,2),u=o[0],c=o[1];return u(t),c(),u.templateId=i,u}},e.stopNode=function(){},Object.defineProperty(e,"__esModule",{value:!0})}); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(e.MHTML={})}(this,function(e){"use strict";function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document.createTextNode(""),t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:document.createTextNode("");return this.childNodes=[],this.firstChild=null,this.lastChild=null,this.startNode=e,this.stopNode=t,e&&t?e.parentNode!==t.parentNode?this:(this.update(),this):this}t.prototype.appendChild=function(e){this.stopNode.parentNode.insertBefore(e,this.stopNode),this.update()},t.prototype.removeChild=function(e){this.stopNode.parentNode.removeChild(e),this.update()},t.prototype.replaceChild=function(e,t){t.parentNode.replaceChild(e,t),this.update()},t.prototype.extractContents=function(){var e=document.createDocumentFragment();return e.appendChild(this.startNode),this.childNodes.reduce(function(e,t){return e.appendChild(t),e},e),e.appendChild(this.stopNode),e},t.prototype.update=function(){this.childNodes=[];for(var e=this.startNode.nextSibling;e&&e!==this.stopNode;e=e.nextSibling)this.childNodes.push(e);this.firstChild=this.childNodes[0],this.lastChild=this.childNodes[this.childNodes.length-1]};var n,r=1,o=3,i=8,u=document.body.namespaceURI,a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function c(e){for(;e.firstChild;)e.removeChild(e.firstChild)}function s(e,t){return e.nodeType===t.nodeType&&(e.tagName&&t.tagName&&e.tagName.toLowerCase()===t.tagName.toLowerCase())}function d(e){return void 0!==e}function l(e){return!!e&&("object"===(void 0===e?"undefined":a(e))||f(e))&&f(e.then)}function f(e){return"function"==typeof e}function p(e){return(void 0===e?"undefined":a(e))===a(!0)}function h(e){return e.replace(/[-[\]{}()*+!<=:?.\/\\^$|#\s,]/g,"\\$&")}function m(){}var v=function(){return function(e,t){if(Array.isArray(e))return e;if(Symbol.iterator in Object(e))return function(e,t){var n=[],r=!0,o=!1,i=void 0;try{for(var u,a=e[Symbol.iterator]();!(r=(u=a.next()).done)&&(n.push(u.value),!t||n.length!==t);r=!0);}catch(e){o=!0,i=e}finally{try{!r&&a.return&&a.return()}finally{if(o)throw i}}return n}(e,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}();function g(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var y,N={},C=new Map,b=new Map,x=new DOMParser,w="{modulor_html_chunk_"+ +new Date+":",j="}",T="modulor_sanitize_node_"+ +new Date+":",k=new RegExp("<([ /])?("+["table","tr","td","style"].join("|")+")([ ][^]>)?","igm"),A="modulor-dynamic-tag-"+ +new Date,D="modulor-chunk-"+ +new Date,R=(y="("+h(w)+"([^ >]+)"+h(j)+")",new RegExp("(<([ /])?)(([^ >]+)?("+y+")([a-zA-Z0-9-_]+)?)(([ ][^])?>)?","igm")),E=/<([^\s]+)([ ].+)?\/([ ]+)?>/gim,O=new RegExp(G(),"ig"),F=new RegExp(G(!0),"ig"),S=new RegExp("^"+G(!0)+"$"),L="preventChildRendering",_="function",$="array",I="element",M="promise",U="undefined",B="text";function P(e){return f(e)?_:e instanceof Array?$:e instanceof Node?I:l(e)?M:d(e)?B:U}function z(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];return e.replace(F,function(e,n,r){var o=t[r];return d(o)?o:""})}function H(e,t,n){var r=t.name,o=t.value;if(l(o))o.then(function(t){return H(e,{name:r,value:t},n)});else if("style"!==r)if(f(r))r(e,o);else{if(!(n&&""===o)&&r in e)try{return void(e[r]=o)}catch(e){}e.setAttribute(r,o)}else!function(e){return e&&"object"===(void 0===e?"undefined":a(e))&&e.constructor===Object}(o)?e.setAttribute(r,o):Object.assign(e.style,o)}function V(e,t){if(d(e)&&""!==e){var n=P(e);return n===M?e.then(function(e){return V(e,t)}):(n===$?e:(""+e).split(" ")).forEach(function(e){return t(e)})}}function Z(e){for(var t={nodeType:e.nodeType,namespaceURI:e.namespaceURI,textContent:e.textContent,attributes:[],childNodes:[]},n=t.attributes,r=t.childNodes,u=e.attributes||[],a=function(t){var r=u[t],o=r.name,i=r.value;if(o===D)return"continue";var a=o.match(O),c=i.match(O),s=o.match(S),d=i.match(S);if("class"===o){var l=i.split(" ").reduce(function(e,t){return e[t.match(O)?0:1].push(t),e},[[],[]]),f=v(l,2),h=f[0],m=f[1];return n.push({name:o,value:m.join(" ")}),h.length&&n.push(function(e){return function(t,n){var r=h.reduce(function(r,o){var i=o.match(S),u=i?t[i[2]]:z(o,t),a=i?n[i[2]]:z(o,n);return a!==u?(a&&V(a,function(t){return e.classList.remove(t)}),u&&V(u,function(t){return e.classList.add(t)}),!0):r},!1);return[{key:"className",value:e.className},r]}}),"continue"}a||c?n.push(function(t){return function(n,r){var u=s?n[s[2]]:z(o,n),a=s?r[s[2]]:z(o,r),c=d?n[d[2]]:z(i,n),l=d?r[d[2]]:z(i,r),f={key:u,value:c};return u===a&&c===l?[f,!1]:(u!==a&&t.removeAttribute(a),u?(f[u]=c,H(t,{name:u,value:c},p(e[u])),[f,!0]):[f,!0])}}):n.push({name:o,value:i,isBoolean:p(e[o])})},c=0;c<u.length;c++)a(c);for(var s=e.childNodes||[],d=function(e){var t=s[e];return t.nodeType===o?(t.textContent.split(O).filter(function(e){return!!e}).forEach(function(e){var t=e.match(S);if(t){var n=t[2];r.push(function(e){return function(t){return J(t[n],e)}})}else r.push({nodeType:o,textContent:e})}),"continue"):t.nodeType===i?(t.textContent.match(O)?r.push(function(e){var n=document.createComment(""),r=t.textContent;return e.appendChild(n),function(e){n.textContent=z(r,e)}}):r.push({nodeType:i,textContent:t.textContent}),"continue"):void r.push(Z(t))},l=0;l<s.length;l++)d(l);var f=e.tagName;if(f===A.toUpperCase()){var h=e.attributes[D].value,y=h.match(S);return function(e){var n=void 0;return function(r,o){var i=y?r[y[2]]:z(h,r),u=y?o[y[2]]:z(h,o);if(n&&i===u)return n(r);var a,c,s,d=P(i),l={childNodes:[Object.assign({},t,{tagName:i})]};if(d===_){var f=W(l,{appendChild:m,replaceChild:m,childNodes:[(a={props:function(t){return J(i(t),e)}},c={},s=Object.assign(g({props:m,tagName:null,setAttribute:function(e,t){"class"===e&&(c=t.split(" ").reduce(function(e,t){return Object.assign(e,g({},t,!0))},{}))},removeAttribute:m,classList:{add:function(e){c[e]=!0,s.className=Object.keys(c).join(" ").trim()},remove:function(e){delete c[e],s.className=Object.keys(c).join(" ").trim()}},className:"",attributes:[],childNodes:[],appendChild:m,isVirtual:!0},L,!0),a),s)]}),p=v(f,1);return(n=p[0])(r)}var N=W(l,e,{useDocFragment:!0}),C=v(N,2),b=C[0],x=C[1];b(r),x(),n=b}}}return f&&(t.tagName=e.tagName.toLowerCase().replace(T,"").toUpperCase()),t}function q(e){var t,n=(t=e,Array.isArray(t)?t:Array.from(t)),r=n[0];return n.slice(1).reduce(function(e,t,n){var r=""+w+n+j;return e.concat(r).concat(t)},r)}function G(e){var t=(e?"(":"")+"\\d+"+(e?")":"");return"("+h(w)+t+h(j)+")"}function J(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:document.createDocumentFragment(),n=C.get(t)||{},r=P(e),o=n.lastChunk,i=n.lastRenderedChunkType,u=n.update;if(o===e)return t;if(r===M)return K[M](t,e),t;if(i!==r)c(t);else if(u){var a=u(e);return f(a)&&(n.update=a),n.lastChunk=e,t}return C.set(t,{update:K[r](t,e),lastRenderedChunkType:r,lastChunk:e}),t}var K=(g(n={},$,function e(t,n){var r=W({childNodes:[].concat(n).map(function(e,t){return function(e){return function(n){return J(n[t],e)}}})},t,{useDocFragment:!0}),o=v(r,2),i=o[0],u=o[1];return i(n),u(),function(r){if(r.length!==n.length)return e(t,r);i(r)}}),g(n,U,c),g(n,B,function(e,t){var n=document.createTextNode(t);return e.appendChild(n),function(e){return n.textContent=e}}),g(n,I,function(e,t){return e.appendChild(t),function(t){e.childNodes.length>1&&e.childNodes.slice(1).forEach(function(t){return e.removeChild(t)}),e.replaceChild(t,e.childNodes[0])}}),g(n,M,function(e,t){t.then(function(t){e.update(),J(t,e)})}),g(n,_,function(e,t){var n=t(e);return function(t){n=t(e,n)}}),n);function Q(e,t,n){for(var r=t.attributes,o=e.attributes,i=0;i<o.length;i++)e.removeAttribute(o[i].name);for(var u=[],a={},c=0;c<r.length;c++){var s=r[c];if(f(s))u.push(s(e));else{var d=s.name,l=s.value,p=s.isBoolean;H(e,{name:d,value:l},p),a["class"===d?"className":d]=l}}if(e[L]&&u.push(function(e,n){return[{key:"children",value:function(n,r){if(r)return r(e),r;var o=W(t,n,{useDocFragment:!0}),i=v(o,2),u=i[0],a=i[1];return u(e),a(),u}},!0]}),"props"in e){var h=f(e.props)?e.props:function(t,n){return n&&(e.props=t)};if(u.length)return[function(e,t){var n=u.reduce(function(n,r){var o=v(n,2),i=o[0],u=o[1],a=r(e,t),c=v(a,2),s=c[0],d=s.key,l=s.value,f=c[1],p="string"==typeof d||"number"==typeof d?g({},d,l):{};return[Object.assign({},i,p),u||f]},[a,!1]),r=v(n,2),o=r[0],i=r[1];h(o,i)}];h(a,!0)}return u}function W(e,n){var a=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(n[L])return[m,[]];for(var c,d,l=[],p=a.useDocFragment?document.createDocumentFragment():n,h=e.childNodes,v=function(e){return e?function(t){return n.replaceChild(t,e)}:function(e){return p.appendChild(e)}},g=0,y=0;;g++){var N=h[g],C=n.childNodes[g+y];if(!N&&!C)break;if(N)if(C&&s(N,C))d=C,(o===(c=N).nodeType!==c.nodeType||c.textContent!==d.textContent)&&s(N,C)&&(l=l.concat(W(N,C)[0]).concat(Q(C,N)));else{var x=v(C);if(f(N)){var w=b.get(C);if(!w){var j=(w=new t).startNode;x(w.extractContents()),b.set(j,w)}var T=N(w);T&&l.push(T),w.update(),y+=w.childNodes.length+1;continue}switch(N.nodeType){case o:x(document.createTextNode(N.textContent));break;case i:x(document.createComment(N.textContent));break;case r:var k=N.namespaceURI,A=N.tagName,D=void 0;D=C&&C[L]?C:k===u?document.createElement(A.toLowerCase()):document.createElementNS(k,A.toLowerCase()),l=l.concat(W(N,D)[0]).concat(Q(D,N)),x(D)}}else n.removeChild(C),g--}var R=[];return[function e(t){return l.forEach(function(e){return e(t,R)}),R=t,e},function(){return a.useDocFragment?n.appendChild(p):void 0}]}e.NodesRange=t,e.emptyNode=c,e.render=J,e.morph=W,e.html=function(){for(var e=arguments.length,t=Array(e>1?e-1:0),n=1;n<e;n++)t[n-1]=arguments[n];var r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];if(!r.length)return this;var o,i=function(e){for(var t=5381,n=e.length;n;)t=33*t^e.charCodeAt(--n);return t>>>0}(r.join(w+j)),u=N[i],a=void 0;if(d(u))a=u;else{var c=q(r);o=function(e){return e.replace(k,"<$1"+T+"$2")}(function(e){return e.replace(E,"<$1$2></$1>")}(c.replace(R,function(){for(var e=arguments.length,t=Array(e),n=0;n<e;n++)t[n]=arguments[n];t[0];var r=t[1],o=t[2],i=t[3],u=(t[4],t[5],t[6],t[7],t[8],t[9]);return o?"</"+A+">":""+r+A+" "+D+'="'+i+'"'+(u||"")}))),a=Z(x.parseFromString(o,"text/html").body),N[i]=a}return function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:document.createDocumentFragment(),n=arguments[1];if(n&&n.templateId===i)return n(t);var r=W(a,e,{useDocFragment:!0}),o=v(r,2),u=o[0],c=o[1];return u(t),c(),u.templateId=i,u}},e.stopNode=function(){},Object.defineProperty(e,"__esModule",{value:!0})}); |
{ | ||
"name": "@modulor-js/html", | ||
"version": "1.1.3", | ||
"version": "1.2.0", | ||
"description": "Template engine based on tagged template literals", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -54,3 +54,3 @@ # modulor-html [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Missing%20template%20engine%20for%20Web%20Components&url=https://github.com/modulor-js/modulor-html&hashtags=webcomponents,template-engine,js) | ||
- Small size (**3.3kb** minigzipped) | ||
- Small size (**4kb** minigzipped) | ||
@@ -57,0 +57,0 @@ - High performance |
@@ -55,1 +55,3 @@ import { TEXT_NODE } from './constants'; | ||
}; | ||
export function noop(){} |
253
src/html.js
import { NodesRange } from './range'; | ||
import { | ||
emptyNode, same, hash, regExpEscape, | ||
emptyNode, same, hash, regExpEscape, noop, | ||
isSameTextNode, isDefined, isPromise, isFunction, isObject, isBoolean | ||
@@ -25,2 +25,6 @@ } from './helpers'; | ||
let specialTagName = `modulor-dynamic-tag-${+new Date()}`; | ||
let specialAttributeName = `modulor-chunk-${+new Date()}`; | ||
let dynamicTagsRegex = getDynamicTagsRegex(); | ||
const selfClosingRegex = /<([^\s]+)([ ].+)?\/([ ]+)?>/igm; | ||
@@ -32,16 +36,24 @@ | ||
let preventChildRenderingProp = 'preventChildRendering'; | ||
const CHUNK_TYPE_FUNCTION = 'function'; | ||
const CHUNK_TYPE_ARRAY = 'array'; | ||
const CHUNK_TYPE_ELEMENT = 'element'; | ||
const CHUNK_TYPE_PROMISE = 'promise'; | ||
const CHUNK_TYPE_UNDEFINED = 'undefined'; | ||
const CHUNK_TYPE_TEXT = 'text'; | ||
function getChunkType(chunk){ | ||
if(isFunction(chunk)){ | ||
return 'function'; | ||
return CHUNK_TYPE_FUNCTION; | ||
} else if(chunk instanceof Array){ | ||
return 'array'; | ||
return CHUNK_TYPE_ARRAY; | ||
} else if(chunk instanceof Node){ | ||
return 'element'; | ||
return CHUNK_TYPE_ELEMENT; | ||
} else if(isPromise(chunk)){ | ||
return 'promise'; | ||
return CHUNK_TYPE_PROMISE; | ||
} else if(!isDefined(chunk)){ | ||
return 'undefined'; | ||
return CHUNK_TYPE_UNDEFINED; | ||
} | ||
return 'text'; | ||
return CHUNK_TYPE_TEXT; | ||
} | ||
@@ -90,9 +102,43 @@ | ||
const chunkType = getChunkType(value); | ||
if(chunkType === 'promise'){ | ||
if(chunkType === CHUNK_TYPE_PROMISE){ | ||
return value.then((newValue) => applyClassFn(newValue, fn)); | ||
} | ||
let classesArray = chunkType === 'array' ? value : ('' + value).split(' '); | ||
let classesArray = chunkType === CHUNK_TYPE_ARRAY ? value : ('' + value).split(' '); | ||
return classesArray.forEach((className) => fn(className)); | ||
} | ||
function createVirtualElement(extend){ | ||
let classes = {}; | ||
const element = Object.assign({ | ||
props: noop, | ||
tagName: null, | ||
setAttribute: (name, value) => { | ||
if(name === 'class'){ | ||
classes = value.split(' ').reduce((acc, className) => Object.assign(acc, { | ||
[className]: true | ||
}), {}); | ||
} | ||
}, | ||
removeAttribute: noop, | ||
classList: { | ||
add: (value) => { | ||
classes[value] = true; | ||
element.className = Object.keys(classes).join(' ').trim(); | ||
}, | ||
remove: (value) => { | ||
delete classes[value]; | ||
element.className = Object.keys(classes).join(' ').trim(); | ||
} | ||
}, | ||
className: '', | ||
attributes: [], | ||
childNodes: [], | ||
appendChild: noop, | ||
isVirtual: true, | ||
[preventChildRenderingProp]: true | ||
}, extend); | ||
return element; | ||
}; | ||
function processNode($container){ | ||
@@ -107,5 +153,3 @@ const nodeCopy = { | ||
if($container.tagName){ | ||
nodeCopy.tagName = $container.tagName.toLowerCase().replace(sanitizeNodePrefix, '').toUpperCase(); | ||
} | ||
const { attributes, childNodes } = nodeCopy; | ||
@@ -115,2 +159,5 @@ const childAttributes = $container.attributes || []; | ||
const { name, value } = childAttributes[j]; | ||
if(name === specialAttributeName){ | ||
continue; | ||
} | ||
@@ -128,6 +175,6 @@ const nameIsDynamic = name.match(findChunksRegex); | ||
}, [[], []]); | ||
nodeCopy.attributes.push({ name, value: initial.join(' ') }); | ||
dynamic.length && nodeCopy.attributes.push((target, cbk) => { | ||
attributes.push({ name, value: initial.join(' ') }); | ||
dynamic.length && attributes.push((target) => { | ||
return (values, prevValues) => { | ||
dynamic.forEach((className) => { | ||
const updated = dynamic.reduce((acc, className) => { | ||
const matchClass = className.match(matchChunkRegex); | ||
@@ -139,4 +186,7 @@ const newValue = matchClass ? values[matchClass[2]] : replaceTokens(className, values); | ||
newValue && applyClassFn(newValue, (className) => target.classList.add(className)); | ||
return true; | ||
} | ||
}); | ||
return acc; | ||
}, false); | ||
return [{ key: 'className', value: target.className }, updated]; | ||
}; | ||
@@ -148,4 +198,4 @@ }); | ||
if(nameIsDynamic || valueIsDynamic){ | ||
nodeCopy.attributes.push((target) => { | ||
return (values, prevValues) => { | ||
attributes.push((target) => { | ||
return function update(values, prevValues){ | ||
const preparedName = matchName ? values[matchName[2]] : replaceTokens(name, values); | ||
@@ -157,4 +207,5 @@ const preparedPrevName = matchName ? prevValues[matchName[2]] : replaceTokens(name, prevValues); | ||
const prop = { key: preparedName, value: preparedValue }; | ||
if(preparedName === preparedPrevName && preparedValue === preparedPrevValue){ | ||
return; | ||
return [prop, false]; | ||
} | ||
@@ -167,8 +218,8 @@ | ||
if(!preparedName){ | ||
return; | ||
return [prop, true]; | ||
} | ||
prop[preparedName] = preparedValue; | ||
applyAttribute(target, { name: preparedName, value: preparedValue }, isBoolean($container[preparedName])); | ||
return { [preparedName]: preparedValue }; | ||
return [prop, true]; | ||
}; | ||
@@ -178,9 +229,9 @@ | ||
} else { | ||
nodeCopy.attributes.push({ name, value, isBoolean: isBoolean($container[name]) }); | ||
attributes.push({ name, value, isBoolean: isBoolean($container[name]) }); | ||
} | ||
} | ||
const childNodes = $container.childNodes || []; | ||
for(let i = 0; i < childNodes.length; i++){ | ||
const $childNode = childNodes[i]; | ||
const containerChildNodes = $container.childNodes || []; | ||
for(let i = 0; i < containerChildNodes.length; i++){ | ||
const $childNode = containerChildNodes[i]; | ||
if($childNode.nodeType === TEXT_NODE){ | ||
@@ -192,7 +243,7 @@ const chunks = $childNode.textContent.split(findChunksRegex); | ||
const matchIndex = match[2]; | ||
nodeCopy.childNodes.push((range) => { | ||
childNodes.push((range) => { | ||
return (values) => render(values[matchIndex], range); | ||
}); | ||
} else { | ||
nodeCopy.childNodes.push({ | ||
childNodes.push({ | ||
nodeType: TEXT_NODE, | ||
@@ -207,3 +258,3 @@ textContent: chunk, | ||
if($childNode.textContent.match(findChunksRegex)){ | ||
nodeCopy.childNodes.push((range) => { | ||
childNodes.push((range) => { | ||
const $element = document.createComment(''); | ||
@@ -217,3 +268,3 @@ const content = $childNode.textContent; | ||
} else { | ||
nodeCopy.childNodes.push({ | ||
childNodes.push({ | ||
nodeType: COMMENT_NODE, | ||
@@ -225,5 +276,50 @@ textContent: $childNode.textContent, | ||
} | ||
nodeCopy.childNodes.push(processNode($childNode)); | ||
childNodes.push(processNode($childNode)); | ||
} | ||
const tagName = $container.tagName; | ||
if(tagName === specialTagName.toUpperCase()){ | ||
const chunkName = $container.attributes[specialAttributeName].value; | ||
const matchChunk = chunkName.match(matchChunkRegex); | ||
return (range) => { | ||
let update; | ||
return (values, prevValues) => { | ||
const newValue = matchChunk ? values[matchChunk[2]] : replaceTokens(chunkName, values); | ||
const oldValue = matchChunk ? prevValues[matchChunk[2]] : replaceTokens(chunkName, prevValues); | ||
if(update && newValue === oldValue){ | ||
return update(values); | ||
} | ||
const chunkType = getChunkType(newValue); | ||
const container = { | ||
childNodes: [Object.assign({}, nodeCopy, { | ||
tagName: newValue | ||
})] | ||
}; | ||
if(chunkType === CHUNK_TYPE_FUNCTION){ | ||
const target = { | ||
appendChild: noop, | ||
replaceChild: noop, | ||
childNodes: [createVirtualElement({ | ||
props: (value) => render(newValue(value), range) | ||
})] | ||
}; | ||
[update] = morph(container, target); | ||
return update(values); | ||
} | ||
const [newUpdate, initialRender] = morph(container, range, { useDocFragment: true }); | ||
newUpdate(values); | ||
initialRender(); | ||
update = newUpdate; | ||
}; | ||
}; | ||
} else if(tagName){ | ||
nodeCopy.tagName = $container.tagName.toLowerCase().replace(sanitizeNodePrefix, '').toUpperCase(); | ||
} | ||
return nodeCopy; | ||
@@ -252,2 +348,16 @@ } | ||
function getDynamicTagsRegex(groupMatches = false){ | ||
const tokenRegEx = `(${regExpEscape(PREFIX)}([^ >]+)${regExpEscape(POSTFIX)})`; | ||
return new RegExp(`(<([ /])?)(([^ >]+)?(${tokenRegEx})([a-zA-Z0-9-_]+)?)(([ ][^])?>)?`, 'igm'); | ||
} | ||
function replaceDynamicTags(str){ | ||
return str.replace(dynamicTagsRegex, (...args) => { | ||
const [_, opening, isClosing, chunkName, __, ___, suffix, ____, _____, closing] = args; | ||
return isClosing | ||
? `</${specialTagName}>` | ||
: `${opening}${specialTagName} ${specialAttributeName}="${chunkName}"${closing || ''}` | ||
}); | ||
}; | ||
function openSelfClosingTags(str){ | ||
@@ -264,4 +374,4 @@ return str.replace(selfClosingRegex, '<$1$2></$1>'); | ||
} | ||
if(chunkType === 'promise'){ | ||
chunkProcessingFunctions['promise'](range, value); | ||
if(chunkType === CHUNK_TYPE_PROMISE){ | ||
chunkProcessingFunctions[CHUNK_TYPE_PROMISE](range, value); | ||
return range; | ||
@@ -286,3 +396,3 @@ } | ||
const chunkProcessingFunctions = { | ||
'array': function processArrayChunk(range, value){ | ||
[CHUNK_TYPE_ARRAY]: function processArrayChunk(range, value){ | ||
const preprocessedChunksContainer = { | ||
@@ -303,4 +413,4 @@ childNodes: [].concat(value).map((chunk, index) => { | ||
}, | ||
'undefined': emptyNode, | ||
'text': (range, value) => { | ||
[CHUNK_TYPE_UNDEFINED]: emptyNode, | ||
[CHUNK_TYPE_TEXT]: (range, value) => { | ||
const textNode = document.createTextNode(value); | ||
@@ -310,3 +420,3 @@ range.appendChild(textNode); | ||
}, | ||
'element': (range, value) => { | ||
[CHUNK_TYPE_ELEMENT]: (range, value) => { | ||
range.appendChild(value); | ||
@@ -320,3 +430,3 @@ return (value) => { | ||
}, | ||
'promise': (range, value) => { | ||
[CHUNK_TYPE_PROMISE]: (range, value) => { | ||
value.then((response) => { | ||
@@ -327,3 +437,3 @@ range.update(); | ||
}, | ||
'function': (range, value) => { | ||
[CHUNK_TYPE_FUNCTION]: (range, value) => { | ||
let result = value(range); | ||
@@ -336,3 +446,3 @@ return (value) => { | ||
function copyAttributes(target, source){ | ||
function copyAttributes(target, source, interceptChildrenRendering){ | ||
const sourceAttributes = source.attributes; | ||
@@ -360,15 +470,36 @@ const targetAttributes = target.attributes; | ||
applyAttribute(target, { name, value }, isBoolean); | ||
props[name] = value; | ||
props[name === 'class' ? 'className' : name] = value; | ||
} | ||
if(target[preventChildRenderingProp]){ | ||
updates.push((values, prevValues) => { | ||
const children = (range, update) => { | ||
if(update){ | ||
update(values); | ||
return update; | ||
} | ||
const [newUpdate, initialRender] = morph(source, range, { useDocFragment: true }); | ||
newUpdate(values); | ||
initialRender(); | ||
return newUpdate; | ||
}; | ||
return [{ key: 'children', value: children }, true]; | ||
}); | ||
} | ||
if('props' in target){ | ||
const setProps = isFunction(target.props) | ||
? target.props | ||
: (props, updated) => updated && (target.props = props); | ||
if(updates.length){ | ||
return [(values, prevValues) => { | ||
const dynamicProps = updates.reduce((acc, u) => Object.assign(acc, u(values, prevValues)), {}); | ||
if(Object.keys(dynamicProps).length){ | ||
target.props = Object.assign(props, dynamicProps); | ||
} | ||
const [newProps, updated] = updates.reduce(([props, accUpdated], u) => { | ||
const [{ key, value }, updated] = u(values, prevValues); | ||
const prop = (typeof key === 'string' || typeof key === 'number') ? { [key]: value } : {}; | ||
return [Object.assign({}, props, prop), accUpdated || updated]; | ||
}, [props, false]); | ||
setProps(newProps, updated); | ||
}]; | ||
} | ||
target.props = props; | ||
setProps(props, true); | ||
} | ||
@@ -381,2 +512,6 @@ return updates; | ||
if($target[preventChildRenderingProp]){ | ||
return [noop, []]; | ||
} | ||
let updates = []; | ||
@@ -447,9 +582,16 @@ | ||
const namespaceURI = $sourceElement.namespaceURI; | ||
const tagName = $sourceElement.tagName.toLowerCase(); | ||
const newChild = namespaceURI === DEFAULT_NAMESPACE_URI | ||
? document.createElement(tagName) | ||
: document.createElementNS(namespaceURI, tagName); | ||
const tagName = $sourceElement.tagName; | ||
updates = updates | ||
.concat(morph($sourceElement, newChild)[0]) | ||
let newChild; | ||
let morphUpdates = []; | ||
if($targetElement && $targetElement[preventChildRenderingProp]){ | ||
newChild = $targetElement; | ||
} else { | ||
newChild = namespaceURI === DEFAULT_NAMESPACE_URI | ||
? document.createElement(tagName.toLowerCase()) | ||
: document.createElementNS(namespaceURI, tagName.toLowerCase()); | ||
} | ||
updates = updates.concat(morph($sourceElement, newChild)[0]) | ||
.concat(copyAttributes(newChild, $sourceElement)); | ||
@@ -506,3 +648,3 @@ | ||
const template = prepareLiterals(chunks); | ||
container = generateContainer(sanitize(openSelfClosingTags(template))); | ||
container = generateContainer(sanitize(openSelfClosingTags(replaceDynamicTags(template)))); | ||
templatesCache[templateId] = container; | ||
@@ -540,6 +682,8 @@ } else { | ||
replaceTokens, processNode, generateContainer, | ||
sanitize, copyAttributes, prepareLiterals, openSelfClosingTags, | ||
sanitize, copyAttributes, prepareLiterals, openSelfClosingTags, replaceDynamicTags, | ||
setPrefix: (value) => PREFIX = value, | ||
setPostfix: (value) => POSTFIX = value, | ||
setSanitizeNodePrefix: (value) => sanitizeNodePrefix = value, | ||
setSpecialTagName: (value) => specialTagName = value, | ||
setSpecialAttributeName: (value) => specialAttributeName = value, | ||
updateChunkRegexes: () => { | ||
@@ -549,2 +693,3 @@ findChunksRegex = new RegExp(getTokenRegExp(), 'ig'); | ||
matchChunkRegex = new RegExp(`^${getTokenRegExp(true)}$`); | ||
dynamicTagsRegex = getDynamicTagsRegex(); | ||
} | ||
@@ -551,0 +696,0 @@ }); |
@@ -1,2 +0,2 @@ | ||
import { html, render, r, stopNode, Template, containersMap } from '../src/html'; | ||
import { html, render, r, stopNode, Template, containersMap } from '@modulor-js/html'; | ||
@@ -8,19 +8,33 @@ | ||
//const container = document.createElement('div'); | ||
////const container2 = document.createElement('div'); | ||
const container = document.createElement('div'); | ||
//const container2 = document.createElement('div'); | ||
const comp = (props) => { | ||
//console.log(props); | ||
//return children; | ||
return html` | ||
<div test="myel"> | ||
${props.foo} | ||
${props.foo ? props.children : void 0} | ||
</div> | ||
`; | ||
}; | ||
//const tpl = (scope) => html` | ||
//${scope.a.map((item, index) => html` | ||
//<span class="foo foo-${index} bar-${item}">${item}</span> | ||
//`)} | ||
const tpl = (scope) => html` | ||
<${comp} foo="${scope.a[0]}"> | ||
<div>${scope.a[1]}</div> | ||
</${comp}> | ||
`; | ||
//const tpl2 = (scope) => html` | ||
//<span bla="${scope.a}">${scope.b}</div> | ||
//`; | ||
////const tpl2 = (scope) => html` | ||
////<span bla="${scope.a}">${scope.b}</div> | ||
////`; | ||
//const rr = tpl2({ a: 1}); | ||
////const rr = tpl2({ a: 1}); | ||
//render(tpl({ a: [1,2,3] }), container); | ||
render(tpl({ a: [1,6,3] }), container); | ||
render(tpl({ a: [2,6,3] }), container); | ||
render(tpl({ a: [0,7,3] }), container); | ||
//console.log(container.innerHTML); | ||
render(tpl({ a: [1,8,3] }), container); | ||
//console.log(container.innerHTML); | ||
//render(tpl({ a: [3,4,5] }), container); | ||
@@ -27,0 +41,0 @@ //console.log(container.innerHTML); |
@@ -622,1 +622,624 @@ import 'web-components-polyfill'; | ||
}); | ||
describe('dynamic tags', () => { | ||
describe('string value', () => { | ||
it('renders tag with dynamic name', () => { | ||
const $container = document.createElement('div'); | ||
const tpl = (scope) => html` | ||
<x-${scope[0]}/> | ||
<x-${scope[1]}></x-${scope[1]}> | ||
<${scope[2]}-y-${scope[0]}/> | ||
<${scope[1]}/> | ||
`; | ||
const values1 = ['foo', 'bar', 'baz']; | ||
render(tpl(values1), $container); | ||
expect($container.innerHTML).toBe(`<x-${values1[0]}></x-${values1[0]}> | ||
<x-${values1[1]}></x-${values1[1]}> | ||
<${values1[2]}-y-${values1[0]}></${values1[2]}-y-${values1[0]}> | ||
<${values1[1]}></${values1[1]}> | ||
`); | ||
const values2 = ['quux', 'bla', 'test']; | ||
render(tpl(values2), $container); | ||
expect($container.innerHTML).toBe(`<x-${values2[0]}></x-${values2[0]}> | ||
<x-${values2[1]}></x-${values2[1]}> | ||
<${values2[2]}-y-${values2[0]}></${values2[2]}-y-${values2[0]}> | ||
<${values2[1]}></${values2[1]}> | ||
`); | ||
const values3 = [1, 'bar', 'baz']; | ||
render(tpl(values3), $container); | ||
expect($container.innerHTML).toBe(`<x-${values3[0]}></x-${values3[0]}> | ||
<x-${values3[1]}></x-${values3[1]}> | ||
<${values3[2]}-y-${values3[0]}></${values3[2]}-y-${values3[0]}> | ||
<${values3[1]}></${values3[1]}> | ||
`); | ||
}); | ||
it('handles attributes in dynamic tags', () => { | ||
const $container = document.createElement('div'); | ||
const tpl = ({ tagName, values }) => html` | ||
<span></span> | ||
<x-${tagName} foo="${values[0]}" bar-${values[1]}="${values[2]}" class="quux ${values[3]}"/> | ||
`; | ||
const values1 = { tagName: 'div', values: ['foo', 1, 'bar', 'baz'] }; | ||
render(tpl(values1), $container); | ||
expect($container.innerHTML).toBe(`<span></span> | ||
<x-${values1.tagName} class="quux ${values1.values[3]}" foo="${values1.values[0]}" bar-${values1.values[1]}="${values1.values[2]}"></x-${values1.tagName}> | ||
`); | ||
const values2 = { tagName: 'span', values: ['foo', 1, 'bar', 'baz'] }; | ||
render(tpl(values2), $container); | ||
expect($container.innerHTML).toBe(`<span></span> | ||
<x-${values2.tagName} class="quux ${values2.values[3]}" foo="${values2.values[0]}" bar-${values2.values[1]}="${values2.values[2]}"></x-${values2.tagName}> | ||
`); | ||
const values3 = { tagName: 'span', values: ['quux', 'test', 3, 'baz'] }; | ||
render(tpl(values3), $container); | ||
expect($container.innerHTML).toBe(`<span></span> | ||
<x-${values3.tagName} class="quux ${values3.values[3]}" foo="${values3.values[0]}" bar-${values3.values[1]}="${values3.values[2]}"></x-${values3.tagName}> | ||
`); | ||
}); | ||
it('renders child content correctly', () => { | ||
const $container = document.createElement('div'); | ||
const tpl = ({ tagName, value, list }) => html` | ||
<x-${tagName}> | ||
${list ? list.map((val) => html` | ||
<span>${val}</span> | ||
`) : void 0} | ||
<div>${value}</div> | ||
</x-${tagName}> | ||
`; | ||
const values1 = { tagName: 'foo', value: 'bar' }; | ||
render(tpl(values1), $container); | ||
expect($container.innerHTML).toBe(`<x-${values1.tagName}> | ||
<div>${values1.value}</div> | ||
</x-${values1.tagName}> | ||
`); | ||
const values2 = { tagName: 'foo', value: 'baz', list: ['xxx', 'yyy'] }; | ||
render(tpl(values2), $container); | ||
expect($container.innerHTML).toBe(`<x-${values2.tagName}> | ||
<span>${values2.list[0]}</span> | ||
<span>${values2.list[1]}</span> | ||
<div>${values2.value}</div> | ||
</x-${values2.tagName}> | ||
`); | ||
const values3 = { tagName: 'bar', value: 'baz', list: ['xxx', 'yyy'] }; | ||
render(tpl(values3), $container); | ||
expect($container.innerHTML).toBe(`<x-${values3.tagName}> | ||
<span>${values3.list[0]}</span> | ||
<span>${values3.list[1]}</span> | ||
<div>${values3.value}</div> | ||
</x-${values3.tagName}> | ||
`); | ||
const values4 = { tagName: 'bar', value: 'baz' }; | ||
render(tpl(values4), $container); | ||
expect($container.innerHTML).toBe(`<x-${values4.tagName}> | ||
<div>${values4.value}</div> | ||
</x-${values4.tagName}> | ||
`); | ||
}); | ||
it('renders child content only on demand', () => { | ||
const $container = document.createElement('div'); | ||
const fn1 = jest.fn(); | ||
const fn2 = jest.fn(); | ||
const fn3 = jest.fn(); | ||
const fn4 = jest.fn(); | ||
const tpl = ({ tagName, tagName2 }) => html` | ||
<x-${tagName} ${fn3}> | ||
${fn1} | ||
</x-${tagName}> | ||
<${tagName2} ${fn4}> | ||
${fn2} | ||
</${tagName2}> | ||
`; | ||
const values1 = { tagName: 'foo', tagName2: 'div' }; | ||
render(tpl(values1), $container); | ||
expect($container.innerHTML).toBe(`<x-${values1.tagName}> | ||
</x-${values1.tagName}> | ||
<${values1.tagName2}> | ||
</${values1.tagName2}> | ||
`); | ||
expect(fn1).toHaveBeenCalledTimes(1); | ||
expect(fn2).toHaveBeenCalledTimes(1); | ||
expect(fn3).toHaveBeenCalledTimes(1); | ||
expect(fn4).toHaveBeenCalledTimes(1); | ||
const values2 = { tagName: 'foo', tagName2: 'span' }; | ||
render(tpl(values2), $container); | ||
expect($container.innerHTML).toBe(`<x-${values2.tagName}> | ||
</x-${values2.tagName}> | ||
<${values2.tagName2}> | ||
</${values2.tagName2}> | ||
`); | ||
expect(fn1).toHaveBeenCalledTimes(1); | ||
expect(fn2).toHaveBeenCalledTimes(2); | ||
expect(fn3).toHaveBeenCalledTimes(1); | ||
expect(fn4).toHaveBeenCalledTimes(2); | ||
const values3 = { tagName: 'foo', tagName2: 'div' }; | ||
render(tpl(values3), $container); | ||
expect($container.innerHTML).toBe(`<x-${values3.tagName}> | ||
</x-${values3.tagName}> | ||
<${values3.tagName2}> | ||
</${values3.tagName2}> | ||
`); | ||
expect(fn1).toHaveBeenCalledTimes(1); | ||
expect(fn2).toHaveBeenCalledTimes(3); | ||
expect(fn3).toHaveBeenCalledTimes(1); | ||
expect(fn4).toHaveBeenCalledTimes(3); | ||
}); | ||
}); | ||
describe('function value', () => { | ||
describe('props handling', () => { | ||
const $container = document.createElement('div'); | ||
const Component = jest.fn(); | ||
it('handles simple attributes', () => { | ||
const tpl = (values) => html` | ||
<${Component} foo="xxx" ${values[0]}="yyy" ${values[1]}="${values[2]}"/> | ||
`; | ||
const values = ['zzz', 'aaa', true]; | ||
render(tpl(values), $container); | ||
expect(Component).toHaveBeenCalledWith({ | ||
foo: 'xxx', | ||
[values[0]]: 'yyy', | ||
[values[1]]: values[2], | ||
children: expect.any(Function) | ||
}); | ||
//maybe incorrect behaviour below. maybe should not be called | ||
Component.mockReset(); | ||
render(tpl(values), $container); | ||
expect(Component).toHaveBeenCalledWith({ | ||
foo: 'xxx', | ||
[values[0]]: 'yyy', | ||
[values[1]]: values[2], | ||
children: expect.any(Function) | ||
}); | ||
Component.mockReset(); | ||
render(tpl(values), $container); | ||
expect(Component).toHaveBeenCalledWith({ | ||
foo: 'xxx', | ||
[values[0]]: 'yyy', | ||
[values[1]]: values[2], | ||
children: expect.any(Function) | ||
}); | ||
Component.mockReset(); | ||
const values2 = ['bbb', 'ccc', { a: [] }]; | ||
render(tpl(values2), $container); | ||
expect(Component).toHaveBeenCalledWith({ | ||
foo: 'xxx', | ||
[values2[0]]: 'yyy', | ||
[values2[1]]: values2[2], | ||
children: expect.any(Function) | ||
}); | ||
Component.mockReset(); | ||
const values3 = ['bbb', 'ccc']; | ||
render(tpl(values3), $container); | ||
expect(Component).toHaveBeenCalledWith({ | ||
foo: 'xxx', | ||
[values3[0]]: 'yyy', | ||
children: expect.any(Function) | ||
}); | ||
Component.mockReset(); | ||
const values4 = ['bbb', void 0, [1]]; | ||
render(tpl(values4), $container); | ||
expect(Component).toHaveBeenCalledWith({ | ||
foo: 'xxx', | ||
[values4[0]]: 'yyy', | ||
children: expect.any(Function) | ||
}); | ||
}); | ||
it('passes only attributes where name is string or number', () => { | ||
const tpl = (values) => html` | ||
<${Component} ${values[0]}="yyy" ${values[1]}="${values[2]}"/> | ||
`; | ||
const values = ['zzz', 'aaa', true]; | ||
render(tpl(values), $container); | ||
expect(Component).toHaveBeenCalledWith({ | ||
[values[0]]: 'yyy', | ||
[values[1]]: values[2], | ||
children: expect.any(Function) | ||
}); | ||
Component.mockReset(); | ||
const values2 = [() => {}, true, true]; | ||
render(tpl(values2), $container); | ||
expect(Component).toHaveBeenCalledWith({ | ||
children: expect.any(Function) | ||
}); | ||
Component.mockReset(); | ||
const values3 = [null, {}, true]; | ||
render(tpl(values3), $container); | ||
expect(Component).toHaveBeenCalledWith({ | ||
children: expect.any(Function) | ||
}); | ||
}); | ||
it('handles simple classes', () => { | ||
const tpl = (values) => html` | ||
<${Component} class="foo ${values[0]} ${values[1]}"/> | ||
`; | ||
const values = ['zzz']; | ||
render(tpl(values), $container); | ||
expect(Component).toHaveBeenCalledWith({ | ||
className: `foo ${values[0]}`, | ||
children: expect.any(Function) | ||
}); | ||
Component.mockReset(); | ||
const values2 = []; | ||
render(tpl(values2), $container); | ||
expect(Component).toHaveBeenCalledWith({ | ||
className: `foo`, | ||
children: expect.any(Function) | ||
}); | ||
Component.mockReset(); | ||
const values3 = ['bla', 'bar']; | ||
render(tpl(values3), $container); | ||
expect(Component).toHaveBeenCalledWith({ | ||
className: `foo ${values3[0]} ${values3[1]}`, | ||
children: expect.any(Function) | ||
}); | ||
}); | ||
it('handles complex classes', () => { | ||
const Component = jest.fn(); | ||
const tpl = (values) => html` | ||
<${Component} class="${values[0] ? values[1] : void 0} ${values[2]} ${values[3]}"/> | ||
`; | ||
const values = [true, 'zzz', ['yyy', 'aaa'], null]; | ||
render(tpl(values), $container); | ||
expect(Component).toHaveBeenCalledWith({ | ||
className: `${values[1]} ${values[2].join(' ')}`, | ||
children: expect.any(Function) | ||
}); | ||
Component.mockReset(); | ||
const values2 = [false, 'zzz', 'yyy', false]; | ||
render(tpl(values2), $container); | ||
expect(Component).toHaveBeenCalledWith({ | ||
className: `${values2[2]}`, | ||
children: expect.any(Function) | ||
}); | ||
Component.mockReset(); | ||
const values3 = [true, ['bbb'], ['yyy xxx', 'zzz']]; | ||
render(tpl(values3), $container); | ||
expect(Component).toHaveBeenCalledWith({ | ||
className: `${values3[1].join(' ')} ${values3[2].join(' ')}`, | ||
children: expect.any(Function) | ||
}); | ||
}); | ||
}); | ||
describe('child content rendering', () => { | ||
it('handles ternary operator', () => { | ||
const $container = document.createElement('div'); | ||
const Component = ({ children, show }) => html` | ||
<span id="bar">${show}</span> | ||
<div class="component-wrapper"> | ||
${show ? children : void 0} | ||
</div> | ||
`; | ||
const tpl = ({ show, values }) => html` | ||
<${Component} show=${show}> | ||
<div id="foo"></div> | ||
<span test="${values[0]}">${values[1]}</span> | ||
</${Component}> | ||
`; | ||
const values1 = { show: true, values: ['foo', 'bar'] }; | ||
render(tpl(values1), $container); | ||
expect($container.innerHTML).toBe(`<span id="bar">${values1.show}</span> | ||
<div class="component-wrapper"> | ||
<div id="foo"></div> | ||
<span test="${values1.values[0]}">${values1.values[1]}</span> | ||
</div> | ||
`); | ||
const values2 = { show: false, values: ['foo', 'bar'] }; | ||
render(tpl(values2), $container); | ||
expect($container.innerHTML).toBe(`<span id="bar">${values2.show}</span> | ||
<div class="component-wrapper"> | ||
</div> | ||
`); | ||
const values3 = { show: true, values: ['baz', 'quux'] }; | ||
render(tpl(values3), $container); | ||
expect($container.innerHTML).toBe(`<span id="bar">${values3.show}</span> | ||
<div class="component-wrapper"> | ||
<div id="foo"></div> | ||
<span test="${values3.values[0]}">${values3.values[1]}</span> | ||
</div> | ||
`); | ||
const values4 = { show: true, values: ['baz', 'zzz'] }; | ||
render(tpl(values4), $container); | ||
expect($container.innerHTML).toBe(`<span id="bar">${values4.show}</span> | ||
<div class="component-wrapper"> | ||
<div id="foo"></div> | ||
<span test="${values4.values[0]}">${values4.values[1]}</span> | ||
</div> | ||
`); | ||
}); | ||
it('handles arrays in child content', () => { | ||
const $container = document.createElement('div'); | ||
const Component = ({ children }) => html`${children}`; | ||
const tpl = ({ values }) => html` | ||
<${Component}> | ||
${values.map((value) => html` | ||
<span>${value}</span> | ||
`)} | ||
</${Component}> | ||
`; | ||
const values1 = { values: ['foo', 'bar'] }; | ||
render(tpl(values1), $container); | ||
expect($container.innerHTML).toBe(` | ||
<span>${values1.values[0]}</span> | ||
<span>${values1.values[1]}</span> | ||
`); | ||
const values2 = { values: ['quux', 'zzz'] }; | ||
render(tpl(values2), $container); | ||
expect($container.innerHTML).toBe(` | ||
<span>${values2.values[0]}</span> | ||
<span>${values2.values[1]}</span> | ||
`); | ||
const values3 = { values: ['quux', 'zzz', 'yyy'] }; | ||
render(tpl(values3), $container); | ||
expect($container.innerHTML).toBe(` | ||
<span>${values3.values[0]}</span> | ||
<span>${values3.values[1]}</span> | ||
<span>${values3.values[2]}</span> | ||
`); | ||
const values4 = { values: ['quux', 'yyy'] }; | ||
render(tpl(values4), $container); | ||
expect($container.innerHTML).toBe(` | ||
<span>${values4.values[0]}</span> | ||
<span>${values4.values[1]}</span> | ||
`); | ||
}); | ||
it('handles nested components', () => { | ||
const $container = document.createElement('div'); | ||
const ComponentA = ({ children, foo }) => html` | ||
<div id="component-a" foo=${foo}> | ||
${children} | ||
</div> | ||
`; | ||
const ComponentB = ({ value }) => html` | ||
<span id="component-b"> | ||
<${ComponentA} foo=${value + '-bla'}> | ||
<p>${value}</p> | ||
</${ComponentA}> | ||
</span> | ||
`; | ||
const tpl = ({ values }) => html` | ||
<${ComponentB} value=${values[0]}/> | ||
`; | ||
const values1 = { values: ['foo'] }; | ||
render(tpl(values1), $container); | ||
expect($container.innerHTML).toBe(`<span id="component-b"> | ||
<div id="component-a" foo="${values1.values[0]}-bla"> | ||
<p>${values1.values[0]}</p> | ||
</div> | ||
</span> | ||
`); | ||
const values2 = { values: ['bar'] }; | ||
render(tpl(values2), $container); | ||
expect($container.innerHTML).toBe(`<span id="component-b"> | ||
<div id="component-a" foo="${values2.values[0]}-bla"> | ||
<p>${values2.values[0]}</p> | ||
</div> | ||
</span> | ||
`); | ||
}); | ||
}); | ||
}); | ||
it('handles mixed content', () => { | ||
const $container = document.createElement('div'); | ||
const Component = ({ children }) => html` | ||
<div id="component"> | ||
${children} | ||
</div> | ||
`; | ||
const tpl = ({ tag, childContent }) => html` | ||
<${tag}> | ||
<span>${childContent}</span> | ||
</${tag}> | ||
`; | ||
const values1 = { tag: 'x-foo', childContent: 'test' }; | ||
render(tpl(values1), $container); | ||
expect($container.innerHTML).toBe(`<${values1.tag}> | ||
<span>${values1.childContent}</span> | ||
</${values1.tag}> | ||
`); | ||
const values2 = { tag: Component, childContent: 'foo' }; | ||
render(tpl(values2), $container); | ||
expect($container.innerHTML).toBe(`<div id="component"> | ||
<span>${values2.childContent}</span> | ||
</div> | ||
`); | ||
const values3 = { tag: 'x-foo', childContent: 'foo' }; | ||
render(tpl(values3), $container); | ||
expect($container.innerHTML).toBe(`<${values3.tag}> | ||
<span>${values3.childContent}</span> | ||
</${values3.tag}> | ||
`); | ||
const values4 = { tag: 'x-bar', childContent: 'foo' }; | ||
render(tpl(values4), $container); | ||
expect($container.innerHTML).toBe(`<${values4.tag}> | ||
<span>${values4.childContent}</span> | ||
</${values4.tag}> | ||
`); | ||
}); | ||
}); |
import { | ||
html, prepareLiterals, replaceTokens, sanitize, openSelfClosingTags, setPrefix, setPostfix, setSanitizeNodePrefix, updateChunkRegexes | ||
html, prepareLiterals, replaceTokens, sanitize, openSelfClosingTags, replaceDynamicTags, | ||
setPrefix, setPostfix, setSanitizeNodePrefix, updateChunkRegexes, setSpecialTagName, setSpecialAttributeName | ||
} from '../../src/html'; | ||
@@ -8,2 +9,4 @@ | ||
setSanitizeNodePrefix('sanitize:'); | ||
setSpecialTagName('modulor-dynamic-tag'); | ||
setSpecialAttributeName('modulor-chunk'); | ||
updateChunkRegexes(); | ||
@@ -42,2 +45,6 @@ | ||
expectedReplacedTokens: 'foo 1 bar 2 baz', | ||
}, | ||
{ | ||
parsedString: prepareLiterals`<${'div'}><${'span'}></${'span'}></${'div'}>`, | ||
expectedPreparedLiterals: '<{modulor_html_chunk:0}><{modulor_html_chunk:1}></{modulor_html_chunk:2}></{modulor_html_chunk:3}>', | ||
} | ||
@@ -180,1 +187,109 @@ ]; | ||
describe('replaceDynamicTags', () => { | ||
const testSets = [ | ||
{ | ||
input: '<{modulor_html_chunk:0}></{modulor_html_chunk:1}>', | ||
expectation: '<modulor-dynamic-tag modulor-chunk="{modulor_html_chunk:0}"></modulor-dynamic-tag>' | ||
}, | ||
{ | ||
input: '<{modulor_html_chunk:0}/>', | ||
expectation: '<modulor-dynamic-tag modulor-chunk="{modulor_html_chunk:0}"/>' | ||
}, | ||
{ | ||
input: '<{modulor_html_chunk:0} foo="bar"/>', | ||
expectation: '<modulor-dynamic-tag modulor-chunk="{modulor_html_chunk:0}" foo="bar"/>' | ||
}, | ||
{ | ||
input: '<x-{modulor_html_chunk:1}></x-{modulor_html_chunk:2}>', | ||
expectation: '<modulor-dynamic-tag modulor-chunk="x-{modulor_html_chunk:1}"></modulor-dynamic-tag>' | ||
}, | ||
{ | ||
input: '<{modulor_html_chunk:1}-test foo="bar"></{modulor_html_chunk:2}>', | ||
expectation: '<modulor-dynamic-tag modulor-chunk="{modulor_html_chunk:1}-test" foo="bar"></modulor-dynamic-tag>' | ||
}, | ||
{ | ||
input: '<x-{modulor_html_chunk:1}-test foo="bar"></x-{modulor_html_chunk:2}>', | ||
expectation: '<modulor-dynamic-tag modulor-chunk="x-{modulor_html_chunk:1}-test" foo="bar"></modulor-dynamic-tag>' | ||
}, | ||
{ | ||
input: '<x-{modulor_html_chunk:1}-test-{modulor_html_chunk:2} foo="bar"></x-{modulor_html_chunk:3}>', | ||
expectation: '<modulor-dynamic-tag modulor-chunk="x-{modulor_html_chunk:1}-test-{modulor_html_chunk:2}" foo="bar"></modulor-dynamic-tag>' | ||
}, | ||
{ | ||
input: ` | ||
<{modulor_html_chunk:0}> | ||
<{modulor_html_chunk:1}> | ||
</{modulor_html_chunk:2}> | ||
</{modulor_html_chunk:3}> | ||
`, | ||
expectation: ` | ||
<modulor-dynamic-tag modulor-chunk="{modulor_html_chunk:0}"> | ||
<modulor-dynamic-tag modulor-chunk="{modulor_html_chunk:1}"> | ||
</modulor-dynamic-tag> | ||
</modulor-dynamic-tag> | ||
` | ||
}, | ||
{ | ||
input: ` | ||
<{modulor_html_chunk:0} foo="{modulor_html_chunk:1}" {modulor_html_chunk:1}="{modulor_html_chunk:2}"> | ||
<{modulor_html_chunk:3} | ||
bla="test"> | ||
</{modulor_html_chunk:4}> | ||
</{modulor_html_chunk:5}> | ||
`, | ||
expectation: ` | ||
<modulor-dynamic-tag modulor-chunk="{modulor_html_chunk:0}" foo="{modulor_html_chunk:1}" {modulor_html_chunk:1}="{modulor_html_chunk:2}"> | ||
<modulor-dynamic-tag modulor-chunk="{modulor_html_chunk:3}" | ||
bla="test"> | ||
</modulor-dynamic-tag> | ||
</modulor-dynamic-tag> | ||
` | ||
}, | ||
{ | ||
input: ` | ||
<x-{modulor_html_chunk:0}-y foo="{modulor_html_chunk:1}" {modulor_html_chunk:1}="{modulor_html_chunk:2}"> | ||
<{modulor_html_chunk:3}-foo | ||
bla="test"> | ||
</{modulor_html_chunk:4}> | ||
</x-{modulor_html_chunk:5}-y> | ||
`, | ||
expectation: ` | ||
<modulor-dynamic-tag modulor-chunk="x-{modulor_html_chunk:0}-y" foo="{modulor_html_chunk:1}" {modulor_html_chunk:1}="{modulor_html_chunk:2}"> | ||
<modulor-dynamic-tag modulor-chunk="{modulor_html_chunk:3}-foo" | ||
bla="test"> | ||
</modulor-dynamic-tag> | ||
</modulor-dynamic-tag> | ||
` | ||
}, | ||
{ | ||
input: ` | ||
<{modulor_html_chunk:0}> | ||
{modulor_html_chunk:1} | ||
<{modulor_html_chunk:2}> | ||
{modulor_html_chunk:3} | ||
</{modulor_html_chunk:4}> | ||
{modulor_html_chunk:5} | ||
</{modulor_html_chunk:6}> | ||
{modulor_html_chunk:7} | ||
`, | ||
expectation: ` | ||
<modulor-dynamic-tag modulor-chunk="{modulor_html_chunk:0}"> | ||
{modulor_html_chunk:1} | ||
<modulor-dynamic-tag modulor-chunk="{modulor_html_chunk:2}"> | ||
{modulor_html_chunk:3} | ||
</modulor-dynamic-tag> | ||
{modulor_html_chunk:5} | ||
</modulor-dynamic-tag> | ||
{modulor_html_chunk:7} | ||
` | ||
}, | ||
] | ||
testSets.forEach((testSet, index) => { | ||
it(`set #${index}`, () => { | ||
expect(replaceDynamicTags(testSet.input)).toBe(testSet.expectation); | ||
}); | ||
}) | ||
}); | ||
@@ -330,3 +330,3 @@ import 'document-register-element'; | ||
const tplF = (scope) => html` | ||
<my-test-component-f attr="123" value="${scope.value}" foo="${scope.foo}"></my-test-component-f> | ||
<my-test-component-f attr="123" value="${scope.value}" foo="${scope.foo}" ${scope.value2}="val"></my-test-component-f> | ||
`; | ||
@@ -352,2 +352,3 @@ | ||
value: 'baz', | ||
value2: 'dynamicProp', | ||
foo: 'bar' | ||
@@ -360,2 +361,3 @@ }), container); | ||
value: 'baz', | ||
dynamicProp: 'val', | ||
foo: 'bar' | ||
@@ -371,3 +373,7 @@ }); | ||
expect(propsSetterSpy).not.toHaveBeenCalled(); | ||
expect(propsSetterSpy).toHaveBeenCalledWith({ | ||
attr: '123', | ||
value: 'baz', | ||
foo: 'bar' | ||
}); | ||
@@ -389,2 +395,162 @@ propsSetterSpy.mockReset(); | ||
it('calls props if it is a function on every render', () => { | ||
const propsSetterSpy = jest.fn(); | ||
customElements.define('my-test-component-g', class extends HTMLElement { | ||
props(...args){ | ||
propsSetterSpy(...args); | ||
} | ||
}); | ||
const tplF = (scope) => html` | ||
<my-test-component-g attr="123" value="${scope.value}" foo="${scope.foo}" ${scope.value2}="val" /> | ||
`; | ||
const container = document.createElement('div'); | ||
render(tplF({ | ||
value: 'bla', | ||
foo: 'bar' | ||
}), container); | ||
expect(propsSetterSpy).toHaveBeenCalledTimes(1); | ||
expect(propsSetterSpy).toHaveBeenCalledWith({ | ||
attr: '123', | ||
value: 'bla', | ||
foo: 'bar' | ||
}, true); | ||
render(tplF({ | ||
value: 'bla', | ||
foo: 'bar' | ||
}), container); | ||
expect(propsSetterSpy).toHaveBeenCalledTimes(2); | ||
expect(propsSetterSpy).toHaveBeenCalledWith({ | ||
attr: '123', | ||
value: 'bla', | ||
foo: 'bar' | ||
}, false); | ||
render(tplF({ | ||
value: 'baz', | ||
value2: 'dynamicProp', | ||
foo: 'bar' | ||
}), container); | ||
expect(propsSetterSpy).toHaveBeenCalledTimes(3); | ||
expect(propsSetterSpy).toHaveBeenCalledWith({ | ||
attr: '123', | ||
value: 'baz', | ||
dynamicProp: 'val', | ||
foo: 'bar' | ||
}, true); | ||
render(tplF({ | ||
value: 'baz', | ||
foo: 'bar' | ||
}), container); | ||
expect(propsSetterSpy).toHaveBeenCalledTimes(4); | ||
expect(propsSetterSpy).toHaveBeenCalledWith({ | ||
attr: '123', | ||
value: 'baz', | ||
foo: 'bar' | ||
}, true); | ||
render(tplF({ | ||
value: 'baz', | ||
foo: 'quux' | ||
}), container); | ||
expect(propsSetterSpy).toHaveBeenCalledTimes(5); | ||
expect(propsSetterSpy).toHaveBeenCalledWith({ | ||
attr: '123', | ||
value: 'baz', | ||
foo: 'quux' | ||
}, true); | ||
}); | ||
it('intercepts children rendering correctly', () => { | ||
const propsSetterSpyIntercept = jest.fn(); | ||
const propsSetterSpy = jest.fn(); | ||
const $renderContainer = document.createElement('div'); | ||
const renderContainer = ({ children }) => { | ||
render(children, $renderContainer); | ||
}; | ||
customElements.define('my-test-component-h', class extends HTMLElement { | ||
props(props, updated){ | ||
propsSetterSpyIntercept(props); | ||
renderContainer(props); | ||
} | ||
get preventChildRendering(){ | ||
return true; | ||
} | ||
}); | ||
customElements.define('my-test-component-i', class extends HTMLElement { | ||
props(props, updated){ | ||
propsSetterSpy(props); | ||
} | ||
}); | ||
const tplF = (scope) => html` | ||
<my-test-component-h foo="${scope.foo}"> | ||
<span class="test">${scope.bar}</span> | ||
</my-test-component-h> | ||
<my-test-component-i foo="${scope.foo}"> | ||
<span class="test"></span> | ||
</my-test-component-i> | ||
`; | ||
const container = document.createElement('div'); | ||
render(tplF({ foo: 'bar', bar: 'baz' }), container); | ||
expect(propsSetterSpyIntercept).toHaveBeenCalledWith({ | ||
foo: 'bar', | ||
children: expect.any(Function) | ||
}); | ||
expect(propsSetterSpy).toHaveBeenCalledWith({ | ||
foo: 'bar' | ||
}); | ||
expect(container.querySelector('my-test-component-h .test')).toBe(null); | ||
expect($renderContainer.innerHTML).toEqual(` | ||
<span class="test">baz</span> | ||
`); | ||
expect(container.querySelector('my-test-component-i .test')).not.toBe(null); | ||
propsSetterSpyIntercept.mockReset(); | ||
propsSetterSpy.mockReset(); | ||
render(tplF({ foo: 'bla', bar: 'quux' }), container); | ||
expect(propsSetterSpyIntercept).toHaveBeenCalledWith({ | ||
foo: 'bla', | ||
children: expect.any(Function) | ||
}); | ||
expect(propsSetterSpy).toHaveBeenCalledWith({ | ||
foo: 'bla' | ||
}); | ||
expect(container.querySelector('my-test-component-h .test')).toBe(null); | ||
expect($renderContainer.innerHTML).toEqual(` | ||
<span class="test">quux</span> | ||
`); | ||
expect(container.querySelector('my-test-component-i .test')).not.toBe(null); | ||
}); | ||
}); | ||
@@ -391,0 +557,0 @@ |
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
1
0
129283
32
2984