@visulima/string 1.0.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/CHANGELOG.md +5 -0
- package/LICENSE.md +77 -0
- package/README.md +880 -0
- package/dist/case/camel-case.cjs +59 -0
- package/dist/case/camel-case.d.cts +7 -0
- package/dist/case/camel-case.d.mts +7 -0
- package/dist/case/camel-case.d.ts +7 -0
- package/dist/case/camel-case.mjs +57 -0
- package/dist/case/capital-case.cjs +51 -0
- package/dist/case/capital-case.d.cts +7 -0
- package/dist/case/capital-case.d.mts +7 -0
- package/dist/case/capital-case.d.ts +7 -0
- package/dist/case/capital-case.mjs +49 -0
- package/dist/case/constant-case.cjs +11 -0
- package/dist/case/constant-case.d.cts +7 -0
- package/dist/case/constant-case.d.mts +7 -0
- package/dist/case/constant-case.d.ts +7 -0
- package/dist/case/constant-case.mjs +9 -0
- package/dist/case/dot-case.cjs +11 -0
- package/dist/case/dot-case.d.cts +7 -0
- package/dist/case/dot-case.d.mts +7 -0
- package/dist/case/dot-case.d.ts +7 -0
- package/dist/case/dot-case.mjs +9 -0
- package/dist/case/flat-case.cjs +11 -0
- package/dist/case/flat-case.d.cts +7 -0
- package/dist/case/flat-case.d.mts +7 -0
- package/dist/case/flat-case.d.ts +7 -0
- package/dist/case/flat-case.mjs +9 -0
- package/dist/case/flip-case.cjs +61 -0
- package/dist/case/flip-case.d.cts +8 -0
- package/dist/case/flip-case.d.mts +8 -0
- package/dist/case/flip-case.d.ts +8 -0
- package/dist/case/flip-case.mjs +57 -0
- package/dist/case/identify-case.cjs +30 -0
- package/dist/case/identify-case.d.cts +7 -0
- package/dist/case/identify-case.d.mts +7 -0
- package/dist/case/identify-case.d.ts +7 -0
- package/dist/case/identify-case.mjs +28 -0
- package/dist/case/index.cjs +47 -0
- package/dist/case/index.d.cts +26 -0
- package/dist/case/index.d.mts +26 -0
- package/dist/case/index.d.ts +26 -0
- package/dist/case/index.mjs +20 -0
- package/dist/case/kebab-case.cjs +61 -0
- package/dist/case/kebab-case.d.cts +11 -0
- package/dist/case/kebab-case.d.mts +11 -0
- package/dist/case/kebab-case.d.ts +11 -0
- package/dist/case/kebab-case.mjs +57 -0
- package/dist/case/lower-first.cjs +13 -0
- package/dist/case/lower-first.d.cts +7 -0
- package/dist/case/lower-first.d.mts +7 -0
- package/dist/case/lower-first.d.ts +7 -0
- package/dist/case/lower-first.mjs +11 -0
- package/dist/case/no-case.cjs +11 -0
- package/dist/case/no-case.d.cts +7 -0
- package/dist/case/no-case.d.mts +7 -0
- package/dist/case/no-case.d.ts +7 -0
- package/dist/case/no-case.mjs +9 -0
- package/dist/case/pascal-case.cjs +52 -0
- package/dist/case/pascal-case.d.cts +7 -0
- package/dist/case/pascal-case.d.mts +7 -0
- package/dist/case/pascal-case.d.ts +7 -0
- package/dist/case/pascal-case.mjs +50 -0
- package/dist/case/pascal-snake-case.cjs +32 -0
- package/dist/case/pascal-snake-case.d.cts +7 -0
- package/dist/case/pascal-snake-case.d.mts +7 -0
- package/dist/case/pascal-snake-case.d.ts +7 -0
- package/dist/case/pascal-snake-case.mjs +30 -0
- package/dist/case/path-case.cjs +11 -0
- package/dist/case/path-case.d.cts +7 -0
- package/dist/case/path-case.d.mts +7 -0
- package/dist/case/path-case.d.ts +7 -0
- package/dist/case/path-case.mjs +9 -0
- package/dist/case/sentence-case.cjs +58 -0
- package/dist/case/sentence-case.d.cts +7 -0
- package/dist/case/sentence-case.d.mts +7 -0
- package/dist/case/sentence-case.d.ts +7 -0
- package/dist/case/sentence-case.mjs +56 -0
- package/dist/case/snake-case.cjs +11 -0
- package/dist/case/snake-case.d.cts +7 -0
- package/dist/case/snake-case.d.mts +7 -0
- package/dist/case/snake-case.d.ts +7 -0
- package/dist/case/snake-case.mjs +9 -0
- package/dist/case/split-by-case.cjs +638 -0
- package/dist/case/split-by-case.d.cts +16 -0
- package/dist/case/split-by-case.d.mts +16 -0
- package/dist/case/split-by-case.d.ts +16 -0
- package/dist/case/split-by-case.mjs +634 -0
- package/dist/case/title-case.cjs +47 -0
- package/dist/case/title-case.d.cts +7 -0
- package/dist/case/title-case.d.mts +7 -0
- package/dist/case/title-case.d.ts +7 -0
- package/dist/case/title-case.mjs +45 -0
- package/dist/case/train-case.cjs +44 -0
- package/dist/case/train-case.d.cts +7 -0
- package/dist/case/train-case.d.mts +7 -0
- package/dist/case/train-case.d.ts +7 -0
- package/dist/case/train-case.mjs +42 -0
- package/dist/case/upper-first.cjs +13 -0
- package/dist/case/upper-first.d.cts +7 -0
- package/dist/case/upper-first.d.mts +7 -0
- package/dist/case/upper-first.d.ts +7 -0
- package/dist/case/upper-first.mjs +11 -0
- package/dist/get-string-truncated-width.cjs +386 -0
- package/dist/get-string-truncated-width.d.cts +25 -0
- package/dist/get-string-truncated-width.d.mts +25 -0
- package/dist/get-string-truncated-width.d.ts +25 -0
- package/dist/get-string-truncated-width.mjs +382 -0
- package/dist/get-string-width.cjs +11 -0
- package/dist/get-string-width.d.cts +6 -0
- package/dist/get-string-width.d.mts +6 -0
- package/dist/get-string-width.d.ts +6 -0
- package/dist/get-string-width.mjs +7 -0
- package/dist/index.cjs +60 -0
- package/dist/index.d.cts +43 -0
- package/dist/index.d.mts +43 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.mjs +26 -0
- package/dist/native-string-types.d.cts +128 -0
- package/dist/native-string-types.d.mts +128 -0
- package/dist/native-string-types.d.ts +128 -0
- package/dist/outdent.cjs +122 -0
- package/dist/outdent.d.cts +15 -0
- package/dist/outdent.d.mts +15 -0
- package/dist/outdent.d.ts +15 -0
- package/dist/outdent.mjs +118 -0
- package/dist/packem_shared/LRUCache-Crb-m7cw.cjs +53 -0
- package/dist/packem_shared/LRUCache-udNErhWw.mjs +51 -0
- package/dist/packem_shared/WrapMode-BgOOthJV.mjs +545 -0
- package/dist/packem_shared/WrapMode-ZqTeXK8X.cjs +550 -0
- package/dist/packem_shared/constants-BnVGLrDr.mjs +99 -0
- package/dist/packem_shared/constants-HevSYUjU.cjs +149 -0
- package/dist/packem_shared/generate-cache-key-D8f1auDB.cjs +10 -0
- package/dist/packem_shared/generate-cache-key-YSju5pj2.mjs +8 -0
- package/dist/packem_shared/joinSegments-BH1CT4dK.cjs +48 -0
- package/dist/packem_shared/joinSegments-BHY8PPnk.mjs +46 -0
- package/dist/packem_shared/normalize-german-eszett-DJK0S2qj.cjs +11 -0
- package/dist/packem_shared/normalize-german-eszett-fpJ4lZL6.mjs +9 -0
- package/dist/packem_shared/types-BAd3WDHo.d.mts +59 -0
- package/dist/packem_shared/types-BNbQJg02.d.cts +41 -0
- package/dist/packem_shared/types-BNbQJg02.d.mts +41 -0
- package/dist/packem_shared/types-BNbQJg02.d.ts +41 -0
- package/dist/packem_shared/types-BttWSSIk.d.ts +59 -0
- package/dist/packem_shared/types-D2-6p7t1.d.cts +59 -0
- package/dist/slice.cjs +413 -0
- package/dist/slice.d.cts +10 -0
- package/dist/slice.d.mts +10 -0
- package/dist/slice.d.ts +10 -0
- package/dist/slice.mjs +409 -0
- package/dist/test/utils.cjs +77 -0
- package/dist/test/utils.d.cts +30 -0
- package/dist/test/utils.d.mts +30 -0
- package/dist/test/utils.d.ts +30 -0
- package/dist/test/utils.mjs +71 -0
- package/dist/test/vitest.cjs +11 -0
- package/dist/test/vitest.d.cts +8 -0
- package/dist/test/vitest.d.mts +8 -0
- package/dist/test/vitest.d.ts +8 -0
- package/dist/test/vitest.mjs +7 -0
- package/dist/truncate.cjs +90 -0
- package/dist/truncate.d.cts +12 -0
- package/dist/truncate.d.mts +12 -0
- package/dist/truncate.d.ts +12 -0
- package/dist/truncate.mjs +86 -0
- package/dist/utils.cjs +9 -0
- package/dist/utils.d.cts +14 -0
- package/dist/utils.d.mts +14 -0
- package/dist/utils.d.ts +14 -0
- package/dist/utils.mjs +1 -0
- package/package.json +468 -0
package/README.md
ADDED
|
@@ -0,0 +1,880 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h3>visulima string</h3>
|
|
3
|
+
<p>
|
|
4
|
+
A robust string manipulation library providing utilities for common string operations with support for multiple languages.
|
|
5
|
+
</p>
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<br />
|
|
9
|
+
|
|
10
|
+
<div align="center">
|
|
11
|
+
|
|
12
|
+
[![typescript-image]][typescript-url] [![npm-image]][npm-url] [![license-image]][license-url]
|
|
13
|
+
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<div align="center">
|
|
19
|
+
<p>
|
|
20
|
+
<sup>
|
|
21
|
+
Daniel Bannert's open source work is supported by the community on <a href="https://github.com/sponsors/prisis">GitHub Sponsors</a>
|
|
22
|
+
</sup>
|
|
23
|
+
</p>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
### Case Conversion
|
|
29
|
+
|
|
30
|
+
- **Multiple Case Styles**:
|
|
31
|
+
- `camelCase`: Convert to camelCase style
|
|
32
|
+
- `PascalCase`: Convert to PascalCase style
|
|
33
|
+
- `snake_case`: Convert to snake_case style
|
|
34
|
+
- `kebab-case`: Convert to kebab-case style
|
|
35
|
+
- `CONSTANT_CASE`: Convert to CONSTANT_CASE style
|
|
36
|
+
- `dot.case`: Convert to dot.case style
|
|
37
|
+
- `path/case`: Convert to path/case style
|
|
38
|
+
- `Sentence case`: Convert to Sentence case style
|
|
39
|
+
- `Title Case`: Convert to Title Case with smart minor word handling
|
|
40
|
+
|
|
41
|
+
### String Manipulation
|
|
42
|
+
|
|
43
|
+
- **Smart String Splitting**:
|
|
44
|
+
- Split by case transitions (camelCase → ["camel", "Case"])
|
|
45
|
+
- Split by script boundaries (日本語Text → ["日本語", "Text"])
|
|
46
|
+
- Split by separators (foo-bar → ["foo", "bar"])
|
|
47
|
+
- Preserve known acronyms (XMLHttpRequest → ["XML", "Http", "Request"])
|
|
48
|
+
- **Text Indentation**:
|
|
49
|
+
- `outdent`: Remove leading indentation while preserving relative indentation
|
|
50
|
+
- Handles template literals and string inputs
|
|
51
|
+
- Normalizes newlines across platforms
|
|
52
|
+
- Configurable trimming behavior
|
|
53
|
+
- **String Width Calculation**:
|
|
54
|
+
- `getStringWidth`: Calculate visual width of strings with Unicode support
|
|
55
|
+
- `getStringTruncatedWidth`: Width calculation with smart truncation
|
|
56
|
+
- Handles CJK characters, emojis, ANSI codes, and more
|
|
57
|
+
- Configurable character width settings
|
|
58
|
+
- Support for zero-width and combining characters
|
|
59
|
+
- Customizable truncation with ellipsis
|
|
60
|
+
|
|
61
|
+
### Multi-Script Support
|
|
62
|
+
|
|
63
|
+
- **CJK Scripts**:
|
|
64
|
+
- Japanese (Hiragana, Katakana, Kanji)
|
|
65
|
+
- Korean (Hangul)
|
|
66
|
+
- Chinese (Han characters)
|
|
67
|
+
- **European Scripts**:
|
|
68
|
+
- Cyrillic (Russian, Ukrainian, etc.)
|
|
69
|
+
- Greek
|
|
70
|
+
- Latin (with extended characters)
|
|
71
|
+
- **RTL Scripts**:
|
|
72
|
+
- Arabic
|
|
73
|
+
- Hebrew
|
|
74
|
+
- **Indic Scripts**:
|
|
75
|
+
- Devanagari
|
|
76
|
+
- Bengali
|
|
77
|
+
- Tamil
|
|
78
|
+
- And more...
|
|
79
|
+
- **Southeast Asian**:
|
|
80
|
+
- Thai
|
|
81
|
+
- Lao
|
|
82
|
+
- Khmer
|
|
83
|
+
|
|
84
|
+
### Enhanced Type Safety
|
|
85
|
+
|
|
86
|
+
- **Native String Type Extensions**:
|
|
87
|
+
- Type-safe string operations
|
|
88
|
+
- Compile-time type checking
|
|
89
|
+
- Improved IDE support
|
|
90
|
+
- **Generic Type Parameters**:
|
|
91
|
+
- Flexible type constraints
|
|
92
|
+
- Template literal type support
|
|
93
|
+
- Conditional type inference
|
|
94
|
+
|
|
95
|
+
### Performance Features
|
|
96
|
+
|
|
97
|
+
- **Optimized Processing**:
|
|
98
|
+
- Precompiled regex patterns
|
|
99
|
+
- Efficient string manipulation
|
|
100
|
+
- **Caching Mechanisms**:
|
|
101
|
+
- Smart caching for repeated operations
|
|
102
|
+
- WeakMap-based caching for template literals
|
|
103
|
+
- Configurable cache options
|
|
104
|
+
- Fast paths for common cases
|
|
105
|
+
- **Memory Efficiency**:
|
|
106
|
+
- Minimal string allocations
|
|
107
|
+
- Efficient string splitting
|
|
108
|
+
- Optimized concatenation
|
|
109
|
+
|
|
110
|
+
### Developer Experience
|
|
111
|
+
|
|
112
|
+
- **Comprehensive API**:
|
|
113
|
+
- Consistent method signatures
|
|
114
|
+
- Chainable operations
|
|
115
|
+
- Flexible configuration options
|
|
116
|
+
- **Robust Error Handling**:
|
|
117
|
+
- Graceful handling of edge cases
|
|
118
|
+
- Clear error messages
|
|
119
|
+
- Type-safe error prevention
|
|
120
|
+
- **Full TypeScript Support**:
|
|
121
|
+
- Complete type definitions
|
|
122
|
+
- IntelliSense support
|
|
123
|
+
- Type inference
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Install
|
|
128
|
+
|
|
129
|
+
```sh
|
|
130
|
+
npm install @visulima/string
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
yarn add @visulima/string
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
pnpm add @visulima/string
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Usage
|
|
142
|
+
|
|
143
|
+
### Outdent (Remove Indentation)
|
|
144
|
+
|
|
145
|
+
The `outdent` function removes leading indentation from multi-line strings while preserving the relative indentation structure.
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import { outdent } from '@visulima/string';
|
|
149
|
+
|
|
150
|
+
// Basic usage with template literals
|
|
151
|
+
const text = outdent`
|
|
152
|
+
This text will have its indentation removed
|
|
153
|
+
while preserving relative indentation.
|
|
154
|
+
This line will still be indented relative to the others.
|
|
155
|
+
`;
|
|
156
|
+
|
|
157
|
+
// Output:
|
|
158
|
+
// This text will have its indentation removed
|
|
159
|
+
// while preserving relative indentation.
|
|
160
|
+
// This line will still be indented relative to the others.
|
|
161
|
+
|
|
162
|
+
// With string input
|
|
163
|
+
const result = outdent.string('
|
|
164
|
+
Hello
|
|
165
|
+
World
|
|
166
|
+
');
|
|
167
|
+
// Output: "Hello\nWorld"
|
|
168
|
+
|
|
169
|
+
// With custom options
|
|
170
|
+
const customOutdent = outdent({
|
|
171
|
+
trimLeadingNewline: false, // Keep the leading newline
|
|
172
|
+
trimTrailingNewline: false, // Keep the trailing newline
|
|
173
|
+
newline: '\r\n', // Normalize all newlines to CRLF
|
|
174
|
+
cache: true // Enable caching (default)
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Using with interpolation
|
|
178
|
+
const name = 'World';
|
|
179
|
+
const greeting = outdent`
|
|
180
|
+
Hello ${name}!
|
|
181
|
+
Welcome to outdent.
|
|
182
|
+
`;
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### Performance Optimization with Caching
|
|
186
|
+
|
|
187
|
+
The `outdent` function supports caching to improve performance when the same template is used multiple times:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
// Default behavior - caching enabled
|
|
191
|
+
const dedent = outdent();
|
|
192
|
+
|
|
193
|
+
// Disable caching if memory usage is a concern
|
|
194
|
+
const noCacheDedent = outdent({ cache: false });
|
|
195
|
+
|
|
196
|
+
// Use a custom cache store (advanced usage)
|
|
197
|
+
const customCache = new WeakMap();
|
|
198
|
+
const customCacheDedent = outdent({ cacheStore: customCache });
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Word Wrapping
|
|
202
|
+
|
|
203
|
+
The `wordWrap` function provides flexible text wrapping with support for ANSI color codes and Unicode.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
import { wordWrap, WrapMode } from "@visulima/string";
|
|
207
|
+
|
|
208
|
+
// Basic usage with default options (80 character width, preserve words)
|
|
209
|
+
const wrapped = wordWrap("This is a long text that will be wrapped to fit within the specified width limit.");
|
|
210
|
+
|
|
211
|
+
// With custom width (40 characters)
|
|
212
|
+
const narrowWrapped = wordWrap("This text will be wrapped to fit within a 40-character width.", { width: 40 });
|
|
213
|
+
|
|
214
|
+
// Different wrapping modes
|
|
215
|
+
const preserveWords = wordWrap("Long words will stay intact but may exceed the width limit.", {
|
|
216
|
+
width: 20,
|
|
217
|
+
wrapMode: WrapMode.PRESERVE_WORDS, // Default - keeps words intact
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const breakAtCharacters = wordWrap("Words will be broken at character boundaries to fit width.", {
|
|
221
|
+
width: 20,
|
|
222
|
+
wrapMode: WrapMode.BREAK_AT_CHARACTERS, // Breaks words to fit width exactly
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const strictWidth = wordWrap("Text will be broken exactly at the width limit.", {
|
|
226
|
+
width: 20,
|
|
227
|
+
wrapMode: WrapMode.STRICT_WIDTH, // Forces strict adherence to width
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Handling ANSI color codes
|
|
231
|
+
const coloredText = "\u001B[31mThis red text\u001B[0m will be wrapped while preserving the color codes.";
|
|
232
|
+
const wrappedColored = wordWrap(coloredText, { width: 20 });
|
|
233
|
+
// Color codes are preserved across line breaks
|
|
234
|
+
|
|
235
|
+
// Additional options
|
|
236
|
+
const customWrapped = wordWrap("Text with\u200Bzero-width characters and\u200Btrailing spaces.", {
|
|
237
|
+
width: 30,
|
|
238
|
+
trim: false, // Don't trim whitespace from lines (default: true)
|
|
239
|
+
removeZeroWidthCharacters: false, // Don't remove zero-width characters (default: true)
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### String Splitting
|
|
244
|
+
|
|
245
|
+
The `splitByCase` function is a powerful utility that splits strings based on various patterns:
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { splitByCase } from "@visulima/string";
|
|
249
|
+
|
|
250
|
+
// Basic Case Transitions
|
|
251
|
+
splitByCase("camelCase"); // ['camel', 'Case']
|
|
252
|
+
splitByCase("PascalCase"); // ['Pascal', 'Case']
|
|
253
|
+
splitByCase("snake_case"); // ['snake', 'case']
|
|
254
|
+
splitByCase("kebab-case"); // ['kebab', 'case']
|
|
255
|
+
|
|
256
|
+
// Numbers and Acronyms
|
|
257
|
+
splitByCase("XMLHttpRequest"); // ['XML', 'Http', 'Request']
|
|
258
|
+
splitByCase("iOS8"); // ['i', 'OS', '8']
|
|
259
|
+
splitByCase("IPv6Address"); // ['IP', 'v6', 'Address']
|
|
260
|
+
|
|
261
|
+
// Multi-Script Support
|
|
262
|
+
|
|
263
|
+
// Japanese
|
|
264
|
+
splitByCase("ひらがなカタカナABC", { locale: "ja" });
|
|
265
|
+
// ['ひらがな', 'カタカナ', 'ABC']
|
|
266
|
+
|
|
267
|
+
// Korean
|
|
268
|
+
splitByCase("한글Text", { locale: "ko" });
|
|
269
|
+
// ['한글', 'Text']
|
|
270
|
+
|
|
271
|
+
// Chinese
|
|
272
|
+
splitByCase("中文Text", { locale: "zh" });
|
|
273
|
+
// ['中文', 'Text']
|
|
274
|
+
|
|
275
|
+
// Cyrillic
|
|
276
|
+
splitByCase("русскийText", { locale: "ru" });
|
|
277
|
+
// ['русский', 'Text']
|
|
278
|
+
|
|
279
|
+
// Greek
|
|
280
|
+
splitByCase("ελληνικάText", { locale: "el" });
|
|
281
|
+
// ['ελληνικά', 'Text']
|
|
282
|
+
|
|
283
|
+
// Advanced Options
|
|
284
|
+
splitByCase("MyXMLParser", {
|
|
285
|
+
knownAcronyms: ["XML"], // Preserve known acronyms
|
|
286
|
+
normalize: true, // Normalize case
|
|
287
|
+
locale: "en", // Specify locale
|
|
288
|
+
});
|
|
289
|
+
// ['My', 'XML', 'Parser']
|
|
290
|
+
|
|
291
|
+
// ANSI and Emoji Handling
|
|
292
|
+
splitByCase("🎉HappyBirthday🎂", {
|
|
293
|
+
handleEmoji: true, // Handle emoji boundaries
|
|
294
|
+
});
|
|
295
|
+
// ['🎉', 'Happy', 'Birthday', '🎂']
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Case Conversion Functions
|
|
299
|
+
|
|
300
|
+
#### camelCase
|
|
301
|
+
|
|
302
|
+
Converts a string to camelCase.
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
camelCase("foo bar"); // 'fooBar'
|
|
306
|
+
camelCase("foo-bar"); // 'fooBar'
|
|
307
|
+
camelCase("foo_bar"); // 'fooBar'
|
|
308
|
+
camelCase("XMLHttpRequest"); // 'xmlHttpRequest'
|
|
309
|
+
camelCase("AJAXRequest"); // 'ajaxRequest'
|
|
310
|
+
camelCase("QueryXML123String"); // 'queryXml123String'
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
#### pascalCase
|
|
314
|
+
|
|
315
|
+
Converts a string to PascalCase.
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
pascalCase("foo bar"); // 'FooBar'
|
|
319
|
+
pascalCase("foo-bar"); // 'FooBar'
|
|
320
|
+
pascalCase("foo_bar"); // 'FooBar'
|
|
321
|
+
pascalCase("XMLHttpRequest"); // 'XmlHttpRequest'
|
|
322
|
+
pascalCase("AJAXRequest"); // 'AjaxRequest'
|
|
323
|
+
pascalCase("QueryXML123String"); // 'QueryXml123String'
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
#### snakeCase
|
|
327
|
+
|
|
328
|
+
Converts a string to snake_case.
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
snakeCase("fooBar"); // 'foo_bar'
|
|
332
|
+
snakeCase("foo bar"); // 'foo_bar'
|
|
333
|
+
snakeCase("foo-bar"); // 'foo_bar'
|
|
334
|
+
snakeCase("XMLHttpRequest"); // 'xml_http_request'
|
|
335
|
+
snakeCase("AJAXRequest"); // 'ajax_request'
|
|
336
|
+
snakeCase("QueryXML123String"); // 'query_xml_123_string'
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
#### kebabCase
|
|
340
|
+
|
|
341
|
+
Converts a string to kebab-case.
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
kebabCase("fooBar"); // 'foo-bar'
|
|
345
|
+
kebabCase("foo bar"); // 'foo-bar'
|
|
346
|
+
kebabCase("foo_bar"); // 'foo-bar'
|
|
347
|
+
kebabCase("XMLHttpRequest"); // 'xml-http-request'
|
|
348
|
+
kebabCase("AJAXRequest"); // 'ajax-request'
|
|
349
|
+
kebabCase("QueryXML123String"); // 'query-xml-123-string'
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
#### titleCase
|
|
353
|
+
|
|
354
|
+
Converts a string to Title Case, with smart handling of minor words.
|
|
355
|
+
|
|
356
|
+
```typescript
|
|
357
|
+
titleCase("this-IS-aTitle"); // 'This is a Title'
|
|
358
|
+
titleCase("XMLHttpRequest"); // 'XML Http Request'
|
|
359
|
+
titleCase("AJAXRequest"); // 'AJAX Request'
|
|
360
|
+
titleCase("QueryXML123String"); // 'Query XML 123 String'
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
#### pathCase
|
|
364
|
+
|
|
365
|
+
Converts a string to path/case.
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
pathCase("foo bar"); // 'foo/bar'
|
|
369
|
+
pathCase("foo-bar"); // 'foo/bar'
|
|
370
|
+
pathCase("foo_bar"); // 'foo/bar'
|
|
371
|
+
pathCase("XMLHttpRequest"); // 'xml/http/request'
|
|
372
|
+
pathCase("AJAXRequest"); // 'ajax/request'
|
|
373
|
+
pathCase("QueryXML123String"); // 'query/xml/123/string'
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
#### dotCase
|
|
377
|
+
|
|
378
|
+
Converts a string to dot.case.
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
dotCase("foo bar"); // 'foo.bar'
|
|
382
|
+
dotCase("foo-bar"); // 'foo.bar'
|
|
383
|
+
dotCase("foo_bar"); // 'foo.bar'
|
|
384
|
+
dotCase("XMLHttpRequest"); // 'xml.http.request'
|
|
385
|
+
dotCase("AJAXRequest"); // 'ajax.request'
|
|
386
|
+
dotCase("QueryXML123String"); // 'query.xml.123.string'
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
#### constantCase
|
|
390
|
+
|
|
391
|
+
Converts a string to CONSTANT_CASE.
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
constantCase("foo bar"); // 'FOO_BAR'
|
|
395
|
+
constantCase("foo-bar"); // 'FOO_BAR'
|
|
396
|
+
constantCase("foo_bar"); // 'FOO_BAR'
|
|
397
|
+
constantCase("XMLHttpRequest"); // 'XML_HTTP_REQUEST'
|
|
398
|
+
constantCase("AJAXRequest"); // 'AJAX_REQUEST'
|
|
399
|
+
constantCase("QueryXML123String"); // 'QUERY_XML_123_STRING'
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
#### sentenceCase
|
|
403
|
+
|
|
404
|
+
Converts a string to Sentence case.
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
sentenceCase("foo bar"); // 'Foo bar'
|
|
408
|
+
sentenceCase("foo-bar"); // 'Foo bar'
|
|
409
|
+
sentenceCase("foo_bar"); // 'Foo bar'
|
|
410
|
+
sentenceCase("XMLHttpRequest"); // 'Xml http request'
|
|
411
|
+
sentenceCase("AJAXRequest"); // 'Ajax request'
|
|
412
|
+
sentenceCase("QueryXML123String"); // 'Query xml 123 string'
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### String Width Calculation
|
|
416
|
+
|
|
417
|
+
The package provides two functions for calculating string widths: `getStringWidth` for basic width calculation and `getStringTruncatedWidth` for width calculation with truncation support.
|
|
418
|
+
|
|
419
|
+
#### Basic Width Calculation
|
|
420
|
+
|
|
421
|
+
The `getStringWidth` function calculates the visual width of strings, taking into account various Unicode characters, emojis, ANSI escape codes, and more:
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
import { getStringWidth } from "@visulima/string";
|
|
425
|
+
|
|
426
|
+
// Basic usage
|
|
427
|
+
getStringWidth("hello"); // => 5
|
|
428
|
+
getStringWidth("👋 hello"); // => 7 (emoji is width 2)
|
|
429
|
+
getStringWidth("あいう"); // => 6 (each character is width 2)
|
|
430
|
+
|
|
431
|
+
// With custom options
|
|
432
|
+
getStringWidth("hello", { regularWidth: 2 }); // => 10
|
|
433
|
+
getStringWidth("あいう", { ambiguousIsNarrow: true }); // => 3
|
|
434
|
+
|
|
435
|
+
// ANSI escape codes
|
|
436
|
+
getStringWidth("\u001B[31mRed\u001B[39m"); // => 3
|
|
437
|
+
getStringWidth("\u001B[31mRed\u001B[39m", { countAnsiEscapeCodes: true }); // => 11
|
|
438
|
+
|
|
439
|
+
// Advanced Unicode support
|
|
440
|
+
getStringWidth("한글"); // => 4 (Korean characters)
|
|
441
|
+
getStringWidth("你好"); // => 4 (Chinese characters)
|
|
442
|
+
getStringWidth("👨👩👧👦"); // => 2 (family emoji with ZWJ sequences)
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
#### Configuration Options
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
interface StringWidthOptions {
|
|
449
|
+
ambiguousIsNarrow?: boolean; // Treat ambiguous-width characters as narrow
|
|
450
|
+
ambiguousWidth?: number; // Width of ambiguous-width characters (default: 1)
|
|
451
|
+
ansiWidth?: number; // Width of ANSI escape sequences (default: 0)
|
|
452
|
+
controlWidth?: number; // Width of control characters (default: 0)
|
|
453
|
+
countAnsiEscapeCodes?: boolean; // Include ANSI escape codes in width (default: false)
|
|
454
|
+
emojiWidth?: number; // Width of emoji characters (default: 2)
|
|
455
|
+
fullWidth?: number; // Width of full-width characters (default: 2)
|
|
456
|
+
regularWidth?: number; // Width of regular characters (default: 1)
|
|
457
|
+
tabWidth?: number; // Width of tab characters (default: 8)
|
|
458
|
+
wideWidth?: number; // Width of wide characters (default: 2)
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
#### Width Calculation with Truncation
|
|
463
|
+
|
|
464
|
+
The `getStringTruncatedWidth` function extends the basic width calculation with truncation support:
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
import { getStringTruncatedWidth } from "@visulima/string";
|
|
468
|
+
|
|
469
|
+
// Basic truncation
|
|
470
|
+
getStringTruncatedWidth("hello world", {
|
|
471
|
+
limit: 8,
|
|
472
|
+
ellipsis: "...",
|
|
473
|
+
}); // => { width: 8, truncated: true, ellipsed: true, index: 5 }
|
|
474
|
+
|
|
475
|
+
// Custom character widths with truncation
|
|
476
|
+
getStringTruncatedWidth("あいうえお", {
|
|
477
|
+
limit: 6,
|
|
478
|
+
ellipsis: "...",
|
|
479
|
+
fullWidth: 2,
|
|
480
|
+
}); // => { width: 6, truncated: true, ellipsed: true, index: 2 }
|
|
481
|
+
|
|
482
|
+
// ANSI codes with truncation
|
|
483
|
+
getStringTruncatedWidth("\u001B[31mRed Text\u001B[39m", {
|
|
484
|
+
limit: 5,
|
|
485
|
+
ellipsis: "...",
|
|
486
|
+
countAnsiEscapeCodes: true,
|
|
487
|
+
}); // => { width: 5, truncated: true, ellipsed: true, index: 4 }
|
|
488
|
+
|
|
489
|
+
// Complex Unicode with truncation
|
|
490
|
+
getStringTruncatedWidth("👨👩👧👦 Family", {
|
|
491
|
+
limit: 7,
|
|
492
|
+
ellipsis: "...",
|
|
493
|
+
}); // => { width: 7, truncated: true, ellipsed: true, index: 11 }
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### String Truncation
|
|
497
|
+
|
|
498
|
+
The `truncate` function provides a convenient way to truncate strings with support for different positions, Unicode characters, ANSI escape codes, and more.
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
import { truncate } from "@visulima/string";
|
|
502
|
+
|
|
503
|
+
// Basic truncation (end position)
|
|
504
|
+
truncate("unicorn", 4); // => 'un…'
|
|
505
|
+
truncate("unicorn", 4, { position: "end" }); // => 'un…'
|
|
506
|
+
|
|
507
|
+
// Different positions
|
|
508
|
+
truncate("unicorn", 5, { position: "start" }); // => '…orn'
|
|
509
|
+
truncate("unicorn", 5, { position: "middle" }); // => 'un…n'
|
|
510
|
+
|
|
511
|
+
// With custom ellipsis
|
|
512
|
+
truncate("unicorns", 5, { ellipsis: "." }); // => 'unic.'
|
|
513
|
+
truncate("unicorns", 5, { ellipsis: " ." }); // => 'uni .'
|
|
514
|
+
|
|
515
|
+
// Smart truncation on spaces
|
|
516
|
+
truncate("dragons are awesome", 15, { position: "end", preferTruncationOnSpace: true }); // => 'dragons are…'
|
|
517
|
+
truncate("unicorns rainbow dragons", 20, { position: "middle", preferTruncationOnSpace: true }); // => 'unicorns…dragons'
|
|
518
|
+
|
|
519
|
+
// With ANSI escape codes
|
|
520
|
+
truncate("\u001B[31municorn\u001B[39m", 4); // => '\u001B[31mun\u001B[39m…'
|
|
521
|
+
|
|
522
|
+
// With Unicode characters
|
|
523
|
+
truncate("안녕하세요", 3, { width: { fullWidth: 2 } }); // => '안…'
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
#### Truncation Options
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
interface TruncateOptions {
|
|
530
|
+
// String to append when truncation occurs
|
|
531
|
+
ellipsis?: string; // default: ''
|
|
532
|
+
|
|
533
|
+
// Width of the ellipsis string
|
|
534
|
+
// If not provided, it will be calculated using getStringTruncatedWidth
|
|
535
|
+
ellipsisWidth?: number;
|
|
536
|
+
|
|
537
|
+
// The position to truncate the string
|
|
538
|
+
position?: "end" | "middle" | "start"; // default: 'end'
|
|
539
|
+
|
|
540
|
+
// Truncate the string from a whitespace if it is within 3 characters
|
|
541
|
+
// from the actual breaking point
|
|
542
|
+
preferTruncationOnSpace?: boolean; // default: false
|
|
543
|
+
|
|
544
|
+
// Width calculation options
|
|
545
|
+
width?: Omit<StringTruncatedWidthOptions, "ellipsis" | "ellipsisWidth" | "limit">;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
interface StringTruncatedWidthOptions extends StringWidthOptions {
|
|
549
|
+
// Truncation-specific options
|
|
550
|
+
ellipsis?: string; // String to append when truncation occurs (default: '')
|
|
551
|
+
ellipsisWidth?: number; // Width of ellipsis, auto-calculated if not provided
|
|
552
|
+
limit?: number; // Maximum width limit for the string (default: Infinity)
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Return value structure
|
|
556
|
+
interface StringTruncatedWidthResult {
|
|
557
|
+
width: number; // The calculated visual width of the string
|
|
558
|
+
truncated: boolean; // Whether the string was truncated
|
|
559
|
+
ellipsed: boolean; // Whether an ellipsis was added
|
|
560
|
+
index: number; // The index at which truncation occurred (if any)
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### Common Options
|
|
565
|
+
|
|
566
|
+
All case conversion functions accept these common options:
|
|
567
|
+
|
|
568
|
+
```typescript
|
|
569
|
+
interface CaseOptions {
|
|
570
|
+
// Enable caching for better performance
|
|
571
|
+
cache?: boolean;
|
|
572
|
+
|
|
573
|
+
// Maximum size of the cache (default: 1000)
|
|
574
|
+
cacheMaxSize?: number;
|
|
575
|
+
|
|
576
|
+
// Custom cache store
|
|
577
|
+
cacheStore?: Map<string, string>;
|
|
578
|
+
|
|
579
|
+
// Known acronyms to preserve
|
|
580
|
+
knownAcronyms?: ReadonlyArray<string>;
|
|
581
|
+
|
|
582
|
+
// Locale for script-aware case conversion
|
|
583
|
+
locale?: string;
|
|
584
|
+
}
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### String Splitting
|
|
588
|
+
|
|
589
|
+
The `splitByCase` function accepts these configuration options:
|
|
590
|
+
|
|
591
|
+
```typescript
|
|
592
|
+
interface SplitOptions {
|
|
593
|
+
// Locale for script-aware splitting (e.g., 'ja', 'ko', 'zh')
|
|
594
|
+
locale?: string;
|
|
595
|
+
|
|
596
|
+
// Known acronyms to preserve (e.g., ['XML', 'HTTP'])
|
|
597
|
+
knownAcronyms?: ReadonlyArray<string>;
|
|
598
|
+
|
|
599
|
+
// Handle ANSI escape sequences
|
|
600
|
+
handleAnsi?: boolean;
|
|
601
|
+
|
|
602
|
+
// Handle emoji sequences
|
|
603
|
+
handleEmoji?: boolean;
|
|
604
|
+
|
|
605
|
+
// Normalize case (convert all-upper tokens to title case)
|
|
606
|
+
normalize?: boolean;
|
|
607
|
+
|
|
608
|
+
// Custom separators (string[] or RegExp)
|
|
609
|
+
separators?: ReadonlyArray<string> | RegExp;
|
|
610
|
+
|
|
611
|
+
// Strip ANSI sequences
|
|
612
|
+
stripAnsi?: boolean;
|
|
613
|
+
|
|
614
|
+
// Strip emoji sequences
|
|
615
|
+
stripEmoji?: boolean;
|
|
616
|
+
}
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
### Supported Scripts
|
|
620
|
+
|
|
621
|
+
The library provides comprehensive support for various scripts and writing systems:
|
|
622
|
+
|
|
623
|
+
- **Latin**: Standard ASCII and extended Latin characters
|
|
624
|
+
- **CJK**:
|
|
625
|
+
- Japanese (Hiragana, Katakana, Kanji)
|
|
626
|
+
- Korean (Hangul)
|
|
627
|
+
- Chinese (Han characters)
|
|
628
|
+
- **Cyrillic**: Russian, Ukrainian, Bulgarian, etc.
|
|
629
|
+
- **Greek**: Modern Greek script
|
|
630
|
+
- **RTL Scripts**: Arabic, Hebrew
|
|
631
|
+
- **Indic Scripts**: Devanagari, Bengali, Tamil, etc.
|
|
632
|
+
- **Southeast Asian**: Thai, Lao, Khmer
|
|
633
|
+
|
|
634
|
+
### Performance Optimization
|
|
635
|
+
|
|
636
|
+
The library includes several optimizations:
|
|
637
|
+
|
|
638
|
+
- Precompiled regex patterns for script detection
|
|
639
|
+
- Fast paths for single-script strings
|
|
640
|
+
- Efficient handling of case transitions
|
|
641
|
+
- Smart caching of character type checks
|
|
642
|
+
|
|
643
|
+
### Error Handling
|
|
644
|
+
|
|
645
|
+
The library handles various edge cases gracefully:
|
|
646
|
+
|
|
647
|
+
```typescript
|
|
648
|
+
// Empty strings
|
|
649
|
+
splitByCase(""); // []
|
|
650
|
+
|
|
651
|
+
// Invalid input
|
|
652
|
+
splitByCase(null); // []
|
|
653
|
+
splitByCase(undefined); // []
|
|
654
|
+
|
|
655
|
+
// Single characters
|
|
656
|
+
splitByCase("A"); // ['A']
|
|
657
|
+
|
|
658
|
+
// All uppercase
|
|
659
|
+
splitByCase("URL", { knownAcronyms: ["URL"] }); // ['URL']
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
### Native String Types
|
|
663
|
+
|
|
664
|
+
The library provides enhanced TypeScript type definitions for native string methods. These types provide better type inference and compile-time checks.
|
|
665
|
+
|
|
666
|
+
#### Configuration
|
|
667
|
+
|
|
668
|
+
Configure your `tsconfig.json` file to include the types:
|
|
669
|
+
|
|
670
|
+
```json
|
|
671
|
+
{
|
|
672
|
+
"compilerOptions": {
|
|
673
|
+
"types": ["@visulima/string/native-string-types"]
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
Alternatively, you can add a triple-slash reference in your TypeScript files:
|
|
679
|
+
|
|
680
|
+
```typescript
|
|
681
|
+
/// <reference types="@visulima/string/native-string-types" />
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
#### Usage Examples
|
|
685
|
+
|
|
686
|
+
```typescript
|
|
687
|
+
// Type-safe string operations
|
|
688
|
+
const str = "Hello, World!";
|
|
689
|
+
|
|
690
|
+
// charAt with type inference
|
|
691
|
+
str.charAt<typeof str, 0>(); // type: 'H'
|
|
692
|
+
str.charAt<typeof str, 1>(); // type: 'e'
|
|
693
|
+
|
|
694
|
+
// concat with type inference
|
|
695
|
+
str.concat<typeof str, "Hi">(); // type: 'Hello, World!Hi'
|
|
696
|
+
|
|
697
|
+
// endsWith with literal type checking
|
|
698
|
+
str.endsWith<typeof str, "World!">(); // type: true
|
|
699
|
+
str.endsWith<typeof str, "Hello">(); // type: false
|
|
700
|
+
|
|
701
|
+
// includes with position
|
|
702
|
+
str.includes<typeof str, "World", 0>(); // type: true
|
|
703
|
+
str.includes<typeof str, "World", 7>(); // type: false
|
|
704
|
+
|
|
705
|
+
// length with type inference
|
|
706
|
+
type Length = (typeof str)["length"]; // type: 13
|
|
707
|
+
|
|
708
|
+
// padStart/padEnd with type inference
|
|
709
|
+
str.padStart<typeof str, 15, "_">(); // type: '_Hello, World!'
|
|
710
|
+
str.padEnd<typeof str, 15, "_">(); // type: 'Hello, World!__'
|
|
711
|
+
|
|
712
|
+
// replace with type inference
|
|
713
|
+
str.replace<typeof str, "World", "TypeScript">(); // type: 'Hello, TypeScript!'
|
|
714
|
+
|
|
715
|
+
// replaceAll with type inference
|
|
716
|
+
str.replaceAll<typeof str, "l", "L">(); // type: 'HeLLo, WorLd!'
|
|
717
|
+
|
|
718
|
+
// slice with type inference
|
|
719
|
+
str.slice<typeof str, 0, 5>(); // type: 'Hello'
|
|
720
|
+
|
|
721
|
+
// split with type inference
|
|
722
|
+
str.split<typeof str, ", ">(); // type: ['Hello', 'World!']
|
|
723
|
+
|
|
724
|
+
// startsWith with type inference
|
|
725
|
+
str.startsWith<typeof str, "Hello">(); // type: true
|
|
726
|
+
|
|
727
|
+
// toLowerCase/toUpperCase with type inference
|
|
728
|
+
str.toLowerCase<typeof str>(); // type: 'hello, world!'
|
|
729
|
+
str.toUpperCase<typeof str>(); // type: 'HELLO, WORLD!'
|
|
730
|
+
|
|
731
|
+
// trim/trimStart/trimEnd with type inference
|
|
732
|
+
const paddedStr = " hello ";
|
|
733
|
+
paddedStr.trim<typeof paddedStr>(); // type: 'hello'
|
|
734
|
+
paddedStr.trimStart<typeof paddedStr>(); // type: 'hello '
|
|
735
|
+
paddedStr.trimEnd<typeof paddedStr>(); // type: ' hello'
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
These enhanced types provide several benefits:
|
|
739
|
+
|
|
740
|
+
1. **Compile-Time Type Safety**:
|
|
741
|
+
|
|
742
|
+
- Catch type errors before runtime
|
|
743
|
+
- Get accurate type inference for method results
|
|
744
|
+
- Validate string operations at compile time
|
|
745
|
+
|
|
746
|
+
2. **Better IDE Support**:
|
|
747
|
+
|
|
748
|
+
- Improved autocompletion
|
|
749
|
+
- More accurate type hints
|
|
750
|
+
- Better refactoring support
|
|
751
|
+
|
|
752
|
+
3. **Type-Level String Manipulation**:
|
|
753
|
+
|
|
754
|
+
- Perform string operations at the type level
|
|
755
|
+
- Get literal type results for string operations
|
|
756
|
+
- Chain operations with type safety
|
|
757
|
+
|
|
758
|
+
4. **Advanced Type Features**:
|
|
759
|
+
- Generic type parameters for flexible usage
|
|
760
|
+
- Conditional type inference
|
|
761
|
+
- Template literal type support
|
|
762
|
+
|
|
763
|
+
```typescript
|
|
764
|
+
// Example of chaining operations with type safety
|
|
765
|
+
const result = "Hello, World!".toLowerCase<string>().replace<string, "hello", "hi">().split<string, " ">().join("-");
|
|
766
|
+
|
|
767
|
+
// TypeScript knows the exact type at each step
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
## Testing Utilities
|
|
771
|
+
|
|
772
|
+
The package includes specialized utilities for testing ANSI colored strings, making it easier to write tests for terminal output and colored text.
|
|
773
|
+
|
|
774
|
+
### ANSI String Formatting and Comparison
|
|
775
|
+
|
|
776
|
+
The `formatAnsiString` function helps format ANSI strings for test output, providing multiple representations:
|
|
777
|
+
|
|
778
|
+
```typescript
|
|
779
|
+
import { formatAnsiString } from "@visulima/string/test/utils";
|
|
780
|
+
import { red } from "@visulima/colorize";
|
|
781
|
+
|
|
782
|
+
const coloredText = red("Error message");
|
|
783
|
+
const formatted = formatAnsiString(coloredText);
|
|
784
|
+
|
|
785
|
+
// Returns an object with:
|
|
786
|
+
// - ansi: Original string with ANSI codes
|
|
787
|
+
// - stripped: String with ANSI codes removed
|
|
788
|
+
// - visible: String with escape codes shown as visible characters
|
|
789
|
+
// - json: JSON stringified version
|
|
790
|
+
// - lengthDifference: Difference between ANSI and stripped length
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
### Comparing ANSI Strings
|
|
794
|
+
|
|
795
|
+
The `compareAnsiStrings` function provides detailed comparison between two ANSI strings:
|
|
796
|
+
|
|
797
|
+
```typescript
|
|
798
|
+
import { compareAnsiStrings } from "@visulima/string/test/utils";
|
|
799
|
+
import { red, blue } from "@visulima/colorize";
|
|
800
|
+
|
|
801
|
+
const string1 = red("Error");
|
|
802
|
+
const string2 = blue("Error");
|
|
803
|
+
|
|
804
|
+
const result = compareAnsiStrings(string1, string2);
|
|
805
|
+
// Returns comparison details including:
|
|
806
|
+
// - ansiEqual: Whether the strings are identical including ANSI codes
|
|
807
|
+
// - strippedEqual: Whether the visible content is the same
|
|
808
|
+
// - summary: Length information and comparison results
|
|
809
|
+
// - actual/expected: Formatted representations of both strings
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
### Vitest Integration
|
|
813
|
+
|
|
814
|
+
The package includes a custom matcher for [Vitest](https://vitest.dev/) that makes testing ANSI strings straightforward:
|
|
815
|
+
|
|
816
|
+
```typescript
|
|
817
|
+
import { expect, describe, it } from "vitest";
|
|
818
|
+
import { toEqualAnsi } from "@visulima/string/test/vitest";
|
|
819
|
+
import { red, green } from "@visulima/colorize";
|
|
820
|
+
|
|
821
|
+
// Extend Vitest with the custom matcher
|
|
822
|
+
expect.extend({ toEqualAnsi });
|
|
823
|
+
|
|
824
|
+
describe("colored output tests", () => {
|
|
825
|
+
it("should display the correct error message", () => {
|
|
826
|
+
const actual = getErrorMessage(); // Returns colored string
|
|
827
|
+
const expected = red("Error: ") + green("File not found");
|
|
828
|
+
|
|
829
|
+
// Compare ANSI strings with detailed error messages on failure
|
|
830
|
+
expect(actual).toEqualAnsi(expected);
|
|
831
|
+
});
|
|
832
|
+
});
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
The custom matcher provides detailed error messages when tests fail, showing:
|
|
836
|
+
|
|
837
|
+
- The visible content of both strings
|
|
838
|
+
- The ANSI escape codes in both strings
|
|
839
|
+
- Whether the visible content matches but the colors differ
|
|
840
|
+
- Length information for both strings
|
|
841
|
+
|
|
842
|
+
## Related
|
|
843
|
+
|
|
844
|
+
- [change-case](https://github.com/blakeembrey/change-case) - Simple string case utilities
|
|
845
|
+
- [lodash](https://lodash.com/) - Comprehensive utility library with string manipulation
|
|
846
|
+
- [scule](https://github.com/unjs/scule) - 🧵 String Case Utils
|
|
847
|
+
- [case-anything](https://github.com/mesqueeb/case-anything) - camelCase, kebab-case, PascalCase... a simple integration with nano package size. (SMALL footprint!)
|
|
848
|
+
- [cli-truncate](https://github.com/sindresorhus/cli-truncate) - Truncate strings for terminal output
|
|
849
|
+
- [string-width](https://github.com/sindresorhus/string-width) - Measure string width
|
|
850
|
+
- [ansi-slice](https://github.com/sindresorhus/ansi-slice) - Slice strings with ANSI escape codes
|
|
851
|
+
- [fast-string-truncated-width](https://github.com/fabiospampinato/fast-string-truncated-width) - Fast string truncated width
|
|
852
|
+
- [ansi-truncate](https://github.com/fabiospampinato/ansi-truncate) - Truncate strings with ANSI escape codes
|
|
853
|
+
- [string-ts](https://github.com/gustavoguichard/string-ts) - Strongly typed string functions
|
|
854
|
+
|
|
855
|
+
## Supported Node.js Versions
|
|
856
|
+
|
|
857
|
+
Libraries in this ecosystem make the best effort to track [Node.js’ release schedule](https://github.com/nodejs/release#release-schedule).
|
|
858
|
+
Here’s [a post on why we think this is important](https://medium.com/the-node-js-collection/maintainers-should-consider-following-node-js-release-schedule-ab08ed4de71a).
|
|
859
|
+
|
|
860
|
+
## Contributing
|
|
861
|
+
|
|
862
|
+
If you would like to help, take a look at the [list of issues](https://github.com/visulima/visulima/issues) and check our [Contributing](.github/CONTRIBUTING.md) guidelines.
|
|
863
|
+
|
|
864
|
+
> **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.
|
|
865
|
+
|
|
866
|
+
## Credits
|
|
867
|
+
|
|
868
|
+
- [Daniel Bannert](https://github.com/prisis)
|
|
869
|
+
- [All Contributors](https://github.com/visulima/visulima/graphs/contributors)
|
|
870
|
+
|
|
871
|
+
## License
|
|
872
|
+
|
|
873
|
+
The visulima string is open-sourced software licensed under the [MIT][license-url]
|
|
874
|
+
|
|
875
|
+
[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
|
|
876
|
+
[typescript-url]: https://www.typescriptlang.org/ "TypeScript"
|
|
877
|
+
[license-image]: https://img.shields.io/npm/l/@visulima/string?color=blueviolet&style=for-the-badge
|
|
878
|
+
[license-url]: LICENSE.md "license"
|
|
879
|
+
[npm-image]: https://img.shields.io/npm/v/@visulima/string/latest.svg?style=for-the-badge&logo=npm
|
|
880
|
+
[npm-url]: https://www.npmjs.com/package/@visulima/string/v/latest "npm"
|