diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1ed55d5..346585c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,10 +12,9 @@ jobs: node-version: - 20 - 18 - - 16 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm install diff --git a/index.d.ts b/index.d.ts index 71ecc24..459f9b8 100644 --- a/index.d.ts +++ b/index.d.ts @@ -3,6 +3,9 @@ export type Options = { Count [ambiguous width characters](https://www.unicode.org/reports/tr11/#Ambiguous) as having narrow width (count of 1) instead of wide width (count of 2). @default true + + > Ambiguous characters behave like wide or narrow characters depending on the context (language tag, script identification, associated font, source of data, or explicit markup; all can provide the context). __If the context cannot be established reliably, they should be treated as narrow characters by default.__ + > - http://www.unicode.org/reports/tr11/ */ readonly ambiguousIsNarrow?: boolean; diff --git a/index.js b/index.js index 62f995a..4e6906e 100644 --- a/index.js +++ b/index.js @@ -1,19 +1,18 @@ import stripAnsi from 'strip-ansi'; -import eastAsianWidth from 'eastasianwidth'; +import {eastAsianWidth} from 'get-east-asian-width'; import emojiRegex from 'emoji-regex'; -export default function stringWidth(string, options) { +export default function stringWidth(string, options = {}) { if (typeof string !== 'string' || string.length === 0) { return 0; } - options = { - ambiguousIsNarrow: true, - countAnsiEscapeCodes: false, - ...options, - }; + const { + ambiguousIsNarrow = true, + countAnsiEscapeCodes = false, + } = options; - if (!options.countAnsiEscapeCodes) { + if (!countAnsiEscapeCodes) { string = stripAnsi(string); } @@ -21,7 +20,6 @@ export default function stringWidth(string, options) { return 0; } - const ambiguousCharacterWidth = options.ambiguousIsNarrow ? 1 : 2; let width = 0; for (const {segment: character} of new Intl.Segmenter().segment(string)) { @@ -42,23 +40,7 @@ export default function stringWidth(string, options) { continue; } - const code = eastAsianWidth.eastAsianWidth(character); - switch (code) { - case 'F': - case 'W': { - width += 2; - break; - } - - case 'A': { - width += ambiguousCharacterWidth; - break; - } - - default: { - width += 1; - } - } + width += eastAsianWidth(codePoint, {ambiguousAsWide: !ambiguousIsNarrow}); } return width; diff --git a/package.json b/package.json index 778914a..9a73ee5 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,12 @@ "url": "https://sindresorhus.com" }, "type": "module", - "exports": "./index.js", + "exports": { + "types": "./index.d.ts", + "default": "./index.js" + }, "engines": { - "node": ">=16" + "node": ">=18" }, "scripts": { "test": "xo && ava && tsd" @@ -44,16 +47,17 @@ "chinese", "japanese", "korean", - "fixed-width" + "fixed-width", + "east-asian-width" ], "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^10.2.1", - "strip-ansi": "^7.0.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "devDependencies": { - "ava": "^5.2.0", - "tsd": "^0.28.1", - "xo": "^0.54.2" + "ava": "^5.3.1", + "tsd": "^0.29.0", + "xo": "^0.56.0" } } diff --git a/readme.md b/readme.md index 49931c9..1cccd00 100644 --- a/readme.md +++ b/readme.md @@ -48,6 +48,9 @@ Default: `true` Count [ambiguous width characters](https://www.unicode.org/reports/tr11/#Ambiguous) as having narrow width (count of 1) instead of wide width (count of 2). +> Ambiguous characters behave like wide or narrow characters depending on the context (language tag, script identification, associated font, source of data, or explicit markup; all can provide the context). **If the context cannot be established reliably, they should be treated as narrow characters by default.** +> - http://www.unicode.org/reports/tr11/ + ##### countAnsiEscapeCodes Type: `boolean`\ @@ -60,3 +63,4 @@ Whether [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) shou - [string-width-cli](https://github.com/sindresorhus/string-width-cli) - CLI for this module - [string-length](https://github.com/sindresorhus/string-length) - Get the real length of a string - [widest-line](https://github.com/sindresorhus/widest-line) - Get the visual width of the widest line in a string +- [get-east-asian-width](https://github.com/sindresorhus/get-east-asian-width) - Determine the East Asian Width of a Unicode character diff --git a/test.js b/test.js index f82b060..e61dc94 100644 --- a/test.js +++ b/test.js @@ -2,6 +2,7 @@ import test from 'ava'; import stringWidth from './index.js'; test('main', t => { + t.is(stringWidth('⛣', {ambiguousIsNarrow: false}), 2); t.is(stringWidth('abcde'), 5); t.is(stringWidth('古池や'), 6); t.is(stringWidth('あいうabc'), 9); @@ -22,6 +23,7 @@ test('main', t => { t.is(stringWidth('\u{845B}\u{E0100}'), 2, 'Variation Selectors'); t.is(stringWidth('ปฏัก'), 3, 'Thai script'); t.is(stringWidth('_\u0E34'), 1, 'Thai script'); + t.is(stringWidth('“', {ambiguousIsNarrow: false}), 2); }); test('ignores control characters', t => {