Gathering detailed insights and metrics for @visulima/string
Gathering detailed insights and metrics for @visulima/string
Gathering detailed insights and metrics for @visulima/string
Gathering detailed insights and metrics for @visulima/string
Visulima is the next-gen JavaScript framework for JAMStack blogs, sites & apps.
npm install @visulima/string
Typescript
Module System
Min. Node Version
Node Version
NPM Version
@visulima/cerebro@1.1.46
Updated on Jun 04, 2025
@visulima/boxen@2.0.2
Updated on Jun 04, 2025
@visulima/pail@2.1.25
Updated on Jun 04, 2025
@visulima/api-platform@3.0.44
Updated on Jun 04, 2025
@visulima/jsdoc-open-api@2.0.81
Updated on Jun 04, 2025
@visulima/find-cache-dir@1.0.31
Updated on Jun 04, 2025
TypeScript (93.51%)
JavaScript (5.17%)
MDX (1.02%)
Handlebars (0.14%)
Shell (0.08%)
CSS (0.08%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
16 Stars
2,703 Commits
3 Forks
2 Watchers
17 Branches
2 Contributors
Updated on Jun 10, 2025
Latest Version
1.5.1
Package Id
@visulima/string@1.5.1
Unpacked Size
966.35 kB
Size
256.71 kB
File Count
209
NPM Version
10.9.2
Node Version
18.20.8
Published on
Jun 04, 2025
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
No dependencies detected.
A robust string manipulation library providing utilities for common string operations with support for multiple languages.
Daniel Bannert's open source work is supported by the community on GitHub Sponsors
camelCase
: Convert to camelCase stylePascalCase
: Convert to PascalCase stylesnake_case
: Convert to snake_case stylekebab-case
: Convert to kebab-case styleCONSTANT_CASE
: Convert to CONSTANT_CASE styledot.case
: Convert to dot.case stylepath/case
: Convert to path/case styleSentence case
: Convert to Sentence case styleTitle Case
: Convert to Title Case with smart minor word handlingoutdent
: Remove leading indentation while preserving relative indentationgetStringWidth
: Calculate visual width of strings with Unicode supportgetStringTruncatedWidth
: Width calculation with smart truncation1npm install @visulima/string
1yarn add @visulima/string
1pnpm add @visulima/string
The outdent
function removes leading indentation from multi-line strings while preserving the relative indentation structure.
1import { outdent } from '@visulima/string'; 2 3// Basic usage with template literals 4const text = outdent` 5 This text will have its indentation removed 6 while preserving relative indentation. 7 This line will still be indented relative to the others. 8`; 9 10// Output: 11// This text will have its indentation removed 12// while preserving relative indentation. 13// This line will still be indented relative to the others. 14 15// With string input 16const result = outdent.string(' 17 Hello 18 World 19'); 20// Output: "Hello\nWorld" 21 22// With custom options 23const customOutdent = outdent({ 24 trimLeadingNewline: false, // Keep the leading newline 25 trimTrailingNewline: false, // Keep the trailing newline 26 newline: '\r\n', // Normalize all newlines to CRLF 27 cache: true // Enable caching (default) 28}); 29 30// Using with interpolation 31const name = 'World'; 32const greeting = outdent` 33 Hello ${name}! 34 Welcome to outdent. 35`;
The outdent
function supports caching to improve performance when the same template is used multiple times:
1// Default behavior - caching enabled 2const dedent = outdent(); 3 4// Disable caching if memory usage is a concern 5const noCacheDedent = outdent({ cache: false }); 6 7// Use a custom cache store (advanced usage) 8const customCache = new WeakMap(); 9const customCacheDedent = outdent({ cacheStore: customCache });
The wordWrap
function provides flexible text wrapping with support for ANSI color codes and Unicode.
1import { wordWrap, WrapMode } from "@visulima/string"; 2 3// Basic usage with default options (80 character width, preserve words) 4const wrapped = wordWrap("This is a long text that will be wrapped to fit within the specified width limit."); 5 6// With custom width (40 characters) 7const narrowWrapped = wordWrap("This text will be wrapped to fit within a 40-character width.", { width: 40 }); 8 9// Different wrapping modes 10const preserveWords = wordWrap("Long words will stay intact but may exceed the width limit.", { 11 width: 20, 12 wrapMode: WrapMode.PRESERVE_WORDS, // Default - keeps words intact 13}); 14 15const breakAtCharacters = wordWrap("Words will be broken at character boundaries to fit width.", { 16 width: 20, 17 wrapMode: WrapMode.BREAK_AT_CHARACTERS, // Breaks words to fit width exactly 18}); 19 20const strictWidth = wordWrap("Text will be broken exactly at the width limit.", { 21 width: 20, 22 wrapMode: WrapMode.STRICT_WIDTH, // Forces strict adherence to width 23}); 24 25// Handling ANSI color codes 26const coloredText = "\u001B[31mThis red text\u001B[0m will be wrapped while preserving the color codes."; 27const wrappedColored = wordWrap(coloredText, { width: 20 }); 28// Color codes are preserved across line breaks 29 30// Additional options 31const customWrapped = wordWrap("Text with\u200Bzero-width characters and\u200Btrailing spaces.", { 32 width: 30, 33 trim: false, // Don't trim whitespace from lines (default: true) 34 removeZeroWidthCharacters: false, // Don't remove zero-width characters (default: true) 35});
The replaceString
function provides advanced string replacement capabilities, allowing multiple search/replace operations (using strings or RegExps) while respecting specified index ranges that should be ignored. It also handles match precedence correctly (earlier start index wins, then longer match wins if starts are identical) and ensures only the highest-priority, non-overlapping, non-ignored match is applied in any given segment.
Note: Use this function when you need fine-grained control over multiple replacements, especially when needing to ignore specific index ranges or handle complex overlapping matches with defined precedence rules. For simple, non-overlapping replacements without ignore ranges, native
String.prototype.replaceAll
might be sufficient.
1import replaceString from "@visulima/string/replace-string"; // Adjust import path if needed 2import type { IntervalArray, OptionReplaceArray } from "@visulima/string"; 3 4// Basic Usage 5const source1 = "Replace AB and CD"; 6const searches1: OptionReplaceArray = [ 7 ["AB", "ab"], 8 ["CD", "cd"], 9]; 10const result1 = replaceString(source1, searches1, []); 11// result1: "Replace ab and cd" 12 13// With Ignore Ranges 14const source2 = "Replace AB and ignore CD and replace XY"; 15const searches2: OptionReplaceArray = [ 16 ["AB", "ab"], 17 ["CD", "cd"], // This should be ignored by the range 18 ["XY", "xy"], 19]; 20// Ignore indices 19-20 (targets "re" in "ignore") 21const ignoreRanges2: IntervalArray = [[19, 20]]; 22const result2 = replaceString(source2, searches2, ignoreRanges2); 23// result2: "Replace ab and ignore cd and replace xy" 24// Note: "CD" is replaced because it doesn't overlap the ignore range [19, 20]. 25 26// With Overlapping Matches (Longer match takes precedence) 27const source3 = "abcde"; 28const searches3: OptionReplaceArray = [ 29 ["abc", "123"], // Lower precedence 30 ["abcde", "54321"], // Higher precedence (longer) 31]; 32const result3 = replaceString(source3, searches3, []); 33// result3: "54321" 34 35// With Overlapping Matches (Earlier start index takes precedence) 36const source4 = "ababab"; 37const searches4: OptionReplaceArray = [ 38 ["aba", "X"], // Starts at 0 39 ["bab", "Y"], // Starts at 1 40]; 41const result4 = replaceString(source4, searches4, []); 42// result4: "Xbab" (Applies "X" at 0, which covers indices 0-2. Skips "Y" at 1. Appends rest.) 43 44// With Zero-Length Matches (e.g., inserting before each char) 45const source5 = "abc"; 46const searches5: OptionReplaceArray = [[/(?=.)/g, "^"]]; // Lookahead for position before char 47const result5 = replaceString(source5, searches5, []); 48// result5: "^a^b^c" 49 50// Zero-Length Match at End 51const source6 = "abc"; 52const searches6: OptionReplaceArray = [[/$/g, "$"]]; // Matches end of string 53const result6 = replaceString(source6, searches6, []); 54// result6: "abc$" 55 56// Using $& and $n replacements 57const source7 = "Firstname Lastname"; 58const searches7: OptionReplaceArray = [[/(\w+)\s+(\w+)/, "$2, $1 ($& - Group 1: $1)"]]; 59const result7 = replaceString(source7, searches7, []); 60// result7: "Lastname, Firstname (Firstname Lastname - Group 1: Firstname)"
The splitByCase
function is a powerful utility that splits strings based on various patterns:
1import { splitByCase } from "@visulima/string"; 2 3// Basic Case Transitions 4splitByCase("camelCase"); // ['camel', 'Case'] 5splitByCase("PascalCase"); // ['Pascal', 'Case'] 6splitByCase("snake_case"); // ['snake', 'case'] 7splitByCase("kebab-case"); // ['kebab', 'case'] 8 9// Numbers and Acronyms 10splitByCase("XMLHttpRequest"); // ['XML', 'Http', 'Request'] 11splitByCase("iOS8"); // ['i', 'OS', '8'] 12splitByCase("IPv6Address"); // ['IP', 'v6', 'Address'] 13 14// Multi-Script Support 15 16// Japanese 17splitByCase("ひらがなカタカナABC", { locale: "ja" }); 18// ['ひらがな', 'カタカナ', 'ABC'] 19 20// Korean 21splitByCase("한글Text", { locale: "ko" }); 22// ['한글', 'Text'] 23 24// Chinese 25splitByCase("中文Text", { locale: "zh" }); 26// ['中文', 'Text'] 27 28// Cyrillic 29splitByCase("русскийText", { locale: "ru" }); 30// ['русский', 'Text'] 31 32// Greek 33splitByCase("ελληνικάText", { locale: "el" }); 34// ['ελληνικά', 'Text'] 35 36// Advanced Options 37splitByCase("MyXMLParser", { 38 knownAcronyms: ["XML"], // Preserve known acronyms 39 normalize: true, // Normalize case 40 locale: "en", // Specify locale 41}); 42// ['My', 'XML', 'Parser'] 43 44// ANSI and Emoji Handling 45splitByCase("🎉HappyBirthday🎂", { 46 handleEmoji: true, // Handle emoji boundaries 47}); 48// ['🎉', 'Happy', 'Birthday', '🎂']
Converts a string to camelCase.
1camelCase("foo bar"); // 'fooBar' 2camelCase("foo-bar"); // 'fooBar' 3camelCase("foo_bar"); // 'fooBar' 4camelCase("XMLHttpRequest"); // 'xmlHttpRequest' 5camelCase("AJAXRequest"); // 'ajaxRequest' 6camelCase("QueryXML123String"); // 'queryXml123String'
Converts a string to PascalCase.
1pascalCase("foo bar"); // 'FooBar' 2pascalCase("foo-bar"); // 'FooBar' 3pascalCase("foo_bar"); // 'FooBar' 4pascalCase("XMLHttpRequest"); // 'XmlHttpRequest' 5pascalCase("AJAXRequest"); // 'AjaxRequest' 6pascalCase("QueryXML123String"); // 'QueryXml123String'
Converts a string to snake_case.
1snakeCase("fooBar"); // 'foo_bar' 2snakeCase("foo bar"); // 'foo_bar' 3snakeCase("foo-bar"); // 'foo_bar' 4snakeCase("XMLHttpRequest"); // 'xml_http_request' 5snakeCase("AJAXRequest"); // 'ajax_request' 6snakeCase("QueryXML123String"); // 'query_xml_123_string'
Converts a string to kebab-case.
1kebabCase("fooBar"); // 'foo-bar' 2kebabCase("foo bar"); // 'foo-bar' 3kebabCase("foo_bar"); // 'foo-bar' 4kebabCase("XMLHttpRequest"); // 'xml-http-request' 5kebabCase("AJAXRequest"); // 'ajax-request' 6kebabCase("QueryXML123String"); // 'query-xml-123-string'
Converts a string to Title Case, with smart handling of minor words.
1titleCase("this-IS-aTitle"); // 'This is a Title' 2titleCase("XMLHttpRequest"); // 'XML Http Request' 3titleCase("AJAXRequest"); // 'AJAX Request' 4titleCase("QueryXML123String"); // 'Query XML 123 String'
Converts a string to path/case.
1pathCase("foo bar"); // 'foo/bar' 2pathCase("foo-bar"); // 'foo/bar' 3pathCase("foo_bar"); // 'foo/bar' 4pathCase("XMLHttpRequest"); // 'xml/http/request' 5pathCase("AJAXRequest"); // 'ajax/request' 6pathCase("QueryXML123String"); // 'query/xml/123/string'
Converts a string to dot.case.
1dotCase("foo bar"); // 'foo.bar' 2dotCase("foo-bar"); // 'foo.bar' 3dotCase("foo_bar"); // 'foo.bar' 4dotCase("XMLHttpRequest"); // 'xml.http.request' 5dotCase("AJAXRequest"); // 'ajax.request' 6dotCase("QueryXML123String"); // 'query.xml.123.string'
Converts a string to CONSTANT_CASE.
1constantCase("foo bar"); // 'FOO_BAR' 2constantCase("foo-bar"); // 'FOO_BAR' 3constantCase("foo_bar"); // 'FOO_BAR' 4constantCase("XMLHttpRequest"); // 'XML_HTTP_REQUEST' 5constantCase("AJAXRequest"); // 'AJAX_REQUEST' 6constantCase("QueryXML123String"); // 'QUERY_XML_123_STRING'
Converts a string to Sentence case.
1sentenceCase("foo bar"); // 'Foo bar' 2sentenceCase("foo-bar"); // 'Foo bar' 3sentenceCase("foo_bar"); // 'Foo bar' 4sentenceCase("XMLHttpRequest"); // 'Xml http request' 5sentenceCase("AJAXRequest"); // 'Ajax request' 6sentenceCase("QueryXML123String"); // 'Query xml 123 string'
The package provides two functions for calculating string widths: getStringWidth
for basic width calculation and getStringTruncatedWidth
for width calculation with truncation support.
The getStringWidth
function calculates the visual width of strings, taking into account various Unicode characters, emojis, ANSI escape codes, and more:
1import { getStringWidth } from "@visulima/string"; 2 3// Basic usage 4getStringWidth("hello"); // => 5 5getStringWidth("👋 hello"); // => 7 (emoji is width 2) 6getStringWidth("あいう"); // => 6 (each character is width 2) 7 8// With custom options 9getStringWidth("hello", { regularWidth: 2 }); // => 10 10getStringWidth("あいう", { ambiguousIsNarrow: true }); // => 3 11 12// ANSI escape codes 13getStringWidth("\u001B[31mRed\u001B[39m"); // => 3 14getStringWidth("\u001B[31mRed\u001B[39m", { countAnsiEscapeCodes: true }); // => 11 15 16// Advanced Unicode support 17getStringWidth("한글"); // => 4 (Korean characters) 18getStringWidth("你好"); // => 4 (Chinese characters) 19getStringWidth("👨👩👧👦"); // => 2 (family emoji with ZWJ sequences)
1interface StringWidthOptions { 2 ambiguousIsNarrow?: boolean; // Treat ambiguous-width characters as narrow 3 ansiWidth?: number; // Width of ANSI escape sequences (default: 0) 4 controlWidth?: number; // Width of control characters (default: 0) 5 countAnsiEscapeCodes?: boolean; // Include ANSI escape codes in width (default: false) 6 emojiWidth?: number; // Width of emoji characters (default: 2) 7 fullWidth?: number; // Width of full-width characters (default: 2) 8 regularWidth?: number; // Width of regular characters (default: 1) 9 tabWidth?: number; // Width of tab characters (default: 8) 10 wideWidth?: number; // Width of wide characters (default: 2) 11}
The getStringTruncatedWidth
function extends the basic width calculation with truncation support:
1import { getStringTruncatedWidth } from "@visulima/string"; 2 3// Basic truncation 4getStringTruncatedWidth("hello world", { 5 limit: 8, 6 ellipsis: "...", 7}); // => { width: 8, truncated: true, ellipsed: true, index: 5 } 8 9// Custom character widths with truncation 10getStringTruncatedWidth("あいうえお", { 11 limit: 6, 12 ellipsis: "...", 13 fullWidth: 2, 14}); // => { width: 6, truncated: true, ellipsed: true, index: 2 } 15 16// ANSI codes with truncation 17getStringTruncatedWidth("\u001B[31mRed Text\u001B[39m", { 18 limit: 5, 19 ellipsis: "...", 20 countAnsiEscapeCodes: true, 21}); // => { width: 5, truncated: true, ellipsed: true, index: 4 } 22 23// Complex Unicode with truncation 24getStringTruncatedWidth("👨👩👧👦 Family", { 25 limit: 7, 26 ellipsis: "...", 27}); // => { width: 7, truncated: true, ellipsed: true, index: 11 }
The truncate
function provides a convenient way to truncate strings with support for different positions, Unicode characters, ANSI escape codes, and more.
1import { truncate } from "@visulima/string"; 2 3// Basic truncation (end position) 4truncate("unicorn", 4); // => 'un…' 5truncate("unicorn", 4, { position: "end" }); // => 'un…' 6 7// Different positions 8truncate("unicorn", 5, { position: "start" }); // => '…orn' 9truncate("unicorn", 5, { position: "middle" }); // => 'un…n' 10 11// With custom ellipsis 12truncate("unicorns", 5, { ellipsis: "." }); // => 'unic.' 13truncate("unicorns", 5, { ellipsis: " ." }); // => 'uni .' 14 15// Smart truncation on spaces 16truncate("dragons are awesome", 15, { position: "end", preferTruncationOnSpace: true }); // => 'dragons are…' 17truncate("unicorns rainbow dragons", 20, { position: "middle", preferTruncationOnSpace: true }); // => 'unicorns…dragons' 18 19// With ANSI escape codes 20truncate("\u001B[31municorn\u001B[39m", 4); // => '\u001B[31mun\u001B[39m…' 21 22// With Unicode characters 23truncate("안녕하세요", 3, { width: { fullWidth: 2 } }); // => '안…'
1interface TruncateOptions { 2 // String to append when truncation occurs 3 ellipsis?: string; // default: '' 4 5 // Width of the ellipsis string 6 // If not provided, it will be calculated using getStringTruncatedWidth 7 ellipsisWidth?: number; 8 9 // The position to truncate the string 10 position?: "end" | "middle" | "start"; // default: 'end' 11 12 // Truncate the string from a whitespace if it is within 3 characters 13 // from the actual breaking point 14 preferTruncationOnSpace?: boolean; // default: false 15 16 // Width calculation options 17 width?: Omit<StringTruncatedWidthOptions, "ellipsis" | "ellipsisWidth" | "limit">; 18} 19 20interface StringTruncatedWidthOptions extends StringWidthOptions { 21 // Truncation-specific options 22 ellipsis?: string; // String to append when truncation occurs (default: '') 23 ellipsisWidth?: number; // Width of ellipsis, auto-calculated if not provided 24 limit?: number; // Maximum width limit for the string (default: Infinity) 25} 26 27// Return value structure 28interface StringTruncatedWidthResult { 29 width: number; // The calculated visual width of the string 30 truncated: boolean; // Whether the string was truncated 31 ellipsed: boolean; // Whether an ellipsis was added 32 index: number; // The index at which truncation occurred (if any) 33}
All case conversion functions accept these common options:
1interface CaseOptions { 2 // Enable caching for better performance 3 cache?: boolean; 4 5 // Maximum size of the cache (default: 1000) 6 cacheMaxSize?: number; 7 8 // Custom cache store 9 cacheStore?: Map<string, string>; 10 11 // Known acronyms to preserve 12 knownAcronyms?: ReadonlyArray<string>; 13 14 // Locale for script-aware case conversion 15 locale?: string; 16}
The splitByCase
function accepts these configuration options:
1interface SplitOptions { 2 // Locale for script-aware splitting (e.g., 'ja', 'ko', 'zh') 3 locale?: string; 4 5 // Known acronyms to preserve (e.g., ['XML', 'HTTP']) 6 knownAcronyms?: ReadonlyArray<string>; 7 8 // Handle ANSI escape sequences 9 handleAnsi?: boolean; 10 11 // Handle emoji sequences 12 handleEmoji?: boolean; 13 14 // Normalize case (convert all-upper tokens to title case) 15 normalize?: boolean; 16 17 // Custom separators (string[] or RegExp) 18 separators?: ReadonlyArray<string> | RegExp; 19 20 // Strip ANSI sequences 21 stripAnsi?: boolean; 22 23 // Strip emoji sequences 24 stripEmoji?: boolean; 25}
The library provides comprehensive support for various scripts and writing systems:
The library includes several optimizations:
The library handles various edge cases gracefully:
1// Empty strings 2splitByCase(""); // [] 3 4// Invalid input 5splitByCase(null); // [] 6splitByCase(undefined); // [] 7 8// Single characters 9splitByCase("A"); // ['A'] 10 11// All uppercase 12splitByCase("URL", { knownAcronyms: ["URL"] }); // ['URL']
The library provides enhanced TypeScript type definitions for native string methods. These types provide better type inference and compile-time checks.
Configure your tsconfig.json
file to include the types:
1{ 2 "compilerOptions": { 3 "types": ["@visulima/string/native-string-types"] 4 } 5}
Alternatively, you can add a triple-slash reference in your TypeScript files:
1/// <reference types="@visulima/string/native-string-types" />
1// Type-safe string operations 2const str = "Hello, World!"; 3 4// charAt with type inference 5str.charAt<typeof str, 0>(); // type: 'H' 6str.charAt<typeof str, 1>(); // type: 'e' 7 8// concat with type inference 9str.concat<typeof str, "Hi">(); // type: 'Hello, World!Hi' 10 11// endsWith with literal type checking 12str.endsWith<typeof str, "World!">(); // type: true 13str.endsWith<typeof str, "Hello">(); // type: false 14 15// includes with position 16str.includes<typeof str, "World", 0>(); // type: true 17str.includes<typeof str, "World", 7>(); // type: false 18 19// length with type inference 20type Length = (typeof str)["length"]; // type: 13 21 22// padStart/padEnd with type inference 23str.padStart<typeof str, 15, "_">(); // type: '_Hello, World!' 24str.padEnd<typeof str, 15, "_">(); // type: 'Hello, World!__' 25 26// replace with type inference 27str.replace<typeof str, "World", "TypeScript">(); // type: 'Hello, TypeScript!' 28 29// replaceAll with type inference 30str.replaceAll<typeof str, "l", "L">(); // type: 'HeLLo, WorLd!' 31 32// slice with type inference 33str.slice<typeof str, 0, 5>(); // type: 'Hello' 34 35// split with type inference 36str.split<typeof str, ", ">(); // type: ['Hello', 'World!'] 37 38// startsWith with type inference 39str.startsWith<typeof str, "Hello">(); // type: true 40 41// toLowerCase/toUpperCase with type inference 42str.toLowerCase<typeof str>(); // type: 'hello, world!' 43str.toUpperCase<typeof str>(); // type: 'HELLO, WORLD!' 44 45// trim/trimStart/trimEnd with type inference 46const paddedStr = " hello "; 47paddedStr.trim<typeof paddedStr>(); // type: 'hello' 48paddedStr.trimStart<typeof paddedStr>(); // type: 'hello ' 49paddedStr.trimEnd<typeof paddedStr>(); // type: ' hello'
These enhanced types provide several benefits:
Compile-Time Type Safety:
Better IDE Support:
Type-Level String Manipulation:
Advanced Type Features:
1// Example of chaining operations with type safety 2const result = "Hello, World!".toLowerCase<string>().replace<string, "hello", "hi">().split<string, " ">().join("-"); 3 4// TypeScript knows the exact type at each step
The package includes specialized utilities for testing ANSI colored strings, making it easier to write tests for terminal output and colored text.
The formatAnsiString
function helps format ANSI strings for test output, providing multiple representations:
1import { formatAnsiString } from "@visulima/string/test/utils"; 2import { red } from "@visulima/colorize"; 3 4const coloredText = red("Error message"); 5const formatted = formatAnsiString(coloredText); 6 7// Returns an object with: 8// - ansi: Original string with ANSI codes 9// - stripped: String with ANSI codes removed 10// - visible: String with escape codes shown as visible characters 11// - json: JSON stringified version 12// - lengthDifference: Difference between ANSI and stripped length
The compareAnsiStrings
function provides detailed comparison between two ANSI strings:
1import { compareAnsiStrings } from "@visulima/string/test/utils"; 2import { red, blue } from "@visulima/colorize"; 3 4const string1 = red("Error"); 5const string2 = blue("Error"); 6 7const result = compareAnsiStrings(string1, string2); 8// Returns comparison details including: 9// - ansiEqual: Whether the strings are identical including ANSI codes 10// - strippedEqual: Whether the visible content is the same 11// - summary: Length information and comparison results 12// - actual/expected: Formatted representations of both strings
The package includes a custom matcher for Vitest that makes testing ANSI strings straightforward:
1import { expect, describe, it } from "vitest"; 2import { toEqualAnsi } from "@visulima/string/test/vitest"; 3import { red, green } from "@visulima/colorize"; 4 5// Extend Vitest with the custom matcher 6expect.extend({ toEqualAnsi }); 7 8describe("colored output tests", () => { 9 it("should display the correct error message", () => { 10 const actual = getErrorMessage(); // Returns colored string 11 const expected = red("Error: ") + green("File not found"); 12 13 // Compare ANSI strings with detailed error messages on failure 14 expect(actual).toEqualAnsi(expected); 15 }); 16});
The custom matcher provides detailed error messages when tests fail, showing:
replaceString(source, searches, ignoreRanges?)
Replaces occurrences of search patterns within a string, respecting ignored ranges. This function is designed to handle overlapping matches and ignore ranges correctly. It prioritizes matches that start earlier and, for matches starting at the same position, prefers longer matches. Replacements within ignored ranges are skipped.
Parameters:
source
: The input string.searches
: An array of search pairs. Each pair can be:
[string | RegExp, string]
: A literal string or regex to search for, and its replacement string.
Regex flags like g
(global) are respected.ignoreRanges?
: Optional. An array of [start, end]
index pairs (inclusive) specifying ranges within the
source
string that should be ignored during replacement.Returns:
string
: The string with replacements applied, respecting ignore ranges.Usage:
1import { replaceString } from "@visulima/string"; 2 3const text = "abc abc abc"; 4const searches = [ 5 [/a/g, "X"], 6 ["abc", "YYY"], 7]; 8const ignoreRanges = [[4, 6]]; // Ignore the second "abc" 9 10const result = replaceString(text, searches, ignoreRanges); 11// result will be: "YYY abc YYY" 12// First 'abc' is replaced by 'YYY' (longer match takes precedence over 'X'). 13// Second 'abc' is ignored. 14// Third 'abc' is replaced by 'YYY'.
transliterate(source, options?)
Performs transliteration of characters in a string based on an extensive character map and provided options. This function is useful for converting characters from one script to another (e.g., Latin with diacritics to basic Latin, Cyrillic to Latin) or for custom character replacements.
Parameters:
source: string
: The input string to transliterate.options?
: Optional OptionsTransliterate
object:
fixChineseSpacing?: boolean
: If true
, adds a space between transliterated Chinese Pinyin syllables. (Default: true
).ignore?: string[]
: An array of strings or characters to ignore during transliteration. These segments will be preserved in their original form. (Default: []
).replaceBefore?: Array<[string | RegExp, string]> | Record<string, string>
: Custom replacement rules to apply before the main character map transliteration. (Default: []
).replaceAfter?: Array<[string | RegExp, string]> | Record<string, string>
: Custom replacement rules to apply after the main character map transliteration. (Default: []
).trim?: boolean
: If true
, trims whitespace from the beginning and end of the result. (Default: false
).unknown?: string
: The character or string to use for characters that are not found in the character map and are not covered by other rules. (Default: ""
- removes unknown characters).Returns:
string
: The transliterated string.Usage:
1import { transliterate } from "@visulima/string"; // Assuming named export from package root 2 3// Basic transliteration 4transliterate("Crème brûlée"); // Expected: 'Creme brulee' 5transliterate("你好世界"); // Expected: 'Ni Hao Shi Jie' (due to fixChineseSpacing: true) 6transliterate("你好世界", { fixChineseSpacing: false }); // Expected: 'NiHaoShiJie' 7 8// Using ignore 9transliterate("Don't change THIS, but change that.", { ignore: ["THIS"] }); 10// Expected: 'Dont change THIS, but change that.' 11 12// Using replaceBefore 13transliterate("Replace C++ before map.", { replaceBefore: { "C++": "cpp" } }); 14// Expected: 'Replace cpp before map.' 15 16// Using replaceAfter 17// Example: charmap turns é -> e, then replaceAfter turns e -> E 18transliterate("café", { replaceAfter: { e: "E" } }); 19// Expected: 'cafE' 20 21// Handling unknown characters 22transliterate("a🚀b", { unknown: "[?]" }); // Expected: 'a[?]b'
slugify(input, options?)
Converts a string into a URL-friendly slug.
It transliterates non-ASCII characters using the transliterate
function (if enabled), optionally converts case, removes disallowed characters (replacing with separator), and collapses separators.
Parameters:
input
: The string to convert.options?
: Optional SlugifyOptions
object:
allowedChars?: string
: Characters allowed in the slug. Others are replaced by separator
. (Default: "a-zA-Z0-9-_.~"
)fixChineseSpacing?: boolean
: Passed to transliterate
. Determines if a space is added between transliterated Chinese characters (default: true
).ignore?: string[]
: Passed to transliterate
. Characters/strings to ignore during the initial transliteration phase (default: []
).lowercase?: boolean
: Convert to lowercase. (Default: true
). Cannot be true if uppercase
is true.replaceAfter?: OptionReplaceCombined
: Passed to transliterate
. Search/replace pairs to apply after the character map transliteration but before slugification logic (default: []
).replaceBefore?: OptionReplaceCombined
: Passed to transliterate
. Search/replace pairs to apply before any transliteration (default: []
).separator?: string
: Custom separator. (Default: "-"
).transliterate?: boolean
: Whether to perform the initial transliteration of non-ASCII characters. If false
, only case conversion and character filtering/replacement are performed on the input string. (Default: true
).unknown?: string
: Passed to transliterate
. Character to use for unknown characters during transliteration (default: ""
).uppercase?: boolean
: Convert to uppercase. (Default: false
). Cannot be true if lowercase
is true.Returns:
string
: The generated slug.Usage:
1import { slugify } from "@visulima/string"; 2 3slugify("你好 World!"); // 'ni-hao-world' (fixChineseSpacing=true by default) 4slugify("你好World!", { fixChineseSpacing: false }); // 'nihaoworld' 5slugify("Crème Brûlée"); // 'creme-brulee' 6slugify("foo & bar * baz"); // 'foo-bar-baz' (&, *, space are disallowed) 7slugify("FOO BAR", { lowercase: false, uppercase: true }); // 'FOO-BAR' 8slugify("foo bar baz", { separator: "_", allowedChars: "a-z_" }); // 'foo_bar_baz' 9slugify("Keep C++", { replaceBefore: { "C++": "cpp" } }); // 'keep-cpp' 10slugify("Keep !@#$", { allowedChars: "a-z!@$" }); // 'keep!@$'
The alignText
function aligns text (including multi-line strings and strings with ANSI escape codes) to the left, center, or right. It can handle both single strings (which can be split into lines based on the split
option) and arrays of strings.
1import { alignText } from "@visulima/string"; 2// For type information, you might also import: 3// import type { AlignTextOptions, StringWidthOptions } from "@visulima/string"; 4 5// Example 1: Aligning a single multi-line string to the right 6const text1 = "First line\nSecond, much longer line"; 7const alignedText1 = alignText(text1, { align: "right" }); 8// alignedText1 (string output, exact padding depends on calculated widths): 9// " First line\nSecond, much longer line" 10 11// Example 2: Aligning an array of strings to the center 12const textArray = ["Short", "Medium length", "A very very long line indeed"]; 13const alignedArray = alignText(textArray, { align: "center" }); 14// alignedArray (array output, exact padding depends on calculated widths): 15// [ 16// " Short ", 17// " Medium length ", 18// "A very very long line indeed" 19// ] 20 21// Example 3: Custom padding, split, and stringWidthOptions for emojis 22const emojiText = "Line1😊*WiderLine😊😊"; 23const alignedEmojiText = alignText(emojiText, { 24 align: "center", 25 split: "*", 26 pad: "-", 27 stringWidthOptions: { emojiWidth: 2 } // Crucial for correct width calculation with emojis 28}); 29// alignedEmojiText (string output, exact padding depends on calculated widths): 30// "--Line1😊---\nWiderLine😊😊"
The alignText
function accepts an options
object of type AlignTextOptions
with the following properties:
align?: "center" | "left" | "right"
: Specifies the alignment direction. Defaults to "center"
. Note: left
alignment primarily ensures line splitting if text
is a single string; it doesn't typically add padding on the left unless the string was not pre-split.pad?: string
: The character or string to use for padding. Defaults to " "
.split?: string
: The character or string used to split the input text
into multiple lines if it's provided as a single string. Defaults to "\n"
.stringWidthOptions?: StringWidthOptions
: Options passed to an internal string width calculation function (similar to getStringWidth
) for determining the visual width of each line. This is important for accurately handling ANSI escape codes, CJK characters, emojis, etc. Refer to the getStringWidth
documentation for details on StringWidthOptions
.Libraries in this ecosystem make the best effort to track Node.js' release schedule. Here's a post on why we think this is important.
If you would like to help, take a look at the list of issues and check our Contributing guidelines.
Note: please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.
The visulima string is open-sourced software licensed under the MIT
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
project has 4 contributing companies or organizations
Details
Reason
no dangerous workflow patterns detected
Reason
update tool detected
Details
Reason
license file detected
Details
Reason
30 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 10
Reason
SAST tool detected: CodeQL
Details
Reason
dependency not pinned by hash detected -- score normalized to 8
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
Found 0/30 approved changesets -- score normalized to 0
Reason
project is not fuzzed
Details
Reason
security policy file not detected
Details
Reason
detected GitHub workflow tokens with excessive permissions
Details
Score
Last Scanned on 2025-07-08T07:32:37Z
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More