* Fallback JavaScript implementation of stringWidth when Bun.stringWidth is not available. * * Get the display width of a string as it would appear in a terminal. * * This is a more accurate alternative to the string-width package that correctly handles * characters like ⚠ (U+26A0) which string
(str: string)
| 18 | * recommended by the Unicode standard for Western contexts. |
| 19 | */ |
| 20 | function stringWidthJavaScript(str: string): number { |
| 21 | if (typeof str !== 'string' || str.length === 0) { |
| 22 | return 0 |
| 23 | } |
| 24 | |
| 25 | // Fast path: pure ASCII string (no ANSI codes, no wide chars) |
| 26 | let isPureAscii = true |
| 27 | for (let i = 0; i < str.length; i++) { |
| 28 | const code = str.charCodeAt(i) |
| 29 | // Check for non-ASCII or ANSI escape (0x1b) |
| 30 | if (code >= 127 || code === 0x1b) { |
| 31 | isPureAscii = false |
| 32 | break |
| 33 | } |
| 34 | } |
| 35 | if (isPureAscii) { |
| 36 | // Count printable characters (exclude control chars) |
| 37 | let width = 0 |
| 38 | for (let i = 0; i < str.length; i++) { |
| 39 | const code = str.charCodeAt(i) |
| 40 | if (code > 0x1f) { |
| 41 | width++ |
| 42 | } |
| 43 | } |
| 44 | return width |
| 45 | } |
| 46 | |
| 47 | // Strip ANSI if escape character is present |
| 48 | if (str.includes('\x1b')) { |
| 49 | str = stripAnsi(str) |
| 50 | if (str.length === 0) { |
| 51 | return 0 |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | // Fast path: simple Unicode (no emoji, variation selectors, or joiners) |
| 56 | if (!needsSegmentation(str)) { |
| 57 | let width = 0 |
| 58 | for (const char of str) { |
| 59 | const codePoint = char.codePointAt(0)! |
| 60 | if (!isZeroWidth(codePoint)) { |
| 61 | width += eastAsianWidth(codePoint, { ambiguousAsWide: false }) |
| 62 | } |
| 63 | } |
| 64 | return width |
| 65 | } |
| 66 | |
| 67 | let width = 0 |
| 68 | |
| 69 | for (const { segment: grapheme } of getGraphemeSegmenter().segment(str)) { |
| 70 | // Check for emoji first (most emoji sequences are width 2) |
| 71 | EMOJI_REGEX.lastIndex = 0 |
| 72 | if (EMOJI_REGEX.test(grapheme)) { |
| 73 | width += getEmojiWidth(grapheme) |
| 74 | continue |
| 75 | } |
| 76 | |
| 77 | // Calculate width for non-emoji graphemes |
nothing calls this directly
no test coverage detected