import stripAnsi from 'strip-ansi'; import eastAsianWidth from 'eastasianwidth'; import emojiRegex from 'emoji-regex'; export default function stringWidth(string, options = {}) { if (typeof string !== 'string' || string.length === 0) { return 0; } options = { ambiguousIsNarrow: true, ...options }; string = stripAnsi(string); if (string.length === 0) { return 0; } string = string.replace(emojiRegex(), ' '); const ambiguousCharacterWidth = options.ambiguousIsNarrow ? 1 : 2; let width = 0; for (const character of string) { const codePoint = character.codePointAt(0); // Ignore control characters if (codePoint <= 0x1F || (codePoint >= 0x7F && codePoint <= 0x9F)) { continue; } // Ignore combining characters if (codePoint >= 0x300 && codePoint <= 0x36F) { continue; } const code = eastAsianWidth.eastAsianWidth(character); switch (code) { case 'F': case 'W': width += 2; break; case 'A': width += ambiguousCharacterWidth; break; default: width += 1; } } return width; }