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:
becarta
2025-05-23 12:43:00 +02:00
parent f40db0f5c9
commit a544759a3b
11127 changed files with 1647032 additions and 0 deletions

21
node_modules/regex/LICENSE generated vendored Normal file
View 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.

861
node_modules/regex/README.md generated vendored Normal file
View File

@@ -0,0 +1,861 @@
<div align="center">
<a href="https://github.com/slevithan/regex#readme">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.jsdelivr.net/gh/slevithan/regex@5.0.1/media/regex-logo-dark.svg">
<img alt="regex logo" height="180" src="https://cdn.jsdelivr.net/gh/slevithan/regex@5.0.1/media/regex-logo.svg">
</picture>
</a>
<br><br>
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![bundle][bundle-src]][bundle-href]
</div>
Regex+ (aka `regex`, based on its package and tag name) is a template tag that extends JavaScript regular expressions with key features that make regexes more powerful and dramatically more readable. It returns native `RegExp` instances that run with native performance, and can exceed the performance of regex literals you'd write yourself. It's also lightweight, supports all ES2025 regex features, has built-in types, and can be used as a [Babel plugin](https://github.com/slevithan/babel-plugin-transform-regex) to avoid any runtime dependencies or user runtime cost.
Highlights include support for insignificant whitespace and comments, atomic groups and possessive quantifiers (that can help you avoid [ReDoS](https://en.wikipedia.org/wiki/ReDoS)), subroutines and subroutine definition groups (that enable powerful subpattern composition), and context-aware interpolation of regexes, escaped strings, and partial patterns.
With the Regex+ library, JavaScript steps up as one of the best regex flavors alongside PCRE and Perl, possibly surpassing C++, Java, .NET, Python, and Ruby.
<details>
<summary><b>Table of contents</b></summary>
- [Features](#-features)
- [Install and use](#-install-and-use)
- [Examples](#-examples)
- [Context](#-context)
- [Extended regex syntax](#-extended-regex-syntax)
- [Atomic groups](#atomic-groups)
- [Possessive quantifiers](#possessive-quantifiers)
- [Subroutines](#subroutines)
- [Subroutine definition groups](#subroutine-definition-groups)
- [Recursion](#recursion)
- [Flags](#-flags)
- [Implicit flags](#implicit-flags)
- [Flag <kbd>v</kbd>](#flag-v)
- [Flag <kbd>x</kbd>](#flag-x)
- [Flag <kbd>n</kbd>](#flag-n)
- [Interpolation](#-interpolation)
- [`RegExp` instances](#interpolating-regexes)
- [Escaped strings](#interpolating-escaped-strings)
- [Partial patterns](#interpolating-partial-patterns)
- [Interpolation principles](#interpolation-principles)
- [Interpolation contexts](#interpolation-contexts)
- [Options](#-options)
- [Returning a string](#returning-a-string)
- [Performance](#-performance)
- [Compatibility](#-compatibility)
- [FAQ](#-faq)
</details>
## 💎 Features
**A modern regex baseline** so you don't need to continually opt-in to best practices.
- Always-on flag <kbd>v</kbd> gives you the best level of Unicode support and strict errors.
- New flags:
- Always-on flag <kbd>x</kbd> allows you to freely add whitespace and comments to your regexes.
- Always-on flag <kbd>n</kbd> (*named capture only* mode) improves regex readability and efficiency.
- No unreadable escaped backslashes `\\\\` since it's a raw string template tag.
**Extended regex syntax**.
- Atomic groups and possessive quantifiers can dramatically improve performance and prevent ReDoS.
- Subroutines and definition groups enable powerful composition, improving readability and maintainability.
- Recursive matching via an official plugin.
**Context-aware and safe interpolation** of regexes, strings, and partial patterns.
- Interpolated strings have their special characters escaped.
- Interpolated regexes locally preserve the meaning of their own flags (or their absense), and their numbered backreferences are adjusted to work within the overall pattern.
## 🕹️ Install and use
```sh
npm install regex
```
```js
import {regex} from 'regex';
// Works with all string/regexp methods since it returns a native regexp
const str = 'abc';
regex`\w`.test(str); // → true
str.match(regex('g')`\w`); // → ['a', 'b', 'c']
```
<details>
<summary>In browsers</summary>
ESM:
```html
<script type="module">
import {regex} from 'https://esm.run/regex';
// …
</script>
```
Using a global name:
```html
<script src="https://cdn.jsdelivr.net/npm/regex/dist/regex.min.js"></script>
<script>
const {regex} = Regex;
// …
</script>
```
</details>
## 🪧 Examples
```js
import {regex, pattern} from 'regex';
// Subroutines and subroutine definition group
const record = regex`
^ Admitted: \g<date> \n
Released: \g<date> $
(?(DEFINE)
(?<date> \g<year>-\g<month>-\g<day>)
(?<year> \d{4})
(?<month> \d{2})
(?<day> \d{2})
)
`;
// Atomic group: Avoids ReDoS from the nested, overlapping quantifier
const words = regex`^(?>\w+\s?)+$`;
// Context-aware interpolation
const re = regex('m')`
# Only the inner regex is case insensitive (flag i)
# Also, the outer regex's flag m is not applied to it
${/^a.b$/i}
|
# Strings are escaped and repeated as complete units
^ ${'a.b'}+ $
|
# This string is contextually sandboxed but not escaped
${pattern('^ a.b $')}
`;
// Numbered backreferences in interpolated regexes are adjusted
const double = /(.)\1/;
regex`^ (?<first>.) ${double} ${double} $`;
// → /^(?<first>.)(.)\2(.)\3$/v
```
See also this example of using a subroutine definition group to [refactor an IP address regex for readability](https://x.com/slevithan/status/1828112006353953055).
## ❓ Context
Due to years of legacy and backward compatibility, regular expression syntax in JavaScript is a bit of a mess. There are four different sets of incompatible syntax and behavior rules that might apply to your regexes depending on the flags and features you use. The differences are just plain hard to fully grok and can easily create subtle bugs.
<details>
<summary>See the four parsing modes</summary>
1. Unicode-unaware (legacy) mode is the default and can easily and silently create Unicode-related bugs.
2. Named capture mode changes the meaning of `\k` when a named capture appears anywhere in a regex.
3. Unicode mode with flag <kbd>u</kbd> adds strict errors (for unreserved escapes, octal escapes, quantified lookahead, etc.), switches to code point matching (changing the potential handling of the dot, negated sets like `\W`, character class ranges, and quantifiers), changes flag <kbd>i</kbd> to apply Unicode case-folding, and adds support for new syntax.
4. UnicodeSets mode with flag <kbd>v</kbd> (an upgrade to <kbd>u</kbd>) incompatibly changes escaping rules within character classes, fixes case-insensitive matching for `\p` and `\P` within negated `[^…]`, and adds support for new features/syntax.
</details>
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 strings, insignificant whitespace, comments, subroutines, subroutine definition groups, interpolation, and *named capture only* mode — even long and complex regexes can be beautiful, grammatical, and intuitive.
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 (in environments without native <kbd>v</kbd>, flag <kbd>u</kbd> is automatically used instead while applying <kbd>v</kbd>'s escaping rules so your regexes are forward and backward compatible). It also supports atomic groups and possessive quantifiers to help you avoid catastrophic backtracking, and it gives you best-in-class, context-aware interpolation of `RegExp` instances, escaped strings, and partial patterns.
## 🦾 Extended regex syntax
Historically, JavaScript regexes were not as powerful or readable as other major regex flavors like Java, .NET, PCRE, Perl, Python, and Ruby. With recent advancements and the Regex+ library, those days are over. Modern JavaScript regexes have [significantly improved](https://github.com/slevithan/awesome-regex#javascript-regex-evolution), adding lookbehind, named capture, Unicode properties, set subtraction and intersection, etc. The extended syntax and implicit flags provided by Regex+ add the key remaining pieces needed to stand alongside or surpass other major flavors.
### Atomic groups
Atomic groups are noncapturing groups with special behavior, and are written as `(?>…)`. After matching the contents of an atomic group, the regex engine automatically throws away all backtracking positions remembered by any tokens within the group. Atomic groups are most commonly used to improve performance, and are a much needed feature that Regex+ brings to native JavaScript regular expressions.
Example:
```js
regex`^(?>\w+\s?)+$`
```
This matches strings that contain word characters separated by spaces, with the final space being optional. Thanks to the atomic group, it instantly fails to find a match if given a long list of words that end with something not allowed, like `'A target string that takes a long time or can even hang your browser!'`.
Try running this without the atomic group (as `/^(?:\w+\s?)+$/`) and, due to the exponential backtracking triggered by the many ways to divide the work of the inner and outer `+` quantifiers, it will either take a *very* long time, hang your browser/server, or throw an internal error after a delay. This is called *[catastrophic backtracking](https://www.regular-expressions.info/catastrophic.html)* or *[ReDoS](https://en.wikipedia.org/wiki/ReDoS)*, and it has taken down major services like [Cloudflare](https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019) and [Stack Overflow](https://stackstatus.tumblr.com/post/147710624694/outage-postmortem-july-20-2016). Regex+ and atomic groups to the rescue!
<details>
<summary>👉 <b>Learn more with examples</b></summary>
Consider `` regex`(?>a+)ab` `` vs `` regex`(?:a+)ab` ``. The former (with an atomic group) doesn't match `'aaaab'`, but the latter does. The former doesn't match because:
- The regex engine starts by using the greedy `a+` within the atomic group to match all the `a`s in the target string.
- Then, when it tries to match the additional `a` outside the group, it fails (the next character in the target string is a `b`), so the regex engine backtracks.
- But because it can't backtrack into the atomic group to make the `+` give up its last matched `a`, there are no additional options to try and the overall match attempt fails.
For a more useful example, consider how this can affect lazy (non-greedy) quantifiers. Let's say you want to match `<b></b>` tags that are followed by `!`. You might try this:
```js
const re = regex('gis')`<b>.*?</b>!`;
// This is OK
'<b>Hi</b>! <b>Bye</b>.'.match(re);
// → ['<b>Hi</b>!']
// But not this
'<b>Hi</b>. <b>Bye</b>!'.match(re);
// → ['<b>Hi</b>. <b>Bye</b>!'] 👎
```
What happened with the second string was that, when an `!` wasn't found immediately following the first `</b>`, the regex engine backtracked and expanded the lazy `.*?` to match an additional character (in this case, the `<` of the `</b>` tag) and then continued onward, all the way to just before the `</b>!` at the end.
You can prevent this by wrapping the lazily quantified token and its following delimiter in an atomic group, as follows:
```js
const re = regex('gis')`<b>(?>.*?</b>)!`;
'<b>Hi</b>. <b>Bye</b>!'.match(re);
// → ['<b>Bye</b>!'] 👍
```
Now, after the regex engine finds the first `</b>` and exits the atomic group, it can no longer backtrack into the group and change what the `.*?` already matched. As a result, the match attempt fails at the beginning of this example string. The regex engine then moves on and starts over at subsequent positions in the string, eventually finding `<b>Bye</b>!`. Success.
</details>
> [!NOTE]
> Atomic groups are supported in many other regex flavors. There's a [proposal](https://github.com/tc39/proposal-regexp-atomic-operators) to add them to JavaScript.
### Possessive quantifiers
Possessive quantifiers are created by adding `+` to a quantifier, and they're similar to greedy quantifiers except they don't allow backtracking. Although greedy quantifiers start out by matching as much as possible, if the remainder of the regex doesn't find a match, the regex engine will backtrack and try all permutations of how many times the quantifier should repeat. Possessive quantifiers prevent the regex engine from doing this.
> Possessive quantifiers are syntactic sugar for [atomic groups](#atomic-groups) when their contents are a single repeated item (which could be a token, character class, or group).
Like atomic groups, possessive quantifiers are mostly useful for performance and preventing ReDoS, but they can also be used to eliminate certain matches. For example, `` regex`a++.` `` matches one or more `a` followed by a character other than `a`. Unlike `/a+./`, it won't match a sequence of only `a` characters like `'aaa'`. The possessive `++` doesn't give back any of the `a`s it matched, so in this case there's nothing left for the following `.` to match.
Here's how possessive quantifier syntax compares to the greedy and lazy quantifiers that JavaScript supports natively:
| | Greedy | Lazy | Possessive |
| :- | :-: | :-: | :-: |
| <b>Repeat</b> | As many times as possible,<br>giving back as needed | As few times as possible,<br>expanding as needed | As many times as possible,<br>without giving back
| Zero or one | `?` | `??` | `?+` |
| Zero or more | `*` | `*?` | `*+` |
| One or more | `+` | `+?` | `++` |
| *N* or more | `{2,}` | `{2,}?` | `{2,}+` |
| Between *N* and *M* | `{0,5}` | `{0,5}?` | `{0,5}+` |
> Fixed repetition quantifiers behave the same whether they're greedy `{2}`, lazy `{2}?`, or possessive `{2}+`.
> [!NOTE]
> Possessive quantifiers are supported in many other regex flavors. There's a [proposal](https://github.com/tc39/proposal-regexp-atomic-operators) to add them to JavaScript.
### Subroutines
Subroutines are written as `\g<name>` (where *name* refers to a named group), and they treat the referenced group as an independent subpattern that they try to match at the current position. This enables subpattern composition and reuse, which improves readability and maintainability.
The following example illustrates how subroutines and backreferences differ:
```js
// A backreference with \k<name>
regex`(?<prefix>sens|respons)e\ and\ \k<prefix>ibility`
/* Matches:
- 'sense and sensibility'
- 'response and responsibility' */
// A subroutine with \g<name>
regex`(?<prefix>sens|respons)e\ and\ \g<prefix>ibility`
/* Matches:
- 'sense and sensibility'
- 'sense and responsibility'
- 'response and sensibility'
- 'response and responsibility' */
```
Subroutines go beyond the composition benefits of [interpolation](#-interpolation). Apart from the obvious difference that they don't require variables to be defined outside of the regex, they also don't simply insert the referenced subpattern.
1. They can reference groups that themselves contain subroutines, chained to any depth.
2. Any capturing groups that are set during the subroutine call revert to their previous values afterwards.
3. They don't create named captures that are visible outside of the subroutine, so using subroutines doesn't lead to "duplicate capture group name" errors.
To illustrate points 2 and 3, consider:
```js
regex`
(?<double> (?<char>.)\k<char>)
\g<double>
\k<double>
`
```
The backreference `\k<double>` matches whatever was matched by capturing group `(?<double>…)`, regardless of what was matched in between by the subroutine `\g<double>`. For example, this regex matches `'xx!!xx'`, but not `'xx!!!!'`.
<details>
<summary>👉 <b>Show more details</b></summary>
- Subroutines can appear before the groups they reference.
- If there are [duplicate capture names](https://github.com/tc39/proposal-duplicate-named-capturing-groups), subroutines refer to the first instance of the given group (matching the behavior of PCRE and Perl).
- Although subroutines can be chained to any depth, a descriptive error is thrown if they're used recursively. Support for recursion can be added via a plugin (see [*Recursion*](#recursion)).
- Like backreferences, subroutines can't be used *within* character classes.
- As with all extended syntax in `regex`, subroutines are applied after interpolation, giving them maximal flexibility.
</details>
<details>
<summary>👉 <b>Show how to define subpatterns for use by reference only</b></summary>
The following regex matches an IPv4 address such as "192.168.12.123":
```js
const ipv4 = regex`
\b \g<byte> (\. \g<byte>){3} \b
# Define the 'byte' subpattern
(?<byte> 25[0-5] | 2[0-4]\d | 1\d\d | [1-9]?\d){0}
`;
```
Above, the `{0}` quantifier at the end of the `(?<byte>…)` group allows *defining* the group without *matching* it at that position. The subpattern within it can then be used by reference elsewhere within the pattern.
This next regex matches a record with multiple date fields, and captures each value:
```js
const record = regex`
^ Admitted:\ (?<admitted> \g<date>) \n
Released:\ (?<released> \g<date>) $
# Define subpatterns
( (?<date> \g<year>-\g<month>-\g<day>)
(?<year> \d{4})
(?<month> \d{2})
(?<day> \d{2})
){0}
`;
```
Here, the `{0}` quantifier at the end once again prevents matching its group at that position, while enabling all of the named groups within it to be used by reference.
When using a regex to find matches (e.g. via the string `matchAll` method), named groups defined this way appear on each match's `groups` object with the value `undefined` (which is the value for any capturing group that didn't participate in a match). See the next section [*Subroutine definition groups*](#subroutine-definition-groups) for a way to prevent such groups from appearing on the `groups` object.
</details>
> [!NOTE]
> Subroutines are based on the feature in PCRE and Perl. PCRE allows several syntax options including the `\g<name>` used by Regex+, whereas Perl uses `(?&name)`. Ruby also supports subroutines (and uses the `\g<name>` syntax), but it has behavior differences related to capturing and backreferences that arguably make its subroutines less useful.
### Subroutine definition groups
The syntax `(?(DEFINE)…)` can be used at the end of a regex to define subpatterns for use by reference only. When combined with [subroutines](#subroutines), this enables writing regexes in a grammatical way that can significantly improve readability and maintainability.
> Named groups defined within subroutine definition groups don't appear on the `groups` object of matches.
Example:
```js
const re = regex`
^ Admitted:\ (?<admitted> \g<date>) \n
Released:\ (?<released> \g<date>) $
(?(DEFINE)
(?<date> \g<year>-\g<month>-\g<day>)
(?<year> \d{4})
(?<month> \d{2})
(?<day> \d{2})
)
`;
const record = 'Admitted: 2024-01-01\nReleased: 2024-01-03';
const match = record.match(re);
console.log(match.groups);
/* → {
admitted: '2024-01-01',
released: '2024-01-03'
} */
```
> [!NOTE]
> Subroutine definition groups are based on the feature in PCRE and Perl. However, Regex+ supports a stricter version since it limits their placement, quantity, and the top-level syntax that can be used within them.
<details>
<summary>👉 <b>Show more details</b></summary>
- **Quantity:** Only one definition group is allowed per regex, but it can contain any number of named groups and those groups can appear in any order.
- **Placement:** Apart from trailing whitespace and comments (allowed by implicit flag <kbd>x</kbd>), definition groups must appear at the end of their pattern.
- **Contents:** At the top level of definition groups, only named groups, whitespace, and comments are allowed.
- **Duplicate names:** All named groups within definition groups must use unique names.
- **Casing:** The word `DEFINE` must appear in uppercase.
</details>
### Recursion
The official Regex+ plugin [regex-recursion](https://github.com/slevithan/regex-recursion) enables the syntax `(?R)` and `\g<name>` to match recursive/balanced patterns up to a specified max depth (2100).
## 🚩 Flags
Flags are added like this:
```js
regex('gm')`^.+`
```
`RegExp` instances interpolated into the pattern preserve their own flags locally (see [*Interpolating regexes*](#interpolating-regexes)).
### Implicit flags
Flag <kbd>v</kbd> and emulated flags <kbd>x</kbd> and <kbd>n</kbd> are always on when using `regex`, giving your regexes a modern baseline syntax and avoiding the need to continually opt-in to their superior modes.
> For special situations such as when using Regex+ within other tools, implicit flags can be disabled. See: [*Options*](#-options).
### Flag `v`
JavaScript's native flag <kbd>v</kbd> gives you the best level of Unicode support, strict errors, and all the latest regex features like character class set operations and properties of strings (see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicodeSets)). It's always on when using `regex`, which helps avoid numerous Unicode-related bugs, and means there's only one way to parse a regex instead of [four](#-context) (so you only need to remember one set of regex syntax and behavior).
Flag <kbd>v</kbd> is applied to the full pattern after interpolation happens.
> In environments without native support for flag <kbd>v</kbd>, flag <kbd>u</kbd> is automatically used instead while applying <kbd>v</kbd>'s escaping rules so your regexes are forward and backward compatible.
### Flag `x`
Emulated flag <kbd>x</kbd> makes whitespace insignificant and adds support for line comments (starting with `#`), allowing you to freely format your regexes for readability. It's always implicitly on, though it doesn't extend into interpolated `RegExp` instances (to avoid changing their meaning).
Example:
```js
const re = regex`
# Match a date in YYYY-MM-DD format
(?<year> \d{4}) - # Year part
(?<month> \d{2}) - # Month part
(?<day> \d{2}) # Day part
# Escape whitespace and hashes to match them literally
\ # space char
\x20 # space char
\# # hash char
\s # any whitespace char
# Since embedded strings are always matched literally, you can also match
# whitespace by embedding it as a string
${' '}+
# Patterns are directly embedded, so they use free spacing
${pattern`\d + | [a - z]`}
# Interpolated regexes use their own flags, so they preserve their whitespace
${/^Fat cat$/m}
`;
```
> [!NOTE]
> Flag <kbd>x</kbd> is based on the JavaScript [proposal](https://github.com/tc39/proposal-regexp-x-mode) for it as well as support in many other regex flavors. Note that the rules for whitespace *within character classes* are inconsistent across regex flavors, so Regex+ follows the JavaScript proposal and the flag <kbd>xx</kbd> option from Perl and PCRE.
<details>
<summary>👉 <b>Show more details</b></summary>
- Within a character class, `#` is not a special character. It matches a literal `#` and doesn't start a comment. Additionally, the only insignificant whitespace characters within character classes are <kbd>space</kbd> and <kbd>tab</kbd>.
- Outside of character classes, insignificant whitespace includes all Unicode characters matched natively by `\s`.
- Whitespace and comments still separate tokens, so they aren't *ignored*. This is important with e.g. `\0 1`, which matches a null character followed by a literal `1`, rather than throwing as the invalid token `\01` would. Conversely, things like `\x 0A` and `(? :` are errors because the whitespace splits a valid node into incomplete parts.
- Quantifiers that follow whitespace or comments apply to the preceeding token, so `x +` is equivalent to `x+`.
- Whitespace is not insignificant within most enclosed tokens like `\p{…}` and `\u{…}`. The exception is `[\q{…}]`.
- Line comments with `#` do not extend into or beyond interpolation, so interpolation effectively acts as a terminating newline for the comment.
</details>
### Flag `n`
Emulated flag <kbd>n</kbd> gives you *named capture only* mode, which turns unnamed groups `(…)` into noncapturing groups. It's always implicitly on, though it doesn't extend into interpolated `RegExp` instances (to avoid changing their meaning).
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++, Oniguruma, PCRE, Perl, and XRegExp. It's not always specified by a flag, but where it can be (.NET, PCRE, Perl, XRegExp) it's always <kbd>n</kbd>. The option is variously called *explicit capture*, *no auto capture*, *don't capture group*, or *nosubs*. In Regex+, flag <kbd>n</kbd> also prevents using numbered backreferences to refer to named groups, which follows the behavior of C++ and the default handling of Oniguruma and Ruby. Referring to named groups by number is a footgun, and the way that named groups are numbered is inconsistent across regex flavors.
## 🧩 Interpolation
### Interpolating regexes
The meaning of flags (or their absense) on interpolated regexes is preserved. For example, with flag <kbd>i</kbd> (`ignoreCase`):
```js
regex`hello-${/world/i}`
// Matches 'hello-WORLD' but not 'HELLO-WORLD'
regex('i')`hello-${/world/}`
// Matches 'HELLO-world' but not 'HELLO-WORLD'
```
This is also true for other flags that can change how an inner regex is matched: `m` (`multiline`) and `s` (`dotAll`).
> As with all interpolation in `regex`, embedded regexes are sandboxed and treated as complete units. For example, a following quantifier repeats the entire embedded regex rather than just its last token, and top-level alternation in the embedded regex will not break out to affect the meaning of the outer regex. Numbered backreferences within embedded regexes are adjusted to work within the overall pattern.
<details>
<summary>👉 <b>Show more details</b></summary>
- 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. E.g. `new RegExp(/./, 's')`.
</details>
### Interpolating escaped strings
The `regex` tag escapes special characters in interpolated strings (and values coerced to strings). This escaping is done in a context-aware and safe way that prevents changing the meaning or error status of characters outside the interpolated string.
> 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.
As a result, `regex` is a safe and context-aware alternative to JavaScript proposal [`RegExp.escape`](https://github.com/tc39/proposal-regex-escaping).
```js
// Instead of
RegExp.escape(str)
// You can say
regex`${str}`.source
// Instead of
new RegExp(`^(?:${RegExp.escape(str)})+$`)
// You can say
regex`^${str}+$`
// Instead of
new RegExp(`[a-${RegExp.escape(str)}]`, 'u') // Flag u/v required to avoid bugs
// You can say
regex`[a-${str}]`
// Given the context at the end of a range, throws if more than one char in str
// Instead of
new RegExp(`[\\w--[${RegExp.escape(str)}]]`, 'v') // Set subtraction
// You can say
regex`[\w--${str}]`
```
Some examples of where context awareness comes into play:
- A `~` is not escaped at the top level, but it must be escaped within character classes if it's immediately preceded or followed by another `~` (in or outside of the interpolation) which would turn it into a reserved UnicodeSets double punctuator.
- Leading digits must be escaped if they're preceded by a numbered backreference or `\0`, else `RegExp` throws (or in Unicode-unaware mode they might turn into octal escapes).
- Letters `A`-`Z` and `a`-`z` must be escaped if preceded by uncompleted token `\c`, else they'll convert what should be an error into a valid token that probably doesn't match what you expect.
- You can't escape your way out of protecting against a preceding unescaped `\`. Doing nothing could turn e.g. `w` into `\w` and introduce a bug, but then escaping the first character wouldn't prevent the `\` from mangling it, and if you escaped the preceding `\` elsewhere in your code you'd change its meaning.
These and other issues (including the effects of current and potential future flags like <kbd>x</kbd>) make escaping without context unsafe to use at arbitrary positions in a regex, or at least complicated to get right. The existing popular regex escaping libraries don't even attempt to handle these kinds of issues.
`regex` solves all of this via context awareness. So instead of remembering anything above, you should just switch to always safely escaping regex syntax via `regex`.
### Interpolating partial patterns
As an alternative to interpolating `RegExp` instances, you might sometimes want to interpolate partial regex patterns as strings. Some example use cases:
- Adding a pattern inside a character class (not allowed for `RegExp` instances since their top-level syntax context doesn't match).
- When you don't want the pattern to specify its own, local flags.
- Composing a dynamic number of strings escaped via `regex` interpolation.
- Dynamically adding backreferences without their corresponding captures (which wouldn't be valid as a standalone `RegExp`).
For all of these cases, you can `import {pattern} from 'regex'` and then interpolate `pattern(str)` to avoid escaping special characters in the string or creating an intermediary `RegExp` instance. You can also use `` pattern`` `` as a tag, as shorthand for ``pattern(String.raw``)``.
Apart from edge cases, `pattern` just embeds the provided string or other value directly. But because it handles the edge cases, patterns can safely be interpolated anywhere in a regex without worrying about their meaning being changed by (or making unintended changes in meaning to) the surrounding expression.
> As with all interpolation in `regex`, patterns are sandboxed and treated as complete units. This is relevant e.g. if a pattern is followed by a quantifier, if it contains top-level alternation, or if it's bordered by a character class range, subtraction, or intersection operator.
If you want to understand the handling of interpolated patterns more deeply, let's look at some edge cases…
<details>
<summary>👉 <b>Show some edge cases</b></summary>
First, let's consider:
```js
regex`[${pattern`^`}a]`
regex`[a${pattern`^`}]`
```
Although `[^…]` is a negated character class, `^` *within* a class doesn't need to be escaped, even with the strict escaping rules of flags <kbd>u</kbd> and <kbd>v</kbd>.
Both of these examples therefore match a literal `^` or `a`. The interpolated patterns don't change the meaning of the surrounding character class. However, note that the `^` is not simply escaped, as it would be with `` regex`[${'^'}a]` ``. You can see this by the fact that embedding `` pattern`^^` `` in a character class correctly leads to an "invalid set operation" error due to the use of a reserved double-punctuator.
> If you wanted to dynamically choose whether to negate a character class, you could put the whole character class inside the pattern.
Moving on, the following lines all throw because otherwise the embedded patterns would break out of their interpolation sandboxes and change the meaning of surrounding syntax:
```js
regex`(${pattern(')')})`
regex`[${pattern(']')}]`
regex`[${pattern('a\\')}]]`
```
But these are fine since they don't break out:
```js
regex`(${pattern('()')})`
regex`[\w--${pattern('[_]')}]`
regex`[${pattern('\\\\')}]`
```
Patterns can be embedded within any token scope:
```js
// Not using `pattern` for values that are not escaped anyway, but the behavior
// would be the same if you did
regex`.{1,${6}}`
regex`\p{${'Letter'}}`
regex`\u{${'000A'}}`
regex`(?<${'name'}>…)\k<${'name'}>`
regex`[a-${'z'}]`
regex`[\w--${'_'}]`
```
But again, changing the meaning or error status of characters outside the interpolation is an error:
```js
// Not using `pattern` for values that are not escaped anyway
/* 1.*/ regex`\u${'000A'}`
/* 2.*/ regex`\u{${pattern`A}`}`
/* 3.*/ regex`(${pattern`?:`}…)`
```
These last examples are all errors due to the corresponding reasons below:
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 interpolated pattern is not allowed to break out of its sandbox.
3. The group opening `(` can't be quantified with `?`.
> Characters outside the interpolation such as a preceding, unescaped `\` or an escaped number also can't change the meaning of tokens inside the embedded pattern.
And since interpolated values are handled as complete units, consider the following:
```js
// This works fine
regex`[\0-${pattern`\cZ`}]`
// But this is an error since you can't create a range from 'a' to the set 'de'
regex`[a-${'de'}]`
// It's the same as if you tried to use /[a-[de]]/v
// Instead, use either of
regex`[a-${'d'}${'e'}]`
regex`[a-${'d'}e]`
// These are equivalent to /[a-de]/ or /[[a-d][e]]/v
```
</details>
> Implementation note: `pattern` returns an object with a custom `toString` that simply returns `String(value)`.
> Patterns are not intended as an intermediate regex type. You can think of `pattern` as a directive to the `regex` tag: treat this string as a partial pattern rather than a string to be matched literally.
### Interpolation principles
The above descriptions of interpolation might feel complex. But there are three simple rules that guide the behavior in all cases:
1. Interpolation never changes the meaning or error status of characters outside of the interpolation, and vice versa.
2. Interpolated values are always aware of the context of where they're embedded.
3. When relevant, interpolated values are always treated as complete units.
> Examples where rule #3 is relevant: With following quantifiers, if they contain top-level alternation or numbered backreferences, or if they're placed in a character class range or set operation.
### Interpolation contexts
<table>
<tr>
<th>Context</th>
<th>Example</th>
<th>String / coerced</th>
<th>Pattern</th>
<th>RegExp</th>
</tr>
<tr valign="top">
<td>Default</td>
<td><code>regex`${'^.+'}`</code></td>
<td>&nbsp;Sandboxed <br>&nbsp;Atomized <br>&nbsp;Escaped</td>
<td>&nbsp;Sandboxed <br>&nbsp;Atomized</td>
<td>&nbsp;Sandboxed <br>&nbsp;Atomized <br>&nbsp;Backrefs adjusted <br>&nbsp;Flags localized</td>
</tr>
<tr valign="top">
<td>Character class: <code>[…]</code>, <code>[^…]</code>, <code>[[…]]</code>, etc.</td>
<td><code>regex`[${'a-z'}]`</code></td>
<td>&nbsp;Sandboxed <br>&nbsp;Atomized <br>&nbsp;Escaped</td>
<td>&nbsp;Sandboxed <br>&nbsp;Atomized</td>
<td><i>Error</i></td>
</tr>
<tr valign="top">
<td>Interval quantifier: <code>{…}</code></td>
<td><code>regex`.{1,${5}}`</code></td>
<td rowspan="3">&nbsp;Sandboxed <br>&nbsp;Escaped</td>
<td rowspan="3">&nbsp;Sandboxed</td>
<td rowspan="3"><i>Error</i></td>
</tr>
<tr valign="top">
<td>Enclosed token: <code>\p{…}</code>, <code>\P{…}</code>, <code>\u{…}</code>, <code>[\q{…}]</code></td>
<td><code>regex`\u{${'A0'}}`</code></td>
</tr>
<tr valign="top">
<td>Group name: <code>(?<…>)</code>, <code>\k<…></code>, <code>\g<…></code></td>
<td><code>regex`…\k<${'a'}>`</code></td>
</tr>
</table>
<details>
<summary>👉 <b>Show more details</b></summary>
- *Atomized* means that the value is treated as a complete unit; it isn't related to the *atomic groups* feature. For example, in default context, `${foo}*` matches any number of `foo`; not just its last token. In character class context, subtraction and intersection operators apply to the entire atom.
- *Sandboxed* means that the value can't change the meaning or error status of characters outside of the interpolation, and vice versa.
- Character classes have a sub-context on the borders of ranges. Only one character node (e.g. `a` or `\u0061`) can be interpolated at these positions.
- Numbers interpolated into an enclosed `\u{…}` context are converted to hexadecimal.
- The implementation details vary for how `regex` accomplishes sandboxing and atomization, based on the details of the specific pattern. But the concepts should always hold up.
</details>
## 🔩 Options
Typically, `regex` is used as follows:
```js
regex`…` // Without flags
regex('gi')`…` // With flags
```
However, several options are available that can be provided via an options object in place of the flags argument. These options aren't usually needed, and are primarily intended for use within other tools.
Following are the available options and their default values:
```js
regex({
flags: '',
subclass: false,
plugins: [],
unicodeSetsPlugin: <function>,
disable: {
x: false,
n: false,
v: false,
atomic: false,
subroutines: false,
},
force: {
v: false,
},
})`…`;
```
<details>
<summary>👉 <b>Show details for each option</b></summary>
**`flags`** — For providing flags when using an options object.
**`subclass`** — When `true`, the resulting regex is constructed using a `RegExp` subclass that avoids edge case issues with numbered backreferences. Without subclassing, submatches referenced *by number* from outside of the regex (e.g. in replacement strings) might reference the wrong values, because `regex`'s emulation of extended syntax (including atomic groups and subroutines) can add unnamed captures to generated regex source that might affect group numbering.
Context: `regex`'s implicit flag <kbd>n</kbd> (*named capture only* mode) means that all captures have names, so normally there's no need to reference submatches by number. In fact, flag <kbd>n</kbd> *prevents* you from doing so within the regex. And even in edge cases (such as when interpolating `RegExp` instances with numbered backreferences, or when flag <kbd>n</kbd> is explicitly disabled), any numbered backreferences within the regex are automatically adjusted to work correctly. However, issues can arise if you reference submatches by number (instead of their group names) from outside of the regex. Setting `subclass: true` resolves this, since the subclass knows about added "emulation groups" and automatically adjusts match results in all contexts.
> This option isn't enabled by default because it would prevent Regex+'s Babel plugin from emitting regex literals. It also has a small performance cost, and is rarely needed. The primary use case is tools that use `regex` internally with flag <kbd>n</kbd> disabled.
**`plugins`** — An array of functions. Plugins are called in order, after applying emulated flags and interpolation, but before the built-in plugins for extended syntax. This means that plugins can output extended syntax like atomic groups and subroutines. Plugins are expected to return an updated pattern string, and are called with two arguments:
1. The pattern, as processed so far by preceding plugins, etc.
2. An object with a `flags` property that includes the native (non-emulated) flags that will be used by the regex.
The final result after running all plugins is provided to the `RegExp` constructor.
> The tiny [regex-utilities](https://github.com/slevithan/regex-utilities) library is intended for use in plugins, and can make it easier to work with regex syntax.
**`unicodeSetsPlugin`** — A plugin function that's used when flag <kbd>v</kbd> isn't supported natively, or when implicit flag <kbd>v</kbd> is disabled. The default value is a built-in function that provides basic backward compatibility by applying flag <kbd>v</kbd>'s escaping rules and throwing on use of <kbd>v</kbd>-only syntax (nested character classes, set subtraction/intersection, etc.).
- Setting `unicodeSetsPlugin` to `null` prevents `regex` from applying flag <kbd>v</kbd>'s escaping rules. This can be useful in combination with option `disable: {v: true}` for tools that want to use `regex`'s extended syntax and/or flags but need to accept input with flag <kbd>u</kbd>'s escaping rules.
- Regex+ is not primarily a backward compatibility library, so in order to remain lightweight, it doesn't transpile flag <kbd>v</kbd>'s new features out of the box. By replacing the default function, you can add backward compatible support for these features. See also: [*Compatibility*](#-compatibility).
- This plugin runs last, which means it's possible to wrap an existing library (e.g. [regexpu-core](https://github.com/mathiasbynens/regexpu-core), used by Babel to [transpile <kbd>v</kbd>](https://babel.dev/docs/babel-plugin-transform-unicode-sets-regex)), without the library needing to understand `regex`'s extended syntax.
**`disable`** — A set of options that can be individually disabled by setting their values to `true`.
- **`x`** — Disables implicit, emulated [flag <kbd>x</kbd>](#flag-x).
- **`n`** — Disables implicit, emulated [flag <kbd>n</kbd>](#flag-n). Note that, although it's safe to use unnamed captures and numbered backreferences within a regex when flag <kbd>n</kbd> is disabled, referencing submatches by number from *outside* a regex (e.g. in replacement strings) can result in incorrect values because extended syntax (atomic groups and subroutines) might add "emulation groups" to generated regex source. It's therefore recommended to enable the `subclass` option when disabling `n`.
- **`v`** — Disables implicit [flag <kbd>v</kbd>](#flag-v) even when it's supported natively, resulting in flag <kbd>u</kbd> being added instead (in combination with the `unicodeSetsPlugin`).
- **`atomic`** — Disables [atomic groups](#atomic-groups) and [possessive quantifiers](#possessive-quantifiers), resulting in a syntax error if they're used.
- **`subroutines`** — Disables [subroutines](#subroutines) and [subroutine definition groups](#subroutine-definition-groups), resulting in a syntax error if they're used.
**`force`** — Options that, if set to `true`, override default settings (as well as options set on the `disable` object).
- **`v`** — Forces the use of flag <kbd>v</kbd> even when it's not supported natively (resulting in an error).
</details>
### Returning a string
Function `rewrite` returns an object with properties `expression` and `flags` as strings, rather than returning a `RegExp` instance. This can be useful when you want to apply postprocessing to the output.
```js
import {rewrite} from 'regex';
rewrite('^ (ab | cd)', {flags: 'm'});
// → {expression: '^(?:ab|cd)', flags: 'mv'}
```
`rewrite` shares all of `regex`'s options (described above) except `subclass`. Providing the resulting `expression` and `flags` to the `RegExp` constructor produces the same result as using the `regex` tag.
> Since `rewrite` isn't a template tag, it doesn't provide context-aware interpolation and doesn't automatically handle input as a raw string (you need to escape your backslashes).
## ⚡ Performance
`regex` transpiles its input to native `RegExp` instances. Therefore regexes created by `regex` perform equally as fast as native regexes. The use of `regex` can also be transpiled via a [Babel plugin](https://github.com/slevithan/babel-plugin-transform-regex), avoiding the tiny overhead of transpiling at runtime.
For regexes that rely on or have the potential to trigger heavy backtracking, you can dramatically improve beyond native performance via `regex`'s [atomic groups](#atomic-groups) and [possessive quantifiers](#possessive-quantifiers).
## 🪶 Compatibility
`regex` uses flag <kbd>v</kbd> (`unicodeSets`) when it's supported natively. Flag <kbd>v</kbd> is supported by Node.js 20 and 2023-era browsers ([compat table](https://caniuse.com/mdn-javascript_builtins_regexp_unicodesets)). When <kbd>v</kbd> isn't available, flag <kbd>u</kbd> is automatically used instead while enforcing <kbd>v</kbd>'s escaping rules, which extends support to Node.js 14 and 2020-era browsers or earlier. The exception is Safari, which is supported starting with v16.4 (released 2023-03-27).
The following edge cases rely on modern JavaScript features:
- To ensure atomization, `regex` uses nested character classes (which require flag <kbd>v</kbd>) when interpolating more than one token at a time *inside character classes*. A descriptive error is thrown when this isn't supported, which you can avoid by not interpolating multi-token patterns/strings into character classes. There's also an easy workaround: put the whole character class in a `pattern` and interpolate a string into the pattern.
- Using an interpolated `RegExp` instance with a different value for flag <kbd>i</kbd> than its outer regex relies on [pattern modifiers](https://github.com/tc39/proposal-regexp-modifiers), an ES2025 feature available in Node.js 23, Chrome/Edge 125, Firefox 132, and Opera 111. 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 doesn't rely on this feature.
## 🙋 FAQ
<details name="faq">
<summary><b>How are you comparing regex flavors?</b></summary>
The claim that JavaScript with the Regex+ library is among the best regex flavors is based on a holistic view. Following are some of the aspects considered:
1. **Performance:** An important aspect, but not the main one since mature regex implementations are generally pretty fast. JavaScript is strong on regex performance (at least considering V8's Irregexp engine and JavaScriptCore), but it uses a backtracking engine that's missing any syntax for backtracking control — a major limitation that makes ReDoS vulnerability more common. `regex` adds atomic groups to native JavaScript regexes, which is a solution to this problem and therefore can dramatically improve performance.
2. **Support for advanced features** that handle common or important use cases: Here, JavaScript stepped up its game with ES2018 and ES2024. JavaScript is now best in class for some features like lookbehind (with it's infinite-length support) and Unicode properties (with multicharacter "properties of strings", set subtraction and intersection, and script extensions). These features are either not supported or not as robust in many other flavors.
3. **Ability to write readable and maintainable patterns:** Here, native JavaScript has long been the worst of the major flavors, since it lacks the <kbd>x</kbd> (extended) flag that allows insignificant whitespace and comments. `regex` not only adds <kbd>x</kbd> (and turns it on by default), but it additionally adds regex subroutines and subroutine definition groups (matched only by PCRE and Perl, although some other flavors have inferior versions) which enable powerful subpattern composition and reuse. And it includes context-aware interpolation of `RegExp` instances, escaped strings, and partial patterns, all of which can also help with composition and readability.
</details>
<details name="faq">
<summary><b>Can <code>regex</code> be called as a function instead of using it with backticks?</b></summary>
Yes, but you might not need to. If you want to use `regex` with dynamic input, you can interpolate a `pattern` call as the full expression. For example:
```js
import {regex, pattern} from 'regex';
const str = '…';
const re = regex('g')`${pattern(str)}`;
```
If you prefer to call `regex` as a function (rather than using it as a template tag), that requires explicitly providing the raw template strings array, as follows:
```js
import {regex} from 'regex';
const str = '…';
const re = regex('g')({raw: [str]});
```
</details>
<details name="faq">
<summary><b>Why are flags added via <code>regex('g')`…`</code> rather than <code>regex`/…/g`</code>?</b></summary>
The alternative syntax isn't used because it has several disadvantages:
- It doesn't match the `RegExp` constructor's syntax.
- It doesn't match regex literal syntax either, since there are no multiline regex literals (and they're not planned for the future), plus regex literals don't allow unescaped `/` outside of character classes.
- Flags-up-front can be more readable, especially with long or multiline regexes that make flags easy to miss when they're at the end. And since some flags change the meaning of regex syntax, it can help to read them first.
- It would most likely be incompatible if a standardized regex template tag was added to the JavaScript language in the future. To date, TC39 discussions about a standardized tag for regexes have not favored the `` `/…/g` `` format.
</details>
## 🏷️ About
Regex+ was created by [Steven Levithan](https://github.com/slevithan) and [contributors](https://github.com/slevithan/regex/graphs/contributors). Inspiration included [PCRE](https://github.com/PCRE2Project/pcre2), [XRegExp](https://github.com/slevithan/xregexp), and [regexp-make-js](https://github.com/mikesamuel/regexp-make-js).
If you want to support this project, I'd love your help by contributing improvements, sharing it with others, or [sponsoring](https://github.com/sponsors/slevithan) ongoing development.
© 2024present. MIT License.
<!-- Badges -->
[npm-version-src]: https://img.shields.io/npm/v/regex?color=78C372
[npm-version-href]: https://npmjs.com/package/regex
[npm-downloads-src]: https://img.shields.io/npm/dm/regex?color=78C372
[npm-downloads-href]: https://npmjs.com/package/regex
[bundle-src]: https://img.shields.io/bundlejs/size/regex?color=78C372&label=minzip
[bundle-href]: https://bundlejs.com/?q=regex&treeshake=[*]

16
node_modules/regex/dist/cjs/atomic.d.ts generated vendored Normal file
View File

@@ -0,0 +1,16 @@
/**
Apply transformations for atomic groups: `(?>…)`.
@param {string} expression
@param {import('./regex.js').PluginData} [data]
@returns {string}
*/
export function atomic(expression: string, data?: import("./regex.js").PluginData): string;
/**
Transform posessive quantifiers into atomic groups. The posessessive quantifiers are:
`?+`, `*+`, `++`, `{N}+`, `{N,}+`, `{N,N}+`.
This follows Java, PCRE, Perl, and Python.
Possessive quantifiers in Oniguruma and Onigmo are only: `?+`, `*+`, `++`.
@param {string} expression
@returns {string}
*/
export function possessive(expression: string): string;

7
node_modules/regex/dist/cjs/backcompat.d.ts generated vendored Normal file
View File

@@ -0,0 +1,7 @@
/**
Applies flag v rules when using flag u, for forward compatibility.
Assumes flag u and doesn't worry about syntax errors that are caught by it.
@param {string} expression
@returns {string}
*/
export function backcompatPlugin(expression: string): string;

4
node_modules/regex/dist/cjs/flag-n.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
export function flagNPreprocessor(value: import("./utils.js").InterpolatedValue, runningContext: import("./utils.js").RunningContext, options: Required<import("./utils.js").RegexTagOptions>): {
transformed: string;
runningContext: import("./utils.js").RunningContext;
};

10
node_modules/regex/dist/cjs/flag-x.d.ts generated vendored Normal file
View File

@@ -0,0 +1,10 @@
/**
Remove `(?:)` token separators (most likely added by flag x) in cases where it's safe to do so.
@param {string} expression
@returns {string}
*/
export function clean(expression: string): string;
export function flagXPreprocessor(value: import("./utils.js").InterpolatedValue, runningContext: import("./utils.js").RunningContext, options: Required<import("./utils.js").RegexTagOptions>): {
transformed: string;
runningContext: import("./utils.js").RunningContext;
};

2
node_modules/regex/dist/cjs/internals.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export { atomic, possessive } from "./atomic.js";
export { emulationGroupMarker, RegExpSubclass } from "./subclass.js";

1
node_modules/regex/dist/cjs/package.json generated vendored Normal file
View File

@@ -0,0 +1 @@
{"type":"commonjs"}

43
node_modules/regex/dist/cjs/pattern.d.ts generated vendored Normal file
View File

@@ -0,0 +1,43 @@
export class Pattern {
/** @param {string} value */
constructor(value: string);
/** @returns {string} */
toString(): string;
#private;
}
/**
Returns a value that can be interpolated into a `regex` template string without having its special
characters escaped.
Can be called as a function or template tag:
- `pattern(value)` - String or value coerced to string.
- `` pattern`…` `` - Same as ``pattern(String.raw`…`)``.
@overload
@param {string | number} value
@returns {Pattern}
@overload
@param {TemplateStringsArray} template
@param {...string} substitutions
@returns {Pattern}
*/
export function pattern(value: string | number): Pattern;
/**
Returns a value that can be interpolated into a `regex` template string without having its special
characters escaped.
Can be called as a function or template tag:
- `pattern(value)` - String or value coerced to string.
- `` pattern`…` `` - Same as ``pattern(String.raw`…`)``.
@overload
@param {string | number} value
@returns {Pattern}
@overload
@param {TemplateStringsArray} template
@param {...string} substitutions
@returns {Pattern}
*/
export function pattern(template: TemplateStringsArray, ...substitutions: string[]): Pattern;

96
node_modules/regex/dist/cjs/regex.d.ts generated vendored Normal file
View File

@@ -0,0 +1,96 @@
export type InterpolatedValue = string | RegExp | Pattern | number;
export type PluginData = {
flags?: string;
useEmulationGroups?: boolean;
};
export type RawTemplate = TemplateStringsArray | {
raw: Array<string>;
};
export type RegexTagOptions = {
flags?: string;
subclass?: boolean;
plugins?: Array<(expression: string, data: PluginData) => string>;
unicodeSetsPlugin?: ((expression: string, data: PluginData) => string) | null;
disable?: {
x?: boolean;
n?: boolean;
v?: boolean;
atomic?: boolean;
subroutines?: boolean;
};
force?: {
v?: boolean;
};
};
export type RegexTag<T> = {
(template: RawTemplate, ...substitutions: ReadonlyArray<InterpolatedValue>): T;
(flags?: string): RegexTag<T>;
(options: RegexTagOptions & {
subclass?: false;
}): RegexTag<T>;
(options: RegexTagOptions & {
subclass: true;
}): RegexTag<RegExpSubclass>;
};
export type RegexFromTemplate<T> = {
(options: RegexTagOptions, template: RawTemplate, ...substitutions: ReadonlyArray<InterpolatedValue>): T;
};
import { pattern } from './pattern.js';
/**
@typedef {string | RegExp | Pattern | number} InterpolatedValue
@typedef {{
flags?: string;
useEmulationGroups?: boolean;
}} PluginData
@typedef {TemplateStringsArray | {raw: Array<string>}} RawTemplate
@typedef {{
flags?: string;
subclass?: boolean;
plugins?: Array<(expression: string, data: PluginData) => string>;
unicodeSetsPlugin?: ((expression: string, data: PluginData) => string) | null;
disable?: {
x?: boolean;
n?: boolean;
v?: boolean;
atomic?: boolean;
subroutines?: boolean;
};
force?: {
v?: boolean;
};
}} RegexTagOptions
*/
/**
@template T
@typedef RegexTag
@type {{
(template: RawTemplate, ...substitutions: ReadonlyArray<InterpolatedValue>): T;
(flags?: string): RegexTag<T>;
(options: RegexTagOptions & {subclass?: false}): RegexTag<T>;
(options: RegexTagOptions & {subclass: true}): RegexTag<RegExpSubclass>;
}}
*/
/**
Template tag for constructing a regex with extended syntax and context-aware interpolation of
regexes, strings, and patterns.
Can be called in several ways:
1. `` regex`…` `` - Regex pattern as a raw string.
2. `` regex('gi')`…` `` - To specify flags.
3. `` regex({flags: 'gi'})`…` `` - With options.
@type {RegexTag<RegExp>}
*/
export const regex: RegexTag<RegExp>;
/**
Returns the processed expression and flags as strings.
@param {string} expression
@param {RegexTagOptions} [options]
@returns {{expression: string; flags: string;}}
*/
export function rewrite(expression?: string, options?: RegexTagOptions): {
expression: string;
flags: string;
};
import { Pattern } from './pattern.js';
import { RegExpSubclass } from './subclass.js';
export { pattern };

1288
node_modules/regex/dist/cjs/regex.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

7
node_modules/regex/dist/cjs/regex.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

28
node_modules/regex/dist/cjs/subclass.d.ts generated vendored Normal file
View File

@@ -0,0 +1,28 @@
export const emulationGroupMarker: "$E$";
/**
Works the same as JavaScript's native `RegExp` constructor in all contexts, but automatically
adjusts matches and subpattern indices (with flag `d`) to account for injected emulation groups.
*/
export class RegExpSubclass extends RegExp {
/**
@param {string | RegExpSubclass} expression
@param {string} [flags]
@param {{useEmulationGroups: boolean;}} [options]
*/
constructor(expression: string | RegExpSubclass, flags?: string, options?: {
useEmulationGroups: boolean;
});
/**
@private
@type {Array<{
exclude: boolean;
transfer?: number;
}> | undefined}
*/
private _captureMap;
/**
@private
@type {Record<number, string> | undefined}
*/
private _namesByIndex;
}

12
node_modules/regex/dist/cjs/subroutines.d.ts generated vendored Normal file
View File

@@ -0,0 +1,12 @@
export type NamedCapturingGroupsMap = Map<string, {
isUnique: boolean;
contents?: string;
groupNum?: number;
numCaptures?: number;
}>;
/**
@param {string} expression
@param {import('./regex.js').PluginData} [data]
@returns {string}
*/
export function subroutines(expression: string, data?: import("./regex.js").PluginData): string;

9
node_modules/regex/dist/cjs/utils-internals.d.ts generated vendored Normal file
View File

@@ -0,0 +1,9 @@
export const noncapturingDelim: any;
/**
@param {string} str
@param {number} pos
@param {string} oldValue
@param {string} newValue
@returns {string}
*/
export function spliceStr(str: string, pos: number, oldValue: string, newValue: string): string;

112
node_modules/regex/dist/cjs/utils.d.ts generated vendored Normal file
View File

@@ -0,0 +1,112 @@
export type RunningContext = {
regexContext: string;
charClassContext: string;
charClassDepth: number;
lastPos: number;
};
export type InterpolatedValue = import("./regex.js").InterpolatedValue;
export type RawTemplate = import("./regex.js").RawTemplate;
export type RegexTagOptions = import("./regex.js").RegexTagOptions;
export type Preprocessor = (value: InterpolatedValue, runningContext: RunningContext, options: Required<RegexTagOptions>) => {
transformed: string;
runningContext: RunningContext;
};
/**
@param {string} expression
@param {number} precedingCaptures
@returns {string}
*/
export function adjustNumberedBackrefs(expression: string, precedingCaptures: number): string;
export const capturingDelim: any;
export namespace CharClassContext {
let DEFAULT: string;
let ENCLOSED_P: string;
let ENCLOSED_Q: string;
let ENCLOSED_U: string;
let INVALID_INCOMPLETE_TOKEN: string;
let RANGE: string;
}
export function containsCharClassUnion(charClassPattern: any): boolean;
/**
@param {string} expression
@returns {number}
*/
export function countCaptures(expression: string): number;
export const doublePunctuatorChars: "&!#$%*+,.:;<=>?@^`~";
export const enclosedTokenCharClassContexts: any;
export const enclosedTokenRegexContexts: any;
export const envSupportsFlagGroups: boolean;
export const envSupportsFlagV: boolean;
/**
Escape special characters for the given context, assuming flag v.
@param {string} str String to escape
@param {'DEFAULT' | 'CHAR_CLASS'} context `Context` option from lib `regex-utilities`
@returns {string} Escaped string
*/
export function escapeV(str: string, context: "DEFAULT" | "CHAR_CLASS"): string;
export function getBreakoutChar(expression: any, regexContext: any, charClassContext: any): any;
/**
@typedef {{
regexContext: string;
charClassContext: string;
charClassDepth: number;
lastPos: number;
}} RunningContext
*/
/**
Accepts and returns its full state so it doesn't have to reprocess parts that have already been
seen. Assumes flag v and doesn't worry about syntax errors that are caught by it.
@param {string} incompleteExpression
@param {Partial<RunningContext>} [runningContext]
@returns {RunningContext}
*/
export function getEndContextForIncompleteExpression(incompleteExpression: string, runningContext?: Partial<RunningContext>): RunningContext;
export const namedCapturingDelim: any;
/**
@typedef {import('./regex.js').InterpolatedValue} InterpolatedValue
@typedef {import('./regex.js').RawTemplate} RawTemplate
@typedef {import('./regex.js').RegexTagOptions} RegexTagOptions
@typedef {(
value: InterpolatedValue,
runningContext: RunningContext,
options: Required<RegexTagOptions>
) => {
transformed: string;
runningContext: RunningContext;
}} Preprocessor
*/
/**
Returns transformed versions of a template and substitutions, using the given preprocessor. Only
processes substitutions that are instanceof `Pattern`.
@param {RawTemplate} template
@param {ReadonlyArray<InterpolatedValue>} substitutions
@param {Preprocessor} preprocessor
@param {Required<RegexTagOptions>} options
@returns {{template: RawTemplate; substitutions: ReadonlyArray<InterpolatedValue>;}}
*/
export function preprocess(template: RawTemplate, substitutions: ReadonlyArray<InterpolatedValue>, preprocessor: Preprocessor, options: Required<RegexTagOptions>): {
template: RawTemplate;
substitutions: ReadonlyArray<InterpolatedValue>;
};
export namespace RegexContext {
let DEFAULT_1: string;
export { DEFAULT_1 as DEFAULT };
export let CHAR_CLASS: string;
let ENCLOSED_P_1: string;
export { ENCLOSED_P_1 as ENCLOSED_P };
let ENCLOSED_U_1: string;
export { ENCLOSED_U_1 as ENCLOSED_U };
export let GROUP_NAME: string;
export let INTERVAL_QUANTIFIER: string;
let INVALID_INCOMPLETE_TOKEN_1: string;
export { INVALID_INCOMPLETE_TOKEN_1 as INVALID_INCOMPLETE_TOKEN };
}
export function sandboxLoneCharClassCaret(str: any): any;
export function sandboxLoneDoublePunctuatorChar(str: any): any;
/**
Converts `\0` tokens to `\x00` in the given context.
@param {string} str
@param {'DEFAULT' | 'CHAR_CLASS'} [context] `Context` option from lib `regex-utilities`
@returns {string}
*/
export function sandboxUnsafeNulls(str: string, context?: "DEFAULT" | "CHAR_CLASS"): string;

16
node_modules/regex/dist/esm/atomic.d.ts generated vendored Normal file
View File

@@ -0,0 +1,16 @@
/**
Apply transformations for atomic groups: `(?>…)`.
@param {string} expression
@param {import('./regex.js').PluginData} [data]
@returns {string}
*/
export function atomic(expression: string, data?: import("./regex.js").PluginData): string;
/**
Transform posessive quantifiers into atomic groups. The posessessive quantifiers are:
`?+`, `*+`, `++`, `{N}+`, `{N,}+`, `{N,N}+`.
This follows Java, PCRE, Perl, and Python.
Possessive quantifiers in Oniguruma and Onigmo are only: `?+`, `*+`, `++`.
@param {string} expression
@returns {string}
*/
export function possessive(expression: string): string;

7
node_modules/regex/dist/esm/backcompat.d.ts generated vendored Normal file
View File

@@ -0,0 +1,7 @@
/**
Applies flag v rules when using flag u, for forward compatibility.
Assumes flag u and doesn't worry about syntax errors that are caught by it.
@param {string} expression
@returns {string}
*/
export function backcompatPlugin(expression: string): string;

4
node_modules/regex/dist/esm/flag-n.d.ts generated vendored Normal file
View File

@@ -0,0 +1,4 @@
export function flagNPreprocessor(value: import("./utils.js").InterpolatedValue, runningContext: import("./utils.js").RunningContext, options: Required<import("./utils.js").RegexTagOptions>): {
transformed: string;
runningContext: import("./utils.js").RunningContext;
};

10
node_modules/regex/dist/esm/flag-x.d.ts generated vendored Normal file
View File

@@ -0,0 +1,10 @@
/**
Remove `(?:)` token separators (most likely added by flag x) in cases where it's safe to do so.
@param {string} expression
@returns {string}
*/
export function clean(expression: string): string;
export function flagXPreprocessor(value: import("./utils.js").InterpolatedValue, runningContext: import("./utils.js").RunningContext, options: Required<import("./utils.js").RegexTagOptions>): {
transformed: string;
runningContext: import("./utils.js").RunningContext;
};

2
node_modules/regex/dist/esm/internals.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export { atomic, possessive } from "./atomic.js";
export { emulationGroupMarker, RegExpSubclass } from "./subclass.js";

1
node_modules/regex/dist/esm/package.json generated vendored Normal file
View File

@@ -0,0 +1 @@
{"type":"module"}

43
node_modules/regex/dist/esm/pattern.d.ts generated vendored Normal file
View File

@@ -0,0 +1,43 @@
export class Pattern {
/** @param {string} value */
constructor(value: string);
/** @returns {string} */
toString(): string;
#private;
}
/**
Returns a value that can be interpolated into a `regex` template string without having its special
characters escaped.
Can be called as a function or template tag:
- `pattern(value)` - String or value coerced to string.
- `` pattern`…` `` - Same as ``pattern(String.raw`…`)``.
@overload
@param {string | number} value
@returns {Pattern}
@overload
@param {TemplateStringsArray} template
@param {...string} substitutions
@returns {Pattern}
*/
export function pattern(value: string | number): Pattern;
/**
Returns a value that can be interpolated into a `regex` template string without having its special
characters escaped.
Can be called as a function or template tag:
- `pattern(value)` - String or value coerced to string.
- `` pattern`…` `` - Same as ``pattern(String.raw`…`)``.
@overload
@param {string | number} value
@returns {Pattern}
@overload
@param {TemplateStringsArray} template
@param {...string} substitutions
@returns {Pattern}
*/
export function pattern(template: TemplateStringsArray, ...substitutions: string[]): Pattern;

96
node_modules/regex/dist/esm/regex.d.ts generated vendored Normal file
View File

@@ -0,0 +1,96 @@
export type InterpolatedValue = string | RegExp | Pattern | number;
export type PluginData = {
flags?: string;
useEmulationGroups?: boolean;
};
export type RawTemplate = TemplateStringsArray | {
raw: Array<string>;
};
export type RegexTagOptions = {
flags?: string;
subclass?: boolean;
plugins?: Array<(expression: string, data: PluginData) => string>;
unicodeSetsPlugin?: ((expression: string, data: PluginData) => string) | null;
disable?: {
x?: boolean;
n?: boolean;
v?: boolean;
atomic?: boolean;
subroutines?: boolean;
};
force?: {
v?: boolean;
};
};
export type RegexTag<T> = {
(template: RawTemplate, ...substitutions: ReadonlyArray<InterpolatedValue>): T;
(flags?: string): RegexTag<T>;
(options: RegexTagOptions & {
subclass?: false;
}): RegexTag<T>;
(options: RegexTagOptions & {
subclass: true;
}): RegexTag<RegExpSubclass>;
};
export type RegexFromTemplate<T> = {
(options: RegexTagOptions, template: RawTemplate, ...substitutions: ReadonlyArray<InterpolatedValue>): T;
};
import { pattern } from './pattern.js';
/**
@typedef {string | RegExp | Pattern | number} InterpolatedValue
@typedef {{
flags?: string;
useEmulationGroups?: boolean;
}} PluginData
@typedef {TemplateStringsArray | {raw: Array<string>}} RawTemplate
@typedef {{
flags?: string;
subclass?: boolean;
plugins?: Array<(expression: string, data: PluginData) => string>;
unicodeSetsPlugin?: ((expression: string, data: PluginData) => string) | null;
disable?: {
x?: boolean;
n?: boolean;
v?: boolean;
atomic?: boolean;
subroutines?: boolean;
};
force?: {
v?: boolean;
};
}} RegexTagOptions
*/
/**
@template T
@typedef RegexTag
@type {{
(template: RawTemplate, ...substitutions: ReadonlyArray<InterpolatedValue>): T;
(flags?: string): RegexTag<T>;
(options: RegexTagOptions & {subclass?: false}): RegexTag<T>;
(options: RegexTagOptions & {subclass: true}): RegexTag<RegExpSubclass>;
}}
*/
/**
Template tag for constructing a regex with extended syntax and context-aware interpolation of
regexes, strings, and patterns.
Can be called in several ways:
1. `` regex`…` `` - Regex pattern as a raw string.
2. `` regex('gi')`…` `` - To specify flags.
3. `` regex({flags: 'gi'})`…` `` - With options.
@type {RegexTag<RegExp>}
*/
export const regex: RegexTag<RegExp>;
/**
Returns the processed expression and flags as strings.
@param {string} expression
@param {RegexTagOptions} [options]
@returns {{expression: string; flags: string;}}
*/
export function rewrite(expression?: string, options?: RegexTagOptions): {
expression: string;
flags: string;
};
import { Pattern } from './pattern.js';
import { RegExpSubclass } from './subclass.js';
export { pattern };

1266
node_modules/regex/dist/esm/regex.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

7
node_modules/regex/dist/esm/regex.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

28
node_modules/regex/dist/esm/subclass.d.ts generated vendored Normal file
View File

@@ -0,0 +1,28 @@
export const emulationGroupMarker: "$E$";
/**
Works the same as JavaScript's native `RegExp` constructor in all contexts, but automatically
adjusts matches and subpattern indices (with flag `d`) to account for injected emulation groups.
*/
export class RegExpSubclass extends RegExp {
/**
@param {string | RegExpSubclass} expression
@param {string} [flags]
@param {{useEmulationGroups: boolean;}} [options]
*/
constructor(expression: string | RegExpSubclass, flags?: string, options?: {
useEmulationGroups: boolean;
});
/**
@private
@type {Array<{
exclude: boolean;
transfer?: number;
}> | undefined}
*/
private _captureMap;
/**
@private
@type {Record<number, string> | undefined}
*/
private _namesByIndex;
}

12
node_modules/regex/dist/esm/subroutines.d.ts generated vendored Normal file
View File

@@ -0,0 +1,12 @@
export type NamedCapturingGroupsMap = Map<string, {
isUnique: boolean;
contents?: string;
groupNum?: number;
numCaptures?: number;
}>;
/**
@param {string} expression
@param {import('./regex.js').PluginData} [data]
@returns {string}
*/
export function subroutines(expression: string, data?: import("./regex.js").PluginData): string;

9
node_modules/regex/dist/esm/utils-internals.d.ts generated vendored Normal file
View File

@@ -0,0 +1,9 @@
export const noncapturingDelim: any;
/**
@param {string} str
@param {number} pos
@param {string} oldValue
@param {string} newValue
@returns {string}
*/
export function spliceStr(str: string, pos: number, oldValue: string, newValue: string): string;

112
node_modules/regex/dist/esm/utils.d.ts generated vendored Normal file
View File

@@ -0,0 +1,112 @@
export type RunningContext = {
regexContext: string;
charClassContext: string;
charClassDepth: number;
lastPos: number;
};
export type InterpolatedValue = import("./regex.js").InterpolatedValue;
export type RawTemplate = import("./regex.js").RawTemplate;
export type RegexTagOptions = import("./regex.js").RegexTagOptions;
export type Preprocessor = (value: InterpolatedValue, runningContext: RunningContext, options: Required<RegexTagOptions>) => {
transformed: string;
runningContext: RunningContext;
};
/**
@param {string} expression
@param {number} precedingCaptures
@returns {string}
*/
export function adjustNumberedBackrefs(expression: string, precedingCaptures: number): string;
export const capturingDelim: any;
export namespace CharClassContext {
let DEFAULT: string;
let ENCLOSED_P: string;
let ENCLOSED_Q: string;
let ENCLOSED_U: string;
let INVALID_INCOMPLETE_TOKEN: string;
let RANGE: string;
}
export function containsCharClassUnion(charClassPattern: any): boolean;
/**
@param {string} expression
@returns {number}
*/
export function countCaptures(expression: string): number;
export const doublePunctuatorChars: "&!#$%*+,.:;<=>?@^`~";
export const enclosedTokenCharClassContexts: any;
export const enclosedTokenRegexContexts: any;
export const envSupportsFlagGroups: boolean;
export const envSupportsFlagV: boolean;
/**
Escape special characters for the given context, assuming flag v.
@param {string} str String to escape
@param {'DEFAULT' | 'CHAR_CLASS'} context `Context` option from lib `regex-utilities`
@returns {string} Escaped string
*/
export function escapeV(str: string, context: "DEFAULT" | "CHAR_CLASS"): string;
export function getBreakoutChar(expression: any, regexContext: any, charClassContext: any): any;
/**
@typedef {{
regexContext: string;
charClassContext: string;
charClassDepth: number;
lastPos: number;
}} RunningContext
*/
/**
Accepts and returns its full state so it doesn't have to reprocess parts that have already been
seen. Assumes flag v and doesn't worry about syntax errors that are caught by it.
@param {string} incompleteExpression
@param {Partial<RunningContext>} [runningContext]
@returns {RunningContext}
*/
export function getEndContextForIncompleteExpression(incompleteExpression: string, runningContext?: Partial<RunningContext>): RunningContext;
export const namedCapturingDelim: any;
/**
@typedef {import('./regex.js').InterpolatedValue} InterpolatedValue
@typedef {import('./regex.js').RawTemplate} RawTemplate
@typedef {import('./regex.js').RegexTagOptions} RegexTagOptions
@typedef {(
value: InterpolatedValue,
runningContext: RunningContext,
options: Required<RegexTagOptions>
) => {
transformed: string;
runningContext: RunningContext;
}} Preprocessor
*/
/**
Returns transformed versions of a template and substitutions, using the given preprocessor. Only
processes substitutions that are instanceof `Pattern`.
@param {RawTemplate} template
@param {ReadonlyArray<InterpolatedValue>} substitutions
@param {Preprocessor} preprocessor
@param {Required<RegexTagOptions>} options
@returns {{template: RawTemplate; substitutions: ReadonlyArray<InterpolatedValue>;}}
*/
export function preprocess(template: RawTemplate, substitutions: ReadonlyArray<InterpolatedValue>, preprocessor: Preprocessor, options: Required<RegexTagOptions>): {
template: RawTemplate;
substitutions: ReadonlyArray<InterpolatedValue>;
};
export namespace RegexContext {
let DEFAULT_1: string;
export { DEFAULT_1 as DEFAULT };
export let CHAR_CLASS: string;
let ENCLOSED_P_1: string;
export { ENCLOSED_P_1 as ENCLOSED_P };
let ENCLOSED_U_1: string;
export { ENCLOSED_U_1 as ENCLOSED_U };
export let GROUP_NAME: string;
export let INTERVAL_QUANTIFIER: string;
let INVALID_INCOMPLETE_TOKEN_1: string;
export { INVALID_INCOMPLETE_TOKEN_1 as INVALID_INCOMPLETE_TOKEN };
}
export function sandboxLoneCharClassCaret(str: any): any;
export function sandboxLoneDoublePunctuatorChar(str: any): any;
/**
Converts `\0` tokens to `\x00` in the given context.
@param {string} str
@param {'DEFAULT' | 'CHAR_CLASS'} [context] `Context` option from lib `regex-utilities`
@returns {string}
*/
export function sandboxUnsafeNulls(str: string, context?: "DEFAULT" | "CHAR_CLASS"): string;

76
node_modules/regex/dist/regex.min.js generated vendored Normal file

File diff suppressed because one or more lines are too long

7
node_modules/regex/dist/regex.min.js.map generated vendored Normal file

File diff suppressed because one or more lines are too long

61
node_modules/regex/package.json generated vendored Normal file
View File

@@ -0,0 +1,61 @@
{
"name": "regex",
"version": "5.1.1",
"description": "Regex template tag with extended syntax, context-aware interpolation, and always-on best practices",
"author": "Steven Levithan",
"license": "MIT",
"type": "module",
"exports": {
".": {
"import": {
"types": "./dist/esm/regex.d.ts",
"default": "./dist/esm/regex.js"
},
"require": {
"types": "./dist/cjs/regex.d.ts",
"default": "./dist/cjs/regex.js"
}
},
"./internals": {
"types": "./dist/esm/internals.d.ts",
"import": "./src/internals.js"
}
},
"browser": "./dist/regex.min.js",
"main": "./dist/cjs/regex.js",
"types": "./dist/cjs/regex.d.ts",
"scripts": {
"bundle:global": "esbuild src/regex.js --global-name=Regex --bundle --minify --sourcemap --outfile=dist/regex.min.js",
"bundle:esm": "esbuild src/regex.js --format=esm --bundle --sourcemap --outfile=dist/esm/regex.js",
"bundle:cjs": "esbuild src/regex.js --format=cjs --bundle --sourcemap --outfile=dist/cjs/regex.js",
"types": "tsc src/regex.js src/internals.js --rootDir src --declaration --allowJs --emitDeclarationOnly --outDir types",
"prebuild": "rm -rf dist/* types/*",
"build": "pnpm run bundle:global && pnpm run bundle:esm && pnpm run bundle:cjs && pnpm run types",
"postbuild": "node scripts/postbuild.js",
"pretest": "pnpm run build",
"test": "jasmine && tsc --project spec/types/tsconfig.test.json && attw --pack . --entrypoints .",
"prepublishOnly": "pnpm test"
},
"files": [
"dist",
"src"
],
"repository": {
"type": "git",
"url": "git+https://github.com/slevithan/regex.git"
},
"keywords": [
"regex",
"regexp"
],
"dependencies": {
"regex-utilities": "^2.3.0"
},
"devDependencies": {
"@arethetypeswrong/cli": "~0.17.2",
"esbuild": "^0.24.2",
"expect-type": "^1.1.0",
"jasmine": "^5.5.0",
"typescript": "~5.7.2"
}
}

187
node_modules/regex/src/atomic.js generated vendored Normal file
View File

@@ -0,0 +1,187 @@
import {emulationGroupMarker} from './subclass.js';
import {noncapturingDelim, spliceStr} from './utils-internals.js';
import {Context, replaceUnescaped} from 'regex-utilities';
const atomicPluginToken = new RegExp(String.raw`(?<noncapturingStart>${noncapturingDelim})|(?<capturingStart>\((?:\?<[^>]+>)?)|\\?.`, 'gsu');
/**
Apply transformations for atomic groups: `(?>…)`.
@param {string} expression
@param {import('./regex.js').PluginData} [data]
@returns {string}
*/
function atomic(expression, data) {
if (!/\(\?>/.test(expression)) {
return expression;
}
const aGDelim = '(?>';
const emulatedAGDelim = `(?:(?=(${data?.useEmulationGroups ? emulationGroupMarker : ''}`;
const captureNumMap = [0];
let numCapturesBeforeAG = 0;
let numAGs = 0;
let aGPos = NaN;
let hasProcessedAG;
do {
hasProcessedAG = false;
let numCharClassesOpen = 0;
let numGroupsOpenInAG = 0;
let inAG = false;
let match;
atomicPluginToken.lastIndex = Number.isNaN(aGPos) ? 0 : aGPos + emulatedAGDelim.length;
while (match = atomicPluginToken.exec(expression)) {
const {0: m, index, groups: {capturingStart, noncapturingStart}} = match;
if (m === '[') {
numCharClassesOpen++;
} else if (!numCharClassesOpen) {
if (m === aGDelim && !inAG) {
aGPos = index;
inAG = true;
} else if (inAG && noncapturingStart) {
numGroupsOpenInAG++;
} else if (capturingStart) {
if (inAG) {
numGroupsOpenInAG++;
} else {
numCapturesBeforeAG++;
captureNumMap.push(numCapturesBeforeAG + numAGs);
}
} else if (m === ')' && inAG) {
if (!numGroupsOpenInAG) {
numAGs++;
// Replace `expression` and use `<$$N>` as a temporary wrapper for the backref so it
// can avoid backref renumbering afterward. Need to wrap the whole substitution
// (including the lookahead and following backref) in a noncapturing group to handle
// following quantifiers and literal digits
expression = `${expression.slice(0, aGPos)}${emulatedAGDelim}${
expression.slice(aGPos + aGDelim.length, index)
}))<$$${numAGs + numCapturesBeforeAG}>)${expression.slice(index + 1)}`;
hasProcessedAG = true;
break;
}
numGroupsOpenInAG--;
}
} else if (m === ']') {
numCharClassesOpen--;
}
}
// Start over from the beginning of the last atomic group's contents, in case the processed group
// contains additional atomic groups
} while (hasProcessedAG);
// Second pass to adjust numbered backrefs
expression = replaceUnescaped(
expression,
String.raw`\\(?<backrefNum>[1-9]\d*)|<\$\$(?<wrappedBackrefNum>\d+)>`,
({0: m, groups: {backrefNum, wrappedBackrefNum}}) => {
if (backrefNum) {
const bNum = +backrefNum;
if (bNum > captureNumMap.length - 1) {
throw new Error(`Backref "${m}" greater than number of captures`);
}
return `\\${captureNumMap[bNum]}`;
}
return `\\${wrappedBackrefNum}`;
},
Context.DEFAULT
);
return expression;
}
const baseQuantifier = String.raw`(?:[?*+]|\{\d+(?:,\d*)?\})`;
// Complete tokenizer for base syntax; doesn't (need to) know about character-class-only syntax
const possessivePluginToken = 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>${baseQuantifier})(?<qMod>[?+]?)(?<invalidQ>[?*+\{]?)
| \\?.
`.replace(/\s+/g, ''), 'gsu');
/**
Transform posessive quantifiers into atomic groups. The posessessive quantifiers are:
`?+`, `*+`, `++`, `{N}+`, `{N,}+`, `{N,N}+`.
This follows Java, PCRE, Perl, and Python.
Possessive quantifiers in Oniguruma and Onigmo are only: `?+`, `*+`, `++`.
@param {string} expression
@returns {string}
*/
function possessive(expression) {
if (!(new RegExp(`${baseQuantifier}\\+`).test(expression))) {
return expression;
}
const openGroupIndices = [];
let lastGroupIndex = null;
let lastCharClassIndex = null;
let lastToken = '';
let numCharClassesOpen = 0;
let match;
possessivePluginToken.lastIndex = 0;
while (match = possessivePluginToken.exec(expression)) {
const {0: m, index, groups: {qBase, qMod, invalidQ}} = match;
if (m === '[') {
if (!numCharClassesOpen) {
lastCharClassIndex = index;
}
numCharClassesOpen++;
} else if (m === ']') {
if (numCharClassesOpen) {
numCharClassesOpen--;
// Unmatched `]`
} else {
lastCharClassIndex = null;
}
} else if (!numCharClassesOpen) {
if (qMod === '+' && lastToken && !lastToken.startsWith('(')) {
// Invalid following quantifier would become valid via the wrapping group
if (invalidQ) {
throw new Error(`Invalid quantifier "${m}"`);
}
let charsAdded = -1; // -1 for removed trailing `+`
// Possessivizing fixed repetition quantifiers like `{2}` does't change their behavior, so
// avoid doing so (convert them to greedy)
if (/^\{\d+\}$/.test(qBase)) {
expression = spliceStr(expression, index + qBase.length, qMod, '');
} else {
if (lastToken === ')' || lastToken === ']') {
const nodeIndex = lastToken === ')' ? lastGroupIndex : lastCharClassIndex;
// Unmatched `)` would break out of the wrapping group and mess with handling.
// Unmatched `]` wouldn't be a problem, but it's unnecessary to have dedicated support
// for unescaped `]++` since this won't work with flag u or v anyway
if (nodeIndex === null) {
throw new Error(`Invalid unmatched "${lastToken}"`);
}
expression = `${expression.slice(0, nodeIndex)}(?>${expression.slice(nodeIndex, index)}${qBase})${expression.slice(index + m.length)}`;
} else {
expression = `${expression.slice(0, index - lastToken.length)}(?>${lastToken}${qBase})${expression.slice(index + m.length)}`;
}
charsAdded += 4; // `(?>)`
}
possessivePluginToken.lastIndex += charsAdded;
} else if (m[0] === '(') {
openGroupIndices.push(index);
} else if (m === ')') {
lastGroupIndex = openGroupIndices.length ? openGroupIndices.pop() : null;
}
}
lastToken = m;
}
return expression;
}
export {
atomic,
possessive,
};

59
node_modules/regex/src/backcompat.js generated vendored Normal file
View File

@@ -0,0 +1,59 @@
import {doublePunctuatorChars} from './utils.js';
const incompatibleEscapeChars = '&!#%,:;<=>@`~';
const token = new RegExp(String.raw`
\[\^?-?
| --?\]
| (?<dp>[${doublePunctuatorChars}])\k<dp>
| --
| \\(?<vOnlyEscape>[${incompatibleEscapeChars}])
| \\[pPu]\{[^}]+\}
| \\?.
`.replace(/\s+/g, ''), 'gsu');
/**
Applies flag v rules when using flag u, for forward compatibility.
Assumes flag u and doesn't worry about syntax errors that are caught by it.
@param {string} expression
@returns {string}
*/
function backcompatPlugin(expression) {
const unescapedLiteralHyphenMsg = 'Invalid unescaped "-" in character class';
let inCharClass = false;
let result = '';
for (const {0: m, groups: {dp, vOnlyEscape}} of expression.matchAll(token)) {
if (m[0] === '[') {
if (inCharClass) {
throw new Error('Invalid nested character class when flag v not supported; possibly from interpolation');
}
if (m.endsWith('-')) {
throw new Error(unescapedLiteralHyphenMsg);
}
inCharClass = true;
} else if (m.endsWith(']')) {
if (m[0] === '-') {
throw new Error(unescapedLiteralHyphenMsg);
}
inCharClass = false;
} else if (inCharClass) {
if (m === '&&' || m === '--') {
throw new Error(`Invalid set operator "${m}" when flag v not supported`);
} else if (dp) {
throw new Error(`Invalid double punctuator "${m}", reserved by flag v`);
} else if ('(){}/|'.includes(m)) {
throw new Error(`Invalid unescaped "${m}" in character class`);
} else if (vOnlyEscape) {
// Remove the escaping backslash to emulate flag v rules, since this character is allowed
// to be escaped within character classes with flag v but not with flag u
result += vOnlyEscape;
continue;
}
}
result += m;
}
return result;
}
export {
backcompatPlugin,
};

46
node_modules/regex/src/flag-n.js generated vendored Normal file
View File

@@ -0,0 +1,46 @@
import {getEndContextForIncompleteExpression, RegexContext} from './utils.js';
import {noncapturingDelim} from './utils-internals.js';
const token = new RegExp(String.raw`
${noncapturingDelim}
| \(\?<
| (?<backrefNum>\\[1-9]\d*)
| \\?.
`.replace(/\s+/g, ''), 'gsu');
/**
Apply transformations for flag n (named capture only).
Preprocessors are applied to the outer regex and interpolated patterns, but not interpolated
regexes or strings.
@type {import('./utils.js').Preprocessor}
*/
function flagNPreprocessor(value, runningContext) {
value = String(value);
let expression = '';
let transformed = '';
for (const {0: m, groups: {backrefNum}} of value.matchAll(token)) {
expression += m;
runningContext = getEndContextForIncompleteExpression(expression, runningContext);
const {regexContext} = runningContext;
if (regexContext === RegexContext.DEFAULT) {
if (m === '(') {
transformed += '(?:';
} else if (backrefNum) {
throw new Error(`Invalid decimal escape "${m}" with implicit flag n; replace with named backreference`);
} else {
transformed += m;
}
} else {
transformed += m;
}
}
return {
transformed,
runningContext,
};
}
export {
flagNPreprocessor,
};

195
node_modules/regex/src/flag-x.js generated vendored Normal file
View File

@@ -0,0 +1,195 @@
import {emulationGroupMarker} from './subclass.js';
import {CharClassContext, doublePunctuatorChars, getEndContextForIncompleteExpression, RegexContext, sandboxLoneDoublePunctuatorChar, sandboxUnsafeNulls} from './utils.js';
import {noncapturingDelim} from './utils-internals.js';
import {Context, replaceUnescaped} from 'regex-utilities';
const ws = /^\s$/;
const escapedWsOrHash = /^\\[\s#]$/;
const charClassWs = /^[ \t]$/;
const escapedCharClassWs = /^\\[ \t]$/;
const token = new RegExp(String.raw`
\\(?: [gk]<
| [pPu]\{
| c[A-Za-z]
| u[A-Fa-f\d]{4}
| x[A-Fa-f\d]{2}
| 0\d+
)
| \[\^
| ${noncapturingDelim}
| \(\?<
| (?<dp>[${doublePunctuatorChars}])\k<dp>
| --
| \\?.
`.replace(/\s+/g, ''), 'gsu');
/**
Apply transformations for flag x (insignificant whitespace and line comments).
Preprocessors are applied to the outer regex and interpolated patterns, but not interpolated
regexes or strings.
@type {import('./utils.js').Preprocessor}
*/
function flagXPreprocessor(value, runningContext, options) {
value = String(value);
let ignoringWs = false;
let ignoringCharClassWs = false;
let ignoringComment = false;
let expression = '';
let transformed = '';
let lastSignificantToken = '';
let lastSignificantCharClassContext = '';
let separatorNeeded = false;
const update = (str, options) => {
const opts = {
prefix: true,
postfix: false,
...options,
};
str = (separatorNeeded && opts.prefix ? '(?:)' : '') + str + (opts.postfix ? '(?:)' : '');
separatorNeeded = false;
return str;
};
for (const {0: m, index} of value.matchAll(token)) {
if (ignoringComment) {
if (m === '\n') {
ignoringComment = false;
separatorNeeded = true;
}
continue;
}
if (ignoringWs) {
if (ws.test(m)) {
continue;
}
ignoringWs = false;
separatorNeeded = true;
} else if (ignoringCharClassWs) {
if (charClassWs.test(m)) {
continue;
}
ignoringCharClassWs = false;
}
expression += m;
runningContext = getEndContextForIncompleteExpression(expression, runningContext);
const {regexContext, charClassContext} = runningContext;
if (
// `--` is matched in one step, so boundary chars aren't `-` unless separated by whitespace
m === '-' &&
regexContext === RegexContext.CHAR_CLASS &&
lastSignificantCharClassContext === CharClassContext.RANGE &&
(options.flags.includes('v') || options.unicodeSetsPlugin)
) {
// Need to handle this here since the main regex-parsing code would think the hyphen forms
// part of a subtraction operator since we've removed preceding whitespace
throw new Error('Invalid unescaped hyphen as the end value for a range');
}
if (
// `??` is matched in one step by the double punctuator token
(regexContext === RegexContext.DEFAULT && /^(?:[?*+]|\?\?)$/.test(m)) ||
(regexContext === RegexContext.INTERVAL_QUANTIFIER && m === '{')
) {
// Skip the separator prefix and connect the quantifier to the previous token. This also
// allows whitespace between a quantifier and the `?` that makes it lazy. Add a postfix
// separator if `m` is `?` and we're following token `(`, to sandbox the `?` from following
// tokens (since `?` can be a group-type marker). Ex: `( ?:)` becomes `(?(?:):)` and throws.
// The loop we're in matches valid group openings in one step, so we won't arrive here if
// matching e.g. `(?:`. Flag n could prevent the need for the postfix since bare `(` is
// converted to `(?:`, but flag x handling always comes first and flag n can be turned off
transformed += update(m, {prefix: false, postfix: lastSignificantToken === '(' && m === '?'});
} else if (regexContext === RegexContext.DEFAULT) {
if (ws.test(m)) {
ignoringWs = true;
} else if (m.startsWith('#')) {
ignoringComment = true;
} else if (escapedWsOrHash.test(m)) {
transformed += update(m[1], {prefix: false});
} else {
transformed += update(m);
}
} else if (regexContext === RegexContext.CHAR_CLASS && m !== '[' && m !== '[^') {
if (
charClassWs.test(m) &&
( charClassContext === CharClassContext.DEFAULT ||
charClassContext === CharClassContext.ENCLOSED_Q ||
charClassContext === CharClassContext.RANGE
)
) {
ignoringCharClassWs = true;
} else if (charClassContext === CharClassContext.INVALID_INCOMPLETE_TOKEN) {
// Need to handle this here since the main regex-parsing code wouldn't know where the token
// ends if we removed whitespace after an incomplete token that is followed by something
// that completes the token
throw new Error(`Invalid incomplete token in character class: "${m}"`);
} else if (
escapedCharClassWs.test(m) &&
(charClassContext === CharClassContext.DEFAULT || charClassContext === CharClassContext.ENCLOSED_Q)
) {
transformed += update(m[1], {prefix: false});
} else if (charClassContext === CharClassContext.DEFAULT) {
const nextChar = value[index + 1] ?? '';
let updated = sandboxUnsafeNulls(m);
// Avoid escaping lone double punctuators unless required, since some of them are not
// allowed to be escaped with flag u (the `unicodeSetsPlugin` already unescapes them when
// using flag u, but it can be set to `null` via an option)
if (charClassWs.test(nextChar) || m === '^') {
updated = sandboxLoneDoublePunctuatorChar(updated);
}
transformed += update(updated);
} else {
transformed += update(m);
}
} else {
transformed += update(m);
}
if (!(ignoringWs || ignoringCharClassWs || ignoringComment)) {
lastSignificantToken = m;
lastSignificantCharClassContext = charClassContext;
}
}
return {
transformed,
runningContext,
};
}
/**
Remove `(?:)` token separators (most likely added by flag x) in cases where it's safe to do so.
@param {string} expression
@returns {string}
*/
function clean(expression) {
const sep = String.raw`\(\?:\)`;
// No need for repeated separators
expression = replaceUnescaped(expression, `(?:${sep}){2,}`, '(?:)', Context.DEFAULT);
// No need for separators at:
// - The beginning, if not followed by a quantifier.
// - The end.
// - Outside of character classes:
// - If followed by one of `)|.[$\\`, or `(` if that's not followed by `DEFINE)`.
// - Technically we shouldn't remove `(?:)` if preceded by `(?(DEFINE` and followed by `)`,
// but in this case flag x injects a sandboxing `(?:)` after the preceding invalid `(?`,
// so we already get an error from that.
// - If preceded by one of `()|.]^>`, `\\[bBdDfnrsStvwW]`, `(?:`, or a lookaround opening.
// - So long as the separator is not followed by a quantifier.
// - And, not followed by an emulation group marker.
// Examples of things that are not safe to remove `(?:)` at the boundaries of:
// - Anywhere: Letters, numbers, or any of `-=_,<?*+{}`.
// - If followed by any of `:!>`.
// - If preceded by any of `\\[cgkpPux]`.
// - Anything inside character classes.
const marker = emulationGroupMarker.replace(/\$/g, '\\$');
expression = replaceUnescaped(
expression,
String.raw`(?:${sep}(?=[)|.[$\\]|\((?!DEFINE)|$)|(?<=[()|.\]^>]|\\[bBdDfnrsStvwW]|\(\?(?:[:=!]|<[=!])|^)${sep}(?![?*+{]))(?!${marker})`,
'',
Context.DEFAULT
);
return expression;
}
export {
clean,
flagXPreprocessor,
};

2
node_modules/regex/src/internals.js generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export {atomic, possessive} from './atomic.js';
export {emulationGroupMarker, RegExpSubclass} from './subclass.js';

45
node_modules/regex/src/pattern.js generated vendored Normal file
View File

@@ -0,0 +1,45 @@
class Pattern {
#value;
/** @param {string} value */
constructor(value) {
this.#value = value;
}
/** @returns {string} */
toString() {
return String(this.#value);
}
}
/**
Returns a value that can be interpolated into a `regex` template string without having its special
characters escaped.
Can be called as a function or template tag:
- `pattern(value)` - String or value coerced to string.
- `` pattern`…` `` - Same as ``pattern(String.raw`…`)``.
@overload
@param {string | number} value
@returns {Pattern}
@overload
@param {TemplateStringsArray} template
@param {...string} substitutions
@returns {Pattern}
*/
function pattern(first, ...substitutions) {
if (Array.isArray(first?.raw)) {
return new Pattern(
// Intersperse raw template strings and substitutions
first.raw.flatMap((raw, i) => i < first.raw.length - 1 ? [raw, substitutions[i]] : raw).join('')
);
} else if (!substitutions.length) {
return new Pattern(first === undefined ? '' : first);
}
throw new Error(`Unexpected arguments: ${JSON.stringify([first, ...substitutions])}`);
}
export {
Pattern,
pattern,
};

352
node_modules/regex/src/regex.js generated vendored Normal file
View File

@@ -0,0 +1,352 @@
import {atomic, possessive} from './atomic.js';
import {backcompatPlugin} from './backcompat.js';
import {flagNPreprocessor} from './flag-n.js';
import {clean, flagXPreprocessor} from './flag-x.js';
import {Pattern, pattern} from './pattern.js';
import {RegExpSubclass} from './subclass.js';
import {subroutines} from './subroutines.js';
import {adjustNumberedBackrefs, CharClassContext, containsCharClassUnion, countCaptures, enclosedTokenCharClassContexts, enclosedTokenRegexContexts, envSupportsFlagGroups, envSupportsFlagV, escapeV, getBreakoutChar, getEndContextForIncompleteExpression, preprocess, RegexContext, sandboxLoneCharClassCaret, sandboxLoneDoublePunctuatorChar, sandboxUnsafeNulls} from './utils.js';
import {Context, hasUnescaped, replaceUnescaped} from 'regex-utilities';
/**
@typedef {string | RegExp | Pattern | number} InterpolatedValue
@typedef {{
flags?: string;
useEmulationGroups?: boolean;
}} PluginData
@typedef {TemplateStringsArray | {raw: Array<string>}} RawTemplate
@typedef {{
flags?: string;
subclass?: boolean;
plugins?: Array<(expression: string, data: PluginData) => string>;
unicodeSetsPlugin?: ((expression: string, data: PluginData) => string) | null;
disable?: {
x?: boolean;
n?: boolean;
v?: boolean;
atomic?: boolean;
subroutines?: boolean;
};
force?: {
v?: boolean;
};
}} RegexTagOptions
*/
/**
@template T
@typedef RegexTag
@type {{
(template: RawTemplate, ...substitutions: ReadonlyArray<InterpolatedValue>): T;
(flags?: string): RegexTag<T>;
(options: RegexTagOptions & {subclass?: false}): RegexTag<T>;
(options: RegexTagOptions & {subclass: true}): RegexTag<RegExpSubclass>;
}}
*/
/**
Template tag for constructing a regex with extended syntax and context-aware interpolation of
regexes, strings, and patterns.
Can be called in several ways:
1. `` regex`…` `` - Regex pattern as a raw string.
2. `` regex('gi')`…` `` - To specify flags.
3. `` regex({flags: 'gi'})`…` `` - With options.
@type {RegexTag<RegExp>}
*/
const regex = (first, ...substitutions) => {
// Given a template
if (Array.isArray(first?.raw)) {
return regexFromTemplate({}, first, ...substitutions);
// Given flags
} else if ((typeof first === 'string' || first === undefined) && !substitutions.length) {
return regexFromTemplate.bind(null, {flags: first ?? ''});
// Given an options object
} else if ({}.toString.call(first) === '[object Object]' && !substitutions.length) {
return regexFromTemplate.bind(null, first);
}
throw new Error(`Unexpected arguments: ${JSON.stringify([first, ...substitutions])}`);
};
/**
@template T
@typedef RegexFromTemplate
@type {{
(options: RegexTagOptions, template: RawTemplate, ...substitutions: ReadonlyArray<InterpolatedValue>) : T;
}}
*/
/**
Returns a RegExp from a template and substitutions to fill the template holes.
@type {RegexFromTemplate<RegExp>}
*/
const regexFromTemplate = (options, template, ...substitutions) => {
const opts = getOptions(options);
const prepped = handlePreprocessors(template, substitutions, opts);
let precedingCaptures = 0;
let expression = '';
let runningContext;
// Intersperse raw template strings and substitutions
prepped.template.raw.forEach((raw, i) => {
const wrapEscapedStr = !!(prepped.template.raw[i] || prepped.template.raw[i + 1]);
// Even with flag n enabled, we might have named captures
precedingCaptures += countCaptures(raw);
// Sandbox `\0` in character classes. Not needed outside character classes because in other
// cases a following interpolated value would always be atomized
expression += sandboxUnsafeNulls(raw, Context.CHAR_CLASS);
runningContext = getEndContextForIncompleteExpression(expression, runningContext);
const {regexContext, charClassContext} = runningContext;
if (i < prepped.template.raw.length - 1) {
const substitution = prepped.substitutions[i];
expression += interpolate(substitution, opts.flags, regexContext, charClassContext, wrapEscapedStr, precedingCaptures);
if (substitution instanceof RegExp) {
precedingCaptures += countCaptures(substitution.source);
} else if (substitution instanceof Pattern) {
precedingCaptures += countCaptures(String(substitution));
}
}
});
expression = handlePlugins(expression, opts);
try {
return opts.subclass ?
new RegExpSubclass(expression, opts.flags, {useEmulationGroups: true}) :
new RegExp(expression, opts.flags);
} catch (err) {
// Improve DX by always including the generated source in the error message. Some browsers
// include it automatically, but not Firefox or Safari
const stripped = err.message.replace(/ \/.+\/[a-z]*:/, '');
err.message = `${stripped}: /${expression}/${opts.flags}`;
throw err;
}
};
/**
Returns the processed expression and flags as strings.
@param {string} expression
@param {RegexTagOptions} [options]
@returns {{expression: string; flags: string;}}
*/
function rewrite(expression = '', options) {
const opts = getOptions(options);
if (opts.subclass) {
// Don't allow including emulation group markers in output
throw new Error('Cannot use option subclass');
}
return {
expression: handlePlugins(
handlePreprocessors({raw: [expression]}, [], opts).template.raw[0],
opts
),
flags: opts.flags,
};
}
/**
Returns a complete set of options, with default values set for options that weren't provided, and
some options augmented for use.
@param {RegexTagOptions} [options]
@returns {Required<RegexTagOptions>}
*/
function getOptions(options) {
const opts = {
flags: '',
subclass: false,
plugins: [],
unicodeSetsPlugin: backcompatPlugin,
disable: {/* n, v, x, atomic, subroutines */},
force: {/* v */},
...options,
};
if (/[nuvx]/.test(opts.flags)) {
throw new Error('Implicit flags v/u/x/n cannot be explicitly added');
}
const useFlagV = opts.force.v || (opts.disable.v ? false : envSupportsFlagV);
opts.flags += useFlagV ? 'v' : 'u';
if (useFlagV) {
opts.unicodeSetsPlugin = null;
}
return opts;
}
/**
@param {RawTemplate} template
@param {ReadonlyArray<InterpolatedValue>} substitutions
@param {Required<RegexTagOptions>} options
@returns {{
template: RawTemplate;
substitutions: ReadonlyArray<InterpolatedValue>;
}}
*/
function handlePreprocessors(template, substitutions, options) {
const preprocessors = [];
// Implicit flag x is handled first because otherwise some regex syntax (if unescaped) within
// comments could cause problems when parsing
if (!options.disable.x) {
preprocessors.push(flagXPreprocessor);
}
// Implicit flag n is a preprocessor because capturing groups affect backreference rewriting in
// both interpolation and plugins
if (!options.disable.n) {
preprocessors.push(flagNPreprocessor);
}
for (const pp of preprocessors) {
({template, substitutions} = preprocess(template, substitutions, pp, options));
}
return {
template,
substitutions,
};
}
/**
@param {string} expression
@param {Required<RegexTagOptions>} options
@returns {string}
*/
function handlePlugins(expression, options) {
const {flags, plugins, unicodeSetsPlugin, disable, subclass} = options;
[ ...plugins, // Run first, so provided plugins can output extended syntax
...(disable.subroutines ? [] : [subroutines]),
...(disable.atomic ? [] : [possessive, atomic]),
...(disable.x ? [] : [clean]),
// Run last, so it doesn't have to worry about parsing extended syntax
...(!unicodeSetsPlugin ? [] : [unicodeSetsPlugin]),
].forEach(p => expression = p(expression, {flags, useEmulationGroups: subclass}));
return expression;
}
/**
@param {InterpolatedValue} value
@param {string} flags
@param {string} regexContext
@param {string} charClassContext
@param {boolean} wrapEscapedStr
@param {number} precedingCaptures
@returns {string}
*/
function interpolate(value, flags, regexContext, charClassContext, wrapEscapedStr, precedingCaptures) {
if (value instanceof RegExp && regexContext !== RegexContext.DEFAULT) {
throw new Error('Cannot interpolate a RegExp at this position because the syntax context does not match');
}
if (regexContext === RegexContext.INVALID_INCOMPLETE_TOKEN || charClassContext === CharClassContext.INVALID_INCOMPLETE_TOKEN) {
// Throw in all cases, but only *need* to handle a preceding unescaped backslash (which would
// break sandboxing) since other errors would be handled by the invalid generated regex syntax
throw new Error('Interpolation preceded by invalid incomplete token');
}
if (
typeof value === 'number' &&
(regexContext === RegexContext.ENCLOSED_U || charClassContext === CharClassContext.ENCLOSED_U)
) {
return value.toString(16);
}
const isPattern = value instanceof Pattern;
let escapedValue = '';
if (!(value instanceof RegExp)) {
value = String(value);
if (!isPattern) {
escapedValue = escapeV(
value,
regexContext === RegexContext.CHAR_CLASS ? Context.CHAR_CLASS : Context.DEFAULT
);
}
// Check `escapedValue` (not just patterns) since possible breakout char `>` isn't escaped
const breakoutChar = getBreakoutChar(escapedValue || value, regexContext, charClassContext);
if (breakoutChar) {
throw new Error(`Unescaped stray "${breakoutChar}" in the interpolated value would have side effects outside it`);
}
}
if (
regexContext === RegexContext.INTERVAL_QUANTIFIER ||
regexContext === RegexContext.GROUP_NAME ||
enclosedTokenRegexContexts.has(regexContext) ||
enclosedTokenCharClassContexts.has(charClassContext)
) {
return isPattern ? String(value) : escapedValue;
} else if (regexContext === RegexContext.CHAR_CLASS) {
if (isPattern) {
if (hasUnescaped(String(value), '^-|^&&|-$|&&$')) {
// Sandboxing so we don't change the chars outside the pattern into being part of an
// operation they didn't initiate. Same problem as starting a pattern with a quantifier
throw new Error('Cannot use range or set operator at boundary of interpolated pattern; move the operation into the pattern or the operator outside of it');
}
const sandboxedValue = sandboxLoneCharClassCaret(sandboxLoneDoublePunctuatorChar(value));
// Atomize via nested character class `[…]` if it contains implicit or explicit union (check
// the unadjusted value)
return containsCharClassUnion(value) ? `[${sandboxedValue}]` : sandboxUnsafeNulls(sandboxedValue);
}
// Atomize via nested character class `[…]` if more than one node
return containsCharClassUnion(escapedValue) ? `[${escapedValue}]` : escapedValue;
}
// `RegexContext.DEFAULT`
if (value instanceof RegExp) {
const transformed = transformForLocalFlags(value, flags);
const backrefsAdjusted = adjustNumberedBackrefs(transformed.value, precedingCaptures);
// Sandbox and atomize; if we used a pattern modifier it has the same effect
return transformed.usedModifier ? backrefsAdjusted : `(?:${backrefsAdjusted})`;
}
if (isPattern) {
// Sandbox and atomize
return `(?:${value})`;
}
// Sandbox and atomize
return wrapEscapedStr ? `(?:${escapedValue})` : escapedValue;
}
/**
@param {RegExp} re
@param {string} outerFlags
@returns {{value: string; usedModifier?: boolean;}}
*/
function transformForLocalFlags(re, outerFlags) {
/** @type {{i: boolean | null; m: boolean | null; s: boolean | null;}} */
const modFlagsObj = {
i: null,
m: null,
s: null,
};
const newlines = '\\n\\r\\u2028\\u2029';
let value = re.source;
if (re.ignoreCase !== outerFlags.includes('i')) {
if (envSupportsFlagGroups) {
modFlagsObj.i = re.ignoreCase;
} else {
throw new Error('Pattern modifiers not supported, so flag i on the outer and interpolated regex must match');
}
}
if (re.dotAll !== outerFlags.includes('s')) {
if (envSupportsFlagGroups) {
modFlagsObj.s = re.dotAll;
} else {
value = replaceUnescaped(value, '\\.', (re.dotAll ? '[^]' : `[^${newlines}]`), Context.DEFAULT);
}
}
if (re.multiline !== outerFlags.includes('m')) {
if (envSupportsFlagGroups) {
modFlagsObj.m = re.multiline;
} else {
value = replaceUnescaped(value, '\\^', (re.multiline ? `(?<=^|[${newlines}])` : '(?<![^])'), Context.DEFAULT);
value = replaceUnescaped(value, '\\$', (re.multiline ? `(?=$|[${newlines}])` : '(?![^])'), Context.DEFAULT);
}
}
if (envSupportsFlagGroups) {
const keys = Object.keys(modFlagsObj);
let modifier = keys.filter(k => modFlagsObj[k] === true).join('');
const modOff = keys.filter(k => modFlagsObj[k] === false).join('');
if (modOff) {
modifier += `-${modOff}`;
}
if (modifier) {
return {
value: `(?${modifier}:${value})`,
usedModifier: true,
};
}
}
return {value};
}
export {
pattern,
regex,
rewrite,
};

147
node_modules/regex/src/subclass.js generated vendored Normal file
View File

@@ -0,0 +1,147 @@
import {Context, replaceUnescaped} from 'regex-utilities';
// This marker was chosen because it's impossible to match (so its extemely unlikely to be used in
// a user-provided regex); it's not at risk of being optimized away, transformed, or flagged as an
// error by a plugin; and it ends with an unquantifiable token
const emulationGroupMarker = '$E$';
// Note: Emulation groups with transfer are also supported. They look like `($N$E$…)` where `N` is
// an integer 1 or greater. They're not used directly by Regex+ but can be used by plugins and
// libraries that use Regex+ internals. Emulation groups with transfer are not only excluded from
// match results, but additionally transfer their match to the group specified by `N`
/**
Works the same as JavaScript's native `RegExp` constructor in all contexts, but automatically
adjusts matches and subpattern indices (with flag `d`) to account for injected emulation groups.
*/
class RegExpSubclass extends RegExp {
// Avoid `#private` to allow for subclassing
/**
@private
@type {Array<{
exclude: boolean;
transfer?: number;
}> | undefined}
*/
_captureMap;
/**
@private
@type {Record<number, string> | undefined}
*/
_namesByIndex;
/**
@param {string | RegExpSubclass} expression
@param {string} [flags]
@param {{useEmulationGroups: boolean;}} [options]
*/
constructor(expression, flags, options) {
if (expression instanceof RegExp && options) {
throw new Error('Cannot provide options when copying a regexp');
}
const useEmulationGroups = !!options?.useEmulationGroups;
const unmarked = useEmulationGroups ? unmarkEmulationGroups(expression) : null;
super(unmarked?.expression || expression, flags);
// The third argument `options` isn't provided when regexes are copied as part of the internal
// handling of string methods `matchAll` and `split`
const src = useEmulationGroups ? unmarked : (expression instanceof RegExpSubclass ? expression : null);
if (src) {
this._captureMap = src._captureMap;
this._namesByIndex = src._namesByIndex;
}
}
/**
Called internally by all String/RegExp methods that use regexes.
@override
@param {string} str
@returns {RegExpExecArray | null}
*/
exec(str) {
const match = RegExp.prototype.exec.call(this, str);
if (!match || !this._captureMap) {
return match;
}
const matchCopy = [...match];
// Empty all but the first value of the array while preserving its other properties
match.length = 1;
let indicesCopy;
if (this.hasIndices) {
indicesCopy = [...match.indices];
match.indices.length = 1;
}
for (let i = 1; i < matchCopy.length; i++) {
if (this._captureMap[i].exclude) {
const transfer = this._captureMap[i].transfer;
if (transfer && match.length > transfer) {
match[transfer] = matchCopy[i];
const transferName = this._namesByIndex[transfer];
if (transferName) {
match.groups[transferName] = matchCopy[i];
if (this.hasIndices) {
match.indices.groups[transferName] = indicesCopy[i];
}
}
if (this.hasIndices) {
match.indices[transfer] = indicesCopy[i];
}
}
} else {
match.push(matchCopy[i]);
if (this.hasIndices) {
match.indices.push(indicesCopy[i]);
}
}
}
return match;
}
}
/**
Build the capturing group map (with emulation groups marked to indicate their submatches shouldn't
appear in results), and remove the markers for captures that were added to emulate extended syntax.
@param {string} expression
@returns {{
_captureMap: Array<{
exclude: boolean;
transfer?: number;
}>;
_namesByIndex: Record<number, string>;
expression: string;
}}
*/
function unmarkEmulationGroups(expression) {
const marker = emulationGroupMarker.replace(/\$/g, '\\$');
const _captureMap = [{exclude: false}];
const _namesByIndex = {0: ''};
let realCaptureNum = 0;
expression = replaceUnescaped(
expression,
String.raw`\((?:(?!\?)|\?<(?![=!])(?<name>[^>]+)>)(?<mark>(?:\$(?<transfer>[1-9]\d*))?${marker})?`,
({0: m, groups: {name, mark, transfer}}) => {
if (mark) {
_captureMap.push({
exclude: true,
transfer: transfer && +transfer,
});
return m.slice(0, -mark.length);
}
realCaptureNum++;
if (name) {
_namesByIndex[realCaptureNum] = name;
}
_captureMap.push({
exclude: false,
});
return m;
},
Context.DEFAULT
);
return {
_captureMap,
_namesByIndex,
expression,
};
}
export {
emulationGroupMarker,
RegExpSubclass,
};

341
node_modules/regex/src/subroutines.js generated vendored Normal file
View File

@@ -0,0 +1,341 @@
import {emulationGroupMarker} from './subclass.js';
import {capturingDelim, countCaptures, namedCapturingDelim} from './utils.js';
import {spliceStr} from './utils-internals.js';
import {Context, execUnescaped, forEachUnescaped, getGroupContents, hasUnescaped, replaceUnescaped} from 'regex-utilities';
/**
@param {string} expression
@param {import('./regex.js').PluginData} [data]
@returns {string}
*/
function subroutines(expression, data) {
// NOTE: subroutines and definition groups fully support numbered backreferences and unnamed
// captures (from interpolated regexes or from turning implicit flag n off), and all of the
// complex forward and backward backreference adjustments that can result
const namedGroups = getNamedCapturingGroups(expression, {includeContents: true});
const transformed = processSubroutines(expression, namedGroups, !!data?.useEmulationGroups);
return processDefinitionGroup(transformed, namedGroups);
}
// Explicitly exclude `&` from subroutine name chars because it's used by extension
// `regex-recursion` for recursive subroutines via `\g<name&R=N>`
const subroutinePattern = String.raw`\\g<(?<subroutineName>[^>&]+)>`;
const token = new RegExp(String.raw`
${subroutinePattern}
| (?<capturingStart>${capturingDelim})
| \\(?<backrefNum>[1-9]\d*)
| \\k<(?<backrefName>[^>]+)>
| \\?.
`.replace(/\s+/g, ''), 'gsu');
/**
@typedef {
Map<string, {
isUnique: boolean;
contents?: string;
groupNum?: number;
numCaptures?: number;
}>} NamedCapturingGroupsMap
*/
/**
Apply transformations for subroutines: `\g<name>`.
@param {string} expression
@param {NamedCapturingGroupsMap} namedGroups
@param {boolean} useEmulationGroups
@returns {string}
*/
function processSubroutines(expression, namedGroups, useEmulationGroups) {
if (!/\\g</.test(expression)) {
return expression;
}
// Can skip a lot of processing and avoid adding captures if there are no backrefs
const hasBackrefs = hasUnescaped(expression, '\\\\(?:[1-9]|k<[^>]+>)', Context.DEFAULT);
const subroutineWrapper = hasBackrefs ? `(${useEmulationGroups ? emulationGroupMarker : ''}` : '(?:';
const openSubroutines = new Map();
const openSubroutinesStack = [];
const captureNumMap = [0];
let numCapturesPassedOutsideSubroutines = 0;
let numCapturesPassedInsideSubroutines = 0;
let numCapturesPassedInsideThisSubroutine = 0;
let numSubroutineCapturesTrackedInRemap = 0;
let numCharClassesOpen = 0;
let result = expression;
let match;
token.lastIndex = 0;
while (match = token.exec(result)) {
const {0: m, index, groups: {subroutineName, capturingStart, backrefNum, backrefName}} = match;
if (m === '[') {
numCharClassesOpen++;
} else if (!numCharClassesOpen) {
if (subroutineName) {
if (!namedGroups.has(subroutineName)) {
throw new Error(`Invalid named capture referenced by subroutine ${m}`);
}
if (openSubroutines.has(subroutineName)) {
throw new Error(`Subroutine ${m} followed a recursive reference`);
}
const contents = namedGroups.get(subroutineName).contents;
// Wrap value in case it has top-level alternation or is followed by a quantifier. The
// wrapper also marks the end of the expanded contents, which we'll track using
// `unclosedGroupCount`. If there are any backrefs in the expression, wrap with `()`
// instead of `(?:)` in case there are backrefs inside the subroutine that refer to their
// containing capturing group
const subroutineValue = `${subroutineWrapper}${contents})`;
if (hasBackrefs) {
numCapturesPassedInsideThisSubroutine = 0;
numCapturesPassedInsideSubroutines++;
}
openSubroutines.set(subroutineName, {
// Incrementally decremented to track when we've left the group
unclosedGroupCount: countOpenParens(subroutineValue),
});
openSubroutinesStack.push(subroutineName);
// Expand the subroutine's contents into the pattern we're looping over
result = spliceStr(result, index, m, subroutineValue);
token.lastIndex -= m.length - subroutineWrapper.length;
} else if (capturingStart) {
// Somewhere within an expanded subroutine
if (openSubroutines.size) {
if (hasBackrefs) {
numCapturesPassedInsideThisSubroutine++;
numCapturesPassedInsideSubroutines++;
}
// Named capturing group
if (m !== '(') {
// Replace named with unnamed capture. Subroutines ideally wouldn't create any new
// captures, but it can't be helped since we need any backrefs to this capture to work.
// Given that flag n prevents unnamed capture and thereby requires you to rely on named
// backrefs and `groups`, switching to unnamed essentially accomplishes not creating a
// capture. Can fully avoid capturing if there are no backrefs in the expression
result = spliceStr(result, index, m, subroutineWrapper);
token.lastIndex -= m.length - subroutineWrapper.length;
}
} else if (hasBackrefs) {
captureNumMap.push(
lastOf(captureNumMap) + 1 +
numCapturesPassedInsideSubroutines -
numSubroutineCapturesTrackedInRemap
);
numSubroutineCapturesTrackedInRemap = numCapturesPassedInsideSubroutines;
numCapturesPassedOutsideSubroutines++;
}
} else if ((backrefNum || backrefName) && openSubroutines.size) {
// Unify handling for named and unnamed by always using the backref num
const num = backrefNum ? +backrefNum : namedGroups.get(backrefName)?.groupNum;
let isGroupFromThisSubroutine = false;
// Search for the group in the contents of the subroutine stack
for (const s of openSubroutinesStack) {
const group = namedGroups.get(s);
if (num >= group.groupNum && num <= (group.groupNum + group.numCaptures)) {
isGroupFromThisSubroutine = true;
break;
}
}
if (isGroupFromThisSubroutine) {
const group = namedGroups.get(lastOf(openSubroutinesStack));
// Replace the backref with metadata we'll need to rewrite it later, using
// `\k<$$bNsNrNcN>` as a temporary wrapper:
// - b: The unmodified matched backref num, or the corresponding num of a named backref
// - s: The capture num of the subroutine we're most deeply nested in, including captures
// added by expanding the contents of preceding subroutines
// - r: The original capture num of the group that the subroutine we're most deeply
// nested in references, not counting the effects of subroutines
// - c: The number of captures within `r`, not counting the effects of subroutines
const subroutineNum = numCapturesPassedOutsideSubroutines + numCapturesPassedInsideSubroutines - numCapturesPassedInsideThisSubroutine;
const metadata = `\\k<$$b${num}s${subroutineNum}r${group.groupNum}c${group.numCaptures}>`;
result = spliceStr(result, index, m, metadata);
token.lastIndex += metadata.length - m.length;
}
} else if (m === ')') {
if (openSubroutines.size) {
const subroutine = openSubroutines.get(lastOf(openSubroutinesStack));
subroutine.unclosedGroupCount--;
if (!subroutine.unclosedGroupCount) {
openSubroutines.delete(openSubroutinesStack.pop());
}
}
}
} else if (m === ']') {
numCharClassesOpen--;
}
}
if (hasBackrefs) {
// Second pass to adjust backrefs
result = replaceUnescaped(
result,
String.raw`\\(?:(?<bNum>[1-9]\d*)|k<\$\$b(?<bNumSub>\d+)s(?<subNum>\d+)r(?<refNum>\d+)c(?<refCaps>\d+)>)`,
({0: m, groups: {bNum, bNumSub, subNum, refNum, refCaps}}) => {
if (bNum) {
const backrefNum = +bNum;
if (backrefNum > captureNumMap.length - 1) {
throw new Error(`Backref "${m}" greater than number of captures`);
}
return `\\${captureNumMap[backrefNum]}`;
}
const backrefNumInSubroutine = +bNumSub;
const subroutineGroupNum = +subNum;
const refGroupNum = +refNum;
const numCapturesInRef = +refCaps;
if (backrefNumInSubroutine < refGroupNum || backrefNumInSubroutine > (refGroupNum + numCapturesInRef)) {
return `\\${captureNumMap[backrefNumInSubroutine]}`;
}
return `\\${subroutineGroupNum - refGroupNum + backrefNumInSubroutine}`;
},
Context.DEFAULT
);
}
return result;
}
// `(?:)` allowed because it can be added by flag x's preprocessing of whitespace and comments
const defineGroupToken = new RegExp(String.raw`${namedCapturingDelim}|\(\?:\)|(?<invalid>\\?.)`, 'gsu');
/**
Remove valid subroutine definition groups: `(?(DEFINE)…)`.
@param {string} expression
@param {NamedCapturingGroupsMap} namedGroups
IMPORTANT: Avoid using the `contents` property of `namedGroups` objects, because at this point
subroutine substitution has been performed on the corresponding substrings in `expression`
@returns {string}
*/
function processDefinitionGroup(expression, namedGroups) {
const defineMatch = execUnescaped(expression, String.raw`\(\?\(DEFINE\)`, 0, Context.DEFAULT);
if (!defineMatch) {
return expression;
}
const defineGroup = getGroup(expression, defineMatch);
if (defineGroup.afterPos < expression.length) {
// Supporting DEFINE at positions other than the end would complicate backref handling.
// NOTE: Flag x's preprocessing permits trailing whitespace and comments
throw new Error('DEFINE group allowed only at the end of a regex');
} else if (defineGroup.afterPos > expression.length) {
throw new Error('DEFINE group is unclosed');
}
let match;
defineGroupToken.lastIndex = 0;
while (match = defineGroupToken.exec(defineGroup.contents)) {
const {captureName, invalid} = match.groups;
if (captureName) {
const group = getGroup(defineGroup.contents, match);
let duplicateName;
if (!namedGroups.get(captureName).isUnique) {
duplicateName = captureName;
} else {
const nestedNamedGroups = getNamedCapturingGroups(group.contents, {includeContents: false});
for (const name of nestedNamedGroups.keys()) {
if (!namedGroups.get(name).isUnique) {
duplicateName = name;
break;
}
}
}
if (duplicateName) {
throw new Error(`Duplicate group name "${duplicateName}" within DEFINE`);
}
defineGroupToken.lastIndex = group.afterPos;
} else if (invalid) {
// Since a DEFINE group is stripped from its expression, we can't easily determine whether
// unreferenced top-level syntax within it is valid. Such syntax serves no purpose, so it's
// easiest to not allow it
throw new Error(`DEFINE group includes unsupported syntax at top level`);
}
}
return expression.slice(0, defineMatch.index);
}
/**
Counts unescaped open parens outside of character classes, regardless of group type
@param {string} expression
@returns {number}
*/
function countOpenParens(expression) {
let num = 0;
forEachUnescaped(expression, '\\(', () => num++, Context.DEFAULT);
return num;
}
/**
@param {string} expression
@param {string} groupName
@returns {number}
*/
function getCaptureNum(expression, groupName) {
let num = 0;
let pos = 0;
let match;
while (match = execUnescaped(expression, capturingDelim, pos, Context.DEFAULT)) {
const {0: m, index, groups: {captureName}} = match;
num++;
if (captureName === groupName) {
break;
}
pos = index + m.length;
}
return num;
}
/**
@param {string} expression
@param {RegExpExecArray} delimMatch
@returns {{contents: string; afterPos: number}}
*/
function getGroup(expression, delimMatch) {
const contentsStart = delimMatch.index + delimMatch[0].length;
const contents = getGroupContents(expression, contentsStart);
const afterPos = contentsStart + contents.length + 1;
return {
contents,
afterPos,
};
}
/**
@param {string} expression
@param {{includeContents: boolean}} options
@returns {NamedCapturingGroupsMap}
*/
function getNamedCapturingGroups(expression, {includeContents}) {
const namedGroups = new Map();
forEachUnescaped(
expression,
namedCapturingDelim,
({0: m, index, groups: {captureName}}) => {
// If there are duplicate capture names, subroutines refer to the first instance of the given
// group (matching the behavior of PCRE and Perl)
if (namedGroups.has(captureName)) {
namedGroups.get(captureName).isUnique = false;
} else {
const group = {isUnique: true};
if (includeContents) {
const contents = getGroupContents(expression, index + m.length);
Object.assign(group, {
contents,
groupNum: getCaptureNum(expression, captureName),
numCaptures: countCaptures(contents),
});
}
namedGroups.set(captureName, group);
}
},
Context.DEFAULT
);
return namedGroups;
}
/**
@param {Array<any>} arr
@returns {any}
*/
function lastOf(arr) {
// Remove when support for ES2022 array method `at` (Node.js 16.6) is no longer an issue:
// <https://caniuse.com/mdn-javascript_builtins_array_at>
return arr[arr.length - 1];
}
export {
subroutines,
};

19
node_modules/regex/src/utils-internals.js generated vendored Normal file
View File

@@ -0,0 +1,19 @@
// Separating some utils for improved tree shaking of the `./internals` export
const noncapturingDelim = String.raw`\(\?(?:[:=!>A-Za-z\-]|<[=!]|\(DEFINE\))`;
/**
@param {string} str
@param {number} pos
@param {string} oldValue
@param {string} newValue
@returns {string}
*/
function spliceStr(str, pos, oldValue, newValue) {
return str.slice(0, pos) + newValue + str.slice(pos + oldValue.length);
}
export {
noncapturingDelim,
spliceStr,
};

398
node_modules/regex/src/utils.js generated vendored Normal file
View File

@@ -0,0 +1,398 @@
import {Pattern, pattern} from './pattern.js';
import {Context, forEachUnescaped, replaceUnescaped} from 'regex-utilities';
const RegexContext = {
DEFAULT: 'DEFAULT',
CHAR_CLASS: 'CHAR_CLASS',
ENCLOSED_P: 'ENCLOSED_P',
ENCLOSED_U: 'ENCLOSED_U',
GROUP_NAME: 'GROUP_NAME',
INTERVAL_QUANTIFIER: 'INTERVAL_QUANTIFIER',
INVALID_INCOMPLETE_TOKEN: 'INVALID_INCOMPLETE_TOKEN',
};
const CharClassContext = {
DEFAULT: 'DEFAULT',
ENCLOSED_P: 'ENCLOSED_P',
ENCLOSED_Q: 'ENCLOSED_Q',
ENCLOSED_U: 'ENCLOSED_U',
INVALID_INCOMPLETE_TOKEN: 'INVALID_INCOMPLETE_TOKEN',
RANGE: 'RANGE',
};
const enclosedTokenRegexContexts = new Set([
RegexContext.ENCLOSED_P,
RegexContext.ENCLOSED_U,
]);
const enclosedTokenCharClassContexts = new Set([
CharClassContext.ENCLOSED_P,
CharClassContext.ENCLOSED_Q,
CharClassContext.ENCLOSED_U,
]);
const envSupportsFlagGroups = (() => {
try {
new RegExp('(?i:)');
} catch {
return false;
}
return true;
})();
const envSupportsFlagV = (() => {
try {
new RegExp('', 'v');
} catch {
return false;
}
return true;
})();
const doublePunctuatorChars = '&!#$%*+,.:;<=>?@^`~';
const namedCapturingDelim = String.raw`\(\?<(?![=!])(?<captureName>[^>]+)>`;
const capturingDelim = String.raw`\((?!\?)(?!(?<=\(\?\()DEFINE\))|${namedCapturingDelim}`;
/**
@param {string} expression
@param {number} precedingCaptures
@returns {string}
*/
function adjustNumberedBackrefs(expression, precedingCaptures) {
return replaceUnescaped(
expression,
String.raw`\\(?<num>[1-9]\d*)`,
({groups: {num}}) => `\\${+num + precedingCaptures}`,
Context.DEFAULT
);
}
// Properties of strings as of ES2024
const stringPropertyNames = [
'Basic_Emoji',
'Emoji_Keycap_Sequence',
'RGI_Emoji_Modifier_Sequence',
'RGI_Emoji_Flag_Sequence',
'RGI_Emoji_Tag_Sequence',
'RGI_Emoji_ZWJ_Sequence',
'RGI_Emoji',
].join('|');
const charClassUnionToken = new RegExp(String.raw`
\\(?: c[A-Za-z]
| p\{(?<pStrProp>${stringPropertyNames})\}
| [pP]\{[^\}]+\}
| (?<qStrProp>q)
| u(?:[A-Fa-f\d]{4}|\{[A-Fa-f\d]+\})
| x[A-Fa-f\d]{2}
| .
)
| --
| &&
| .
`.replace(/\s+/g, ''), 'gsu');
// Assumes flag v and doesn't worry about syntax errors that are caught by it
function containsCharClassUnion(charClassPattern) {
// Return `true` if it contains:
// - `\p` (lowercase only) and the name is a property of strings (case sensitive).
// - `\q`.
// - Two single-char-matching tokens in sequence.
// - One single-char-matching token followed immediately by unescaped `[`.
// - One single-char-matching token preceded immediately by unescaped `]`.
// Else, `false`.
// Ranges with `-` create a single token.
// Subtraction and intersection with `--` and `&&` create a single token.
// Supports any number of nested classes
let hasFirst = false;
let lastM;
for (const {0: m, groups} of charClassPattern.matchAll(charClassUnionToken)) {
if (groups.pStrProp || groups.qStrProp) {
return true;
}
if (m === '[' && hasFirst) {
return true;
}
if (['-', '--', '&&'].includes(m)) {
hasFirst = false;
} else if (m !== '[' && m !== ']') {
if (hasFirst || lastM === ']') {
return true;
}
hasFirst = true;
}
lastM = m;
}
return false;
}
/**
@param {string} expression
@returns {number}
*/
function countCaptures(expression) {
let num = 0;
forEachUnescaped(expression, capturingDelim, () => num++, Context.DEFAULT);
return num;
}
/**
Escape special characters for the given context, assuming flag v.
@param {string} str String to escape
@param {'DEFAULT' | 'CHAR_CLASS'} context `Context` option from lib `regex-utilities`
@returns {string} Escaped string
*/
function escapeV(str, context) {
if (context === Context.CHAR_CLASS) {
// Escape all double punctuators (including ^, which is special on its own in the first
// position) in case they're bordered by the same character in or outside of the escaped string
return str.replace(new RegExp(String.raw`[()\[\]{}|\\/\-${doublePunctuatorChars}]`, 'g'), '\\$&');
}
return str.replace(/[()\[\]{}|\\^$*+?.]/g, '\\$&');
}
// Look for characters that would change the meaning of subsequent tokens outside an interpolated value
function getBreakoutChar(expression, regexContext, charClassContext) {
const escapesRemoved = expression.replace(/\\./gsu, '');
// Trailing unescaped `\`; checking `.includes('\\')` would also work
if (escapesRemoved.endsWith('\\')) {
return '\\';
}
if (regexContext === RegexContext.DEFAULT) {
// Unbalanced `[` or `]` are also errors but don't breakout; they're caught by the wrapper
return getUnbalancedChar(escapesRemoved, '(', ')');
} else if (
regexContext === RegexContext.CHAR_CLASS &&
!enclosedTokenCharClassContexts.has(charClassContext)
) {
return getUnbalancedChar(escapesRemoved, '[', ']');
} else if (
regexContext === RegexContext.INTERVAL_QUANTIFIER ||
enclosedTokenRegexContexts.has(regexContext) ||
enclosedTokenCharClassContexts.has(charClassContext)
) {
if (escapesRemoved.includes('}')) {
return '}';
}
} else if (regexContext === RegexContext.GROUP_NAME) {
if (escapesRemoved.includes('>')) {
return '>';
}
}
return '';
}
const contextToken = new RegExp(String.raw`
(?<groupN>\(\?<(?![=!])|\\[gk]<)
| (?<enclosedPU>\\[pPu]\{)
| (?<enclosedQ>\\q\{)
| (?<intervalQ>\{)
| (?<incompleteT>\\(?: $
| c(?![A-Za-z])
| u(?![A-Fa-f\d]{4})[A-Fa-f\d]{0,3}
| x(?![A-Fa-f\d]{2})[A-Fa-f\d]?
)
)
| --
| \\?.
`.replace(/\s+/g, ''), 'gsu');
/**
@typedef {{
regexContext: string;
charClassContext: string;
charClassDepth: number;
lastPos: number;
}} RunningContext
*/
/**
Accepts and returns its full state so it doesn't have to reprocess parts that have already been
seen. Assumes flag v and doesn't worry about syntax errors that are caught by it.
@param {string} incompleteExpression
@param {Partial<RunningContext>} [runningContext]
@returns {RunningContext}
*/
function getEndContextForIncompleteExpression(incompleteExpression, runningContext) {
let {regexContext, charClassContext, charClassDepth, lastPos} = {
regexContext: RegexContext.DEFAULT,
charClassContext: CharClassContext.DEFAULT,
charClassDepth: 0,
lastPos: 0,
...runningContext,
};
contextToken.lastIndex = lastPos;
let match;
while (match = contextToken.exec(incompleteExpression)) {
const {0: m, groups: {groupN, enclosedPU, enclosedQ, intervalQ, incompleteT}} = match;
if (m === '[') {
charClassDepth++;
regexContext = RegexContext.CHAR_CLASS;
charClassContext = CharClassContext.DEFAULT;
} else if (m === ']' && regexContext === RegexContext.CHAR_CLASS) {
if (charClassDepth) {
charClassDepth--;
}
if (!charClassDepth) {
regexContext = RegexContext.DEFAULT;
}
charClassContext = CharClassContext.DEFAULT;
} else if (regexContext === RegexContext.CHAR_CLASS) {
if (incompleteT) {
charClassContext = CharClassContext.INVALID_INCOMPLETE_TOKEN;
} else if (m === '-') {
charClassContext = CharClassContext.RANGE;
} else if (enclosedPU) {
charClassContext = m[1] === 'u' ? CharClassContext.ENCLOSED_U : CharClassContext.ENCLOSED_P;
} else if (enclosedQ) {
charClassContext = CharClassContext.ENCLOSED_Q;
} else if (
(m === '}' && enclosedTokenCharClassContexts.has(charClassContext)) ||
// Don't continue in these contexts since we've advanced another token
charClassContext === CharClassContext.INVALID_INCOMPLETE_TOKEN ||
charClassContext === CharClassContext.RANGE
) {
charClassContext = CharClassContext.DEFAULT;
}
} else {
if (incompleteT) {
regexContext = RegexContext.INVALID_INCOMPLETE_TOKEN;
} else if (groupN) {
regexContext = RegexContext.GROUP_NAME;
} else if (enclosedPU) {
regexContext = m[1] === 'u' ? RegexContext.ENCLOSED_U : RegexContext.ENCLOSED_P;
} else if (intervalQ) {
regexContext = RegexContext.INTERVAL_QUANTIFIER;
} else if (
(m === '>' && regexContext === RegexContext.GROUP_NAME) ||
(m === '}' && (regexContext === RegexContext.INTERVAL_QUANTIFIER || enclosedTokenRegexContexts.has(regexContext))) ||
// Don't continue in this context since we've advanced another token
regexContext === RegexContext.INVALID_INCOMPLETE_TOKEN
) {
regexContext = RegexContext.DEFAULT;
}
}
}
return {
regexContext,
charClassContext,
charClassDepth,
lastPos: incompleteExpression.length,
};
}
// No special handling for escaped versions of the characters
function getUnbalancedChar(expression, leftChar, rightChar) {
let numOpen = 0;
for (const [m] of expression.matchAll(new RegExp(`[${escapeV(leftChar + rightChar, Context.CHAR_CLASS)}]`, 'g'))) {
numOpen += m === leftChar ? 1 : -1;
if (numOpen < 0) {
return rightChar;
}
}
if (numOpen > 0) {
return leftChar;
}
return '';
}
/**
@typedef {import('./regex.js').InterpolatedValue} InterpolatedValue
@typedef {import('./regex.js').RawTemplate} RawTemplate
@typedef {import('./regex.js').RegexTagOptions} RegexTagOptions
@typedef {(
value: InterpolatedValue,
runningContext: RunningContext,
options: Required<RegexTagOptions>
) => {
transformed: string;
runningContext: RunningContext;
}} Preprocessor
*/
/**
Returns transformed versions of a template and substitutions, using the given preprocessor. Only
processes substitutions that are instanceof `Pattern`.
@param {RawTemplate} template
@param {ReadonlyArray<InterpolatedValue>} substitutions
@param {Preprocessor} preprocessor
@param {Required<RegexTagOptions>} options
@returns {{template: RawTemplate; substitutions: ReadonlyArray<InterpolatedValue>;}}
*/
function preprocess(template, substitutions, preprocessor, options) {
let /** @type {RawTemplate} */ newTemplate = {raw: []};
let newSubstitutions = [];
let runningContext;
template.raw.forEach((raw, i) => {
const result = preprocessor(raw, {...runningContext, lastPos: 0}, options);
newTemplate.raw.push(result.transformed);
runningContext = result.runningContext;
if (i < template.raw.length - 1) {
const substitution = substitutions[i];
if (substitution instanceof Pattern) {
const result = preprocessor(substitution, {...runningContext, lastPos: 0}, options);
newSubstitutions.push(pattern(result.transformed));
runningContext = result.runningContext;
} else {
newSubstitutions.push(substitution);
}
}
});
return {
template: newTemplate,
substitutions: newSubstitutions,
};
}
// Sandbox `^` if relevant, done so it can't change the meaning of the surrounding character class
// if we happen to be at the first position. See `sandboxLoneDoublePunctuatorChar` for more details
function sandboxLoneCharClassCaret(str) {
return str.replace(/^\^/, '\\^^');
}
// Sandbox without escaping by repeating the character and escaping only the first one. The second
// one is so that, if followed by the same symbol, the resulting double punctuator will still throw
// as expected. Details:
// - Only need to check the first position because, if it's part of an implicit union,
// interpolation handling will wrap it in nested `[…]`.
// - Can't just wrap in nested `[…]` here, since the value might be used in a range.
// - Can't add a second unescaped symbol if a lone symbol is the entire string because it might be
// followed by the same unescaped symbol outside an interpolation, and since it won't be wrapped,
// the second symbol wouldn't be sandboxed from the one following it.
function sandboxLoneDoublePunctuatorChar(str) {
return str.replace(new RegExp(`^([${doublePunctuatorChars}])(?!\\1)`), (m, _, pos) => {
return `\\${m}${pos + 1 === str.length ? '' : m}`;
});
}
/**
Converts `\0` tokens to `\x00` in the given context.
@param {string} str
@param {'DEFAULT' | 'CHAR_CLASS'} [context] `Context` option from lib `regex-utilities`
@returns {string}
*/
function sandboxUnsafeNulls(str, context) {
// regex`[\0${0}]` and regex`[${pattern`\0`}0]` can't be guarded against via nested `[…]`
// sandboxing in character classes if the interpolated value doesn't contain union (since it
// might be placed on a range boundary). So escape `\0` in character classes as `\x00`
return replaceUnescaped(str, String.raw`\\0(?!\d)`, '\\x00', context);
}
export {
adjustNumberedBackrefs,
capturingDelim,
CharClassContext,
containsCharClassUnion,
countCaptures,
doublePunctuatorChars,
enclosedTokenCharClassContexts,
enclosedTokenRegexContexts,
envSupportsFlagGroups,
envSupportsFlagV,
escapeV,
getBreakoutChar,
getEndContextForIncompleteExpression,
namedCapturingDelim,
preprocess,
RegexContext,
sandboxLoneCharClassCaret,
sandboxLoneDoublePunctuatorChar,
sandboxUnsafeNulls,
};