No 3rd party dependencies. All dependencies and devDependencies, checked recursively, are Codsen packages.
Permalink to InstallationInstallation
Permalink to Quick TakeQuick Take
Permalink to ExamplesExamples
- Match a character sequence without regard to letter case
- Match repeated sequences on the left and include nearby whitespace
- Control how chomp functions include surrounding whitespace
- Match repeated sequences on the right and include nearby whitespace
- Match a whitespace-tolerant character sequence on the left
- Combine optional and repeated sequence flags
- Mark one sequence value as optional with a question mark
- Match one or more repeated sequence values with an asterisk
- Match a whitespace-tolerant character sequence on the right
- Find the nearest non-whitespace character on the right
- Treat a newline as a stopping point in either direction
- Treat a raw non-breaking space as a stopping point
- Match an astral symbol as two UTF-16 code units
Purpose
This package finds the nearest non-whitespace UTF-16 code unit on either side of an index. It can also match whitespace-tolerant character sequences and return boundaries that callers can use to remove repeated sequences.
Index boundaries
Every lookup starts from an exclusive boundary named idx. The character at str[idx] is not examined.
| Direction | Indexes examined | Include the string endpoint |
|---|---|---|
| Left | ||
| Left | Lower than idx | Pass str.length to include the final code unit |
| Right | ||
| Right | Higher than idx | Pass -1 to include index 0 |
The optional index on left(), right(), and the stop helpers defaults to 0. Passing null or undefined has the same effect.
All ten APIs return null for fractional or non-finite indexes and unsupported JavaScript index types. Leftward APIs accept non-negative integers and clamp indexes above str.length to str.length. Rightward APIs accept integers starting at -1, the boundary before the string; indexes at or beyond the end return null.
API — left() and right()
These functions return the zero-based index of the nearest non-whitespace code unit on the selected side, or null when none exists.
An individual code unit is treated as whitespace when trimming its one-code-unit string produces an empty string.
import { left, right } from "string-left-right";
// Boundary 2 is before "b". left() examines indexes below 2.
console.log(left("a b", 2));
// => 0
// Boundary -1 is before the string. right() can therefore inspect index 0.
console.log(right("a b", -1));
// => 0
API — leftSeq() and rightSeq()
leftSeq() and rightSeq() match a sequence in source order on the selected side of an exclusive boundary. Whitespace between values is ignored.
| Argument | Type | Required | Description |
|---|---|---|---|
strType: string | |||
str | string | Yes | Input string |
idxType: number | |||
idx | number | Yes | Exclusive boundary from which matching begins |
optsType: Opts | |||
opts | Opts | No | Options object; when present, it precedes value |
valueType: string | |||
value | string | Yes | First one-code-unit match pattern |
...valuesType: string[] | |||
...values | string[] | No | Additional patterns in source order |
TypeScript rejects calls without a match value and options objects that are not followed by one. At runtime, omitting the third argument entirely throws an input-validation error; an options-only call returns null.
Each value can carry a suffix:
| Suffix | Meaning |
|---|---|
| None | |
| None | Match exactly once |
? | |
? | Match zero or one time |
* | |
* | Match one or more times greedily |
?* or *? | |
?* or *? | Match zero or more times greedily |
A bare ? or * matcher matches that literal character. Greedy matching does not backtrack. A required value after a greedy value must still match a separate code unit. Every skipped whitespace span—from the boundary to the nearest match and between matched values—is reported in gaps.
An empty-string matcher is skipped for compatibility. Any non-string matcher invalidates the whole sequence and returns null. A sequence that consumes no code units also returns null.
Each matcher represents one UTF-16 code unit, excluding its optional flag suffix. An astral symbol such as 😀 occupies two code units: a matcher containing the whole symbol returns null, while separate "\ud83d" and "\ude00" matchers match both halves. Basic traversal can return the index of either surrogate half. Combining marks and lone surrogates are separate code units; matching neither normalizes Unicode nor segments graphemes. For example, "e\u0301" takes two matchers and is not treated as "é".
The functions return null or a SeqOutput object:
gaps contains those skipped spans as [start, end] arrays in ascending source order. Each range includes start and excludes end. leftmostChar and rightmostChar are the inclusive indexes of the outer matched characters.
import { leftSeq } from "string-left-right";
// Boundary 5 is before "f". Match "c", "d", and "e" on its left.
const result = leftSeq("abcdefghijk", 5, "c", "d", "e");
console.log(result);
// => { gaps: [], leftmostChar: 2, rightmostChar: 4 }
Here is the same match with whitespace gaps:
const result = leftSeq(
"a b c d e f g h i j k",
15,
"c",
"d",
"e",
);
console.log(result);
// => {
// gaps: [[7, 9], [10, 12], [13, 15]],
// leftmostChar: 6,
// rightmostChar: 12
// }
API — chompLeft() and chompRight()
The chomp functions match one or more adjacent occurrences of a complete sequence, ignoring whitespace between values. They return the outer boundary of the matched range, adjusted by the selected whitespace mode, or null when the sequence does not match.
The third argument is either the first string value or an options placeholder followed by the first value. The placeholder can be a ChompOpts object, null, or undefined. At least one string value is required.
Pass match values in source order. They support the same ?, *, ?*, and *? suffixes as the sequence functions.
chompLeft() returns the range’s left boundary. chompRight() returns its end-exclusive right boundary. These values are range boundaries, not indexes of characters that the functions “land on.”
import { chompLeft } from "string-left-right";
// Match repeated "b", "c" sequences on the left of boundary 12.
const res1 = chompLeft("a b c b c x y", 12, "b", "c");
console.log(res1);
// => 2
// Explicit mode 0 produces the same boundary.
const res2 = chompLeft("a b c b c x y", 12, { mode: 0 }, "b", "c");
console.log(res2);
// => 2
API — chompLeft() and chompRight() modes
Modes control how much whitespace outside the repeated match is included in the returned range:
| Mode | Behavior |
|---|---|
0 | |
0 | Consume adjacent whitespace while preserving one whitespace code unit when possible; do not cross CR or LF |
1 | |
1 | Preserve all adjacent whitespace |
2 | |
2 | Consume adjacent whitespace up to, but not including, CR or LF |
3 | |
3 | Consume all adjacent whitespace, including CR and LF, until non-whitespace or the string boundary |
Numeric modes are recommended. For compatibility, "0" through "3" are also accepted. An omitted mode, "", null, or undefined selects mode 0. Other values, including booleans, NaN, bigints, symbols, fractions, and out-of-range numbers, throw a tagged input-validation error.
In this example, LF is at index 1, the first matched b is at index 4, and the exclusive boundary before x is 13:
import { chompLeft } from "string-left-right";
const source = "a\n b c b c x y";
console.log(chompLeft(source, 13, { mode: 0 }, "b", "c"));
// => 2 (immediately after LF)
console.log(chompLeft(source, 13, { mode: 1 }, "b", "c"));
// => 4 (the left boundary of the first "b")
console.log(chompLeft(source, 13, { mode: 2 }, "b", "c"));
// => 2 (immediately after LF)
console.log(chompLeft(source, 13, { mode: 3 }, "b", "c"));
// => 1 (before LF, so the returned range includes it)
chompRight() applies the same matching and whitespace rules towards the right, then returns an end-exclusive boundary.
API — leftStopAtNewLines() and rightStopAtNewLines()
These functions behave like left() and right(), except they also stop at CR and LF line-break characters.
import { right, rightStopAtNewLines } from "string-left-right";
const str = "a \n\n\nb";
const res1 = right(str, 0);
const res2 = rightStopAtNewLines(str, 0);
console.log(`res1 = ${res1}; res2 = ${res2}`);
// res1 = 5; res2 = 2
In a JavaScript string literal, \n is an escape sequence for one LF character, so it occupies one string index.
API — leftStopAtRawNbsp() and rightStopAtRawNbsp()
These functions behave like left() and right(), except they also stop at a raw non-breaking space (U+00A0). The base functions skip U+00A0 as whitespace.
Use these helpers when U+00A0 is a meaningful boundary rather than ignorable whitespace.
import { right, rightStopAtRawNbsp } from "string-left-right";
// "a", a space, two raw non-breaking spaces, a space, then "b"
const str = "a \u00A0\u00A0 b";
console.log(right(str, 0));
// => 5
console.log(rightStopAtRawNbsp(str, 0));
// => 2
API — version
You can import version:
API — types
Opts configures case sensitivity for sequence matching. Case-insensitive comparison uses JavaScript’s toLowerCase() and can expand a code unit, as with İ:
| Key | Type | Default | Description |
|---|---|---|---|
iType: booleanDefault: false | |||
i | boolean | false | Match characters case-insensitively |
import { leftSeq } from "string-left-right";
console.log(leftSeq("abCDefghijk", 5, "c", "d", "e"));
// => null
console.log(leftSeq("abCDefghijk", 5, { i: true }, "c", "d", "e"));
// => { gaps: [], leftmostChar: 2, rightmostChar: 4 }
import type { Opts } from "string-left-right";
Opts, ChompOpts, and SeqOutput are named TypeScript exports. ChompOpts describes the supported whitespace modes, and SeqOutput describes sequence indexes and gaps.
More complex lookups
For substring matching with configurable whitespace or character trimming, use string-match-left-right.