UNPKG

conditional-tag

Version:

Clean, easily readable conditional statements in template literals/strings for Node.js and browsers. Provides `if` / `elseif` / `else` and `switch` / `case` / `default` syntax options.

4 lines (3 loc) 5.32 kB
/* conditional-tag v1.2.4 https://github.com/VividVisions/conditional-tag */ const t=Symbol("condTrue"),e=Symbol("condFalse");function s(s){return{func:"if",cond:s?t:e}}function n(s){return{func:"elseif",cond:s?t:e}}const i=Symbol("else"),r=Symbol("endif");class a{status=[];depth=-1;getStatus(){return this.status[this.depth]}setStatus(t,e){return"string"==typeof t&&void 0!==e?this.status[this.depth][t]=e:this.status[this.depth]=t,this.status[this.depth]}pushStatus(t){return this.depth++,this.status.push(t),this.status.at(-1)}popStatus(){return this.depth--,this.status.pop()}canHandle(){throw new Error("canHandle() not implemented.")}handle(){throw new Error("handle() not implemented.")}}class u extends Error{}class o extends a{static steps={IGNORED:0,IF:1,ELSEIF:2,ELSE:3};#t(t){return"if"===t?.func&&(this.#e(t?.cond)||this.#s(t?.cond))}#n(t){return"elseif"===t?.func&&(this.#e(t?.cond)||this.#s(t?.cond))}#i(t){return t===i}#r(t){return t===r}#e(e){return e===t}#s(t){return t===e}canHandle(t){return this.#t(t)||this.#n(t)||this.#i(t)||this.#r(t)}handle(t,e){let s=this.getStatus();if("beforeAlways"in e&&(e.filterOut=e.beforeAlways,delete e.beforeAlways),void 0===s||s.step!==o.steps.IGNORED||this.#r(t)){if(this.#t(t)){if(!0===e.filterOut)return this.pushStatus({anyCondMet:null,step:o.steps.IGNORED,parentFilter:e.filterOut}),!1;s=this.pushStatus({anyCondMet:this.#e(t.cond),step:o.steps.IF,parentFilter:e.filterOut}),e.filterOut=!s.anyCondMet}else if(this.#n(t)){if(void 0===s)throw new u("_elseif() must be inside if-block.");if(s.step>o.steps.ELSEIF)throw new u("_elseif() must not occur after _else.");s.step=o.steps.ELSEIF,s.anyCondMet?e.filterOut=!0:(this.setStatus("anyCondMet",this.#e(t.cond)),e.filterOut=!s.anyCondMet)}else if(this.#i(t)){if(void 0===s)throw new u("_else must be inside if-block.");if(s.step>o.steps.ELSEIF)throw new u("Only one _else permitted per if-block.");s.step=o.steps.ELSE,s.anyCondMet?e.filterOut=!0:(this.setStatus("anyCondMet",!0),e.filterOut=!1)}else if(this.#r(t)){if(void 0===s)throw new u("_endif must be inside if-block.");const t=this.popStatus();e.filterOut=t.parentFilter}}else;return!1}}const l=Symbol("switch"),c=Symbol("case"),f=Symbol("switchCase");function h(t){return{func:l,switchVar:t,_case:S(t)}}function d(...t){return{func:c,caseVars:t}}const w=Symbol("default"),p=Symbol("endswitch");function S(t){return function(...e){return{func:f,switchVar:t,caseVars:e}}}class y extends a{#a(t){return t?.func===l}#u(t){return t?.func===c}#o(t){return t?.func===f}#l(t){return t===w}#c(t){return t===p}canHandle(t){return this.#a(t)||this.#u(t)||this.#o(t)||this.#l(t)||this.#c(t)}handle(t,e){let s=this.getStatus();if("beforeAlways"in e&&(e.filterOut=e.beforeAlways,delete e.beforeAlways),void 0!==s&&!0===s.ignored&&!this.#c(t))return!1;if(this.#a(t)||this.#o(t)){if(void 0!==s&&!s.case)throw new u("_switch() can only be nested inside other _case () blocks.");if(!0===e.filterOut)return this.pushStatus({ignored:!0,parentFilter:e.filterOut}),!1;s=this.pushStatus({switchVar:t.switchVar,parentFilter:e.filterOut}),this.#o(t)&&(t.func=c)}if(this.#u(t)){if(void 0===s)throw new u("_case() must be inside switch-block.");s.case=!0,t.caseVars.some(t=>t===s.switchVar)?(this.setStatus("anyCondMet",!0),e.filterOut=!1):e.filterOut=!0}else if(this.#l(t)){if(void 0===s)throw new u("_default must be inside switch-block.");if(!s?.case)throw new u("_default must be preceded by at least one _case.");e.filterOut=s.anyCondMet}else if(this.#c(t)){if(void 0===s)throw new u("_endswitch must be inside switch-block");const t=this.popStatus();e.filterOut=t.parentFilter}return!1}}const b=Symbol("always");class E{canHandle(t){return t===b}handle(t,e){return e.beforeAlways=e.filterOut,e.filterOut=!1,!1}}function m(t){return"function"==typeof t&&void 0===t.prototype&&""===t.name}class O{canHandle(t){return m(t)}handle(t,e){e.filterOut||(t[Symbol.toPrimitive]=function(){return this()})}}const C=/(^|\r?\n)[^\S\r\n]*$/,I=/^[^\S\r\n]*(\r?\n|$)/,F=/^[\r\n]+$/;function _(t,e){return t.flatMap((t,s)=>s<e.length?[t,e[s]]:t)}function v(t){const e=new o,s=new y,n=new E,i=new O,r={filterOut:!1},a=new Set;let u=0;return{output:t.filter((t,o)=>{if("string"!=typeof t){if(e.canHandle(t))return a.add(u),e.handle(t,r);if(s.canHandle(t))return a.add(u),s.handle(t,r);if(n.canHandle(t))return a.add(u),n.handle(t,r);i.canHandle(t)&&i.handle(t,r)}return!r.filterOut&&(u++,!0)}),handled:a}}function M(t,e){e.forEach(e=>{const s=e-1,n=C.exec(t[s]),i=I.exec(t[e]);null!==n&&null!==i&&(t[s]=t[s].replace(C,((...t)=>t.every(t=>F.test(t)))(n[1],i[1])?n[1]:""),t[e]=t[e].replace(I,""))})}function g(t,...e){const s=_(t,e);try{const{output:t,handled:e}=v(s);return M(t,e),t.join("")}catch(t){if(t instanceof TypeError)throw new Error(`${t.message} (Maybe there's an async function in an expression? Use tag function _async with await.)`,{cause:t});throw t}}async function H(t,...e){const s=_(t,e),{output:n,handled:i}=v(s),r=await Promise.all(n.map(t=>m(t)?t[Symbol.toPrimitive]():t));return M(r,i),r.join("")}g.if=s,g.async=H,g.elseif=n,g.else=i,g.endif=r,g.switch=h,g.case=d,g.default=w,g.endswitch=p,g.always=b;export{u as ConditionalTagSyntaxError,g as _,b as _always,H as _async,d as _case,w as _default,i as _else,n as _elseif,r as _endif,p as _endswitch,s as _if,h as _switch};