string-find-heads-tails
Version:
Finds where are arbitrary templating marker heads and tails located
30 lines (24 loc) • 7.24 kB
JavaScript
/**
* @name string-find-heads-tails
* @fileoverview Finds where are arbitrary templating marker heads and tails located
* @version 6.1.3
* @author Roy Revelt
* @license MIT
* {@link https://codsen.com/os/string-find-heads-tails/}
*/
import{isStr as s,isPlainObject as D,existy as E}from"codsen-utils";import{matchRightIncl as x}from"string-match-left-right";import{arrayiffy as $}from"arrayiffy-if-string";var T="6.1.3";var C=T,A={fromIndex:0,throwWhenSomethingWrongIsDetected:!0,allowWholeValueToBeOnlyHeadsOrTails:!0,source:"string-find-heads-tails",matchHeadsAndTailsStrictlyInPairsByTheirOrder:!1,relaxedAPI:!1};function P(i,r,n,p){if(E(p)&&!D(p))throw new TypeError(`string-find-heads-tails: [THROW_ID_01] the fourth input argument, an Optional Options Object, must be a plain object! Currently it's equal to: ${p} (type: ${typeof p})`);let t={...A,...p};if(s(t.fromIndex)&&/^\d*$/.test(t.fromIndex))t.fromIndex=Number(t.fromIndex);else if(!Number.isInteger(t.fromIndex)||t.fromIndex<0)throw new TypeError(`${t.source} [THROW_ID_18] the fourth input argument must be a natural number or zero! Currently it's: ${t.fromIndex}`);if(!s(i)||i.length===0){if(t.relaxedAPI)return[];throw new TypeError(`string-find-heads-tails: [THROW_ID_02] the first input argument, input string, must be a non-zero-length string! Currently it's: ${typeof i}, equal to: ${i}`)}let c,a;if(!s(r)&&!Array.isArray(r)){if(t.relaxedAPI)return[];throw new TypeError(`string-find-heads-tails: [THROW_ID_03] the second input argument, heads, must be either a string or an array of strings! Currently it's: ${typeof r}, equal to:
${JSON.stringify(r,null,4)}`)}else if(s(r))if(r.length===0){if(t.relaxedAPI)return[];throw new TypeError("string-find-heads-tails: [THROW_ID_04] the second input argument, heads, must be a non-empty string! Currently it's empty.")}else r=$(r);else if(Array.isArray(r))if(r.length===0){if(t.relaxedAPI)return[];throw new TypeError("string-find-heads-tails: [THROW_ID_05] the second input argument, heads, must be a non-empty array and contain at least one string! Currently it's empty.")}else if(r.every((e,o)=>(c=e,a=o,s(e)))){if(!r.every((e,o)=>(a=o,s(e)&&e.length&&e.trim()!=="")))if(t.relaxedAPI){if(r=r.filter(e=>s(e)&&e.length),r.length===0)return[]}else throw new TypeError(`string-find-heads-tails: [THROW_ID_07] the second input argument, heads, should not contain empty strings! For example, there's one detected at index ${a} of heads array:
${JSON.stringify(r,null,4)}.`)}else if(t.relaxedAPI){if(r=r.filter(e=>s(e)&&e.length),r.length===0)return[]}else throw new TypeError(`string-find-heads-tails: [THROW_ID_06] the second input argument, heads, contains non-string elements! For example, element at ${a}th index is ${typeof c}, equal to:
${JSON.stringify(c,null,4)}. Whole heads array looks like:
${JSON.stringify(r,null,4)}`);if(!s(n)&&!Array.isArray(n)){if(t.relaxedAPI)return[];throw new TypeError(`string-find-heads-tails: [THROW_ID_08] the third input argument, tails, must be either a string or an array of strings! Currently it's: ${typeof n}, equal to:
${JSON.stringify(n,null,4)}`)}else if(s(n))if(n.length===0){if(t.relaxedAPI)return[];throw new TypeError("string-find-heads-tails: [THROW_ID_09] the third input argument, tails, must be a non-empty string! Currently it's empty.")}else n=$(n);else if(Array.isArray(n))if(n.length===0){if(t.relaxedAPI)return[];throw new TypeError("string-find-heads-tails: [THROW_ID_10] the third input argument, tails, must be a non-empty array and contain at least one string! Currently it's empty.")}else if(n.every((e,o)=>(c=e,a=o,s(e)))){if(!n.every((e,o)=>(a=o,s(e)&&e.length&&e.trim()!=="")))if(t.relaxedAPI){if(n=n.filter(e=>s(e)&&e.length),n.length===0)return[]}else throw new TypeError(`string-find-heads-tails: [THROW_ID_12] the third input argument, tails, should not contain empty strings! For example, there's one detected at index ${a}. Whole tails array is equal to:
${JSON.stringify(n,null,4)}`)}else if(t.relaxedAPI){if(n=n.filter(e=>s(e)&&e.length),n.length===0)return[]}else throw new TypeError(`string-find-heads-tails: [THROW_ID_11] the third input argument, tails, contains non-string elements! For example, element at ${a}th index is ${typeof c}, equal to:
${JSON.stringify(c,null,4)}. Whole tails array is equal to:
${JSON.stringify(n,null,4)}`);let g=t.source===A.source;if(t.throwWhenSomethingWrongIsDetected&&!t.allowWholeValueToBeOnlyHeadsOrTails){if($(r).includes(i))throw new Error(`${t.source}${g?": [THROW_ID_16]":""} the whole input string can't be equal to ${s(r)?"":"one of "}heads (${i})!`);if($(n).includes(i))throw new Error(`${t.source}${g?": [THROW_ID_17]":""} the whole input string can't be equal to ${s(n)?"":"one of "}tails (${i})!`)}let b=r.concat(n).map(e=>e.charAt(0)).reduce((e,o)=>o.charCodeAt(0)>e[1]?[e[0],o.charCodeAt(0)]:o.charCodeAt(0)<e[0]?[o.charCodeAt(0),e[1]]:e,[r[0].charCodeAt(0),r[0].charCodeAt(0)]),I=[],m=!1,l={},y="",u;for(let e=t.fromIndex,o=i.length;e<o;e++){let O=i[e].charCodeAt(0);if(O<=b[1]&&O>=b[0]){let d=x(i,e,r);if(d&&t.matchHeadsAndTailsStrictlyInPairsByTheirOrder){for(let f=r.length;f--;)if(r[f]===d){u=f;break}}if(s(d))if(m){if(t.throwWhenSomethingWrongIsDetected)throw new TypeError(`${t.source}${g?": [THROW_ID_19]":""} When processing "${i}", we found heads (${i.slice(e,e+d.length)}) starting at character with index number "${e}" and there was another set of heads before it! Generally speaking, there should be "heads-tails-heads-tails", not "heads-heads-tails"!
We're talking about the area of the code:
--------------------------------------starts
${i.slice(Math.max(e-200,0),e)}
\x1B[33m------->\x1B[39m ${`\x1B[31m${i.slice(e,e+d.length)}\x1B[39m`} \x1B[33m<-------\x1B[39m
${i.slice(e+d.length,Math.min(o,e+200))}
--------------------------------------ends
To turn off this error being thrown, set resolvedOpts.throwWhenSomethingWrongIsDetected to Boolean false.`)}else{l={},l.headsStartAt=e,l.headsEndAt=e+d.length,m=!0,e+=d.length-1,y&&(y="");continue}let h=x(i,e,n);if(m&&h&&t.matchHeadsAndTailsStrictlyInPairsByTheirOrder&&u!==void 0&&n[u]!==void 0&&n[u]!==h){let f;for(let w=n.length;w--;)if(n[w]===h){f=w;break}throw new TypeError(`${t.source}${g?": [THROW_ID_20]":""} When processing "${i}", we had "resolvedOpts.matchHeadsAndTailsStrictlyInPairsByTheirOrder" on. We found heads (${r[u]}) but the tails the followed it were not of the same index, ${u} (${n[u]}) but ${f} (${h}).`)}if(s(h))if(m){l.tailsStartAt=e,l.tailsEndAt=e+h.length,I.push(l),l={},m=!1,e+=h.length-1;continue}else t.throwWhenSomethingWrongIsDetected&&(y=`${t.source}${g?": [THROW_ID_21]":""} When processing "${i}", we found tails (${i.slice(e,e+h.length)}) starting at character with index number "${e}" but there were no heads preceding it. That's very naughty!`)}if(t.throwWhenSomethingWrongIsDetected&&e===o-1){if(Object.keys(l).length!==0)throw new TypeError(`${t.source}${g?": [THROW_ID_22]":""} When processing "${i}", we reached the end of the string and yet didn't find any tails (${JSON.stringify(n,null,4)}) to match the last detected heads (${i.slice(l.headsStartAt,l.headsEndAt)})!`);if(y)throw new Error(y)}}return I}export{A as defaults,P as strFindHeadsTails,C as version};