regex-partial-match 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -55
- package/lib/features/index.d.ts +1 -0
- package/lib/features/index.js +1 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/partialMatchRegExp/createPartialMatchRegExp.js +2 -1
- package/lib/partialMatchRegExp/isPartialMatchRegExp.d.ts +19 -0
- package/lib/partialMatchRegExp/isPartialMatchRegExp.js +23 -0
- package/lib/partialMatchRegExp/partialMatchInternals.d.ts +1 -0
- package/lib/partialMatchRegExp/partialMatchInternals.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Unlike C/C++ (via [PCRE/PCRE2](https://www.pcre.org/original/doc/html/pcrepartia
|
|
|
10
10
|
|
|
11
11
|
This library transforms regular expressions to best-effort support **partial matching**, allowing you to test if an incomplete string could potentially match the full pattern. This is particularly useful for real-time input validation, autocomplete systems, progressive form validation, stream chunk matching, etc.
|
|
12
12
|
|
|
13
|
-
As a side effect of the parse this requires, [`features()`](#features) names the syntactic constructs a `PartialMatchRegExp`'s pattern uses — useful for consumers that need to reason about a pattern without writing their own regex parser.
|
|
13
|
+
As a side effect of the parse this requires, [`features()`](#features) names the syntactic constructs a `PartialMatchRegExp`'s pattern uses — useful for consumers that need to reason about a pattern without writing their own regex parser. For many features, a simple search in the [source](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source) would be insufficient.
|
|
14
14
|
|
|
15
15
|
**Based on an algorithm created by [Lucas Trzesniewski](https://github.com/ltrzesniewski)**, re-created for NPM via ISC license, with permission.
|
|
16
16
|
|
|
@@ -69,19 +69,17 @@ state("2024-06-15"); // 'complete' - accept, enable submit
|
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
> [!NOTE]
|
|
72
|
-
> `hitEnd()` answers "could more input change this match?", not "is this text a match?". `/hello \w+/` matches `"hello world"` in full, and `hitEnd()` is still `true`: `\w+` read the end of the input looking for more. For an exact-length pattern like the date above the two questions coincide, which is what makes the three states work; a validator for an open-ended pattern that wants "valid so far" should test the original pattern as well.
|
|
72
|
+
> [`hitEnd()`](#hitend) answers "could more input change this match?", not "is this text a match?". `/hello \w+/` matches `"hello world"` in full, and `hitEnd()` is still `true`: `\w+` read the end of the input looking for more. For an exact-length pattern like the date above the two questions coincide, which is what makes the three states work; a validator for an open-ended pattern that wants "valid so far" should test the original pattern as well.
|
|
73
73
|
|
|
74
74
|
Upgrading from 1.x, where this was `isComplete()`? See the [migration guide](./codemods/transforms/v1-v2/README.md).
|
|
75
75
|
|
|
76
|
-
[^1]:
|
|
77
|
-
Mimicking JDK's [`Matcher.hitEnd()`](https://docs.oracle.com/javase/8/docs/api/java/util/regex/Matcher.html#hitEnd--)
|
|
76
|
+
[^1]: Mimicking JDK's [`Matcher.hitEnd()`](https://docs.oracle.com/javase/8/docs/api/java/util/regex/Matcher.html#hitEnd--)
|
|
78
77
|
|
|
79
|
-
[^2]:
|
|
80
|
-
Testing the original, untransformed pattern looks like it should answer this — "did the input fully satisfy the original pattern?" — but it asks a different question: whether the original matches *at all* here, not whether *this* match reached the end of the input on its way. The two agree almost always, but a read of the end inside a zero-width assertion can make both return an identical result by different paths. See [Why the question can't be answered from the outside](https://github.com/TomStrepsil/regex-partial-match/blob/main/docs/how-it-works.md#why-the-question-cant-be-answered-from-the-outside) for the case where they diverge.
|
|
78
|
+
[^2]: Testing the original, untransformed pattern looks like it should answer this — "did the input fully satisfy the original pattern?" — but it asks a different question: whether the original matches _at all_ here, not whether _this_ match reached the end of the input on its way. The two agree almost always, but a read of the end inside a zero-width assertion can make both return an identical result by different paths. See [Why the question can't be answered from the outside](https://github.com/TomStrepsil/regex-partial-match/blob/main/docs/how-it-works.md#why-the-question-cant-be-answered-from-the-outside) for the case where they diverge.
|
|
81
79
|
|
|
82
80
|
### A note on Tree-Shaking
|
|
83
81
|
|
|
84
|
-
If your environment doesn't tree-shake (e.g. Deno, or unbundled Node), the default entry loads `hitEnd` and `features` with the class. Import each from its own subpath instead, and only the ones you use:
|
|
82
|
+
If your environment doesn't tree-shake (e.g. Deno, or unbundled Node), the default entry loads [`hitEnd`](#hitend) and [`features`](#features) with the class. Import each from its own subpath instead, and only the ones you use:
|
|
85
83
|
|
|
86
84
|
```javascript
|
|
87
85
|
import PartialMatchRegExp from "regex-partial-match/partialMatchRegExp";
|
|
@@ -91,14 +89,24 @@ import features from "regex-partial-match/features";
|
|
|
91
89
|
|
|
92
90
|
[`hitEnd`](#hitend) and [`features`](#features) accept an instance of any `PartialMatchRegExp` class, including those from [`regex-partial-match/core`](#the-lean-entry-regex-partial-matchcore).
|
|
93
91
|
|
|
92
|
+
To tell an instance from a plain `RegExp` without importing a class, use [`isPartialMatchRegExp()`](#ispartialmatchregexp). It returns `true` for an instance of any `PartialMatchRegExp` class and `false` for anything else, including a plain `RegExp`:
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
import PartialMatchRegExp from "regex-partial-match/core";
|
|
96
|
+
import { isPartialMatchRegExp } from "regex-partial-match/features";
|
|
97
|
+
|
|
98
|
+
isPartialMatchRegExp(new PartialMatchRegExp(/a/)); // true
|
|
99
|
+
isPartialMatchRegExp(/a/); // false
|
|
100
|
+
```
|
|
101
|
+
|
|
94
102
|
### The lean entry: `regex-partial-match/core`
|
|
95
103
|
|
|
96
104
|
`regex-partial-match/core` exports a `PartialMatchRegExp` without two sets of rules, so a bundle that doesn't need them can leave them out. Each set is a module you bind back with `withModules`:
|
|
97
105
|
|
|
98
|
-
| Module
|
|
99
|
-
|
|
100
|
-
| `carets`
|
|
101
|
-
| `backreferences` | `regex-partial-match/modules/backreferences` | matching a backreference against the text its group captured
|
|
106
|
+
| Module | Import | Supplies |
|
|
107
|
+
| ---------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
108
|
+
| `carets` | `regex-partial-match/modules/carets` | the rules for a `^` under the `m` flag that has something before it, and for a `^` in a group or positive lookahead |
|
|
109
|
+
| `backreferences` | `regex-partial-match/modules/backreferences` | matching a backreference against the text its group captured |
|
|
102
110
|
|
|
103
111
|
Both are also named exports of `regex-partial-match/modules`.
|
|
104
112
|
|
|
@@ -113,7 +121,7 @@ const WithBoth = withModules(carets, backreferences);
|
|
|
113
121
|
new WithBoth(/(a|b)\1/).test("a"); // true
|
|
114
122
|
```
|
|
115
123
|
|
|
116
|
-
`withModules` returns a `PartialMatchRegExp` class extending `RegExp`, and the same set of modules in any order returns the same class: `withModules()` is the `core` class, and `withModules(carets, backreferences)` is the default `PartialMatchRegExp`, so `instanceof` holds between them. [`hitEnd
|
|
124
|
+
`withModules` returns a `PartialMatchRegExp` class extending `RegExp`, and the same set of modules in any order returns the same class: `withModules()` is the `core` class, and `withModules(carets, backreferences)` is the default `PartialMatchRegExp`, so `instanceof` holds between them. [`hitEnd`](#hitend) and [`features`](#features) accept instances of any of these classes.
|
|
117
125
|
|
|
118
126
|
A module's type is `Module`, exported from both `regex-partial-match/core` and `regex-partial-match/modules`, for annotating a list of modules: `const modules: Module[] = [carets, backreferences]`. It is opaque, so what a module holds is not part of the API, and `withModules` throws a `TypeError` for anything that isn't a module.
|
|
119
127
|
|
|
@@ -121,11 +129,11 @@ A module's type is `Module`, exported from both `regex-partial-match/core` and `
|
|
|
121
129
|
|
|
122
130
|
If your patterns aren't known when you build, because they come from users or configuration, use `regex-partial-match`. If they are fixed, use `core`: construct each pattern once, and bind the modules construction asks for.
|
|
123
131
|
|
|
124
|
-
| Error when constructed
|
|
125
|
-
|
|
126
|
-
| `TypeError: Needs the carets module`
|
|
127
|
-
| `TypeError: Needs the backreferences module`
|
|
128
|
-
| `TypeError: Needs the carets and backreferences modules` | both
|
|
132
|
+
| Error when constructed | Bind |
|
|
133
|
+
| -------------------------------------------------------- | ---------------- |
|
|
134
|
+
| `TypeError: Needs the carets module` | `carets` |
|
|
135
|
+
| `TypeError: Needs the backreferences module` | `backreferences` |
|
|
136
|
+
| `TypeError: Needs the carets and backreferences modules` | both |
|
|
129
137
|
|
|
130
138
|
The error names every module the pattern needs, and none that is already bound.
|
|
131
139
|
|
|
@@ -180,7 +188,7 @@ The library is compiled to **ES2015** (ECMAScript 6). Certain regular expression
|
|
|
180
188
|
- [**`v` (unicodeSets) flag**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicodeSets) - ES2024+
|
|
181
189
|
- [**Modifiers**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Modifier) (`(?ims:...)`, `(?-ims:...)`, `(?i-ms:...)`) - ES2025+
|
|
182
190
|
|
|
183
|
-
Each of these applies only when the
|
|
191
|
+
Each of these applies only when the _original_ pattern uses the feature — everything else, including construction, `exec()`, `test()` and [`hitEnd()`](#hitend), holds to the ES2015 floor.
|
|
184
192
|
|
|
185
193
|
## ⚠️ Caveats
|
|
186
194
|
|
|
@@ -323,7 +331,7 @@ Available as a named export of the default entry point, `import { hitEnd } from
|
|
|
323
331
|
**Returns:**
|
|
324
332
|
|
|
325
333
|
- `true` when the match read the end of the input: an atom ran out of input and took one of the `|$(?![\s\S])` branches described in [How It Works](#how-it-works); a greedy quantifier stopped at the end because there was nothing left to read; or `$`, `\b` or `\B` held there. More input could extend the match, change which alternative wins, or invalidate it, and its captures are the closest available rather than final — see [Captures on a partial match](./docs/caveats.md#captures-on-a-partial-match).
|
|
326
|
-
- `false` when every atom matched literally and nothing read past the last character consumed. No continuation of the input changes the match's index or text, and the captures are the ones the original pattern produces — except the
|
|
334
|
+
- `false` when every atom matched literally and nothing read past the last character consumed. No continuation of the input changes the match's index or text, and the captures are the ones the original pattern produces — except the three cases in [What it cannot see](#what-it-cannot-see) below, where a read of the end leaves no marker, or a split surrogate pair can still change the match, and `false` is reported despite it.
|
|
327
335
|
|
|
328
336
|
```javascript
|
|
329
337
|
import PartialMatchRegExp, { hitEnd } from "regex-partial-match";
|
|
@@ -340,14 +348,15 @@ hitEnd(greedy, greedy.exec("hello world")); // true — \w+ read the end lookin
|
|
|
340
348
|
```
|
|
341
349
|
|
|
342
350
|
> [!NOTE]
|
|
343
|
-
> Where the JDK is exact, `hitEnd()
|
|
351
|
+
> Where the JDK is exact, [`hitEnd`](#hitend) is conservative in one place: a bounded greedy quantifier (`?`, `{n,m}`) fully taken at the end of the input reports `true`, although the engine attempted no further read there — on a _group_ (`/(ab)?/` on `"ab"` is `true` here and `false` in Java), and the same way for an unequal-bound `{n,m}` directly on a single atom once it's saturated at its maximum (`/a{1,2}/` on `"aa"` is `true`, though no continuation can add a third `a`). Outside the three limits in [What it cannot see](#what-it-cannot-see), it is never wrong in the other direction.
|
|
344
352
|
|
|
345
|
-
See [How It Works](./docs/how-it-works.md#why-the-question-cant-be-answered-from-the-outside) for why this can't be worked out from the match alone, and how `hitEnd()
|
|
353
|
+
See [How It Works](./docs/how-it-works.md#why-the-question-cant-be-answered-from-the-outside) for why this can't be worked out from the match alone, and how [`hitEnd`](#hitend) records a read of the end.
|
|
346
354
|
|
|
347
355
|
#### What it cannot see
|
|
348
356
|
|
|
349
357
|
- **A read of the end inside a lookahead in an earlier iteration of a quantified group.** The probe's [markers](./docs/how-it-works.md#recording-a-read-of-the-end) are capturing groups, and [`RepeatMatcher`](https://tc39.es/ecma262/#sec-runtime-semantics-repeatmatcher-abstract-operation) resets a quantified group's captures at the start of every iteration. `/(?:a(?=bcd)|b)+/` on `"abc"` reads the end inside `(?=bcd)` in its first iteration, matches `b` in its second, and reports `false` — although `"abcx"` changes the match to `"b"` at index 1. Nothing placed inside the repeated atom survives the reset, so this is a limit of the marker approach rather than an oversight, and it is pinned by a test.
|
|
350
358
|
- **A read of the end inside a raw lookaround.** Negative lookaheads and both lookbehinds are kept verbatim (see [Caveats](./docs/caveats.md)), so a read of the end inside them leaves no marker: `/^a(?!b)/` on `"a"` reports `false`, although `"ab"` invalidates the match. A scanner whose output must not depend on where its input was chunked should refuse or buffer patterns that use them.
|
|
359
|
+
- **A high surrogate ending the input under `u` or `v`.** Only whole astral characters are supported (see [Surrogate Pair Matching](./docs/caveats.md#surrogate-pair-matching)), so a match whose last code unit is a lone high surrogate reports `false`, although a following low surrogate would pair with it and change the match's text: `/./u` on `"\ud83d"` matches `"\ud83d"`, but `"😄"` matches `"😄"`. [^3]
|
|
351
360
|
|
|
352
361
|
### `features()`
|
|
353
362
|
|
|
@@ -355,11 +364,11 @@ See [How It Works](./docs/how-it-works.md#why-the-question-cant-be-answered-from
|
|
|
355
364
|
features(partial: PartialMatchRegExp): ReadonlySet<RegexFeature>
|
|
356
365
|
```
|
|
357
366
|
|
|
358
|
-
Building the partial-match regex requires walking the entire source pattern, usually once.
|
|
367
|
+
Building the partial-match regex requires walking the entire source pattern, usually once. [^4] As a side effect of the walk that builds the regex, each instance records which syntactic constructs its pattern actually uses, and `features()` names them as a set — no separate scan of the source is performed to produce it. The set is built on the first call for an instance and the same set is returned after that.
|
|
359
368
|
|
|
360
369
|
Available as a named export of the default entry point, `import { features } from 'regex-partial-match'`, or as the default export of `regex-partial-match/features`. It accepts an instance of any `PartialMatchRegExp` class, including those from [`regex-partial-match/core`](#the-lean-entry-regex-partial-matchcore).
|
|
361
370
|
|
|
362
|
-
This is useful for consumers building on top of `PartialMatchRegExp` who need to reason about which constructs a
|
|
371
|
+
This is useful for consumers building on top of `PartialMatchRegExp` who need to reason about which constructs a _specific_ pattern uses, without writing their own regex parser to find out. Two concrete cases:
|
|
363
372
|
|
|
364
373
|
- **Flagging patterns likely to hit one of the [caveats](./docs/caveats.md).** For example, a pattern combining `backreference` with `lookbehind`, `negativeLookahead`, or `negativeLookbehind` is a candidate for the [atomic-backreference caveat](./docs/caveats.md#backreferences); one combining `backreference` with `disjunction` is a candidate for the [prefix-ambiguous top-level alternation caveat](./docs/caveats.md#prefix-ambiguous-top-level-alternation). A consumer accepting user-supplied patterns can surface a warning instead of letting the edge case surprise someone later.
|
|
365
374
|
- **Restricting which constructs a product surface allows.** e.g. a system that only wants to accept "simple" patterns (no lookaround, no backreferences) from untrusted input can check `features()` against an allow-list and reject the rest, without needing to hand-roll that check against the raw pattern source.
|
|
@@ -376,38 +385,38 @@ features(partial).has("backreference"); // true
|
|
|
376
385
|
|
|
377
386
|
`RegexFeature` is a string union, exported from `regex-partial-match` and `regex-partial-match/features`:
|
|
378
387
|
|
|
379
|
-
| Feature | Matches
|
|
380
|
-
|
|
|
381
|
-
| `patternCharacter`
|
|
382
|
-
| `startAnchor`
|
|
383
|
-
| `endAnchor`
|
|
384
|
-
| `wordBoundary`
|
|
385
|
-
| `nonWordBoundary`
|
|
386
|
-
| `lookahead`
|
|
387
|
-
| `negativeLookahead`
|
|
388
|
-
| `lookbehind`
|
|
389
|
-
| `negativeLookbehind`
|
|
390
|
-
| `backreference`
|
|
391
|
-
| `namedBackreference`
|
|
392
|
-
| `namedGroup`
|
|
393
|
-
| `capturingGroup`
|
|
394
|
-
| `lookaroundCapture`
|
|
395
|
-
| `nonCapturingGroup`
|
|
396
|
-
| `modifierGroup`
|
|
397
|
-
| `modifierGroupWithRemoval`
|
|
398
|
-
| `characterClass`
|
|
399
|
-
| `nestedCharacterClass`
|
|
400
|
-
| `classIntersection`
|
|
401
|
-
| `classSubtraction`
|
|
402
|
-
| `disjunction`
|
|
403
|
-
| `quantifier`
|
|
404
|
-
| `unicodePropertyEscape`
|
|
405
|
-
| `characterClassEscape`
|
|
406
|
-
| `controlEscape`
|
|
407
|
-
| `controlLetterEscape`
|
|
408
|
-
| `hexEscapeSequence`
|
|
409
|
-
| `unicodeEscapeSequence`
|
|
410
|
-
| `otherEscape`
|
|
388
|
+
| Feature | Matches | Notes |
|
|
389
|
+
| -------------------------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
390
|
+
| `patternCharacter` | An ordinary literal character | |
|
|
391
|
+
| `startAnchor` | Top-level `^` | |
|
|
392
|
+
| `endAnchor` | Top-level `$` | |
|
|
393
|
+
| `wordBoundary` | Top-level `\b` | |
|
|
394
|
+
| `nonWordBoundary` | Top-level `\B` | |
|
|
395
|
+
| `lookahead` | `(?=...)` | |
|
|
396
|
+
| `negativeLookahead` | `(?!...)` | |
|
|
397
|
+
| `lookbehind` | `(?<=...)` | |
|
|
398
|
+
| `negativeLookbehind` | `(?<!...)` | |
|
|
399
|
+
| `backreference` | `\1` | |
|
|
400
|
+
| `namedBackreference` | `\k<name>` | |
|
|
401
|
+
| `namedGroup` | `(?<name>...)` | Always accompanied by `capturingGroup` — see below |
|
|
402
|
+
| `capturingGroup` | `(...)`, including named groups | |
|
|
403
|
+
| `lookaroundCapture` | A capturing group inside any lookaround | Nesting, not a construct — always accompanied by `capturingGroup` |
|
|
404
|
+
| `nonCapturingGroup` | `(?:...)` | |
|
|
405
|
+
| `modifierGroup` | `(?ims:...)` | |
|
|
406
|
+
| `modifierGroupWithRemoval` | `(?ims-ims:...)` | Mutually exclusive with `modifierGroup` |
|
|
407
|
+
| `characterClass` | `[...]` | |
|
|
408
|
+
| `nestedCharacterClass` | `[...[...]...]` | `v` flag only |
|
|
409
|
+
| `classIntersection` | `&&` inside a character class | `v` flag only |
|
|
410
|
+
| `classSubtraction` | `--` inside a character class | `v` flag only |
|
|
411
|
+
| `disjunction` | `\|` | |
|
|
412
|
+
| `quantifier` | `*`, `+`, `?`, `{n}`, `{n,}`, `{n,m}` | |
|
|
413
|
+
| `unicodePropertyEscape` | `\p{...}`, `\P{...}` | `u`/`v` flag only — otherwise tagged `otherEscape` |
|
|
414
|
+
| `characterClassEscape` | `\d`, `\D`, `\w`, `\W`, `\s`, `\S` | |
|
|
415
|
+
| `controlEscape` | `\f`, `\n`, `\r`, `\t`, `\v` | |
|
|
416
|
+
| `controlLetterEscape` | `\cX` | |
|
|
417
|
+
| `hexEscapeSequence` | `\xXX` | |
|
|
418
|
+
| `unicodeEscapeSequence` | `\uXXXX`, `\u{...}` | |
|
|
419
|
+
| `otherEscape` | Any other `\X`, e.g. `\.` | `\0` alone is tagged `otherEscape` only under `u`/`v`; otherwise `backreference`, [like any other digit escape](docs/modules/backreferences.md) |
|
|
411
420
|
|
|
412
421
|
Three things worth knowing about how these tags line up with the grammar:
|
|
413
422
|
|
|
@@ -415,6 +424,23 @@ Three things worth knowing about how these tags line up with the grammar:
|
|
|
415
424
|
- **A named capturing group always carries both `namedGroup` and `capturingGroup`.** The grammar treats a capturing group with a name and one without as the same production (`( GroupSpecifier? Disjunction )`), not two, so both tags are added together.
|
|
416
425
|
- **`lookaroundCapture` records nesting, not a construct.** Every other tag names something the source contains; this one names where something sits — a capturing group appearing lexically inside `(?=...)`, `(?!...)`, `(?<=...)` or `(?<!...)`, at any depth of nested groups. It is the one relationship between constructs the flattened set cannot otherwise express: `/(\w+)(?= END)/` and `/a(?=(?:b(?:x|(c))d|b))/` both report `lookahead` and `capturingGroup`, but only the second has a capture whose value the assertion decides.
|
|
417
426
|
|
|
427
|
+
[^3]: A scanner fed from a source that can split a surrogate pair across chunks, rather than a [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder) in streaming mode (`stream: true`, or `TextDecoderStream`), should defer any match ending on a code unit in `0xD800`–`0xDBFF` at the end of the input.
|
|
428
|
+
|
|
429
|
+
[^4]: The [carets module](./docs/modules/carets.md) guesses from a cheap look at the source whether a pattern needs its rules, and when it guesses wrong the pattern is walked a second time with them.
|
|
430
|
+
|
|
431
|
+
### `isPartialMatchRegExp()`
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
isPartialMatchRegExp(value: unknown): value is PartialMatchRegExp
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
Returns `true` for an instance of any `PartialMatchRegExp` class, from `regex-partial-match/core`, from `withModules(...)`, or from the default entry point, and `false` for a plain `RegExp` or any other value. It doesn't import a class or module, so it can be used to accept either a `RegExp` or a ready-built instance without pulling the default class into a bundle.
|
|
438
|
+
|
|
439
|
+
Available as a named export of `regex-partial-match/features`, or of the default entry point, `import { isPartialMatchRegExp } from 'regex-partial-match'`.
|
|
440
|
+
|
|
441
|
+
> [!CAUTION]
|
|
442
|
+
> It recognises instances from the same installed copy of the library only: an instance from a second copy, such as a duplicate install in a bundle, returns `false`.
|
|
443
|
+
|
|
418
444
|
## 📜 License
|
|
419
445
|
|
|
420
446
|
ISC License - see [LICENSE](./LICENSE) file for details.
|
package/lib/features/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type PartialMatchRegExp from "../partialMatchRegExp/index.ts";
|
|
2
2
|
import { type RegexFeature } from "../partialMatchRegExp/regexFeatures.ts";
|
|
3
3
|
export type { RegexFeature };
|
|
4
|
+
export { default as isPartialMatchRegExp } from "../partialMatchRegExp/isPartialMatchRegExp.ts";
|
|
4
5
|
/**
|
|
5
6
|
* The syntactic constructs `partial`'s original pattern uses.
|
|
6
7
|
*
|
package/lib/features/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { compiledOf } from "../partialMatchRegExp/partialMatchInternals.js";
|
|
2
2
|
import { featureSet } from "../partialMatchRegExp/regexFeatures.js";
|
|
3
|
+
export { default as isPartialMatchRegExp } from "../partialMatchRegExp/isPartialMatchRegExp.js";
|
|
3
4
|
/**
|
|
4
5
|
* The syntactic constructs `partial`'s original pattern uses.
|
|
5
6
|
*
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export * from "./partialMatchRegExp/index.ts";
|
|
2
2
|
export { default } from "./partialMatchRegExp/index.ts";
|
|
3
3
|
export { default as hitEnd } from "./hitEnd/index.ts";
|
|
4
|
-
export { default as features } from "./features/index.ts";
|
|
4
|
+
export { default as features, isPartialMatchRegExp } from "./features/index.ts";
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export * from "./partialMatchRegExp/index.js";
|
|
2
2
|
export { default } from "./partialMatchRegExp/index.js";
|
|
3
3
|
export { default as hitEnd } from "./hitEnd/index.js";
|
|
4
|
-
export { default as features } from "./features/index.js";
|
|
4
|
+
export { default as features, isPartialMatchRegExp } from "./features/index.js";
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import compileWith from "./compilePartial/compileWith.js";
|
|
2
|
-
import { compiledPartial } from "./partialMatchInternals.js";
|
|
2
|
+
import { compiledPartial, constructedPartials } from "./partialMatchInternals.js";
|
|
3
3
|
import execFrom from "./execFrom.js";
|
|
4
4
|
export default function createPartialMatchRegExp(hooks) {
|
|
5
5
|
return class PartialMatchRegExp extends RegExp {
|
|
6
6
|
constructor(pattern, flags) {
|
|
7
7
|
super(pattern, flags);
|
|
8
8
|
this[compiledPartial] = compileWith(this, hooks);
|
|
9
|
+
constructedPartials.add(this);
|
|
9
10
|
}
|
|
10
11
|
exec(input) {
|
|
11
12
|
const compiled = this[compiledPartial];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { PartialMatchRegExp } from "./index.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Whether `value` is an instance of a `PartialMatchRegExp` class, from any
|
|
4
|
+
* entry point or `withModules`, without importing any of those classes.
|
|
5
|
+
*
|
|
6
|
+
* @param value - Any value
|
|
7
|
+
* @returns `true` for a `PartialMatchRegExp` instance; `false` for a plain
|
|
8
|
+
* `RegExp` or any other value
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import PartialMatchRegExp from "regex-partial-match/core";
|
|
13
|
+
* import { isPartialMatchRegExp } from "regex-partial-match/features";
|
|
14
|
+
*
|
|
15
|
+
* isPartialMatchRegExp(new PartialMatchRegExp(/a/)); // true
|
|
16
|
+
* isPartialMatchRegExp(/a/); // false
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export default function isPartialMatchRegExp(value: unknown): value is PartialMatchRegExp;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { constructedPartials } from "./partialMatchInternals.js";
|
|
2
|
+
/**
|
|
3
|
+
* Whether `value` is an instance of a `PartialMatchRegExp` class, from any
|
|
4
|
+
* entry point or `withModules`, without importing any of those classes.
|
|
5
|
+
*
|
|
6
|
+
* @param value - Any value
|
|
7
|
+
* @returns `true` for a `PartialMatchRegExp` instance; `false` for a plain
|
|
8
|
+
* `RegExp` or any other value
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import PartialMatchRegExp from "regex-partial-match/core";
|
|
13
|
+
* import { isPartialMatchRegExp } from "regex-partial-match/features";
|
|
14
|
+
*
|
|
15
|
+
* isPartialMatchRegExp(new PartialMatchRegExp(/a/)); // true
|
|
16
|
+
* isPartialMatchRegExp(/a/); // false
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export default function isPartialMatchRegExp(value) {
|
|
20
|
+
return (typeof value === "object" &&
|
|
21
|
+
value !== null &&
|
|
22
|
+
constructedPartials.has(value));
|
|
23
|
+
}
|