@visulima/string 2.0.3 → 2.0.4
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 +15 -0
- package/dist/align-text.js +32 -2
- package/dist/case/camel-case.js +55 -1
- package/dist/case/capital-case.js +47 -1
- package/dist/case/constant-case.js +7 -1
- package/dist/case/dot-case.js +7 -1
- package/dist/case/flat-case.js +7 -1
- package/dist/case/flip-case.js +76 -1
- package/dist/case/identify-case.js +65 -1
- package/dist/case/index.js +20 -1
- package/dist/case/kebab-case.js +55 -1
- package/dist/case/lower-first.js +9 -1
- package/dist/case/no-case.js +7 -1
- package/dist/case/pascal-case.js +48 -1
- package/dist/case/pascal-snake-case.js +28 -1
- package/dist/case/path-case.js +7 -1
- package/dist/case/sentence-case.js +54 -1
- package/dist/case/snake-case.js +7 -1
- package/dist/case/split-by-case.js +644 -1
- package/dist/case/title-case.js +42 -1
- package/dist/case/train-case.js +40 -1
- package/dist/case/upper-first.js +9 -1
- package/dist/closest-string.js +25 -1
- package/dist/compare-similarity.js +12 -1
- package/dist/get-string-truncated-width.js +363 -1
- package/dist/get-string-width.js +5 -1
- package/dist/index.js +36 -1
- package/dist/outdent.js +118 -1
- package/dist/packem_shared/LRUCache-3r3SovvR.js +46 -0
- package/dist/packem_shared/closest-IHuSvnSn.js +139 -0
- package/dist/packem_shared/closestN-BREy4E7C.js +19 -0
- package/dist/packem_shared/constants-CTzC6Gjg.js +95 -0
- package/dist/packem_shared/generate-cache-key-DdayDfjV.js +6 -0
- package/dist/packem_shared/joinSegments-CuU8RGpP.js +44 -0
- package/dist/packem_shared/normalize-german-eszett-DEB8-ZPT.js +7 -0
- package/dist/packem_shared/replaceString-vYvRBMfC.js +190 -0
- package/dist/packem_shared/transliterate-CUF6Ggy_.js +47776 -0
- package/dist/slice.js +413 -1
- package/dist/slugify.js +46 -1
- package/dist/test/utils.js +88 -13
- package/dist/test/vitest.js +5 -1
- package/dist/truncate.js +89 -1
- package/dist/utilities.js +54 -1
- package/dist/word-similarity-sort.js +22 -1
- package/dist/word-wrap.js +637 -8
- package/package.json +1 -1
- package/dist/packem_shared/LRUCache-Bjo6gsbC.js +0 -1
- package/dist/packem_shared/closest-uarbVSbs.js +0 -1
- package/dist/packem_shared/closestN-DjNxwK0t.js +0 -1
- package/dist/packem_shared/constants-CDcaCZKC.js +0 -1
- package/dist/packem_shared/generate-cache-key-DPByFaMH.js +0 -1
- package/dist/packem_shared/joinSegments-DTGeJBZG.js +0 -1
- package/dist/packem_shared/normalize-german-eszett-4k1qRtEP.js +0 -1
- package/dist/packem_shared/replaceString-DwIZyvoL.js +0 -1
- package/dist/packem_shared/transliterate-CxVHmZUa.js +0 -5
package/dist/slice.js
CHANGED
|
@@ -1 +1,413 @@
|
|
|
1
|
-
|
|
1
|
+
import { a as RE_VALID_ANSI_PAIRS, b as RE_VALID_HYPERLINKS, c as RE_ANSI } from './packem_shared/constants-CTzC6Gjg.js';
|
|
2
|
+
import { getStringWidth } from './get-string-width.js';
|
|
3
|
+
import LRUCache from './packem_shared/LRUCache-3r3SovvR.js';
|
|
4
|
+
|
|
5
|
+
const segmentCache = new LRUCache(100);
|
|
6
|
+
const defaultSegmenter = new Intl.Segmenter("en", { granularity: "grapheme" });
|
|
7
|
+
const ANSI_RESET_CODES = /* @__PURE__ */ new Set([
|
|
8
|
+
"0",
|
|
9
|
+
// Full reset
|
|
10
|
+
"22",
|
|
11
|
+
// Bold reset
|
|
12
|
+
"23",
|
|
13
|
+
// Italic reset
|
|
14
|
+
"24",
|
|
15
|
+
// Underline reset
|
|
16
|
+
"27",
|
|
17
|
+
// Inverse reset
|
|
18
|
+
"28",
|
|
19
|
+
// Hidden reset
|
|
20
|
+
"29",
|
|
21
|
+
// Strikethrough reset
|
|
22
|
+
"39",
|
|
23
|
+
// Foreground color reset
|
|
24
|
+
"49"
|
|
25
|
+
// Background color reset
|
|
26
|
+
]);
|
|
27
|
+
const STYLE_CODES = /* @__PURE__ */ new Set([
|
|
28
|
+
"1",
|
|
29
|
+
// Bold
|
|
30
|
+
"3",
|
|
31
|
+
// Italic
|
|
32
|
+
"4",
|
|
33
|
+
// Underline
|
|
34
|
+
"7",
|
|
35
|
+
// Inverse
|
|
36
|
+
"8",
|
|
37
|
+
// Hidden
|
|
38
|
+
"9"
|
|
39
|
+
// Strikethrough
|
|
40
|
+
]);
|
|
41
|
+
const FORMAT_RESET_CODES = /* @__PURE__ */ new Map([
|
|
42
|
+
["22", "1"],
|
|
43
|
+
// Bold reset
|
|
44
|
+
["23", "3"],
|
|
45
|
+
// Italic reset
|
|
46
|
+
["24", "4"],
|
|
47
|
+
// Underline reset
|
|
48
|
+
["27", "7"],
|
|
49
|
+
// Inverse reset
|
|
50
|
+
["28", "8"],
|
|
51
|
+
// Hidden reset
|
|
52
|
+
["29", "9"]
|
|
53
|
+
// Strikethrough reset
|
|
54
|
+
]);
|
|
55
|
+
const isForegroundColor = (code) => code >= "30" && code <= "37" || code >= "90" && code <= "97" || code.startsWith("38;");
|
|
56
|
+
const isBackgroundColor = (code) => code >= "40" && code <= "47" || code >= "100" && code <= "107" || code.startsWith("48;");
|
|
57
|
+
const FORMAT_STYLES = [
|
|
58
|
+
{ close: "\x1B[22m", open: "\x1B[1m" },
|
|
59
|
+
// Bold
|
|
60
|
+
{ close: "\x1B[23m", open: "\x1B[3m" },
|
|
61
|
+
// Italic
|
|
62
|
+
{ close: "\x1B[24m", open: "\x1B[4m" },
|
|
63
|
+
// Underline
|
|
64
|
+
{ close: "\x1B[27m", open: "\x1B[7m" },
|
|
65
|
+
// Inverse
|
|
66
|
+
{ close: "\x1B[28m", open: "\x1B[8m" },
|
|
67
|
+
// Hidden
|
|
68
|
+
{ close: "\x1B[29m", open: "\x1B[9m" }
|
|
69
|
+
// Strikethrough
|
|
70
|
+
];
|
|
71
|
+
const findFirstPositionOfAny = (input, substrings) => {
|
|
72
|
+
let firstPos = -1;
|
|
73
|
+
for (const substring of substrings) {
|
|
74
|
+
const pos = input.indexOf(substring);
|
|
75
|
+
if (pos !== -1 && (firstPos === -1 || pos < firstPos)) {
|
|
76
|
+
firstPos = pos;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return firstPos;
|
|
80
|
+
};
|
|
81
|
+
const processIntoStyledSegments = (input, options) => {
|
|
82
|
+
if (!input.includes("\x1B")) {
|
|
83
|
+
return [
|
|
84
|
+
{
|
|
85
|
+
after: "",
|
|
86
|
+
before: "",
|
|
87
|
+
content: input,
|
|
88
|
+
visibleLength: getStringWidth(input, options.width)
|
|
89
|
+
}
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
const cachedResult = segmentCache.get(input);
|
|
93
|
+
if (cachedResult) {
|
|
94
|
+
return cachedResult;
|
|
95
|
+
}
|
|
96
|
+
const parts = [];
|
|
97
|
+
const matches = [];
|
|
98
|
+
let lastIndex = 0;
|
|
99
|
+
let match;
|
|
100
|
+
RE_ANSI.lastIndex = 0;
|
|
101
|
+
while ((match = RE_ANSI.exec(input)) !== null) {
|
|
102
|
+
const matchedText = match[0];
|
|
103
|
+
parts.push(input.slice(lastIndex, match.index));
|
|
104
|
+
matches.push(matchedText);
|
|
105
|
+
lastIndex = match.index + matchedText.length;
|
|
106
|
+
}
|
|
107
|
+
if (lastIndex < input.length) {
|
|
108
|
+
parts.push(input.slice(lastIndex));
|
|
109
|
+
}
|
|
110
|
+
const closingSequences = [];
|
|
111
|
+
for (const ansi of matches) {
|
|
112
|
+
if (ansi === "\x1B[0m" || ansi === "\x1B]8;;\x07") {
|
|
113
|
+
closingSequences.push(ansi);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (ansi.startsWith("\x1B[") && ansi.endsWith("m")) {
|
|
117
|
+
const code = ansi.slice(2, -1);
|
|
118
|
+
if (ANSI_RESET_CODES.has(code)) {
|
|
119
|
+
closingSequences.push(ansi);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const activeStylesSet = /* @__PURE__ */ new Set();
|
|
125
|
+
const segments = [];
|
|
126
|
+
for (const [index, text] of parts.entries()) {
|
|
127
|
+
if (index > 0 && matches[index - 1]) {
|
|
128
|
+
const ansi = matches[index - 1];
|
|
129
|
+
if (ansi === "\x1B[0m") {
|
|
130
|
+
activeStylesSet.clear();
|
|
131
|
+
} else if (ansi === "\x1B]8;;\x07") {
|
|
132
|
+
for (const style of activeStylesSet) {
|
|
133
|
+
if (style.startsWith("\x1B]8;;") && !style.endsWith("\x1B]8;;\x07")) {
|
|
134
|
+
activeStylesSet.delete(style);
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} else if (ansi.startsWith("\x1B[") && ansi.endsWith("m")) {
|
|
139
|
+
const code = ansi.slice(2, -1);
|
|
140
|
+
if (code === "39") {
|
|
141
|
+
for (const style of activeStylesSet) {
|
|
142
|
+
if (style.startsWith("\x1B[") && style.endsWith("m")) {
|
|
143
|
+
const styleCode = style.slice(2, -1);
|
|
144
|
+
if (isForegroundColor(styleCode)) {
|
|
145
|
+
activeStylesSet.delete(style);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
} else if (code === "49") {
|
|
151
|
+
for (const style of activeStylesSet) {
|
|
152
|
+
if (style.startsWith("\x1B[") && style.endsWith("m")) {
|
|
153
|
+
const styleCode = style.slice(2, -1);
|
|
154
|
+
if (isBackgroundColor(styleCode)) {
|
|
155
|
+
activeStylesSet.delete(style);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
} else if (FORMAT_RESET_CODES.has(code)) {
|
|
161
|
+
const targetCode = FORMAT_RESET_CODES.get(code);
|
|
162
|
+
for (const style of activeStylesSet) {
|
|
163
|
+
if (style === `\x1B[${targetCode}m`) {
|
|
164
|
+
activeStylesSet.delete(style);
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
} else {
|
|
169
|
+
if (isForegroundColor(code)) {
|
|
170
|
+
for (const style of activeStylesSet) {
|
|
171
|
+
if (style.startsWith("\x1B[") && style.endsWith("m")) {
|
|
172
|
+
const styleCode = style.slice(2, -1);
|
|
173
|
+
if (isForegroundColor(styleCode)) {
|
|
174
|
+
activeStylesSet.delete(style);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} else if (isBackgroundColor(code)) {
|
|
180
|
+
for (const style of activeStylesSet) {
|
|
181
|
+
if (style.startsWith("\x1B[") && style.endsWith("m")) {
|
|
182
|
+
const styleCode = style.slice(2, -1);
|
|
183
|
+
if (isBackgroundColor(styleCode)) {
|
|
184
|
+
activeStylesSet.delete(style);
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} else if (STYLE_CODES.has(code)) {
|
|
190
|
+
const targetStyle = `\x1B[${code}m`;
|
|
191
|
+
if (activeStylesSet.has(targetStyle)) {
|
|
192
|
+
activeStylesSet.delete(targetStyle);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
activeStylesSet.add(ansi);
|
|
196
|
+
}
|
|
197
|
+
} else if (ansi.startsWith("\x1B]8;;") && !ansi.endsWith("\x1B]8;;\x07")) {
|
|
198
|
+
for (const style of activeStylesSet) {
|
|
199
|
+
if (style.startsWith("\x1B]8;;") && !style.endsWith("\x1B]8;;\x07")) {
|
|
200
|
+
activeStylesSet.delete(style);
|
|
201
|
+
break;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
activeStylesSet.add(ansi);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (text === "") {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
let closingSequence = "";
|
|
211
|
+
if (activeStylesSet.size > 0) {
|
|
212
|
+
if (closingSequences.includes("\x1B[0m")) {
|
|
213
|
+
closingSequence = "\x1B[0m";
|
|
214
|
+
} else {
|
|
215
|
+
const closingParts = [];
|
|
216
|
+
let needsHyperlinkClose = false;
|
|
217
|
+
for (const style of activeStylesSet) {
|
|
218
|
+
if (style.startsWith("\x1B]8;;") && !style.endsWith("\x1B]8;;\x07")) {
|
|
219
|
+
needsHyperlinkClose = true;
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (needsHyperlinkClose && closingSequences.includes("\x1B]8;;\x07")) {
|
|
224
|
+
closingParts.push("\x1B]8;;\x07");
|
|
225
|
+
}
|
|
226
|
+
let needsForegroundClose = false;
|
|
227
|
+
let needsBackgroundClose = false;
|
|
228
|
+
for (const style of activeStylesSet) {
|
|
229
|
+
if (style.startsWith("\x1B[") && style.endsWith("m")) {
|
|
230
|
+
const styleCode = style.slice(2, -1);
|
|
231
|
+
if (isForegroundColor(styleCode)) {
|
|
232
|
+
needsForegroundClose = true;
|
|
233
|
+
} else if (isBackgroundColor(styleCode)) {
|
|
234
|
+
needsBackgroundClose = true;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (needsForegroundClose && needsBackgroundClose) {
|
|
239
|
+
const fgOpenPos = findFirstPositionOfAny(input, [
|
|
240
|
+
"\x1B[3",
|
|
241
|
+
// Basic colors 30-37
|
|
242
|
+
"\x1B[9",
|
|
243
|
+
// Bright colors 90-97
|
|
244
|
+
"\x1B[38;"
|
|
245
|
+
// RGB/256 colors
|
|
246
|
+
]);
|
|
247
|
+
const bgOpenPos = findFirstPositionOfAny(input, [
|
|
248
|
+
"\x1B[4",
|
|
249
|
+
// Basic colors 40-47
|
|
250
|
+
"\x1B[10",
|
|
251
|
+
// Bright colors 100-107
|
|
252
|
+
"\x1B[48;"
|
|
253
|
+
// RGB/256 colors
|
|
254
|
+
]);
|
|
255
|
+
if (fgOpenPos >= 0 && bgOpenPos >= 0) {
|
|
256
|
+
if (fgOpenPos > bgOpenPos) {
|
|
257
|
+
closingParts.push("\x1B[39m", "\x1B[49m");
|
|
258
|
+
} else {
|
|
259
|
+
closingParts.push("\x1B[49m", "\x1B[39m");
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
closingParts.push("\x1B[39m", "\x1B[49m");
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
if (needsForegroundClose) {
|
|
266
|
+
closingParts.push("\x1B[39m");
|
|
267
|
+
}
|
|
268
|
+
if (needsBackgroundClose) {
|
|
269
|
+
closingParts.push("\x1B[49m");
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (needsForegroundClose && needsBackgroundClose && input.includes("\x1B[39m") && input.includes("\x1B[49m")) {
|
|
273
|
+
if (input.indexOf("\x1B[39m") < input.indexOf("\x1B[49m")) {
|
|
274
|
+
closingParts.length -= needsForegroundClose && needsBackgroundClose ? 2 : needsForegroundClose || needsBackgroundClose ? 1 : 0;
|
|
275
|
+
closingParts.push("\x1B[39m", "\x1B[49m");
|
|
276
|
+
} else if (input.indexOf("\x1B[49m") < input.indexOf("\x1B[39m")) {
|
|
277
|
+
closingParts.length -= needsForegroundClose && needsBackgroundClose ? 2 : needsForegroundClose || needsBackgroundClose ? 1 : 0;
|
|
278
|
+
closingParts.push("\x1B[49m", "\x1B[39m");
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const activeFormatStyles = FORMAT_STYLES.filter((style) => activeStylesSet.has(style.open)).map((style) => {
|
|
282
|
+
return {
|
|
283
|
+
position: input.indexOf(style.open),
|
|
284
|
+
style
|
|
285
|
+
};
|
|
286
|
+
}).filter((item) => item.position >= 0).sort((a, b) => b.position - a.position);
|
|
287
|
+
for (const item of activeFormatStyles) {
|
|
288
|
+
if (closingSequences.includes(item.style.close)) {
|
|
289
|
+
closingParts.push(item.style.close);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
closingSequence = closingParts.join("");
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const activeStylesString = [...activeStylesSet].join("");
|
|
296
|
+
segments.push({
|
|
297
|
+
after: closingSequence,
|
|
298
|
+
before: activeStylesString,
|
|
299
|
+
content: text,
|
|
300
|
+
visibleLength: getStringWidth(text, {
|
|
301
|
+
fullWidth: 1,
|
|
302
|
+
wideWidth: 1
|
|
303
|
+
})
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
segmentCache.set(input, segments);
|
|
307
|
+
return segments;
|
|
308
|
+
};
|
|
309
|
+
const fastSlice = (inputString, startIndex, endIndex, options) => {
|
|
310
|
+
const graphemes = [...options.segmenter.segment(inputString)];
|
|
311
|
+
const result = [];
|
|
312
|
+
let currentWidth = 0;
|
|
313
|
+
let startFound = false;
|
|
314
|
+
for (const grapheme of graphemes) {
|
|
315
|
+
const graphemeWidth = getStringWidth(grapheme.segment, options.width);
|
|
316
|
+
const positionBefore = currentWidth;
|
|
317
|
+
const positionAfter = currentWidth + graphemeWidth;
|
|
318
|
+
if (!startFound) {
|
|
319
|
+
if (positionBefore < startIndex) {
|
|
320
|
+
currentWidth = positionAfter;
|
|
321
|
+
continue;
|
|
322
|
+
} else {
|
|
323
|
+
startFound = true;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (positionBefore >= endIndex) {
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
if (positionAfter > endIndex) {
|
|
330
|
+
break;
|
|
331
|
+
}
|
|
332
|
+
result.push(grapheme.segment);
|
|
333
|
+
currentWidth = positionAfter;
|
|
334
|
+
}
|
|
335
|
+
return result.join("");
|
|
336
|
+
};
|
|
337
|
+
const slice = (inputString, startIndex = 0, endIndex = Number.MAX_SAFE_INTEGER, options = {}) => {
|
|
338
|
+
const config = {
|
|
339
|
+
segmenter: defaultSegmenter,
|
|
340
|
+
...options
|
|
341
|
+
};
|
|
342
|
+
if (startIndex >= endIndex || inputString === "") {
|
|
343
|
+
return "";
|
|
344
|
+
}
|
|
345
|
+
const inputStringWidth = getStringWidth(inputString, config.width);
|
|
346
|
+
if (startIndex === 0 && endIndex >= inputStringWidth) {
|
|
347
|
+
return inputString;
|
|
348
|
+
}
|
|
349
|
+
if (startIndex < 0 || endIndex < 0) {
|
|
350
|
+
throw new RangeError("Negative indices aren't supported");
|
|
351
|
+
}
|
|
352
|
+
if (!inputString.includes("\x1B")) {
|
|
353
|
+
if (/^[\u0000-\u007F]*$/.test(inputString)) {
|
|
354
|
+
return inputString.slice(startIndex, endIndex);
|
|
355
|
+
}
|
|
356
|
+
return fastSlice(inputString, startIndex, endIndex, config);
|
|
357
|
+
}
|
|
358
|
+
RE_VALID_ANSI_PAIRS.lastIndex = 0;
|
|
359
|
+
RE_VALID_HYPERLINKS.lastIndex = 0;
|
|
360
|
+
if (!RE_VALID_ANSI_PAIRS.test(inputString) && !RE_VALID_HYPERLINKS.test(inputString)) {
|
|
361
|
+
return inputString;
|
|
362
|
+
}
|
|
363
|
+
const segments = processIntoStyledSegments(inputString, config);
|
|
364
|
+
if (segments.length === 0) {
|
|
365
|
+
return "";
|
|
366
|
+
}
|
|
367
|
+
let currentPos = 0;
|
|
368
|
+
const visibleSegments = [];
|
|
369
|
+
for (const [index, segment] of segments.entries()) {
|
|
370
|
+
const segmentStart = currentPos;
|
|
371
|
+
const segmentEnd = currentPos + segment.visibleLength;
|
|
372
|
+
if (segmentEnd > startIndex && segmentStart < endIndex) {
|
|
373
|
+
const visibleStart = Math.max(0, startIndex - segmentStart);
|
|
374
|
+
const visibleEnd = Math.min(segment.visibleLength, endIndex - segmentStart);
|
|
375
|
+
visibleSegments.push({
|
|
376
|
+
end: visibleEnd,
|
|
377
|
+
index,
|
|
378
|
+
segment,
|
|
379
|
+
start: visibleStart
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
currentPos = segmentEnd;
|
|
383
|
+
}
|
|
384
|
+
if (visibleSegments.length === 0) {
|
|
385
|
+
return "";
|
|
386
|
+
}
|
|
387
|
+
const resultParts = [];
|
|
388
|
+
const firstSegment = visibleSegments[0].segment;
|
|
389
|
+
resultParts.push(firstSegment.before);
|
|
390
|
+
for (let index = 0; index < visibleSegments.length; index++) {
|
|
391
|
+
const { end, segment, start } = visibleSegments[index];
|
|
392
|
+
if (start === 0 && end === segment.visibleLength) {
|
|
393
|
+
resultParts.push(segment.content);
|
|
394
|
+
} else {
|
|
395
|
+
const graphemes = [...config.segmenter.segment(segment.content)];
|
|
396
|
+
resultParts.push(
|
|
397
|
+
graphemes.slice(start, end).map((entry) => entry.segment).join("")
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
if (index < visibleSegments.length - 1) {
|
|
401
|
+
const nextSegment = visibleSegments[index + 1].segment;
|
|
402
|
+
const segmentAfter = segment.after;
|
|
403
|
+
const nextBefore = nextSegment.before;
|
|
404
|
+
if (segmentAfter !== nextBefore && (segmentAfter || nextBefore)) {
|
|
405
|
+
resultParts.push(segmentAfter, nextBefore);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
resultParts.push(visibleSegments.at(-1).segment.after);
|
|
410
|
+
return resultParts.join("");
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
export { slice };
|
package/dist/slugify.js
CHANGED
|
@@ -1 +1,46 @@
|
|
|
1
|
-
|
|
1
|
+
import transliterate from './packem_shared/transliterate-CUF6Ggy_.js';
|
|
2
|
+
import { escapeRegExp } from './utilities.js';
|
|
3
|
+
|
|
4
|
+
const removeDisallowedChars = (input, allowedChars, separator) => {
|
|
5
|
+
const escapedChars = escapeRegExp(allowedChars).replaceAll(String.raw`\-`, "-");
|
|
6
|
+
const pattern = new RegExp(`[^${escapedChars}]`, "g");
|
|
7
|
+
return input.replaceAll(pattern, separator);
|
|
8
|
+
};
|
|
9
|
+
const slugify = (input, options) => {
|
|
10
|
+
const config = {
|
|
11
|
+
allowedChars: "a-zA-Z0-9-_.~",
|
|
12
|
+
fixChineseSpacing: true,
|
|
13
|
+
ignore: [],
|
|
14
|
+
lowercase: true,
|
|
15
|
+
replaceAfter: [],
|
|
16
|
+
replaceBefore: [],
|
|
17
|
+
separator: "-",
|
|
18
|
+
transliterate: true,
|
|
19
|
+
trim: false,
|
|
20
|
+
unknown: "",
|
|
21
|
+
uppercase: false,
|
|
22
|
+
...options
|
|
23
|
+
};
|
|
24
|
+
if (config.lowercase && config.uppercase) {
|
|
25
|
+
console.warn("slugify: Both lowercase and uppercase options are true. Defaulting to lowercase.");
|
|
26
|
+
config.uppercase = false;
|
|
27
|
+
}
|
|
28
|
+
let slug = config.transliterate ? transliterate(input, config) : input.normalize("NFC");
|
|
29
|
+
if (config.lowercase) {
|
|
30
|
+
slug = slug.toLowerCase();
|
|
31
|
+
} else if (config.uppercase) {
|
|
32
|
+
slug = slug.toUpperCase();
|
|
33
|
+
}
|
|
34
|
+
slug = removeDisallowedChars(slug, config.allowedChars, config.separator);
|
|
35
|
+
const escapedSeparator = escapeRegExp(config.separator);
|
|
36
|
+
if (escapedSeparator) {
|
|
37
|
+
const separatorRegex = new RegExp(`${escapedSeparator}+`, "g");
|
|
38
|
+
slug = slug.replace(separatorRegex, config.separator);
|
|
39
|
+
}
|
|
40
|
+
if (escapedSeparator) {
|
|
41
|
+
return slug.replaceAll(new RegExp(`^${escapedSeparator}+|${escapedSeparator}+$`, "g"), "");
|
|
42
|
+
}
|
|
43
|
+
return slug;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export { slugify as default };
|
package/dist/test/utils.js
CHANGED
|
@@ -1,16 +1,91 @@
|
|
|
1
|
-
|
|
1
|
+
import { createRequire as __cjs_createRequire } from "node:module";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
- Visible content: %s
|
|
5
|
-
- With escape codes: %s
|
|
6
|
-
- JSON: %s
|
|
7
|
-
- Length: %d
|
|
3
|
+
const __cjs_require = __cjs_createRequire(import.meta.url);
|
|
8
4
|
|
|
9
|
-
|
|
10
|
-
- Visible content: %s
|
|
11
|
-
- With escape codes: %s
|
|
12
|
-
- JSON: %s
|
|
13
|
-
- Length: %d
|
|
5
|
+
const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
|
|
14
6
|
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
const __cjs_getBuiltinModule = (module) => {
|
|
8
|
+
// Check if we're in Node.js and version supports getBuiltinModule
|
|
9
|
+
if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
|
|
10
|
+
const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
|
|
11
|
+
// Node.js 20.16.0+ and 22.3.0+
|
|
12
|
+
if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
|
|
13
|
+
return __cjs_getProcess.getBuiltinModule(module);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// Fallback to createRequire
|
|
17
|
+
return __cjs_require(module);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
stripVTControlCharacters,
|
|
22
|
+
format
|
|
23
|
+
} = __cjs_getBuiltinModule("node:util");
|
|
24
|
+
import { getStringWidth } from '../get-string-width.js';
|
|
25
|
+
|
|
26
|
+
const formatAnsiString = (ansiString) => {
|
|
27
|
+
const stripped = stripVTControlCharacters(ansiString);
|
|
28
|
+
return {
|
|
29
|
+
// Original ANSI string
|
|
30
|
+
ansi: ansiString,
|
|
31
|
+
// JSON stringified version to see all escape codes
|
|
32
|
+
json: JSON.stringify(ansiString),
|
|
33
|
+
// Length difference between ANSI and stripped string
|
|
34
|
+
lengthDifference: ansiString.length - stripped.length,
|
|
35
|
+
// String with ANSI codes stripped
|
|
36
|
+
stripped,
|
|
37
|
+
// String with ANSI escape codes shown as visible characters
|
|
38
|
+
visible: ansiString.replaceAll("\x1B", String.raw`\u001B`)
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
const expectAnsiStrings = (actual, expected) => {
|
|
42
|
+
const actualFormatted = formatAnsiString(actual);
|
|
43
|
+
const expectedFormatted = formatAnsiString(expected);
|
|
44
|
+
return {
|
|
45
|
+
message: () => {
|
|
46
|
+
if (actual === expected) {
|
|
47
|
+
return "ANSI strings are identical";
|
|
48
|
+
}
|
|
49
|
+
const strippedEqual = actualFormatted.stripped === expectedFormatted.stripped;
|
|
50
|
+
return format(
|
|
51
|
+
"ANSI string comparison failed:\n\nActual:\n - Visible content: %s\n - With escape codes: %s\n - JSON: %s\n - Length: %d\n\nExpected:\n - Visible content: %s\n - With escape codes: %s\n - JSON: %s\n - Length: %d\n\n%s\n",
|
|
52
|
+
actualFormatted.stripped,
|
|
53
|
+
actualFormatted.visible,
|
|
54
|
+
actualFormatted.json,
|
|
55
|
+
getStringWidth(actualFormatted.ansi),
|
|
56
|
+
expectedFormatted.stripped,
|
|
57
|
+
expectedFormatted.visible,
|
|
58
|
+
expectedFormatted.json,
|
|
59
|
+
getStringWidth(expectedFormatted.ansi),
|
|
60
|
+
strippedEqual ? "✓ Visible content is identical, but escape codes differ" : "✗ Visible content differs"
|
|
61
|
+
);
|
|
62
|
+
},
|
|
63
|
+
pass: actual === expected
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
const compareAnsiStrings = (actual, expected) => {
|
|
67
|
+
const actualFormatted = formatAnsiString(actual);
|
|
68
|
+
const expectedFormatted = formatAnsiString(expected);
|
|
69
|
+
const strippedEqual = actualFormatted.stripped === expectedFormatted.stripped;
|
|
70
|
+
const ansiEqual = actual === expected;
|
|
71
|
+
return {
|
|
72
|
+
// Format information for both strings
|
|
73
|
+
actual: actualFormatted,
|
|
74
|
+
// Are the strings identical including ANSI codes?
|
|
75
|
+
ansiEqual,
|
|
76
|
+
expected: expectedFormatted,
|
|
77
|
+
// Are the strings identical when ANSI codes are stripped?
|
|
78
|
+
strippedEqual,
|
|
79
|
+
// Summary information
|
|
80
|
+
summary: {
|
|
81
|
+
actualLength: actual.length,
|
|
82
|
+
actualStrippedLength: actualFormatted.stripped.length,
|
|
83
|
+
ansiEqual,
|
|
84
|
+
expectedLength: expected.length,
|
|
85
|
+
expectedStrippedLength: expectedFormatted.stripped.length,
|
|
86
|
+
strippedEqual
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export { compareAnsiStrings, expectAnsiStrings, formatAnsiString };
|
package/dist/test/vitest.js
CHANGED
|
@@ -1 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { expectAnsiStrings } from './utils.js';
|
|
2
|
+
|
|
3
|
+
const toEqualAnsi = (actual, expected) => expectAnsiStrings(actual, expected);
|
|
4
|
+
|
|
5
|
+
export { toEqualAnsi };
|
package/dist/truncate.js
CHANGED
|
@@ -1 +1,89 @@
|
|
|
1
|
-
|
|
1
|
+
import { getStringTruncatedWidth } from './get-string-truncated-width.js';
|
|
2
|
+
import { slice } from './slice.js';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_ELLIPSIS = "…";
|
|
5
|
+
const MAX_SPACE_SEARCH_DISTANCE = 3;
|
|
6
|
+
const findNearestSpace = (string_, startIndex, searchRight = false) => {
|
|
7
|
+
if (string_.charAt(startIndex) === " ") {
|
|
8
|
+
return startIndex;
|
|
9
|
+
}
|
|
10
|
+
const direction = searchRight ? 1 : -1;
|
|
11
|
+
const limit = Math.min(MAX_SPACE_SEARCH_DISTANCE, searchRight ? string_.length - startIndex : startIndex);
|
|
12
|
+
for (let offset = 1; offset <= limit; offset++) {
|
|
13
|
+
const index = startIndex + offset * direction;
|
|
14
|
+
if (string_.charAt(index) === " ") {
|
|
15
|
+
return index;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return startIndex;
|
|
19
|
+
};
|
|
20
|
+
const truncate = (input, limit, options = {}) => {
|
|
21
|
+
if (typeof input !== "string") {
|
|
22
|
+
throw new TypeError(`Expected \`input\` to be a string, got ${typeof input}`);
|
|
23
|
+
}
|
|
24
|
+
if (typeof limit !== "number") {
|
|
25
|
+
throw new TypeError(`Expected \`limit\` to be a number, got ${typeof limit}`);
|
|
26
|
+
}
|
|
27
|
+
if (input === "" || limit <= 0) {
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
input = input.normalize("NFC");
|
|
31
|
+
const { ellipsis = DEFAULT_ELLIPSIS, position = "end", preferTruncationOnSpace = false } = options;
|
|
32
|
+
let ellipsisWidth = options.ellipsisWidth ?? ellipsis === DEFAULT_ELLIPSIS ? 1 : void 0;
|
|
33
|
+
if (ellipsisWidth === void 0) {
|
|
34
|
+
ellipsisWidth = getStringTruncatedWidth(ellipsis, {
|
|
35
|
+
...options.width,
|
|
36
|
+
ellipsis: "",
|
|
37
|
+
ellipsisWidth: 0,
|
|
38
|
+
limit: Number.POSITIVE_INFINITY
|
|
39
|
+
}).width;
|
|
40
|
+
}
|
|
41
|
+
const { width } = getStringTruncatedWidth(input, {
|
|
42
|
+
...options.width,
|
|
43
|
+
ellipsis,
|
|
44
|
+
ellipsisWidth
|
|
45
|
+
});
|
|
46
|
+
if (width <= limit) {
|
|
47
|
+
return input;
|
|
48
|
+
}
|
|
49
|
+
switch (position) {
|
|
50
|
+
case "end": {
|
|
51
|
+
if (preferTruncationOnSpace) {
|
|
52
|
+
const nearestSpace = findNearestSpace(input, limit - 1);
|
|
53
|
+
return slice(input, 0, nearestSpace, {
|
|
54
|
+
width: options.width
|
|
55
|
+
}) + ellipsis;
|
|
56
|
+
}
|
|
57
|
+
return slice(input, 0, limit - ellipsisWidth, {
|
|
58
|
+
width: options.width
|
|
59
|
+
}) + ellipsis;
|
|
60
|
+
}
|
|
61
|
+
case "middle": {
|
|
62
|
+
const half = Math.floor(limit / 2);
|
|
63
|
+
if (preferTruncationOnSpace) {
|
|
64
|
+
const firstBreak = findNearestSpace(input, half);
|
|
65
|
+
const secondBreak = findNearestSpace(input, width - (limit - half) + 1, true);
|
|
66
|
+
return slice(input, 0, firstBreak) + ellipsis + slice(input, secondBreak, width).trim();
|
|
67
|
+
}
|
|
68
|
+
return slice(input, 0, half, {
|
|
69
|
+
width: options.width
|
|
70
|
+
}) + ellipsis + slice(input, width - (limit - half) + ellipsisWidth, width, {
|
|
71
|
+
width: options.width
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
case "start": {
|
|
75
|
+
if (preferTruncationOnSpace) {
|
|
76
|
+
const nearestSpace = findNearestSpace(input, width - limit + 1, true);
|
|
77
|
+
return ellipsis + slice(input, nearestSpace, width).trim();
|
|
78
|
+
}
|
|
79
|
+
return ellipsis + slice(input, width - limit + ellipsisWidth, width, {
|
|
80
|
+
width: options.width
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
default: {
|
|
84
|
+
throw new Error(`Invalid position: expected 'start', 'middle' or 'end', got '${position}'`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export { truncate };
|