| import stripAnsi from 'strip-ansi'; | |
| import {eastAsianWidth} from 'get-east-asian-width'; | |
| import emojiRegex from 'emoji-regex'; | |
| const segmenter = new Intl.Segmenter(); | |
| const defaultIgnorableCodePointRegex = /^\p{Default_Ignorable_Code_Point}$/u; | |
| export default function stringWidth(string, options = {}) { | |
| if (typeof string !== 'string' || string.length === 0) { | |
| return 0; | |
| } | |
| const { | |
| ambiguousIsNarrow = true, | |
| countAnsiEscapeCodes = false, | |
| } = options; | |
| if (!countAnsiEscapeCodes) { | |
| string = stripAnsi(string); | |
| } | |
| if (string.length === 0) { | |
| return 0; | |
| } | |
| let width = 0; | |
| const eastAsianWidthOptions = {ambiguousAsWide: !ambiguousIsNarrow}; | |
| for (const {segment: character} of segmenter.segment(string)) { | |
| const codePoint = character.codePointAt(0); | |
| // Ignore control characters | |
| if (codePoint <= 0x1F || (codePoint >= 0x7F && codePoint <= 0x9F)) { | |
| continue; | |
| } | |
| // Ignore zero-width characters | |
| if ( | |
| (codePoint >= 0x20_0B && codePoint <= 0x20_0F) // Zero-width space, non-joiner, joiner, left-to-right mark, right-to-left mark | |
| || codePoint === 0xFE_FF // Zero-width no-break space | |
| ) { | |
| continue; | |
| } | |
| // Ignore combining characters | |
| if ( | |
| (codePoint >= 0x3_00 && codePoint <= 0x3_6F) // Combining diacritical marks | |
| || (codePoint >= 0x1A_B0 && codePoint <= 0x1A_FF) // Combining diacritical marks extended | |
| || (codePoint >= 0x1D_C0 && codePoint <= 0x1D_FF) // Combining diacritical marks supplement | |
| || (codePoint >= 0x20_D0 && codePoint <= 0x20_FF) // Combining diacritical marks for symbols | |
| || (codePoint >= 0xFE_20 && codePoint <= 0xFE_2F) // Combining half marks | |
| ) { | |
| continue; | |
| } | |
| // Ignore surrogate pairs | |
| if (codePoint >= 0xD8_00 && codePoint <= 0xDF_FF) { | |
| continue; | |
| } | |
| // Ignore variation selectors | |
| if (codePoint >= 0xFE_00 && codePoint <= 0xFE_0F) { | |
| continue; | |
| } | |
| // This covers some of the above cases, but we still keep them for performance reasons. | |
| if (defaultIgnorableCodePointRegex.test(character)) { | |
| continue; | |
| } | |
| // TODO: Use `/\p{RGI_Emoji}/v` when targeting Node.js 20. | |
| if (emojiRegex().test(character)) { | |
| width += 2; | |
| continue; | |
| } | |
| width += eastAsianWidth(codePoint, eastAsianWidthOptions); | |
| } | |
| return width; | |
| } | |
Xet Storage Details
- Size:
- 2.23 kB
- Xet hash:
- 6f4f516c1592ee12aaeb924164bc690d2aae0515fa1118decbadac3c02b1badb
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.