regex-partial-match 2.0.1 → 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 +30 -4
- 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
|
@@ -89,6 +89,16 @@ import features from "regex-partial-match/features";
|
|
|
89
89
|
|
|
90
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).
|
|
91
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
|
+
|
|
92
102
|
### The lean entry: `regex-partial-match/core`
|
|
93
103
|
|
|
94
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`:
|
|
@@ -321,7 +331,7 @@ Available as a named export of the default entry point, `import { hitEnd } from
|
|
|
321
331
|
**Returns:**
|
|
322
332
|
|
|
323
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).
|
|
324
|
-
- `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.
|
|
325
335
|
|
|
326
336
|
```javascript
|
|
327
337
|
import PartialMatchRegExp, { hitEnd } from "regex-partial-match";
|
|
@@ -338,7 +348,7 @@ hitEnd(greedy, greedy.exec("hello world")); // true — \w+ read the end lookin
|
|
|
338
348
|
```
|
|
339
349
|
|
|
340
350
|
> [!NOTE]
|
|
341
|
-
> 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
|
|
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.
|
|
342
352
|
|
|
343
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.
|
|
344
354
|
|
|
@@ -346,6 +356,7 @@ See [How It Works](./docs/how-it-works.md#why-the-question-cant-be-answered-from
|
|
|
346
356
|
|
|
347
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.
|
|
348
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]
|
|
349
360
|
|
|
350
361
|
### `features()`
|
|
351
362
|
|
|
@@ -353,7 +364,7 @@ See [How It Works](./docs/how-it-works.md#why-the-question-cant-be-answered-from
|
|
|
353
364
|
features(partial: PartialMatchRegExp): ReadonlySet<RegexFeature>
|
|
354
365
|
```
|
|
355
366
|
|
|
356
|
-
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.
|
|
357
368
|
|
|
358
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).
|
|
359
370
|
|
|
@@ -413,7 +424,22 @@ Three things worth knowing about how these tags line up with the grammar:
|
|
|
413
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.
|
|
414
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.
|
|
415
426
|
|
|
416
|
-
[^3]:
|
|
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`.
|
|
417
443
|
|
|
418
444
|
## 📜 License
|
|
419
445
|
|
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
|
+
}
|