@visulima/string 3.0.0-alpha.3 → 3.0.0-alpha.5

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 CHANGED
@@ -1,3 +1,24 @@
1
+ ## @visulima/string [3.0.0-alpha.5](https://github.com/visulima/visulima/compare/@visulima/string@3.0.0-alpha.4...@visulima/string@3.0.0-alpha.5) (2025-12-27)
2
+
3
+ ### Bug Fixes
4
+
5
+ * **string:** update package files ([ca22f2e](https://github.com/visulima/visulima/commit/ca22f2efdd044ae20d421f729d185e1e93747cd8))
6
+
7
+
8
+ ### Dependencies
9
+
10
+ * **@visulima/colorize:** upgraded to 2.0.0-alpha.4
11
+
12
+ ## @visulima/string [3.0.0-alpha.4](https://github.com/visulima/visulima/compare/@visulima/string@3.0.0-alpha.3...@visulima/string@3.0.0-alpha.4) (2025-12-13)
13
+
14
+ ### Features
15
+
16
+ * add string analysis functions for counting occurrences and detecting text direction ([de84ab0](https://github.com/visulima/visulima/commit/de84ab072e36d3fc834dd8d971db46fd62be249a))
17
+
18
+ ### Miscellaneous Chores
19
+
20
+ * fixed project.json names and schema path ([964722f](https://github.com/visulima/visulima/commit/964722f691db205c7edb9aa6db29e849a647500b))
21
+
1
22
  ## @visulima/string [3.0.0-alpha.3](https://github.com/visulima/visulima/compare/@visulima/string@3.0.0-alpha.2...@visulima/string@3.0.0-alpha.3) (2025-12-11)
2
23
 
3
24
  ### Bug Fixes
package/README.md CHANGED
@@ -59,6 +59,10 @@
59
59
  - Handles template literals and string inputs
60
60
  - Normalizes newlines across platforms
61
61
  - Configurable trimming behavior
62
+ - **String Analysis**:
63
+ - `countOccurrences`: Count occurrences of a substring in a string
64
+ - `direction`: Detect text direction (left-to-right, right-to-left, or neutral)
65
+ - Unicode-aware and handles multi-byte characters
62
66
  - **String Width Calculation**:
63
67
  - `getStringWidth`: Calculate visual width of strings with Unicode support
64
68
  - `getStringTruncatedWidth`: Width calculation with smart truncation
@@ -392,6 +396,42 @@ splitByCase("🎉HappyBirthday🎂", {
392
396
  // ['🎉', 'Happy', 'Birthday', '🎂']
393
397
  ```
394
398
 
399
+ ### String Analysis
400
+
401
+ The `countOccurrences` function counts how many times a substring appears in a string, with full Unicode support:
402
+
403
+ ```typescript
404
+ import { countOccurrences } from "@visulima/string";
405
+
406
+ // Basic counting
407
+ countOccurrences("foo", "o"); // 2
408
+ countOccurrences("fo fooo fo", "o"); // 5
409
+
410
+ // Unicode support
411
+ countOccurrences("a🤔b🤔c", "🤔"); // 2
412
+
413
+ // Empty string handling
414
+ countOccurrences("", "f"); // 0
415
+ ```
416
+
417
+ The `direction` function detects text direction for internationalization:
418
+
419
+ ```typescript
420
+ import { direction } from "@visulima/string";
421
+
422
+ // Left-to-right languages
423
+ direction("english"); // 'ltr'
424
+ direction("Hello World"); // 'ltr'
425
+
426
+ // Right-to-left languages
427
+ direction("الجملة"); // 'rtl' (Arabic)
428
+ direction("נ"); // 'rtl' (Hebrew)
429
+
430
+ // Neutral (numbers, symbols, empty)
431
+ direction("123"); // 'neutral'
432
+ direction(""); // 'neutral'
433
+ ```
434
+
395
435
  ### Case Conversion Functions
396
436
 
397
437
  #### camelCase
package/dist/index.d.ts CHANGED
@@ -36,6 +36,10 @@ export { WordSimilaritySortOptions, wordSimilaritySort } from './word-similarity
36
36
  export { WordWrapOptions, WrapMode, wordWrap } from './word-wrap.js';
37
37
  import './packem_shared/lru-cache-B_t27XwI.js';
38
38
 
39
+ declare const countOccurrences: (value: string, substring: string) => number;
40
+
41
+ declare const direction: (value: string) => "rtl" | "ltr" | "neutral";
42
+
39
43
  declare const excerpt: (html: string, limit: number, options?: ExcerptOptions) => string;
40
44
  type ExcerptOptions = Omit<TruncateOptions, "position">;
41
45
 
@@ -48,5 +52,5 @@ declare const replaceString: (source: string, searches: OptionReplaceArray, igno
48
52
 
49
53
  declare const transliterate: (source: string, options?: OptionsTransliterate) => string;
50
54
 
51
- export { IntervalArray, OptionReplaceArray, OptionsTransliterate, TruncateOptions, closest, closestN, distance, excerpt, replaceString, transliterate };
55
+ export { IntervalArray, OptionReplaceArray, OptionsTransliterate, TruncateOptions, closest, closestN, countOccurrences, direction, distance, excerpt, replaceString, transliterate };
52
56
  export type { ExcerptOptions };
package/dist/index.js CHANGED
@@ -21,6 +21,8 @@ export { default as upperFirst } from './case/upper-first.js';
21
21
  export { default as joinSegments } from './packem_shared/joinSegments-B4H6oZ9b.js';
22
22
  export { closestString } from './closest-string.js';
23
23
  export { compareSimilarity } from './compare-similarity.js';
24
+ export { countOccurrences } from './packem_shared/countOccurrences-CS9XprQN.js';
25
+ export { direction } from './packem_shared/direction-BVhR8MOQ.js';
24
26
  export { excerpt } from './packem_shared/excerpt-GvmR4ESb.js';
25
27
  export { getStringTruncatedWidth } from './get-string-truncated-width.js';
26
28
  export { getStringWidth } from './get-string-width.js';
@@ -0,0 +1,22 @@
1
+ const countOccurrences = (value, substring) => {
2
+ const source = String(value);
3
+ if (typeof substring !== "string") {
4
+ throw new TypeError("Expected character");
5
+ }
6
+ if (substring.length === 0) {
7
+ throw new TypeError("Expected non-empty substring");
8
+ }
9
+ let count = 0;
10
+ let index = source.indexOf(substring);
11
+ while (index !== -1) {
12
+ count++;
13
+ const nextIndex = index + substring.length;
14
+ if (nextIndex <= index) {
15
+ break;
16
+ }
17
+ index = source.indexOf(substring, nextIndex);
18
+ }
19
+ return count;
20
+ };
21
+
22
+ export { countOccurrences };
@@ -0,0 +1,10 @@
1
+ const rtlRange = "֑-߿יִ-﷽ﹰ-ﻼ";
2
+ const ltrRange = "A-Za-zÀ-ÖØ-öø-ʸ̀-֐ࠀ-῿‎Ⰰ-﬜︀-﹯﻽-￿";
3
+ const rtl = new RegExp("^[^" + ltrRange + "]*[" + rtlRange + "]");
4
+ const ltr = new RegExp("^[^" + rtlRange + "]*[" + ltrRange + "]");
5
+ const direction = (value) => {
6
+ const source = String(value || "");
7
+ return rtl.test(source) ? "rtl" : ltr.test(source) ? "ltr" : "neutral";
8
+ };
9
+
10
+ export { direction };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@visulima/string",
3
- "version": "3.0.0-alpha.3",
3
+ "version": "3.0.0-alpha.5",
4
4
  "description": "Functions for manipulating strings.",
5
5
  "keywords": [
6
6
  "visulima",