Comparing version 1.1.0 to 1.1.1
@@ -1,2 +0,2 @@ | ||
var Regex=(()=>{var h=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var Z=(e,t)=>{for(var r in t)h(e,r,{get:t[r],enumerable:!0})},z=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of B(t))!X.call(e,o)&&o!==r&&h(e,o,{get:()=>t[o],enumerable:!(n=v(t,o))||n.enumerable});return e};var J=e=>z(h({},"__esModule",{value:!0}),e);var se={};Z(se,{default:()=>oe,make:()=>W,partial:()=>_,regex:()=>P});var N=class{#e;constructor(t){this.#e=t}toString(){return String(this.#e)}};function _(e,...t){if(Array.isArray(e?.raw))return new N(e.raw.flatMap((r,n)=>n<e.raw.length-1?[r,t[n]]:r).join(""));if(!t.length)return new N(e??"");throw new Error(`Unexpected arguments: ${JSON.stringify([e,...t])}`)}var s={DEFAULT:"DEFAULT",CHAR_CLASS:"CHAR_CLASS",GROUP_NAME:"GROUP_NAME",ENCLOSED_TOKEN:"ENCLOSED_TOKEN",INTERVAL_QUANTIFIER:"INTERVAL_QUANTIFIER",INVALID_INCOMPLETE_TOKEN:"INVALID_INCOMPLETE_TOKEN"},E={DEFAULT:"CC_DEFAULT",RANGE:"CC_RANGE",ENCLOSED_TOKEN:"CC_ENCLOSED_TOKEN",Q_TOKEN:"CC_Q_TOKEN",INVALID_INCOMPLETE_TOKEN:"CC_INVALID_INCOMPLETE_TOKEN"},g=(()=>{let e=!0;try{new RegExp("(?i-ms:)")}catch{e=!1}return e})(),R="&!#$%*+,.:;<=>?@^`~";function C(e,t){return t===s.CHAR_CLASS?e.replace(new RegExp(String.raw`[()\[\]{}|\\/\-${R}]`,"g"),"\\$&"):e.replace(/[()\[\]{}|\\^$*+?.]/g,"\\$&")}function O(e){return e.replace(new RegExp(String.raw`^([${R}])(?!\1)`),(t,r,n)=>`\\${t}${n+1===e.length?"":t}`)}function x(e){return e.replace(/^\^/,"\\^^")}function L(e,t){return A(e,String.raw`\\0(?!\d)`,"\\u{0}",t)}function K(e,t,r){let n=0;for(let[o]of e.matchAll(new RegExp(`[${C(t+r)}]`,"g")))if(n+=o===t?1:-1,n<0)return r;return n>0?t:""}function G(e,t,r){let n=e.replace(/\\./gsu,"");if(n.at(-1)==="\\")return"\\";if(t===s.DEFAULT)return K(n,"(",")");if(t===s.CHAR_CLASS&&!(r===E.ENCLOSED_TOKEN||r===E.Q_TOKEN))return K(n,"[","]");if(t===s.ENCLOSED_TOKEN||t===s.INTERVAL_QUANTIFIER||r===E.ENCLOSED_TOKEN||r===E.Q_TOKEN){if(n.includes("}"))return"}"}else if(t===s.GROUP_NAME&&n.includes(">"))return">";return""}var m=new RegExp(String.raw` | ||
var Regex=(()=>{var R=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var Y=(e,t)=>{for(var r in t)R(e,r,{get:t[r],enumerable:!0})},ee=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of J(t))!X.call(e,o)&&o!==r&&R(e,o,{get:()=>t[o],enumerable:!(n=B(t,o))||n.enumerable});return e};var te=e=>ee(R({},"__esModule",{value:!0}),e);var le={};Y(le,{default:()=>fe,make:()=>Z,partial:()=>O,regex:()=>b});var A={DEFAULT:"DEFAULT",CHAR_CLASS:"CHAR_CLASS"};function m(e,t,r,n){let o=new RegExp(String.raw`(?<found>${t})|\\?.`,"gsu"),a=0,s="";for(let l of e.matchAll(o)){let{0:u,groups:{found:E}}=l;if(E&&(!n||n===A.DEFAULT==!a)){r instanceof Function?s+=r(l):s+=r;continue}u==="["?a++:u==="]"&&a&&a--,s+=u}return s}function re(e,t,r,n){if(!new RegExp(t,"su").test(e))return!1;let o=new RegExp(String.raw`(?<found>${t})|\\?.`,"gsu"),a=0;for(let s of e.matchAll(o)){let{0:l,groups:{found:u}}=s;if(u&&(!n||n===A.DEFAULT==!a))return r&&r(s),!0;l==="["?a++:l==="]"&&a&&a--}return!1}function K(e,t,r){return re(e,t,null,r)}function k(e){if(!K(e,String.raw`\(\?>`,A.DEFAULT))return e;let t=new RegExp(String.raw`(?<noncapturingStart>\(\?(?:[:=!>A-Za-z\-]|<[=!]))|(?<capturingStart>\((?:\?<[^>]+>)?)|(?<backrefNum>\\[1-9]\d*)|\\?.`,"gsu"),r=3,n,o=0,a=0,s=NaN;do{n=!1;let l=0,u=0,E=!1,p;for(t.lastIndex=Number.isNaN(s)?0:s+r;p=t.exec(e);){let{0:i,index:N,groups:{backrefNum:d,capturingStart:_,noncapturingStart:h}}=p;if(i==="[")l++;else if(l)i==="]"&&l--;else if(i==="(?>"&&!E)s=N,E=!0;else if(E&&h)u++;else if(_)E&&u++,o++;else if(i===")"&&E){if(!u){a++,e=`${e.slice(0,s)}(?:(?=(${e.slice(s+r,N)}))\\k<${a+o}>)${e.slice(N+1)}`,n=!0,o--;break}u--}else if(d)throw new Error(`Invalid decimal escape "${i}" in interpolated regex; cannot be used with atomic group`)}}while(n);return e=m(e,String.raw`\\k<(?<backrefNum>\d+)>`,({groups:{backrefNum:l}})=>`\\${l}`,A.DEFAULT),e}var T=class{#e;constructor(t){this.#e=t}toString(){return String(this.#e)}};function O(e,...t){if(Array.isArray(e?.raw))return new T(e.raw.flatMap((r,n)=>n<e.raw.length-1?[r,t[n]]:r).join(""));if(!t.length)return new T(e??"");throw new Error(`Unexpected arguments: ${JSON.stringify([e,...t])}`)}var f={...A,GROUP_NAME:"GROUP_NAME",ENCLOSED_TOKEN:"ENCLOSED_TOKEN",INTERVAL_QUANTIFIER:"INTERVAL_QUANTIFIER",INVALID_INCOMPLETE_TOKEN:"INVALID_INCOMPLETE_TOKEN"},c={DEFAULT:"CC_DEFAULT",RANGE:"CC_RANGE",ENCLOSED_TOKEN:"CC_ENCLOSED_TOKEN",Q_TOKEN:"CC_Q_TOKEN",INVALID_INCOMPLETE_TOKEN:"CC_INVALID_INCOMPLETE_TOKEN"},S=(()=>{let e=!0;try{new RegExp("(?i-ms:)")}catch{e=!1}return e})(),U="&!#$%*+,.:;<=>?@^`~";function C(e,t){return t===f.CHAR_CLASS?e.replace(new RegExp(String.raw`[()\[\]{}|\\/\-${U}]`,"g"),"\\$&"):e.replace(/[()\[\]{}|\\^$*+?.]/g,"\\$&")}function I(e){return e.replace(new RegExp(String.raw`^([${U}])(?!\1)`),(t,r,n)=>`\\${t}${n+1===e.length?"":t}`)}function V(e){return e.replace(/^\^/,"\\^^")}function w(e,t){return m(e,String.raw`\\0(?!\d)`,"\\u{0}",t)}function G(e,t,r){let n=0;for(let[o]of e.matchAll(new RegExp(`[${C(t+r)}]`,"g")))if(n+=o===t?1:-1,n<0)return r;return n>0?t:""}function M(e,t,r){let n=e.replace(/\\./gsu,"");if(n.at(-1)==="\\")return"\\";if(t===f.DEFAULT)return G(n,"(",")");if(t===f.CHAR_CLASS&&!(r===c.ENCLOSED_TOKEN||r===c.Q_TOKEN))return G(n,"[","]");if(t===f.ENCLOSED_TOKEN||t===f.INTERVAL_QUANTIFIER||r===c.ENCLOSED_TOKEN||r===c.Q_TOKEN){if(n.includes("}"))return"}"}else if(t===f.GROUP_NAME&&n.includes(">"))return">";return""}var g=new RegExp(String.raw` | ||
(?<groupN> \(\?< (?! [=!] ) | \\k< ) | ||
@@ -20,11 +20,11 @@ | (?<enclosedT> \\[pPu]\{ ) | ||
| \[\^ | ||
| \(\?[:=!<>ims\-] | ||
| (?<dp> [${R}] ) \k<dp> | ||
| \(\? [:=!<>A-Za-z\-] | ||
| (?<dp> [${U}] ) \k<dp> | ||
| \\[1-9]\d* | ||
| -- | ||
| \\? . | ||
`.replace(/\s+/g,""),"gsu");function T(e,{regexContext:t=s.DEFAULT,charClassContext:r=E.DEFAULT,charClassDepth:n=0,lastPos:o=0}){m.lastIndex=o;let u;for(;u=m.exec(e);){let{0:i,groups:{groupN:c,enclosedT:l,qT:f,intervalQ:a,incompleteT:p}}=u;i==="["||i==="[^"?(n++,t=s.CHAR_CLASS,r=E.DEFAULT):i==="]"&&t===s.CHAR_CLASS?(n&&n--,n||(t=s.DEFAULT),r=E.DEFAULT):t===s.CHAR_CLASS?p?r=E.INVALID_INCOMPLETE_TOKEN:i==="-"?r=E.RANGE:l?r=E.ENCLOSED_TOKEN:f?r=E.Q_TOKEN:(i==="}"&&(r===E.ENCLOSED_TOKEN||r===E.Q_TOKEN)||r===E.INVALID_INCOMPLETE_TOKEN||r===E.RANGE)&&(r=E.DEFAULT):p?t=s.INVALID_INCOMPLETE_TOKEN:c?t=s.GROUP_NAME:l?t=s.ENCLOSED_TOKEN:a?t=s.INTERVAL_QUANTIFIER:(i===">"&&t===s.GROUP_NAME||i==="}"&&(t===s.ENCLOSED_TOKEN||t===s.INTERVAL_QUANTIFIER)||t===s.INVALID_INCOMPLETE_TOKEN)&&(t=s.DEFAULT)}return{regexContext:t,charClassContext:r,charClassDepth:n,lastPos:e.length}}function A(e,t,r,n){let o=new RegExp(String.raw`(?<found>${t})|\\?.`,"gsu"),u=0,i="";for(let c of e.matchAll(o)){let{0:l,groups:{found:f}}=c;if(f&&(!n||n===s.DEFAULT==!u)){r instanceof Function?i+=r(c):i+=r;continue}l==="["?u++:l==="]"&&u&&u--,i+=l}return i}function k(e,t,r){if(!new RegExp(t,"su").test(e))return!1;let n=new RegExp(String.raw`(?<found>${t})|\\?.`,"gsu"),o=0;for(let u of e.matchAll(n)){let{0:i,groups:{found:c}}=u;if(i==="[")o++;else if(o)i==="]"&&o--;else if(c)return r&&r(u),!0}return!1}function U(e){let t=/(?<capture>\((?:(?!\?)|\?<[^>]+>))|\\?./gsu;return Array.from(e.matchAll(t)).filter(r=>r.groups.capture).length}function V(e,t){return e.replace(/\\([1-9]\d*)|\\?./gsu,(r,n)=>n?"\\"+(Number(n)+t):r)}var Y=["Basic_Emoji","Emoji_Keycap_Sequence","RGI_Emoji_Modifier_Sequence","RGI_Emoji_Flag_Sequence","RGI_Emoji_Tag_Sequence","RGI_Emoji_ZWJ_Sequence","RGI_Emoji"].join("|");function $(e){let t=new RegExp(String.raw` | ||
`.replace(/\s+/g,""),"gsu");function L(e,{regexContext:t=f.DEFAULT,charClassContext:r=c.DEFAULT,charClassDepth:n=0,lastPos:o=0}){g.lastIndex=o;let a;for(;a=g.exec(e);){let{0:s,groups:{groupN:l,enclosedT:u,qT:E,intervalQ:p,incompleteT:i}}=a;s==="["||s==="[^"?(n++,t=f.CHAR_CLASS,r=c.DEFAULT):s==="]"&&t===f.CHAR_CLASS?(n&&n--,n||(t=f.DEFAULT),r=c.DEFAULT):t===f.CHAR_CLASS?i?r=c.INVALID_INCOMPLETE_TOKEN:s==="-"?r=c.RANGE:u?r=c.ENCLOSED_TOKEN:E?r=c.Q_TOKEN:(s==="}"&&(r===c.ENCLOSED_TOKEN||r===c.Q_TOKEN)||r===c.INVALID_INCOMPLETE_TOKEN||r===c.RANGE)&&(r=c.DEFAULT):i?t=f.INVALID_INCOMPLETE_TOKEN:l?t=f.GROUP_NAME:u?t=f.ENCLOSED_TOKEN:p?t=f.INTERVAL_QUANTIFIER:(s===">"&&t===f.GROUP_NAME||s==="}"&&(t===f.ENCLOSED_TOKEN||t===f.INTERVAL_QUANTIFIER)||t===f.INVALID_INCOMPLETE_TOKEN)&&(t=f.DEFAULT)}return{regexContext:t,charClassContext:r,charClassDepth:n,lastPos:e.length}}function F(e){let t=/(?<capture>\((?:(?!\?)|\?<[^>]+>))|\\?./gsu;return Array.from(e.matchAll(t)).filter(r=>r.groups.capture).length}function y(e,t){return e.replace(/\\([1-9]\d*)|\\?./gsu,(r,n)=>n?"\\"+(Number(n)+t):r)}var ne=["Basic_Emoji","Emoji_Keycap_Sequence","RGI_Emoji_Modifier_Sequence","RGI_Emoji_Flag_Sequence","RGI_Emoji_Tag_Sequence","RGI_Emoji_ZWJ_Sequence","RGI_Emoji"].join("|");function $(e){let t=new RegExp(String.raw` | ||
\\ (?: | ||
c [A-Za-z] | ||
| p \{ (?<pPropOfStr> ${Y} ) \} | ||
| p \{ (?<pPropOfStr> ${ne} ) \} | ||
| [pP] \{ [^\}]+ \} | ||
@@ -39,5 +39,5 @@ | (?<qPropOfStr> q ) | ||
| . | ||
`.replace(/\s+/g,""),"gsu"),r=!1,n;for(let{0:o,groups:u}of e.matchAll(t)){if(u.pPropOfStr||u.qPropOfStr||o==="["&&r)return!0;if(["-","--","&&"].includes(o))r=!1;else if(!["[","]"].includes(o)){if(r||n==="]")return!0;r=!0}n=o}return!1}function D(e,t,r){let n={raw:[]},o=[],u={};return e.raw.forEach((i,c)=>{let l=r(i,{...u,lastPos:0});if(n.raw.push(l.transformed),u=l.runningContext,c<e.raw.length-1){let f=t[c];if(f instanceof N){let a=r(f,{...u,lastPos:0});o.push(_(a.transformed)),u=a.runningContext}else o.push(f)}}),{template:n,values:o}}function M(e){if(!k(e,String.raw`\(\?>`))return e;let t=new RegExp(String.raw`(?<noncapturingStart>\(\?(?:[:=!>]|<[=!]|[ims\-]+:))|(?<capturingStart>\((?:\?<[^>]+>)?)|(?<backrefNum>\\[1-9]\d*)|\\?.`,"gsu"),r=3,n,o=0,u=0,i=NaN;do{n=!1;let c=0,l=0,f=!1,a;for(t.lastIndex=Number.isNaN(i)?0:i+r;a=t.exec(e);){let{0:p,index:d,groups:{backrefNum:I,capturingStart:S,noncapturingStart:w}}=a;if(p==="[")c++;else if(c)p==="]"&&c--;else if(p==="(?>"&&!f)i=d,f=!0;else if(f&&w)l++;else if(S)f&&l++,o++;else if(p===")"&&f){if(!l){u++,e=`${e.slice(0,i)}(?:(?=(${e.slice(i+r,d)}))\\k<${u+o}>)${e.slice(d+1)}`,n=!0,o--;break}l--}else if(I)throw new Error(`Invalid decimal escape "${p}" in interpolated regex; cannot be used with atomic group`)}}while(n);return e=A(e,String.raw`\\k<(?<backrefNum>\d+)>`,({groups:{backrefNum:c}})=>`\\${c}`,s.DEFAULT),e}function y(e,t){e=String(e);let r="",n="";for(let[o]of e.matchAll(m)){r+=o,t=T(r,t);let{regexContext:u}=t;if(u===s.DEFAULT)if(o==="(")n+="(?:";else{if(/^\\[1-9]/.test(o))throw new Error(`Invalid decimal escape "${o}" with implicit flag n; replace with named backreference`);n+=o}else n+=o}return{transformed:n,runningContext:t}}var j=/^\s$/,ee=/^\\[\s#]$/,Q=/^[ \t]$/,te=/^\\[ \t]$/;function H(e,t){e=String(e);let r=!1,n=!1,o=!1,u="",i="",c="",l=!1,f=(a,{prefix:p=!0,postfix:d=!1}={})=>(a=(l&&p?"(?:)":"")+a+(d?"(?:)":""),l=!1,a);for(let[a]of e.matchAll(m)){if(o){a===` | ||
`&&(o=!1,l=!0);continue}if(r){if(j.test(a))continue;r=!1,l=!0}else if(n){if(Q.test(a))continue;n=!1}u+=a,t=T(u,t);let{regexContext:p,charClassContext:d}=t;if(a==="-"&&p===s.CHAR_CLASS&&c===E.RANGE)throw new Error("Invalid unescaped hyphen as the end value for a range");if(p===s.DEFAULT&&/^[?*+]\??$/.test(a)||p===s.INTERVAL_QUANTIFIER&&a==="{")i+=f(a,{prefix:!1,postfix:a==="?"});else if(p===s.DEFAULT)j.test(a)?r=!0:a.startsWith("#")?o=!0:ee.test(a)?i+=f(a[1],{prefix:!1}):i+=f(a);else if(p===s.CHAR_CLASS&&a!=="["&&a!=="[^")if(Q.test(a)&&(d===E.DEFAULT||d===E.RANGE||d===E.Q_TOKEN))n=!0;else{if(d===E.INVALID_INCOMPLETE_TOKEN)throw new Error(`Invalid incomplete token in character class: "${a}"`);te.test(a)&&(d===E.DEFAULT||d===E.Q_TOKEN)?i+=f(a[1],{prefix:!1}):d===E.DEFAULT?i+=f(O(L(a))):i+=f(a)}else i+=f(a);r||n||o||(c=d)}return{transformed:i,runningContext:t}}function q(e){let t=String.raw`\(\?:\)`;return e=A(e,`${t}(?:${t})+`,"(?:)",s.DEFAULT),e=A(e,String.raw`^${t}(?![?*+{])|${t}$|${t}(?=[()|$\\])|(?<=[()|>^]|\(\?(?:[:=!]|<[=!]))${t}`,"",s.DEFAULT),e}function P(e,...t){let r=this instanceof Function?this:RegExp;if(Array.isArray(e?.raw))return F(r,{flags:""},e,...t);if((typeof e=="string"||e===void 0)&&!t.length)return F.bind(null,r,{flags:e});if({}.toString.call(e)==="[object Object]"&&!t.length)return F.bind(null,r,e);throw new Error(`Unexpected arguments: ${JSON.stringify([e,...t])}`)}function F(e,t,r,...n){let{flags:o="",__flagN:u=!0,__flagX:i=!0,__rake:c=t.__flagX??!0}=t;if(/[vu]/.test(o))throw new Error("Flags v/u cannot be explicitly added since v is always enabled");i&&({template:r,values:n}=D(r,n,H)),u&&({template:r,values:n}=D(r,n,y));let l=0,f="",a={};return r.raw.forEach((p,d)=>{let I=r.raw[d]||r.raw[d+1];l+=U(p),f+=L(p,s.CHAR_CLASS),a=T(f,a);let{regexContext:S,charClassContext:w}=a;if(d<r.raw.length-1){let b=re(n[d],o,S,w,I,l);l+=b.capturesAdded||0,f+=b.value}}),f=M(f),new e(c?q(f):f,`v${o}`)}function re(e,t,r,n,o,u){if(e instanceof RegExp&&r!==s.DEFAULT)throw new Error("Cannot interpolate a RegExp at this position because the syntax context does not match");if(r===s.INVALID_INCOMPLETE_TOKEN||n===E.INVALID_INCOMPLETE_TOKEN)throw new Error("Interpolation preceded by invalid incomplete token");let i=e instanceof N,c;if(!(e instanceof RegExp)){e=String(e),i||(c=C(e,r));let l=G(c||e,r,n);if(l)throw new Error(`Unescaped stray "${l}" in the interpolated value would have side effects outside it`)}if(r===s.ENCLOSED_TOKEN||r===s.INTERVAL_QUANTIFIER||r===s.GROUP_NAME||n===E.ENCLOSED_TOKEN||n===E.Q_TOKEN)return{value:i?e:c};if(r===s.CHAR_CLASS){if(i){if(A(e,"^-|^&&|-$|&&$","")!==e)throw new Error("In character classes, a partial cannot use a range/set operator at its boundary; move the operation into the partial or the operator outside of it");let f=x(O(e));return{value:$(e)?`[${f}]`:L(f)}}return{value:$(c)?`[${c}]`:c}}if(e instanceof RegExp){let l=ne(e,t),f=V(l.value,u);return{value:l.usedModifier?f:`(?:${f})`,capturesAdded:U(e.source)}}return i?{value:`(?:${e})`}:{value:o?`(?:${c})`:c}}function ne(e,t){let r={i:null,m:null,s:null},n="\\n\\r\\u2028\\u2029",o=e.source;if(e.ignoreCase!==t.includes("i"))if(g)r.i=e.ignoreCase;else throw new Error("Pattern modifiers not supported, so the value of flag i on the interpolated RegExp must match the outer regex");if(e.dotAll!==t.includes("s")&&(g?r.s=e.dotAll:o=A(o,"\\.",e.dotAll?"[^]":`[^${n}]`,s.DEFAULT)),e.multiline!==t.includes("m")&&(g?r.m=e.multiline:(o=A(o,"\\^",e.multiline?`(?<=^|[${n}])`:"(?<![^])",s.DEFAULT),o=A(o,"\\$",e.multiline?`(?=$|[${n}])`:"(?![^])",s.DEFAULT))),g){let u=Object.keys(r),i=u.filter(l=>r[l]===!0).join(""),c=u.filter(l=>r[l]===!1).join("");if(c&&(i+=`-${c}`),i)return{value:`(?${i}:${o})`,usedModifier:!0}}return{value:o}}var W=P;var oe={make:W,partial:_,regex:P};return J(se);})(); | ||
//! regex 1.1.0; Steven Levithan; MIT License | ||
`.replace(/\s+/g,""),"gsu"),r=!1,n;for(let{0:o,groups:a}of e.matchAll(t)){if(a.pPropOfStr||a.qPropOfStr||o==="["&&r)return!0;if(["-","--","&&"].includes(o))r=!1;else if(!["[","]"].includes(o)){if(r||n==="]")return!0;r=!0}n=o}return!1}function D(e,t,r){let n={raw:[]},o=[],a={};return e.raw.forEach((s,l)=>{let u=r(s,{...a,lastPos:0});if(n.raw.push(u.transformed),a=u.runningContext,l<e.raw.length-1){let E=t[l];if(E instanceof T){let p=r(E,{...a,lastPos:0});o.push(O(p.transformed)),a=p.runningContext}else o.push(E)}}),{template:n,values:o}}function j(e,t){e=String(e);let r="",n="";for(let[o]of e.matchAll(g)){r+=o,t=L(r,t);let{regexContext:a}=t;if(a===f.DEFAULT)if(o==="(")n+="(?:";else{if(/^\\[1-9]/.test(o))throw new Error(`Invalid decimal escape "${o}" with implicit flag n; replace with named backreference`);n+=o}else n+=o}return{transformed:n,runningContext:t}}var Q=/^\s$/,oe=/^\\[\s#]$/,H=/^[ \t]$/,se=/^\\[ \t]$/;function q(e,t){e=String(e);let r=!1,n=!1,o=!1,a="",s="",l="",u="",E=!1,p=(i,{prefix:N=!0,postfix:d=!1}={})=>(i=(E&&N?"(?:)":"")+i+(d?"(?:)":""),E=!1,i);for(let[i]of e.matchAll(g)){if(o){i===` | ||
`&&(o=!1,E=!0);continue}if(r){if(Q.test(i))continue;r=!1,E=!0}else if(n){if(H.test(i))continue;n=!1}a+=i,t=L(a,t);let{regexContext:N,charClassContext:d}=t;if(i==="-"&&N===f.CHAR_CLASS&&u===c.RANGE)throw new Error("Invalid unescaped hyphen as the end value for a range");if(N===f.DEFAULT&&/^[?*+]\??$/.test(i)||N===f.INTERVAL_QUANTIFIER&&i==="{")s+=p(i,{prefix:!1,postfix:l==="("});else if(N===f.DEFAULT)Q.test(i)?r=!0:i.startsWith("#")?o=!0:oe.test(i)?s+=p(i[1],{prefix:!1}):s+=p(i);else if(N===f.CHAR_CLASS&&i!=="["&&i!=="[^")if(H.test(i)&&(d===c.DEFAULT||d===c.RANGE||d===c.Q_TOKEN))n=!0;else{if(d===c.INVALID_INCOMPLETE_TOKEN)throw new Error(`Invalid incomplete token in character class: "${i}"`);se.test(i)&&(d===c.DEFAULT||d===c.Q_TOKEN)?s+=p(i[1],{prefix:!1}):d===c.DEFAULT?s+=p(I(w(i))):s+=p(i)}else s+=p(i);r||n||o||(l=i,u=d)}return{transformed:s,runningContext:t}}function W(e){let t=String.raw`\(\?:\)`;return e=m(e,`${t}(?:${t})+`,"(?:)",A.DEFAULT),e=m(e,String.raw`^${t}(?![?*+{])|${t}$|${t}(?=[()|$\\])|(?<=[()|>^]|\(\?(?:[:=!]|<[=!]))${t}`,"",A.DEFAULT),e}var b=function(e,...t){let r=this instanceof Function?this:RegExp;if(Array.isArray(e?.raw))return P(r,{flags:""},e,...t);if((typeof e=="string"||e===void 0)&&!t.length)return P.bind(null,r,{flags:e});if({}.toString.call(e)==="[object Object]"&&!t.length)return P.bind(null,r,e);throw new Error(`Unexpected arguments: ${JSON.stringify([e,...t])}`)};function P(e,t,r,...n){let{flags:o="",postprocessors:a=[],__flagN:s=!0,__flagX:l=!0,__rake:u=!0}=t;if(/[vu]/.test(o))throw new Error("Flags v/u cannot be explicitly added since v is always enabled");l&&({template:r,values:n}=D(r,n,q)),s&&({template:r,values:n}=D(r,n,j));let E=0,p="",i={};r.raw.forEach((d,_)=>{let h=r.raw[_]||r.raw[_+1];E+=F(d),p+=w(d,A.CHAR_CLASS),i=L(p,i);let{regexContext:v,charClassContext:z}=i;if(_<r.raw.length-1){let x=ie(n[_],o,v,z,h,E);E+=x.capturesAdded||0,p+=x.value}});let N=[k,...a];u&&N.push(W);for(let d of N)p=d(p);return new e(p,`v${o}`)}function ie(e,t,r,n,o,a){if(e instanceof RegExp&&r!==f.DEFAULT)throw new Error("Cannot interpolate a RegExp at this position because the syntax context does not match");if(r===f.INVALID_INCOMPLETE_TOKEN||n===c.INVALID_INCOMPLETE_TOKEN)throw new Error("Interpolation preceded by invalid incomplete token");let s=e instanceof T,l;if(!(e instanceof RegExp)){e=String(e),s||(l=C(e,r));let u=M(l||e,r,n);if(u)throw new Error(`Unescaped stray "${u}" in the interpolated value would have side effects outside it`)}if(r===f.ENCLOSED_TOKEN||r===f.INTERVAL_QUANTIFIER||r===f.GROUP_NAME||n===c.ENCLOSED_TOKEN||n===c.Q_TOKEN)return{value:s?e:l};if(r===f.CHAR_CLASS){if(s){if(m(e,"^-|^&&|-$|&&$","")!==e)throw new Error("In character classes, a partial cannot use a range/set operator at its boundary; move the operation into the partial or the operator outside of it");let E=V(I(e));return{value:$(e)?`[${E}]`:w(E)}}return{value:$(l)?`[${l}]`:l}}if(e instanceof RegExp){let u=ae(e,t),E=y(u.value,a);return{value:u.usedModifier?E:`(?:${E})`,capturesAdded:F(e.source)}}return s?{value:`(?:${e})`}:{value:o?`(?:${l})`:l}}function ae(e,t){let r={i:null,m:null,s:null},n="\\n\\r\\u2028\\u2029",o=e.source;if(e.ignoreCase!==t.includes("i"))if(S)r.i=e.ignoreCase;else throw new Error("Pattern modifiers not supported, so the value of flag i on the interpolated RegExp must match the outer regex");if(e.dotAll!==t.includes("s")&&(S?r.s=e.dotAll:o=m(o,"\\.",e.dotAll?"[^]":`[^${n}]`,A.DEFAULT)),e.multiline!==t.includes("m")&&(S?r.m=e.multiline:(o=m(o,"\\^",e.multiline?`(?<=^|[${n}])`:"(?<![^])",A.DEFAULT),o=m(o,"\\$",e.multiline?`(?=$|[${n}])`:"(?![^])",A.DEFAULT))),S){let a=Object.keys(r),s=a.filter(u=>r[u]===!0).join(""),l=a.filter(u=>r[u]===!1).join("");if(l&&(s+=`-${l}`),s)return{value:`(?${s}:${o})`,usedModifier:!0}}return{value:o}}var Z=b;var fe={make:Z,partial:O,regex:b};return te(le);})(); | ||
//! regex 1.1.1; Steven Levithan; MIT License | ||
//# sourceMappingURL=regex.min.js.map |
{ | ||
"name": "regex", | ||
"version": "1.1.0", | ||
"description": "Context-aware regex template strings with batteries included", | ||
"version": "1.1.1", | ||
"description": "Context-aware regex template tag with best practices built-in and advanced features", | ||
"exports": "./src/index.js", | ||
@@ -17,9 +17,11 @@ "type": "module", | ||
"type": "git", | ||
"url": "git+https://github.com/slevithan/regex-make.git" | ||
"url": "git+https://github.com/slevithan/regex.git" | ||
}, | ||
"keywords": [ | ||
"regex", | ||
"regexp", | ||
"template" | ||
"regexp" | ||
], | ||
"dependencies": { | ||
"regex-utilities": "^1.0.0" | ||
}, | ||
"devDependencies": { | ||
@@ -26,0 +28,0 @@ "esbuild": "^0.21.4", |
@@ -6,3 +6,3 @@ # `regex` | ||
`regex` is a template tag for dynamically creating readable, high performance, native JavaScript regular expressions with advanced features. It's lightweight (5.5KB), has no dependencies, and supports all ES2024+ regex features. | ||
`regex` is a template tag for dynamically creating readable, high performance, native JavaScript regular expressions with advanced features. It's lightweight (5.6KB) and supports all ES2024+ regex features. | ||
@@ -98,3 +98,3 @@ Highlights include using whitespace and comments in regexes, atomic groups via `(?>…)` which can help you avoid [ReDoS](https://en.wikipedia.org/wiki/ReDoS), and context-aware interpolation of `RegExp` instances, escaped strings, and partial patterns. | ||
Additionally, JavaScript regex syntax is hard to write and even harder to read and refactor. But it doesn't have to be that way! With a few key features — raw template strings, insignificant whitespace, comments, *named capture only* mode, and interpolation (coming soon: definition blocks and subexpressions as subroutines) — even long and complex regexes can be beautiful, grammatical, and easy to understand. | ||
Additionally, JavaScript regex syntax is hard to write and even harder to read and refactor. But it doesn't have to be that way! With a few key features — raw multiline template strings, insignificant whitespace, comments, *named capture only* mode, and interpolation (coming soon: definition blocks and subexpressions as subroutines) — even long and complex regexes can be beautiful, grammatical, and easy to understand. | ||
@@ -205,6 +205,14 @@ `regex` adds all of these features and returns native `RegExp` instances. It always uses flag <kbd>v</kbd> (already a best practice for new regexes) so you never forget to turn it on and don't have to worry about the differences in other parsing modes. It supports atomic groups via `(?>…)` to help you improve the performance of your regexes and avoid catastrophic backtracking. And it gives you best-in-class, context-aware interpolation of `RegExp` instances, escaped strings, and partial patterns. | ||
Requiring the syntactically clumsy `(?:…)` where you could just use `(…)` hurts readability and encourages adding unneeded captures (which hurt efficiency and refactoring). Flag <kbd>n</kbd> fixes this, making your regexes more readable. | ||
Motivation: Requiring the syntactically clumsy `(?:…)` where you could just use `(…)` hurts readability and encourages adding unneeded captures (which hurt efficiency and refactoring). Flag <kbd>n</kbd> fixes this, making your regexes more readable. | ||
Example: | ||
```js | ||
// Doesn't capture | ||
regex`\b(ab|cd)\b` | ||
// Use standard (?<name>…) to capture as `name` | ||
``` | ||
> [!NOTE] | ||
> Flag <kbd>n</kbd> is based on .NET, C++, PCRE, Perl, and XRegExp, which share the `n` flag letter but call it *explicit capture*, *no auto capture*, or *nosubs*. In `regex`, the implicit flag <kbd>n</kbd> also prevents using numbered backreferences to named groups in the outer regex, which follows the behavior of C++. Referring to named groups by number is a footgun, and the way that named groups are numbered is inconsistent across regex flavors. | ||
> Flag <kbd>n</kbd> is based on .NET, C++, PCRE, Perl, and XRegExp, which share the <kbd>n</kbd> flag letter but call it *explicit capture*, *no auto capture*, or *nosubs*. In `regex`, the implicit flag <kbd>n</kbd> also prevents using numbered backreferences to named groups in the outer regex, which follows the behavior of C++. Referring to named groups by number is a footgun, and the way that named groups are numbered is inconsistent across regex flavors. | ||
@@ -234,3 +242,3 @@ > Aside: Flag <kbd>n</kbd>'s behavior also enables `regex` to emulate atomic groups and recursion. | ||
- Regexes can't be interpolated in the middle of a character class (so `` regex`[${/./}]` `` is an error) because the syntax context doesn't match. See [*Interpolating partial patterns*](#interpolating-partial-patterns) for a way to safely embed regex syntax (rather than `RegExp` instances) in character classes and other edge-case locations with different context. | ||
- Regexes can't be interpolated inside character classes (so `` regex`[${/./}]` `` is an error) because the syntax context doesn't match. See [*Interpolating partial patterns*](#interpolating-partial-patterns) for a way to safely embed regex syntax (rather than `RegExp` instances) in character classes and other edge-case locations with different context. | ||
- To change the flags used by an interpolated regex, use the built-in capability of `RegExp` to copy a regex while providing new flags. Ex: `new RegExp(/./, 's')`. | ||
@@ -243,3 +251,3 @@ </details> | ||
> As with all interpolation in `regex`, escaped strings are sandboxed and treated as complete units. For example, a following quantifier repeats the whole unit rather than just the last character. And if interpolating into a character class, the escaped string is treated as a flag-<kbd>v</kbd>-mode nested union if it contains more than one character node. | ||
> As with all interpolation in `regex`, escaped strings are sandboxed and treated as complete units. For example, a following quantifier repeats the entire escaped string rather than just its last character. And if interpolating into a character class, the escaped string is treated as a flag-<kbd>v</kbd>-mode nested union if it contains more than one character node. | ||
@@ -260,3 +268,3 @@ As a result, `regex` is a safe and context-aware alternative to JavaScript proposal [`RegExp.escape`](https://github.com/tc39/proposal-regex-escaping). | ||
// Instead of | ||
new RegExp(`[a-${RegExp.escape(str)}]`, 'u') // Flag u/v required to avoid bug | ||
new RegExp(`[a-${RegExp.escape(str)}]`, 'u') // Flag u/v required to avoid bugs | ||
// You can say | ||
@@ -288,3 +296,3 @@ regex`[a-${str}]` | ||
- Composing a dynamic number of strings. | ||
- Adding a pattern in the middle of a character class (not allowed for `RegExp` instances since their top-level syntax context doesn't match). | ||
- Adding a pattern inside a character class (not allowed for `RegExp` instances since their top-level syntax context doesn't match). | ||
- Dynamically adding backreferences without their corresponding captures (which wouldn't be valid as a standalone `RegExp`). | ||
@@ -317,3 +325,3 @@ - When you don't want the pattern to specify its own, local flags. | ||
Moving on, the following lines all throw because otherwise the partial patterns would break out of their interpolation sandboxes and change the meaning of the surrounding patterns: | ||
Moving on, the following lines all throw because otherwise the partial patterns would break out of their interpolation sandboxes and change the meaning of their surrounding patterns: | ||
@@ -358,3 +366,3 @@ ```js | ||
1. This is an uncompleted `\u` token (which is an error) followed by the tokens `0`, `0`, `0`, `A`. That's because the interpolation does not happen within an enclosed `\u{…}` context. | ||
1. This is an uncompleted `\u` token (which is an error) followed by the tokens `0`, `0`, `0`, `A`. That's because the interpolation doesn't happen within an enclosed `\u{…}` context. | ||
2. The unescaped `}` within the partial is not allowed to break out of its interpolation sandbox. | ||
@@ -369,3 +377,3 @@ 3. The group opening `(` can't be quantified with `?`. | ||
// This works fine | ||
regex`[\0-${partial('\\cZ')}]` | ||
regex`[\0-${partial`\cZ`}]` | ||
@@ -430,6 +438,6 @@ // But this is an error since you can't create a range from 'a' to the set 'de' | ||
<td>• Sandboxed <br> • Atomized <br><br><br></td> | ||
<td>• Sandboxed <br> • Atomized <br> • Backrefs adjusted <br> • Own flags apply locally</td> | ||
<td>• Sandboxed <br> • Atomized <br> • Backrefs adjusted <br> • Flags localized</td> | ||
</tr> | ||
<tr> | ||
<td>Character class: <code>[…]</code>, <code>[^…]</code>, <code>[…[…]]</code>, etc.</td> | ||
<td>Character class: <code>[…]</code>, <code>[^…]</code>, <code>[[…]]</code>, etc.</td> | ||
<td><code>regex`[${'a-z'}]`</code><br><br></td> | ||
@@ -466,3 +474,3 @@ <td>• Sandboxed <br> • Atomized <br> • Escaped</td> | ||
- `regex` relies on flag <kbd>v</kbd> (`unicodeSets`), which has had universal browser support since ~mid-2023 and is available in Node.js 20+. | ||
- `regex` relies on flag <kbd>v</kbd> (`unicodeSets`), which has had universal browser support since ~mid-2023 (see [compat table](https://caniuse.com/mdn-javascript_builtins_regexp_unicodesets)) and is available in Node.js 20+. But it's possible to extend support to older browsers (see [#2](https://github.com/slevithan/regex/issues/2)). | ||
- Using an interpolated `RegExp` instance with a different value for flag <kbd>i</kbd> than its outer regex relies on [regex modifiers](https://github.com/tc39/proposal-regexp-modifiers), a bleeding-edge feature available in Chrome and Edge 125+. A descriptive error is thrown in environments without support, which you can avoid by aligning the use of flag <kbd>i</kbd> on inner and outer regexes. Local-only application of other flags does not rely on this feature. | ||
@@ -475,5 +483,5 @@ - If you want `regex` to use a `RegExp` subclass or other constructor, you can do so by modifying `this`: `` regex.bind(RegExpSubclass)`…` ``. | ||
Version 1.0.0 was named Regex.make and used the tag name `make` instead of `regex`; hence the repo name. | ||
Version 1.0.0 was named Regex.make and used tag name `make` instead of `regex`. `make` is still available as an alias. | ||
Crafted with ❤︎ (for developers and regular expressions) by Steven Levithan.<br> | ||
Crafted by Steven Levithan with ❤︎ for regular expressions and their enthusiasts.<br> | ||
MIT License. |
@@ -5,3 +5,3 @@ describe('flag x', () => { | ||
const ws = '\t\n\v\f\r \xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF'; | ||
expect('ab').toMatch(regex({raw: [`^a${ws}b$`]}, [])); | ||
expect('ab').toMatch(regex({raw: [`^a${ws}b$`]})); | ||
}); | ||
@@ -16,3 +16,3 @@ | ||
newlines.forEach(n => { | ||
expect('ab').not.toMatch(regex({raw: [`^a#comment${n}b\n$`]}, [])); | ||
expect('ab').not.toMatch(regex({raw: [`^a#comment${n}b\n$`]})); | ||
}); | ||
@@ -26,3 +26,3 @@ }); | ||
it('should allow mixing whitespace and line comments', function() { | ||
expect('ab').toMatch(regex({raw: ['\f^ a \t\n ##comment\n #\nb $ # ignored']}, [])); | ||
expect('ab').toMatch(regex({raw: ['\f^ a \t\n ##comment\n #\nb $ # ignored']})); | ||
}); | ||
@@ -32,12 +32,36 @@ | ||
expect('aaa').toMatch(regex`^a +$`); | ||
expect('aaa').toMatch(regex({raw: ['^a#comment\n+$']}, [])); | ||
expect('aaa').toMatch(regex({raw: ['^a #comment\n +$']}, [])); | ||
expect('aaa').toMatch(regex`^(a) +$`); | ||
expect('aaa').toMatch(regex({raw: ['^a#comment\n+$']})); | ||
expect('aaa').toMatch(regex({raw: ['^a #comment\n +$']})); | ||
expect(() => regex`a | ?`).toThrow(); | ||
}); | ||
it('should not let the token following whitespace or line comments modify the preceding token', () => { | ||
expect('\u{0}0').toMatch(regex`\0 0`); | ||
expect('\u{0}1').toMatch(regex`\0 1`); | ||
expect('\u{0}1').toMatch(regex({raw: ['\0#\n1']}, [])); | ||
it('should not allow quantifiers to follow other quantifiers', function() { | ||
expect(() => regex`a?? ?`).toThrow(); | ||
expect(() => regex`a*? ?`).toThrow(); | ||
expect(() => regex`a+? ?`).toThrow(); | ||
expect(() => regex`a{2}? ?`).toThrow(); | ||
expect(() => regex`a* *`).toThrow(); | ||
expect(() => regex`a+ +`).toThrow(); | ||
expect(() => regex`a{2} {2}`).toThrow(); | ||
}); | ||
// Follows Perl, PCRE, .NET, Java | ||
// Not allowed in Python | ||
it('should allow whitespace between a quantifier and the ? that makes it lazy', function() { | ||
expect(regex`^aa? ?`.exec('aaa')[0]).toBe('a'); | ||
expect(regex`^aa* ?`.exec('aaa')[0]).toBe('a'); | ||
expect(regex`^aa+ ?`.exec('aaa')[0]).toBe('aa'); | ||
expect(regex`^aa{1,2} ?`.exec('aaa')[0]).toBe('aa'); | ||
}); | ||
it('should not let the token following whitespace modify the preceding token', () => { | ||
expect('\u{0}0').toMatch(regex`^\0 0$`); | ||
expect('\u{0}1').toMatch(regex`^\0 1$`); | ||
}); | ||
it('should not let the token following a line comment modify the preceding token', () => { | ||
expect('\u{0}1').toMatch(regex({raw: ['^\0#\n1$']})); | ||
}); | ||
it('should preserve the error status of incomplete tokens separated from their completing chars by whitespace', () => { | ||
@@ -52,11 +76,20 @@ const values = [ | ||
'\\x0 0', | ||
'(? :)', | ||
]; | ||
values.forEach(v => { | ||
expect(() => regex({raw: [v]}, [])).withContext(v).toThrow(); | ||
expect(() => regex({raw: [v]})).withContext(v).toThrow(); | ||
}); | ||
}); | ||
it('should not allow quantifying ( with ? when separated by whitespace and followed by group type char', () => { | ||
expect(() => regex`( ?:)`).toThrow(); | ||
expect(() => regex`( ?=)`).toThrow(); | ||
expect(() => regex`( ? : )`).toThrow(); | ||
expect(() => regex`( ? = )`).toThrow(); | ||
}); | ||
it('should allow escaping whitespace to make it significant', () => { | ||
expect(' ').toMatch(regex`\ `); | ||
expect(' ').toMatch(regex` \ \ `); | ||
expect(' ').toMatch(regex`^\ $`); | ||
expect(' ').toMatch(regex`^ \ \ $`); | ||
expect(' t').toMatch(regex`^\ t$`); | ||
}); | ||
@@ -68,2 +101,11 @@ | ||
}); | ||
it('should treat whitespace in enclosed tokens as significant', () => { | ||
expect(() => regex`a{ 6 }`).toThrow(); | ||
expect(() => regex`\p{ L }`).toThrow(); | ||
expect(() => regex`\P{ L }`).toThrow(); | ||
expect(() => regex`\u{ 0 }`).toThrow(); | ||
expect(() => regex`(?< n >)`).toThrow(); | ||
expect(() => regex`(?<n>)\k< n >`).toThrow(); | ||
}); | ||
}); | ||
@@ -74,9 +116,9 @@ | ||
expect(' ').not.toMatch(regex`[ a]`); | ||
expect('\t').not.toMatch(regex({raw: ['[\ta]']}, [])); | ||
expect('\t').not.toMatch(regex({raw: ['[\ta]']})); | ||
}); | ||
it('should not treat whitespace characters apart from space and tab as insignificant', () => { | ||
expect('\n').toMatch(regex({raw: ['[\na]']}, [])); | ||
expect('\xA0').toMatch(regex({raw: ['[\xA0a]']}, [])); | ||
expect('\u2028').toMatch(regex({raw: ['[\u2028a]']}, [])); | ||
expect('\n').toMatch(regex({raw: ['[\na]']})); | ||
expect('\xA0').toMatch(regex({raw: ['[\xA0a]']})); | ||
expect('\u2028').toMatch(regex({raw: ['[\u2028a]']})); | ||
}); | ||
@@ -96,4 +138,4 @@ | ||
it('should not let the token following whitespace modify the preceding token', () => { | ||
expect('0').toMatch(regex`[\0 0]`); | ||
expect('1').toMatch(regex`[\0 1]`); | ||
expect('0').toMatch(regex`^[\0 0]$`); | ||
expect('1').toMatch(regex`^[\0 1]$`); | ||
}); | ||
@@ -112,3 +154,3 @@ | ||
values.forEach(v => { | ||
expect(() => regex({raw: [v]}, [])).withContext(v).toThrow(); | ||
expect(() => regex({raw: [v]})).withContext(v).toThrow(); | ||
}); | ||
@@ -133,3 +175,3 @@ }); | ||
values.forEach(v => { | ||
expect(() => regex({raw: [v]}, [])).withContext(v).toThrow(); | ||
expect(() => regex({raw: [v]})).withContext(v).toThrow(); | ||
}); | ||
@@ -170,6 +212,6 @@ }); | ||
doublePunctuatorChars.forEach(c => { | ||
expect(c).withContext(`[a${c} ${c}b]`).toMatch(regex({raw: [`[a${c} ${c}b]`]}, [])); | ||
expect(c).withContext(`[a${c} ${c} b]`).toMatch(regex({raw: [`[a${c} ${c} b]`]}, [])); | ||
expect(c).withContext(`[a ${c} ${c}b]`).toMatch(regex({raw: [`[a ${c} ${c}b]`]}, [])); | ||
expect(c).withContext(`[a ${c} ${c} b]`).toMatch(regex({raw: [`[a ${c} ${c} b]`]}, [])); | ||
expect(c).withContext(`[a${c} ${c}b]`).toMatch(regex({raw: [`[a${c} ${c}b]`]})); | ||
expect(c).withContext(`[a${c} ${c} b]`).toMatch(regex({raw: [`[a${c} ${c} b]`]})); | ||
expect(c).withContext(`[a ${c} ${c}b]`).toMatch(regex({raw: [`[a ${c} ${c}b]`]})); | ||
expect(c).withContext(`[a ${c} ${c} b]`).toMatch(regex({raw: [`[a ${c} ${c} b]`]})); | ||
}); | ||
@@ -179,5 +221,22 @@ }); | ||
it('should allow escaping whitespace to make it significant', () => { | ||
expect(' ').toMatch(regex`[ \ ]`); | ||
expect(' ').toMatch(regex`[\q{ \ }]`); | ||
expect(' ').toMatch(regex`^[ \ ]$`); | ||
expect(' ').toMatch(regex`^[\q{ \ }]$`); | ||
expect('t ').toMatch(regex`^[\ t]{2}$`); | ||
}); | ||
it('should treat whitespace in enclosed tokens as significant', () => { | ||
expect(() => regex`[\p{ L }]`).toThrow(); | ||
expect(() => regex`[\P{ L }]`).toThrow(); | ||
expect(() => regex`[\u{ 0 }]`).toThrow(); | ||
}); | ||
it('should treat whitespace in [\\q{}] as insignificant', () => { | ||
expect('ab').toMatch(regex`^[\q{ a b | c }]$`); | ||
}); | ||
it('should handle empty character classes with insignificant whitespace', () => { | ||
expect(/[]/v.test('a')).toBe(regex`[ ]`.test('a')); | ||
expect(/[^]/v.test('a')).toBe(regex`[^ ]`.test('a')); | ||
expect(/[\q{}]/v.test('a')).toBe(regex`[ \q{ } ]`.test('a')); | ||
}); | ||
}); | ||
@@ -184,0 +243,0 @@ |
@@ -50,2 +50,7 @@ describe('interpolation: partial patterns', () => { | ||
it('should not let > end an enclosed token', () => { | ||
expect(() => regex`(?<n>)\k<${partial`n>`}`).toThrow(); | ||
expect(() => regex`(?<${partial`n>`}>)`).toThrow(); | ||
}); | ||
it('should not let a preceding unescaped \\ change the first character inside the interpolation', () => { | ||
@@ -52,0 +57,0 @@ // Raw string syntax prevents `\${'w'}` since the raw \ escapes the $ |
@@ -18,2 +18,7 @@ describe('interpolation: escaped strings', () => { | ||
it('should not let > end an enclosed token', () => { | ||
expect(() => regex`(?<n>)\k<${'n>'}`).toThrow(); | ||
expect(() => regex`(?<${'n>'}>)`).toThrow(); | ||
}); | ||
it('should not let a preceding unescaped \\ change the first character inside the interpolation', () => { | ||
@@ -20,0 +25,0 @@ // Raw string syntax prevents `\${'w'}` since the raw \ escapes the $ |
describe('partial', () => { | ||
it('should coerce non-string values', () => { | ||
expect(String(partial('1'))).toBe('1'); | ||
expect(String(partial(1))).toBe('1'); | ||
expect(String(partial(NaN))).toBe('NaN'); | ||
}); | ||
describe('strings', () => { | ||
@@ -7,8 +13,2 @@ it('should accept empty arguments', () => { | ||
}); | ||
it('should coerce to string', () => { | ||
expect(String(partial('1'))).toBe('1'); | ||
expect(String(partial(1))).toBe('1'); | ||
expect(String(partial(NaN))).toBe('NaN'); | ||
}); | ||
}); | ||
@@ -15,0 +15,0 @@ |
@@ -48,8 +48,8 @@ // Ensure transformations have not changed native syntax and errors | ||
reservedDoublePunctuators.forEach(dp => { | ||
expect(() => regex({raw: [`[a${dp}b]`]}, [])).withContext(dp).toThrow(); | ||
expect(() => regex({raw: [`[a${dp}b]`]})).withContext(dp).toThrow(); | ||
}); | ||
allDoublePunctuatorsExcludingCaret.forEach(dp => { | ||
expect(() => regex({raw: [`[${dp}]`]}, [])).withContext(dp).toThrow(); | ||
expect(() => regex({raw: [`[${dp}b]`]}, [])).withContext(dp).toThrow(); | ||
expect(() => regex({raw: [`[a${dp}]`]}, [])).withContext(dp).toThrow(); | ||
expect(() => regex({raw: [`[${dp}]`]})).withContext(dp).toThrow(); | ||
expect(() => regex({raw: [`[${dp}b]`]})).withContext(dp).toThrow(); | ||
expect(() => regex({raw: [`[a${dp}]`]})).withContext(dp).toThrow(); | ||
}); | ||
@@ -56,0 +56,0 @@ expect(() => regex`[^^^]`).withContext('^^').toThrow(); |
@@ -1,8 +0,8 @@ | ||
import {RegexContext, hasUnescapedInDefaultRegexContext, replaceUnescaped} from './utils.js'; | ||
import {Context, hasUnescaped, replaceUnescaped} from 'regex-utilities'; | ||
export function transformAtomicGroups(pattern) { | ||
if (!hasUnescapedInDefaultRegexContext(pattern, String.raw`\(\?>`)) { | ||
if (!hasUnescaped(pattern, String.raw`\(\?>`, Context.DEFAULT)) { | ||
return pattern; | ||
} | ||
const token = new RegExp(String.raw`(?<noncapturingStart>\(\?(?:[:=!>]|<[=!]|[ims\-]+:))|(?<capturingStart>\((?:\?<[^>]+>)?)|(?<backrefNum>\\[1-9]\d*)|\\?.`, 'gsu'); | ||
const token = new RegExp(String.raw`(?<noncapturingStart>\(\?(?:[:=!>A-Za-z\-]|<[=!]))|(?<capturingStart>\((?:\?<[^>]+>)?)|(?<backrefNum>\\[1-9]\d*)|\\?.`, 'gsu'); | ||
const aGDelimLen = '(?>'.length; | ||
@@ -64,5 +64,5 @@ let hasProcessedAG; | ||
({groups: {backrefNum}}) => `\\${backrefNum}`, | ||
RegexContext.DEFAULT | ||
Context.DEFAULT | ||
); | ||
return pattern; | ||
} |
import {RegexContext, contextToken, getEndContextForIncompletePattern} from './utils.js'; | ||
// Applied to the outer regex and interpolated partials, but not interpolated regexes or strings | ||
export function flagNProcessor(value, runningContext) { | ||
@@ -4,0 +5,0 @@ value = String(value); |
@@ -1,2 +0,3 @@ | ||
import {CharClassContext, RegexContext, contextToken, getEndContextForIncompletePattern, replaceUnescaped, sandboxLoneDoublePunctuatorChar, sandboxUnsafeNulls} from './utils.js'; | ||
import {Context, replaceUnescaped} from 'regex-utilities'; | ||
import {CharClassContext, RegexContext, contextToken, getEndContextForIncompletePattern, sandboxLoneDoublePunctuatorChar, sandboxUnsafeNulls} from './utils.js'; | ||
@@ -8,2 +9,3 @@ const ws = /^\s$/; | ||
// Applied to the outer regex and interpolated partials, but not interpolated regexes or strings | ||
export function flagXProcessor(value, runningContext) { | ||
@@ -16,2 +18,3 @@ value = String(value); | ||
let transformed = ''; | ||
let lastSignificantToken = ''; | ||
let lastSignificantCharClassContext = ''; | ||
@@ -60,3 +63,3 @@ let divNeeded = false; | ||
// turned off | ||
transformed += update(m, {prefix: false, postfix: m === '?'}); | ||
transformed += update(m, {prefix: false, postfix: lastSignificantToken === '('}); | ||
} else if (regexContext === RegexContext.DEFAULT) { | ||
@@ -99,2 +102,3 @@ if (ws.test(m)) { | ||
if (!(ignoringWs || ignoringCharClassWs || ignoringComment)) { | ||
lastSignificantToken = m; | ||
lastSignificantCharClassContext = charClassContext; | ||
@@ -113,3 +117,3 @@ } | ||
// No need for repeated separators | ||
pattern = replaceUnescaped(pattern, `${sep}(?:${sep})+`, '(?:)', RegexContext.DEFAULT); | ||
pattern = replaceUnescaped(pattern, `${sep}(?:${sep})+`, '(?:)', Context.DEFAULT); | ||
// No need for separators at: | ||
@@ -124,5 +128,5 @@ // - The beginning, if not followed by a quantifier. | ||
'', | ||
RegexContext.DEFAULT | ||
Context.DEFAULT | ||
); | ||
return pattern; | ||
} |
@@ -1,3 +0,4 @@ | ||
//! regex 1.1.0; Steven Levithan; MIT License | ||
//! regex 1.1.1; Steven Levithan; MIT License | ||
import {Context, replaceUnescaped} from 'regex-utilities'; | ||
import {transformAtomicGroups} from './atomic-groups.js'; | ||
@@ -7,5 +8,14 @@ import {flagNProcessor} from './flag-n.js'; | ||
import {PartialPattern, partial} from './partial.js'; | ||
import {CharClassContext, RegexContext, adjustNumberedBackrefs, containsCharClassUnion, countCaptures, escapeV, getBreakoutChar, getEndContextForIncompletePattern, patternModsOn, replaceUnescaped, sandboxLoneCharClassCaret, sandboxLoneDoublePunctuatorChar, sandboxUnsafeNulls, transformTemplateAndValues} from './utils.js'; | ||
import {CharClassContext, RegexContext, adjustNumberedBackrefs, containsCharClassUnion, countCaptures, escapeV, getBreakoutChar, getEndContextForIncompletePattern, patternModsOn, sandboxLoneCharClassCaret, sandboxLoneDoublePunctuatorChar, sandboxUnsafeNulls, transformTemplateAndValues} from './utils.js'; | ||
/** | ||
@typedef {Object} RegexTagOptions | ||
@prop {string} [flags] | ||
@prop {Array<Function>} [postprocessors] | ||
@prop {boolean} [__flagN] | ||
@prop {boolean} [__flagX] | ||
@prop {boolean} [__rake] | ||
*/ | ||
/** | ||
Template tag for constructing a UnicodeSets-mode RegExp with advanced features and context-aware | ||
@@ -19,7 +29,10 @@ interpolation of regexes, escaped strings, and partial patterns. | ||
4. `` regex.bind(RegExpSubclass)`…` `` - With a `this` that specifies a different constructor. | ||
@param {string | TemplateStringsArray} first Flags or a template. | ||
@param {...any} [values] Values to fill the template holes. | ||
@returns {RegExp | (TemplateStringsArray, ...any) => RegExp} | ||
@type {{ | ||
(flags?: string) => (TemplateStringsArray, ...values) => RegExp; | ||
(options: RegexTagOptions) => (TemplateStringsArray, ...values) => RegExp; | ||
(template: TemplateStringsArray, ...values) => RegExp; | ||
}} | ||
*/ | ||
function regex(first, ...values) { | ||
const regex = function(first, ...values) { | ||
// Allow binding to other constructors | ||
@@ -38,8 +51,8 @@ const constructor = this instanceof Function ? this : RegExp; | ||
throw new Error(`Unexpected arguments: ${JSON.stringify([first, ...values])}`); | ||
} | ||
}; | ||
/** | ||
Makes a UnicodeSets-mode RegExp from a template and values to fill the template holes. | ||
@param {RegExpConstructor} constructor | ||
@param {Object} options | ||
@param {RegExpConstructor | (pattern: string, flags: string) => RegExp} constructor | ||
@param {RegexTagOptions} options | ||
@param {TemplateStringsArray} template | ||
@@ -52,5 +65,6 @@ @param {...any} values | ||
flags = '', | ||
postprocessors = [], | ||
__flagN = true, | ||
__flagX = true, | ||
__rake = options.__flagX ?? true, | ||
__rake = true, | ||
} = options; | ||
@@ -80,3 +94,3 @@ if (/[vu]/.test(flags)) { | ||
// cases a following interpolated value would always be atomized | ||
pattern += sandboxUnsafeNulls(raw, RegexContext.CHAR_CLASS); | ||
pattern += sandboxUnsafeNulls(raw, Context.CHAR_CLASS); | ||
runningContext = getEndContextForIncompletePattern(pattern, runningContext); | ||
@@ -91,4 +105,10 @@ const {regexContext, charClassContext} = runningContext; | ||
pattern = transformAtomicGroups(pattern); | ||
return new constructor(__rake ? rakeSeparators(pattern) : pattern, `v${flags}`); | ||
const postp = [transformAtomicGroups, ...postprocessors]; | ||
if (__rake) { | ||
postp.push(rakeSeparators); | ||
} | ||
for (const p of postp) { | ||
pattern = p(pattern); | ||
} | ||
return new constructor(pattern, `v${flags}`); | ||
} | ||
@@ -186,3 +206,3 @@ | ||
} else { | ||
value = replaceUnescaped(value, '\\.', (re.dotAll ? '[^]' : `[^${newlines}]`), RegexContext.DEFAULT); | ||
value = replaceUnescaped(value, '\\.', (re.dotAll ? '[^]' : `[^${newlines}]`), Context.DEFAULT); | ||
} | ||
@@ -194,4 +214,4 @@ } | ||
} else { | ||
value = replaceUnescaped(value, '\\^', (re.multiline ? `(?<=^|[${newlines}])` : '(?<![^])'), RegexContext.DEFAULT); | ||
value = replaceUnescaped(value, '\\$', (re.multiline ? `(?=$|[${newlines}])` : '(?![^])'), RegexContext.DEFAULT); | ||
value = replaceUnescaped(value, '\\^', (re.multiline ? `(?<=^|[${newlines}])` : '(?<![^])'), Context.DEFAULT); | ||
value = replaceUnescaped(value, '\\$', (re.multiline ? `(?=$|[${newlines}])` : '(?![^])'), Context.DEFAULT); | ||
} | ||
@@ -198,0 +218,0 @@ } |
@@ -0,6 +1,6 @@ | ||
import {Context, replaceUnescaped} from 'regex-utilities'; | ||
import {PartialPattern, partial} from './partial.js'; | ||
export const RegexContext = { | ||
DEFAULT: 'DEFAULT', | ||
CHAR_CLASS: 'CHAR_CLASS', | ||
...Context, | ||
GROUP_NAME: 'GROUP_NAME', | ||
@@ -70,4 +70,4 @@ ENCLOSED_TOKEN: 'ENCLOSED_TOKEN', | ||
// be placed on a range boundary). So escape \0 in character classes as \u{0} | ||
export function sandboxUnsafeNulls(str, inRegexContext) { | ||
return replaceUnescaped(str, String.raw`\\0(?!\d)`, '\\u{0}', inRegexContext); | ||
export function sandboxUnsafeNulls(str, inContext) { | ||
return replaceUnescaped(str, String.raw`\\0(?!\d)`, '\\u{0}', inContext); | ||
} | ||
@@ -152,3 +152,3 @@ | ||
| \[\^ | ||
| \(\?[:=!<>ims\-] | ||
| \(\? [:=!<>A-Za-z\-] | ||
| (?<dp> [${doublePunctuatorChars}] ) \k<dp> | ||
@@ -230,77 +230,2 @@ | \\[1-9]\d* | ||
/** | ||
Replaces patterns only when they're unescaped and in the given context. | ||
Doesn't skip over complete multicharacter tokens (only `\` and folowing char) so must be used with | ||
knowledge of what's safe to do given regex syntax. | ||
Assumes flag v and doesn't worry about syntax errors that are caught by it. | ||
@param {string} pattern | ||
@param {string} needle Search as a regex pattern, with flags `su` | ||
@param {string | (match: RegExpExecArray) => string} replacement | ||
@param {'DEFAULT' | 'CHAR_CLASS'} [inRegexContext] | ||
@returns {string} | ||
@example | ||
replaceUnescaped(String.raw`.\.\\.\\\.[[\.].].`, '\\.', '~'); | ||
// -> String.raw`~\.\\~\\\.[[\.]~]~` | ||
replaceUnescaped(String.raw`.\.\\.\\\.[[\.].].`, '\\.', '~', RegexContext.DEFAULT); | ||
// -> String.raw`~\.\\~\\\.[[\.].]~` | ||
*/ | ||
export function replaceUnescaped(pattern, needle, replacement, inRegexContext) { | ||
const re = new RegExp(String.raw`(?<found>${needle})|\\?.`, 'gsu'); | ||
let numCharClassesOpen = 0; | ||
let result = ''; | ||
for (const match of pattern.matchAll(re)) { | ||
const {0: m, groups: {found}} = match; | ||
if (found && (!inRegexContext || (inRegexContext === RegexContext.DEFAULT) === !numCharClassesOpen)) { | ||
if (replacement instanceof Function) { | ||
result += replacement(match); | ||
} else { | ||
result += replacement; | ||
} | ||
continue; | ||
} | ||
if (m === '[') { | ||
numCharClassesOpen++; | ||
} else if (m === ']' && numCharClassesOpen) { | ||
numCharClassesOpen--; | ||
} | ||
result += m; | ||
} | ||
return result; | ||
} | ||
/** | ||
Check if an unescaped version of a pattern appears outside of a character class. | ||
Doesn't skip over complete multicharacter tokens (only `\` and folowing char) so must be used with | ||
knowledge of what's safe to do given regex syntax. | ||
Assumes flag v and doesn't worry about syntax errors that are caught by it. | ||
@param {string} pattern | ||
@param {string} needle Search as a regex pattern, with flags `su` | ||
@param {(match: RegExpExecArray) => void} [callback] | ||
@returns {boolean} | ||
*/ | ||
export function hasUnescapedInDefaultRegexContext(pattern, needle, callback) { | ||
// Quick partial test; avoid the loop if not needed | ||
if (!(new RegExp(needle, 'su')).test(pattern)) { | ||
return false; | ||
} | ||
const re = new RegExp(String.raw`(?<found>${needle})|\\?.`, 'gsu'); | ||
let numCharClassesOpen = 0; | ||
for (const match of pattern.matchAll(re)) { | ||
const {0: m, groups: {found}} = match; | ||
if (m === '[') { | ||
numCharClassesOpen++; | ||
} else if (!numCharClassesOpen) { | ||
if (found) { | ||
if (callback) { | ||
callback(match); | ||
} | ||
return true; | ||
} | ||
} else if (m === ']') { | ||
numCharClassesOpen--; | ||
} | ||
} | ||
return false; | ||
} | ||
// Assumes flag v and doesn't worry about syntax errors that are caught by it | ||
@@ -307,0 +232,0 @@ export function countCaptures(pattern) { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
159613
1756
475
1
+ Addedregex-utilities@^1.0.0
+ Addedregex-utilities@1.1.1(transitive)