Skip to content
Merged
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [2.1.0] - 2025-08-26

### Added

- `ltrim`, `ltrim` and `ltrim` string type utility functions
Comment thread
neoscie marked this conversation as resolved.
Outdated

## [2.0.0] - 2025-07-29

### Added
Expand Down
62 changes: 61 additions & 1 deletion src/lib/string.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isNullOrEmpty, isNullOrWhitespace, capitalize, uncapitalize, truncate } from "./string";
import { isNullOrEmpty, isNullOrWhitespace, capitalize, uncapitalize, truncate, ltrim, rtrim, trim } from "./string";

describe("string tests", () => {
test.each([
Expand Down Expand Up @@ -120,4 +120,64 @@ describe("string tests", () => {
])("truncate without suffix parameter", (value, maxLength, expected) => {
expect(truncate(value, maxLength)).toBe(expected);
});

test.each([
[null as unknown as string, " ", null],
[undefined as unknown as string, " ", undefined],
[" hello world", " ", "hello world"],
[" hello world", " ", "hello world"],
[" hello world", " ", "hello world"],
])("left trim", (haystack, needle, expected) => {
expect(ltrim(haystack, needle)).toBe(expected);
});

test.each([
[null as unknown as string, " ", null],
[undefined as unknown as string, " ", undefined],
[" hello world", "", " hello world"],
[" hello world", "", " hello world"],
[" hello world", "", " hello world"],
])("left trim without needle", (haystack, needle, expected) => {
expect(ltrim(haystack, needle)).toBe(expected);
});

test.each([
[null as unknown as string, " ", null],
[undefined as unknown as string, " ", undefined],
["hello world ", " ", "hello world"],
["hello world ", " ", "hello world"],
["hello world ", " ", "hello world"],
])("right trim", (haystack, needle, expected) => {
expect(rtrim(haystack, needle)).toBe(expected);
});

test.each([
[null as unknown as string, " ", null],
[undefined as unknown as string, " ", undefined],
["hello world ", "", "hello world "],
["hello world ", "", "hello world "],
["hello world ", "", "hello world "],
])("right trim without needle", (haystack, needle, expected) => {
expect(rtrim(haystack, needle)).toBe(expected);
});

test.each([
[null as unknown as string, " ", null],
[undefined as unknown as string, " ", undefined],
[" hello world ", " ", "hello world"],
[" hello world ", " ", "hello world"],
[" hello world ", " ", "hello world"],
])("trim", (haystack, needle, expected) => {
expect(trim(haystack, needle)).toBe(expected);
});

test.each([
[null as unknown as string, " ", null],
[undefined as unknown as string, " ", undefined],
[" hello world ", "", " hello world "],
[" hello world ", "", " hello world "],
[" hello world ", "", " hello world "],
])("trim without needle", (haystack, needle, expected) => {
expect(trim(haystack, needle)).toBe(expected);
});
});
68 changes: 68 additions & 0 deletions src/lib/string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,71 @@ export function truncate(value: string | undefined, maxLength: number, suffix =

return `${value.slice(0, maxLength)}${suffix}`;
}

/**
* Removes all occurrences of needle from the start of haystack
* @param haystack string to trim
* @param needle the thing to trim
* @returns the string trimmed from the left side
*/
export function ltrim(haystack: string, needle: string): string {
Comment thread
manni497 marked this conversation as resolved.
Outdated
if (!haystack || !needle) return haystack;

const needleLength = needle.length;
Comment thread
manni497 marked this conversation as resolved.
Outdated
if (needleLength === 0 || haystack.length === 0) {
return haystack;
}

let offset = 0;

while (haystack.indexOf(needle, offset) === offset) {
offset = offset + needleLength;
}
return haystack.slice(offset);
}

/**
* Removes all occurrences of needle from the end of haystack
* @param haystack string to trim
* @param needle the thing to trim
* @returns the string trimmed from the right side
*/
export function rtrim(haystack: string, needle: string): string {
Comment thread
manni497 marked this conversation as resolved.
Outdated
if (!haystack || !needle) {
return haystack;
}

const needleLength = needle.length,
Comment thread
manni497 marked this conversation as resolved.
Outdated
haystackLen = haystack.length;

if (needleLength === 0 || haystackLen === 0) {
return haystack;
}

let offset = haystackLen,
idx = -1;

while (true) {
idx = haystack.lastIndexOf(needle, offset - 1);
if (idx === -1 || idx + needleLength !== offset) {
break;
}
if (idx === 0) {
return "";
}
offset = idx;
}

return haystack.slice(0, offset);
}

/**
* Removes all occurrences of needle from the start and the end of haystack
* @param haystack string to trim
* @param needle the thing to trim
* @returns the string trimmed from the right and left side
*/
export function trim(haystack: string, needle: string): string {
const trimmed = ltrim(haystack, needle);
return rtrim(trimmed, needle);
}
Loading