Skip to Content
  • Website
Codsen
  • Home
  • Open Source
  • Articles
  • About

prevOpen Source→string-left-rightnext

string-left-right6.2.4

Looks up the first non-whitespace character to the left/right of a given index

Downloads per monthChangelogMIT Licenselibera manifesto
  • the top
  • Installation
  • Quick Take
  • Examples
  • PURPOSE
  • INDEX BOUNDA…
  • API — LEFT() AND RIGHT()
  • API — LEFTSEQ() AND…
  • API — CHOMPLEFT()…
  • API — CHOMPLEFT()…
  • API — LEFTSTOPATNE…
  • API — LEFTSTOPATRA…
  • API — VERSION
  • API — TYPES
  • MORE COMPLEX…
  • Changelog

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.

DirectionIndexes examinedInclude the string endpoint
Left
LeftLower than idxPass str.length to include the final code unit
Right
RightHigher than idxPass -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.

ArgumentTypeRequiredDescription
str
Type: string
strstringYesInput string
idx
Type: number
idxnumberYesExclusive boundary from which matching begins
opts
Type: Opts
optsOptsNoOptions object; when present, it precedes value
value
Type: string
valuestringYesFirst one-code-unit match pattern
...values
Type: string[]
...valuesstring[]NoAdditional 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:

SuffixMeaning
None
NoneMatch 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:

ModeBehavior
0
0Consume adjacent whitespace while preserving one whitespace code unit when possible; do not cross CR or LF
1
1Preserve all adjacent whitespace
2
2Consume adjacent whitespace up to, but not including, CR or LF
3
3Consume 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+00A0opens in a new tab). 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 İ:

KeyTypeDefaultDescription
i
Type: boolean
Default: false
ibooleanfalseMatch 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.

Permalink to changelogChangelog

Open Changelog
↑ back to top
prev next

Copyright

All rights reserved © Roy Revelt 2026
All our open source packages are under MIT licenceopens in a new tab

Activities

🐛 See a bug? Raise an issueopens in a new tab
💘 Check out the Indiewebopens in a new tab and Libera manifestoopens in a new tab