Refactor routing in App component to enhance navigation and improve error handling by integrating dynamic routes and updating the NotFound route.
This commit is contained in:
21
node_modules/regex-recursion/LICENSE
generated
vendored
Normal file
21
node_modules/regex-recursion/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Steven Levithan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
143
node_modules/regex-recursion/README.md
generated
vendored
Normal file
143
node_modules/regex-recursion/README.md
generated
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
# regex-recursion
|
||||
|
||||
[![npm version][npm-version-src]][npm-version-href]
|
||||
[![bundle][bundle-src]][bundle-href]
|
||||
|
||||
This is an official plugin for [Regex+](https://github.com/slevithan/regex) that adds support for recursive matching up to a specified max depth *N*, where *N* can be between 2 and 100. Generated regexes are native JavaScript `RegExp` instances.
|
||||
|
||||
Recursive matching is added to a regex via one of the following (the recursion depth limit is provided in place of *`N`*):
|
||||
|
||||
- `(?R=N)` — Recursively match the entire regex at this position.
|
||||
- `\g<name&R=N>` or `\g<number&R=N>` — Recursively match the contents of the group referenced by name or number at this position.
|
||||
- The `\g` subroutine must be *within* the referenced group.
|
||||
|
||||
Multiple uses of recursion within the same pattern are allowed if they are non-overlapping. Named captures and backreferences are supported within recursion, and are independent per depth level. So e.g. `groups.name` on a match object is the value captured by group `name` at the top level of the recursion stack.
|
||||
|
||||
## Install and use
|
||||
|
||||
```sh
|
||||
npm install regex regex-recursion
|
||||
```
|
||||
|
||||
```js
|
||||
import {regex} from 'regex';
|
||||
import {recursion} from 'regex-recursion';
|
||||
|
||||
const re = regex({plugins: [recursion]})`…`;
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Using a global name (no import)</summary>
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/regex@5.1.1/dist/regex.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/regex-recursion@5.1.1/dist/regex-recursion.min.js"></script>
|
||||
<script>
|
||||
const {regex} = Regex;
|
||||
const {recursion} = Regex.plugins;
|
||||
|
||||
const re = regex({plugins: [recursion]})`…`;
|
||||
</script>
|
||||
```
|
||||
</details>
|
||||
|
||||
## Examples
|
||||
|
||||
### Match an equal number of two different subpatterns
|
||||
|
||||
#### Anywhere within a string
|
||||
|
||||
```js
|
||||
// Matches sequences of up to 50 'a' chars followed by the same number of 'b'
|
||||
const re = regex({plugins: [recursion]})`a(?R=50)?b`;
|
||||
re.exec('test aaaaaabbb')[0];
|
||||
// → 'aaabbb'
|
||||
```
|
||||
|
||||
#### As the entire string
|
||||
|
||||
```js
|
||||
const re = regex({plugins: [recursion]})`^
|
||||
(?<balanced>
|
||||
a
|
||||
# Recursively match just the specified group
|
||||
\g<balanced&R=50>?
|
||||
b
|
||||
)
|
||||
$`;
|
||||
re.test('aaabbb'); // → true
|
||||
re.test('aaabb'); // → false
|
||||
```
|
||||
|
||||
Notice the `^` and `$` anchors outside of the recursive subpattern.
|
||||
|
||||
### Match balanced parentheses
|
||||
|
||||
```js
|
||||
// Matches all balanced parentheses up to depth 50
|
||||
const parens = regex({flags: 'g', plugins: [recursion]})`
|
||||
\( ( [^\(\)] | (?R=50) )* \)
|
||||
`;
|
||||
|
||||
'test ) (balanced ((parens))) () ((a)) ( (b)'.match(parens);
|
||||
/* → [
|
||||
'(balanced ((parens)))',
|
||||
'()',
|
||||
'((a))',
|
||||
'(b)'
|
||||
] */
|
||||
```
|
||||
|
||||
Following is an alternative that matches the same strings, but adds a nested quantifier. It then uses an atomic group to prevent this nested quantifier from creating the potential for [catastrophic backtracking](https://www.regular-expressions.info/catastrophic.html).
|
||||
|
||||
```js
|
||||
const parens = regex({flags: 'g', plugins: [recursion]})`
|
||||
\( ( (?> [^\(\)]+ ) | (?R=50) )* \)
|
||||
`;
|
||||
```
|
||||
|
||||
This matches sequences of non-parens in one step with the nested `+` quantifier, and avoids backtracking into these sequences by wrapping it with an atomic group `(?>…)`. Given that what the nested quantifier `+` matches overlaps with what the outer group can match with its `*` quantifier, the atomic group is important here. It avoids exponential backtracking when matching long strings with unbalanced parens.
|
||||
|
||||
[Atomic groups](https://github.com/slevithan/regex#atomic-groups) are provided by the base `regex` library.
|
||||
|
||||
### Match palindromes
|
||||
|
||||
#### Match palindromes anywhere within a string
|
||||
|
||||
```js
|
||||
const palindromes = regex({flags: 'gi', plugins: [recursion]})`
|
||||
(?<char> \w )
|
||||
# Recurse, or match a lone unbalanced char in the middle
|
||||
( (?R=15) | \w? )
|
||||
\k<char>
|
||||
`;
|
||||
|
||||
'Racecar, ABBA, and redivided'.match(palindromes);
|
||||
// → ['Racecar', 'ABBA', 'edivide']
|
||||
```
|
||||
|
||||
In the example above, the max length of matched palindromes is 31. That's because it sets the max recursion depth to 15 with `(?R=15)`. So, depth 15 × 2 chars (left + right) for each depth level + 1 optional unbalanced char in the middle = 31. To match longer palindromes, the max recursion depth can be increased to a max of 100, which would enable matching palindromes up to 201 characters long.
|
||||
|
||||
#### Match palindromes as complete words
|
||||
|
||||
```js
|
||||
const palindromeWords = regex({flags: 'gi', plugins: [recursion]})`\b
|
||||
(?<palindrome>
|
||||
(?<char> \w )
|
||||
( \g<palindrome&R=15> | \w? )
|
||||
\k<char>
|
||||
)
|
||||
\b`;
|
||||
|
||||
'Racecar, ABBA, and redivided'.match(palindromeWords);
|
||||
// → ['Racecar', 'ABBA']
|
||||
```
|
||||
|
||||
Notice the `\b` word boundaries outside of the recursive subpattern.
|
||||
|
||||
<!-- Badges -->
|
||||
|
||||
[npm-version-src]: https://img.shields.io/npm/v/regex-recursion?color=78C372
|
||||
[npm-version-href]: https://npmjs.com/package/regex-recursion
|
||||
[bundle-src]: https://img.shields.io/bundlejs/size/regex-recursion?color=78C372&label=minzip
|
||||
[bundle-href]: https://bundlejs.com/?q=regex-recursion&treeshake=[*]
|
17
node_modules/regex-recursion/dist/regex-recursion.min.js
generated
vendored
Normal file
17
node_modules/regex-recursion/dist/regex-recursion.min.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
var Regex;(Regex||={}).plugins=(()=>{var N=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var q=Object.prototype.hasOwnProperty;var z=(e,t)=>{for(var n in t)N(e,n,{get:t[n],enumerable:!0})},H=(e,t,n,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of W(t))!q.call(e,r)&&r!==n&&N(e,r,{get:()=>t[r],enumerable:!(a=P(t,r))||a.enumerable});return e};var Q=e=>H(N({},"__esModule",{value:!0}),e);var X={};z(X,{recursion:()=>K});var i=Object.freeze({DEFAULT:"DEFAULT",CHAR_CLASS:"CHAR_CLASS"});function I(e,t,n,a){let r=new RegExp(String.raw`${t}|(?<$skip>\[\^?|\\?.)`,"gsu"),s=[!1],u=0,o="";for(let c of e.matchAll(r)){let{0:f,groups:{$skip:p}}=c;if(!p&&(!a||a===i.DEFAULT==!u)){n instanceof Function?o+=n(c,{context:u?i.CHAR_CLASS:i.DEFAULT,negated:s[s.length-1]}):o+=n;continue}f[0]==="["?(u++,s.push(f[1]==="^")):f==="]"&&u&&(u--,s.pop()),o+=f}return o}function b(e,t,n,a){I(e,t,n,a)}function Z(e,t,n=0,a){if(!new RegExp(t,"su").test(e))return null;let r=new RegExp(`${t}|(?<$skip>\\\\?.)`,"gsu");r.lastIndex=n;let s=0,u;for(;u=r.exec(e);){let{0:o,groups:{$skip:c}}=u;if(!c&&(!a||a===i.DEFAULT==!s))return u;o==="["?s++:o==="]"&&s&&s--,r.lastIndex==u.index&&r.lastIndex++}return null}function k(e,t,n){return!!Z(e,t,0,n)}function T(e,t){let n=/\\?./gsu;n.lastIndex=t;let a=e.length,r=0,s=1,u;for(;u=n.exec(e);){let[o]=u;if(o==="[")r++;else if(r)o==="]"&&r--;else if(o==="(")s++;else if(o===")"&&(s--,!s)){a=u.index;break}}return e.slice(t,a)}var w="$E$";var L=String.raw`\(\?(?:[:=!>A-Za-z\-]|<[=!]|\(DEFINE\))`;var ce=new RegExp(String.raw`(?<noncapturingStart>${L})|(?<capturingStart>\((?:\?<[^>]+>)?)|\\?.`,"gsu");var j=String.raw`(?:[?*+]|\{\d+(?:,\d*)?\})`,ie=new RegExp(String.raw`
|
||||
\\(?: \d+
|
||||
| c[A-Za-z]
|
||||
| [gk]<[^>]+>
|
||||
| [pPu]\{[^\}]+\}
|
||||
| u[A-Fa-f\d]{4}
|
||||
| x[A-Fa-f\d]{2}
|
||||
)
|
||||
| \((?: \? (?: [:=!>]
|
||||
| <(?:[=!]|[^>]+>)
|
||||
| [A-Za-z\-]+:
|
||||
| \(DEFINE\)
|
||||
))?
|
||||
| (?<qBase>${j})(?<qMod>[?+]?)(?<invalidQ>[?*+\{]?)
|
||||
| \\?.
|
||||
`.replace(/\s+/g,""),"gsu");var l=String.raw,J=l`\\g<(?<gRNameOrNum>[^>&]+)&R=(?<gRDepth>[^>]+)>`,U=l`\(\?R=(?<rDepth>[^\)]+)\)|${J}`,R=l`\(\?<(?![=!])(?<captureName>[^>]+)>`,g=new RegExp(l`${R}|${U}|\(\?|\\?.`,"gsu"),G="Cannot use multiple overlapping recursions",M=new RegExp(l`(?:\$[1-9]\d*)?${w.replace(/\$/g,l`\$`)}`,"y");function K(e,t){if(!new RegExp(U,"su").test(e))return e;if(k(e,l`\(\?\(DEFINE\)`,i.DEFAULT))throw new Error("DEFINE groups cannot be used with recursion");let n=!!t?.useEmulationGroups,a=k(e,l`\\[1-9]`,i.DEFAULT),r=new Map,s=[],u=!1,o=0,c=0,f;for(g.lastIndex=0;f=g.exec(e);){let{0:p,groups:{captureName:A,rDepth:$,gRNameOrNum:d,gRDepth:E}}=f;if(p==="[")o++;else if(o)p==="]"&&o--;else if($){if(_($),u)throw new Error(G);if(a)throw new Error("Numbered backrefs cannot be used with global recursion");let h=e.slice(0,f.index),m=e.slice(g.lastIndex);if(k(m,U,i.DEFAULT))throw new Error(G);return v(h,m,+$,!1,n)}else if(d){_(E);let h=!1;for(let C of s)if(C.name===d||C.num===+d){if(h=!0,C.hasRecursedWithin)throw new Error(G);break}if(!h)throw new Error(l`Recursive \g cannot be used outside the referenced group "\g<${d}&R=${E}>"`);let m=r.get(d),x=T(e,m);if(a&&k(x,l`${R}|\((?!\?)`,i.DEFAULT))throw new Error("Numbered backrefs cannot be used with recursion of capturing groups");let D=e.slice(m,f.index),S=x.slice(D.length+p.length),F=v(D,S,+E,!0,n),O=e.slice(0,m),y=e.slice(m+x.length);e=`${O}${F}${y}`,g.lastIndex+=F.length-p.length-D.length-S.length,s.forEach(C=>C.hasRecursedWithin=!0),u=!0}else if(A)c++,r.set(String(c),g.lastIndex),r.set(A,g.lastIndex),s.push({num:c,name:A});else if(p.startsWith("(")){let h=p==="(";h&&(c++,r.set(String(c),g.lastIndex+(n?V(e,g.lastIndex):0))),s.push(h?{num:c}:{})}else p===")"&&s.pop()}return e}function _(e){let t=`Max depth must be integer between 2 and 100; used ${e}`;if(!/^[1-9]\d*$/.test(e))throw new Error(t);if(e=+e,e<2||e>100)throw new Error(t)}function v(e,t,n,a,r){let s=new Set;a&&b(e+t,R,({groups:{captureName:o}})=>{s.add(o)},i.DEFAULT);let u=n-1;return`${e}${B(`(?:${e}`,u,a?s:null,"forward",r)}(?:)${B(`${t})`,u,a?s:null,"backward",r)}${t}`}function B(e,t,n,a,r){let u=c=>a==="backward"?t-c+2-1:c+2,o="";for(let c=0;c<t;c++){let f=u(c);o+=I(e,l`${R}|\\k<(?<backref>[^>]+)>${r?l`|(?<unnamed>\()(?!\?)(?:${M.source})?`:""}`,({0:p,index:A,groups:{captureName:$,backref:d,unnamed:E}})=>{if(d&&n&&!n.has(d))return p;if(E)return`(${w}`;let h=`_$${f}`;return $?`(?<${$}${h}>${r?w:""}`:l`\k<${d}${h}>`},i.DEFAULT)}return o}function V(e,t){M.lastIndex=t;let n=M.exec(e);return n?n[0].length:0}return Q(X);})();
|
||||
//# sourceMappingURL=regex-recursion.min.js.map
|
7
node_modules/regex-recursion/dist/regex-recursion.min.js.map
generated
vendored
Normal file
7
node_modules/regex-recursion/dist/regex-recursion.min.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
48
node_modules/regex-recursion/package.json
generated
vendored
Normal file
48
node_modules/regex-recursion/package.json
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "regex-recursion",
|
||||
"version": "5.1.1",
|
||||
"description": "Recursive matching plugin for Regex+",
|
||||
"author": "Steven Levithan",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./types/index.d.ts",
|
||||
"import": "./src/index.js"
|
||||
}
|
||||
},
|
||||
"browser": "./dist/regex-recursion.min.js",
|
||||
"types": "./types/index.d.ts",
|
||||
"scripts": {
|
||||
"bundle:global": "esbuild src/index.js --global-name=Regex.plugins --bundle --minify --sourcemap --outfile=dist/regex-recursion.min.js",
|
||||
"types": "tsc src/index.js --rootDir src --declaration --allowJs --emitDeclarationOnly --outDir types",
|
||||
"prebuild": "rm -rf dist/* types/*",
|
||||
"build": "pnpm run bundle:global && pnpm run types",
|
||||
"pretest": "pnpm run build",
|
||||
"test": "jasmine",
|
||||
"prepare": "pnpm test"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"src",
|
||||
"types"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/slevithan/regex-recursion.git"
|
||||
},
|
||||
"keywords": [
|
||||
"recursion",
|
||||
"regex",
|
||||
"regexp"
|
||||
],
|
||||
"dependencies": {
|
||||
"regex": "^5.1.1",
|
||||
"regex-utilities": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.24.2",
|
||||
"jasmine": "^5.5.0",
|
||||
"typescript": "~5.7.2"
|
||||
}
|
||||
}
|
221
node_modules/regex-recursion/src/index.js
generated
vendored
Normal file
221
node_modules/regex-recursion/src/index.js
generated
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
import {Context, forEachUnescaped, getGroupContents, hasUnescaped, replaceUnescaped} from 'regex-utilities';
|
||||
import {emulationGroupMarker} from 'regex/internals';
|
||||
|
||||
const r = String.raw;
|
||||
const gRToken = r`\\g<(?<gRNameOrNum>[^>&]+)&R=(?<gRDepth>[^>]+)>`;
|
||||
const recursiveToken = r`\(\?R=(?<rDepth>[^\)]+)\)|${gRToken}`;
|
||||
const namedCapturingDelim = r`\(\?<(?![=!])(?<captureName>[^>]+)>`;
|
||||
const token = new RegExp(r`${namedCapturingDelim}|${recursiveToken}|\(\?|\\?.`, 'gsu');
|
||||
const overlappingRecursionMsg = 'Cannot use multiple overlapping recursions';
|
||||
// Support emulation groups with transfer marker prefix
|
||||
const emulationGroupMarkerRe = new RegExp(r`(?:\$[1-9]\d*)?${emulationGroupMarker.replace(/\$/g, r`\$`)}`, 'y');
|
||||
|
||||
/**
|
||||
@param {string} expression
|
||||
@param {{
|
||||
flags?: string;
|
||||
useEmulationGroups?: boolean;
|
||||
}} [data]
|
||||
@returns {string}
|
||||
*/
|
||||
export function recursion(expression, data) {
|
||||
// Keep the initial fail-check (which avoids unneeded processing) as fast as possible by testing
|
||||
// without the accuracy improvement of using `hasUnescaped` with default `Context`
|
||||
if (!(new RegExp(recursiveToken, 'su').test(expression))) {
|
||||
return expression;
|
||||
}
|
||||
if (hasUnescaped(expression, r`\(\?\(DEFINE\)`, Context.DEFAULT)) {
|
||||
throw new Error('DEFINE groups cannot be used with recursion');
|
||||
}
|
||||
const useEmulationGroups = !!data?.useEmulationGroups;
|
||||
const hasNumberedBackref = hasUnescaped(expression, r`\\[1-9]`, Context.DEFAULT);
|
||||
const groupContentsStartPos = new Map();
|
||||
const openGroups = [];
|
||||
let hasRecursed = false;
|
||||
let numCharClassesOpen = 0;
|
||||
let numCaptures = 0;
|
||||
let match;
|
||||
token.lastIndex = 0;
|
||||
while ((match = token.exec(expression))) {
|
||||
const {0: m, groups: {captureName, rDepth, gRNameOrNum, gRDepth}} = match;
|
||||
if (m === '[') {
|
||||
numCharClassesOpen++;
|
||||
} else if (!numCharClassesOpen) {
|
||||
|
||||
// `(?R=N)`
|
||||
if (rDepth) {
|
||||
assertMaxInBounds(rDepth);
|
||||
if (hasRecursed) {
|
||||
throw new Error(overlappingRecursionMsg);
|
||||
}
|
||||
if (hasNumberedBackref) {
|
||||
// Could add support for numbered backrefs with extra effort, but it's probably not worth
|
||||
// it. To trigger this error, the regex must include recursion and one of the following:
|
||||
// - An interpolated regex that contains a numbered backref (since other numbered
|
||||
// backrefs are prevented by implicit flag n).
|
||||
// - A numbered backref, when flag n is explicitly disabled.
|
||||
// Note that Regex+'s extended syntax (atomic groups and sometimes subroutines) can also
|
||||
// add numbered backrefs, but those work fine because external plugins like this one run
|
||||
// *before* the transformation of built-in syntax extensions
|
||||
throw new Error('Numbered backrefs cannot be used with global recursion');
|
||||
}
|
||||
const pre = expression.slice(0, match.index);
|
||||
const post = expression.slice(token.lastIndex);
|
||||
if (hasUnescaped(post, recursiveToken, Context.DEFAULT)) {
|
||||
throw new Error(overlappingRecursionMsg);
|
||||
}
|
||||
// No need to parse further
|
||||
return makeRecursive(pre, post, +rDepth, false, useEmulationGroups);
|
||||
// `\g<name&R=N>`, `\g<number&R=N>`
|
||||
} else if (gRNameOrNum) {
|
||||
assertMaxInBounds(gRDepth);
|
||||
let isWithinReffedGroup = false;
|
||||
for (const g of openGroups) {
|
||||
if (g.name === gRNameOrNum || g.num === +gRNameOrNum) {
|
||||
isWithinReffedGroup = true;
|
||||
if (g.hasRecursedWithin) {
|
||||
throw new Error(overlappingRecursionMsg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isWithinReffedGroup) {
|
||||
throw new Error(r`Recursive \g cannot be used outside the referenced group "\g<${gRNameOrNum}&R=${gRDepth}>"`);
|
||||
}
|
||||
const startPos = groupContentsStartPos.get(gRNameOrNum);
|
||||
const groupContents = getGroupContents(expression, startPos);
|
||||
if (
|
||||
hasNumberedBackref &&
|
||||
hasUnescaped(groupContents, r`${namedCapturingDelim}|\((?!\?)`, Context.DEFAULT)
|
||||
) {
|
||||
throw new Error('Numbered backrefs cannot be used with recursion of capturing groups');
|
||||
}
|
||||
const groupContentsPre = expression.slice(startPos, match.index);
|
||||
const groupContentsPost = groupContents.slice(groupContentsPre.length + m.length);
|
||||
const expansion = makeRecursive(groupContentsPre, groupContentsPost, +gRDepth, true, useEmulationGroups);
|
||||
const pre = expression.slice(0, startPos);
|
||||
const post = expression.slice(startPos + groupContents.length);
|
||||
// Modify the string we're looping over
|
||||
expression = `${pre}${expansion}${post}`;
|
||||
// Step forward for the next loop iteration
|
||||
token.lastIndex += expansion.length - m.length - groupContentsPre.length - groupContentsPost.length;
|
||||
openGroups.forEach(g => g.hasRecursedWithin = true);
|
||||
hasRecursed = true;
|
||||
} else if (captureName) {
|
||||
numCaptures++;
|
||||
// NOTE: Not currently handling *named* emulation groups that already exist in the pattern
|
||||
groupContentsStartPos.set(String(numCaptures), token.lastIndex);
|
||||
groupContentsStartPos.set(captureName, token.lastIndex);
|
||||
openGroups.push({
|
||||
num: numCaptures,
|
||||
name: captureName,
|
||||
});
|
||||
} else if (m.startsWith('(')) {
|
||||
const isUnnamedCapture = m === '(';
|
||||
if (isUnnamedCapture) {
|
||||
numCaptures++;
|
||||
groupContentsStartPos.set(
|
||||
String(numCaptures),
|
||||
token.lastIndex + (useEmulationGroups ? emulationGroupMarkerLength(expression, token.lastIndex) : 0)
|
||||
);
|
||||
}
|
||||
openGroups.push(isUnnamedCapture ? {num: numCaptures} : {});
|
||||
} else if (m === ')') {
|
||||
openGroups.pop();
|
||||
}
|
||||
|
||||
} else if (m === ']') {
|
||||
numCharClassesOpen--;
|
||||
}
|
||||
}
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
/**
|
||||
@param {string} max
|
||||
*/
|
||||
function assertMaxInBounds(max) {
|
||||
const errMsg = `Max depth must be integer between 2 and 100; used ${max}`;
|
||||
if (!/^[1-9]\d*$/.test(max)) {
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
max = +max;
|
||||
if (max < 2 || max > 100) {
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@param {string} pre
|
||||
@param {string} post
|
||||
@param {number} maxDepth
|
||||
@param {boolean} isSubpattern
|
||||
@param {boolean} useEmulationGroups
|
||||
@returns {string}
|
||||
*/
|
||||
function makeRecursive(pre, post, maxDepth, isSubpattern, useEmulationGroups) {
|
||||
const namesInRecursed = new Set();
|
||||
// Avoid this work if not needed
|
||||
if (isSubpattern) {
|
||||
forEachUnescaped(pre + post, namedCapturingDelim, ({groups: {captureName}}) => {
|
||||
namesInRecursed.add(captureName);
|
||||
}, Context.DEFAULT);
|
||||
}
|
||||
const reps = maxDepth - 1;
|
||||
// Depth 2: 'pre(?:pre(?:)post)post'
|
||||
// Depth 3: 'pre(?:pre(?:pre(?:)post)post)post'
|
||||
return `${pre}${
|
||||
repeatWithDepth(`(?:${pre}`, reps, (isSubpattern ? namesInRecursed : null), 'forward', useEmulationGroups)
|
||||
}(?:)${
|
||||
repeatWithDepth(`${post})`, reps, (isSubpattern ? namesInRecursed : null), 'backward', useEmulationGroups)
|
||||
}${post}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@param {string} expression
|
||||
@param {number} reps
|
||||
@param {Set<string> | null} namesInRecursed
|
||||
@param {'forward' | 'backward'} direction
|
||||
@param {boolean} useEmulationGroups
|
||||
@returns {string}
|
||||
*/
|
||||
function repeatWithDepth(expression, reps, namesInRecursed, direction, useEmulationGroups) {
|
||||
const startNum = 2;
|
||||
const depthNum = i => direction === 'backward' ? reps - i + startNum - 1 : i + startNum;
|
||||
let result = '';
|
||||
for (let i = 0; i < reps; i++) {
|
||||
const captureNum = depthNum(i);
|
||||
result += replaceUnescaped(
|
||||
expression,
|
||||
// NOTE: Not currently handling *named* emulation groups that already exist in the pattern
|
||||
r`${namedCapturingDelim}|\\k<(?<backref>[^>]+)>${
|
||||
useEmulationGroups ? r`|(?<unnamed>\()(?!\?)(?:${emulationGroupMarkerRe.source})?` : ''
|
||||
}`,
|
||||
({0: m, index, groups: {captureName, backref, unnamed}}) => {
|
||||
if (backref && namesInRecursed && !namesInRecursed.has(backref)) {
|
||||
// Don't alter backrefs to groups outside the recursed subpattern
|
||||
return m;
|
||||
}
|
||||
// Only matches unnamed capture delim if `useEmulationGroups`
|
||||
if (unnamed) {
|
||||
// Add an emulation group marker, possibly replacing an existing marker (removes any
|
||||
// transfer prefix)
|
||||
return `(${emulationGroupMarker}`;
|
||||
}
|
||||
const suffix = `_$${captureNum}`;
|
||||
return captureName ?
|
||||
`(?<${captureName}${suffix}>${useEmulationGroups ? emulationGroupMarker : ''}` :
|
||||
r`\k<${backref}${suffix}>`;
|
||||
},
|
||||
Context.DEFAULT
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function emulationGroupMarkerLength(expression, index) {
|
||||
emulationGroupMarkerRe.lastIndex = index;
|
||||
const match = emulationGroupMarkerRe.exec(expression);
|
||||
return match ? match[0].length : 0;
|
||||
}
|
12
node_modules/regex-recursion/types/index.d.ts
generated
vendored
Normal file
12
node_modules/regex-recursion/types/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
@param {string} expression
|
||||
@param {{
|
||||
flags?: string;
|
||||
useEmulationGroups?: boolean;
|
||||
}} [data]
|
||||
@returns {string}
|
||||
*/
|
||||
export function recursion(expression: string, data?: {
|
||||
flags?: string;
|
||||
useEmulationGroups?: boolean;
|
||||
}): string;
|
Reference in New Issue
Block a user