@suzukihayate/graphemes 0.1.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/LICENSE +21 -0
- package/README.md +43 -0
- package/index.d.ts +17 -0
- package/index.js +65 -0
- package/package.json +17 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hayate Suzuki (้ดๆจ้ขฏ)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# graphemes
|
|
2
|
+
|
|
3
|
+
Emoji- and grapheme-aware string helpers, so `length`, `slice`, `truncate` and `reverse` don't break emoji, flags, skin tones or ZWJ sequences (๐จโ๐ฉโ๐งโ๐ฆ).
|
|
4
|
+
|
|
5
|
+
In JavaScript, `'a๐b'.length` is `4`, and naive `slice`/`reverse` can split an emoji in half. `graphemes` counts and cuts by **user-perceived characters** using `Intl.Segmenter` (with a code-point fallback). Zero dependencies.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
```bash
|
|
9
|
+
npm install @suzukihayate/graphemes
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
```js
|
|
14
|
+
import { length, slice, truncate, reverse, toGraphemes, at } from '@suzukihayate/graphemes';
|
|
15
|
+
|
|
16
|
+
length('a๐b'); // 3 (native .length is 4)
|
|
17
|
+
length('๐จโ๐ฉโ๐งโ๐ฆ'); // 1 (a single ZWJ grapheme)
|
|
18
|
+
|
|
19
|
+
slice('a๐b๐c', 1, 3); // "๐b"
|
|
20
|
+
reverse('a๐b'); // "b๐a" (emoji stays intact)
|
|
21
|
+
truncate('a๐b๐c', 3); // "a๐โฆ" (no broken emoji)
|
|
22
|
+
at('a๐b', 1); // "๐"
|
|
23
|
+
|
|
24
|
+
toGraphemes('a๐b'); // ["a", "๐", "b"]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## API
|
|
28
|
+
- `toGraphemes(str)` โ `string[]`
|
|
29
|
+
- `length(str)` โ grapheme count
|
|
30
|
+
- `slice(str, start?, end?)` โ grapheme-based slice (negative indices ok)
|
|
31
|
+
- `at(str, index)` โ grapheme at index (supports negative)
|
|
32
|
+
- `reverse(str)` โ reverse by graphemes
|
|
33
|
+
- `truncate(str, max, { ellipsis? })` โ truncate to โค `max` graphemes incl. ellipsis
|
|
34
|
+
|
|
35
|
+
> Uses `Intl.Segmenter` when available (Node 16+, modern browsers). Falls back to code points otherwise.
|
|
36
|
+
|
|
37
|
+
## Test
|
|
38
|
+
```bash
|
|
39
|
+
node --test
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
MIT ยฉ 2026 Hayate Suzuki
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Split a string into grapheme clusters (user-perceived characters). */
|
|
2
|
+
export function toGraphemes(str: string): string[];
|
|
3
|
+
/** Number of grapheme clusters. */
|
|
4
|
+
export function length(str: string): number;
|
|
5
|
+
/** Slice by grapheme index (negative indices count from the end). */
|
|
6
|
+
export function slice(str: string, start?: number, end?: number): string;
|
|
7
|
+
/** Grapheme at the given index (supports negative). */
|
|
8
|
+
export function at(str: string, index: number): string | undefined;
|
|
9
|
+
/** Reverse a string by graphemes. */
|
|
10
|
+
export function reverse(str: string): string;
|
|
11
|
+
/** Truncate to at most `max` graphemes (including the ellipsis). */
|
|
12
|
+
export function truncate(str: string, max: number, opts?: { ellipsis?: string }): string;
|
|
13
|
+
declare const _default: {
|
|
14
|
+
toGraphemes: typeof toGraphemes; length: typeof length; slice: typeof slice;
|
|
15
|
+
at: typeof at; reverse: typeof reverse; truncate: typeof truncate;
|
|
16
|
+
};
|
|
17
|
+
export default _default;
|
package/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// graphemes โ Emoji- and grapheme-aware string helpers, so length/slice/truncate
|
|
2
|
+
// don't break emoji, flags, skin tones or ZWJ sequences (๐จโ๐ฉโ๐งโ๐ฆ).
|
|
3
|
+
// Uses Intl.Segmenter when available, with a code-point fallback. Zero dependencies.
|
|
4
|
+
|
|
5
|
+
const hasSegmenter = typeof Intl !== 'undefined' && typeof Intl.Segmenter === 'function';
|
|
6
|
+
const segmenter = hasSegmenter ? new Intl.Segmenter(undefined, { granularity: 'grapheme' }) : null;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Split a string into an array of grapheme clusters (user-perceived characters).
|
|
10
|
+
* @param {string} str
|
|
11
|
+
* @returns {string[]}
|
|
12
|
+
*/
|
|
13
|
+
export function toGraphemes(str) {
|
|
14
|
+
const s = String(str);
|
|
15
|
+
if (segmenter) {
|
|
16
|
+
const out = [];
|
|
17
|
+
for (const { segment } of segmenter.segment(s)) out.push(segment);
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
// Fallback: code points (handles surrogate pairs, but not full ZWJ clusters)
|
|
21
|
+
return Array.from(s);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Number of grapheme clusters (what a human would count as characters). */
|
|
25
|
+
export function length(str) {
|
|
26
|
+
return toGraphemes(str).length;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Slice by grapheme index (negative indices count from the end).
|
|
31
|
+
* @param {string} str
|
|
32
|
+
* @param {number} [start]
|
|
33
|
+
* @param {number} [end]
|
|
34
|
+
*/
|
|
35
|
+
export function slice(str, start, end) {
|
|
36
|
+
return toGraphemes(str).slice(start, end).join('');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Grapheme at the given index (supports negative). */
|
|
40
|
+
export function at(str, index) {
|
|
41
|
+
const g = toGraphemes(str);
|
|
42
|
+
const i = index < 0 ? g.length + index : index;
|
|
43
|
+
return g[i];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Reverse a string by graphemes (keeps emoji/ZWJ sequences intact). */
|
|
47
|
+
export function reverse(str) {
|
|
48
|
+
return toGraphemes(str).reverse().join('');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Truncate to at most `max` graphemes (including the ellipsis).
|
|
53
|
+
* @param {string} str
|
|
54
|
+
* @param {number} max total grapheme budget
|
|
55
|
+
* @param {{ ellipsis?: string }} [opts]
|
|
56
|
+
*/
|
|
57
|
+
export function truncate(str, max, opts = {}) {
|
|
58
|
+
const { ellipsis = 'โฆ' } = opts;
|
|
59
|
+
const g = toGraphemes(str);
|
|
60
|
+
if (g.length <= max) return str;
|
|
61
|
+
const keep = Math.max(0, max - length(ellipsis));
|
|
62
|
+
return g.slice(0, keep).join('') + ellipsis;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export default { toGraphemes, length, slice, at, reverse, truncate };
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@suzukihayate/graphemes",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Emoji- and grapheme-aware string helpers (length, slice, truncate, reverse) using Intl.Segmenter. Zero dependencies.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"types": "index.d.ts",
|
|
8
|
+
"exports": { ".": { "types": "./index.d.ts", "default": "./index.js" } },
|
|
9
|
+
"files": ["index.js", "index.d.ts", "README.md", "LICENSE"],
|
|
10
|
+
"scripts": { "test": "node --test" },
|
|
11
|
+
"keywords": ["grapheme", "emoji", "unicode", "string", "length", "truncate", "slice", "segmenter", "zero-dependency"],
|
|
12
|
+
"repository": { "type": "git", "url": "git+https://github.com/HaYaTedonn/graphemes.git" },
|
|
13
|
+
"author": "Hayate Suzuki",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"engines": { "node": ">=18" },
|
|
16
|
+
"sideEffects": false
|
|
17
|
+
}
|