grapheme-conformance 0.1.3 → 0.1.5

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/README.md CHANGED
@@ -45,10 +45,22 @@ Cases passed:
45
45
  | `grapheme-splitter@1.0.4` | 1175 | 1081 |
46
46
  | `runes2@1.1.4` | 730 | 695 |
47
47
 
48
- `Intl.Segmenter`'s single failure is the input `2701 200D 2701`, a known ICU
49
- deviation and one that arrived with a later ICU rather than an earlier one.
50
- Node 18.20.8 ships an ICU without it and passes every case (`1187` and `1093`);
51
- Node 20 and 22 split that input and score `1186` and `1092`.
48
+ The one case `Intl.Segmenter` misses in both columns is `2701 200D 2701`, and
49
+ it is an artefact of the answer key, not a bug in ICU. Unicode 17.0 removed
50
+ U+2701 from `Extended_Pictographic`, so GB11 no longer applies and the correct
51
+ result became **two** clusters. Scored against the `17.0.0` key, ICU on Node 22
52
+ passes every case:
53
+
54
+ | implementation | U17.0.0 (766) |
55
+ |---|---|
56
+ | `Intl.Segmenter` (ICU, Node 22) | 766 |
57
+ | `unicode-segmenter@0.17.3` | 766 |
58
+ | `graphemer@1.4.0` | 749 |
59
+ | `grapheme-splitter@1.0.4` | 746 |
60
+ | `runes2@1.1.4` | 501 |
61
+
62
+ Score an implementation against the key it targets. A current segmenter judged
63
+ by a superseded key reports failures it does not have.
52
64
 
53
65
  ## Install
54
66
 
@@ -62,23 +74,20 @@ Zero runtime dependencies. Dual ESM/CJS. Node 18+.
62
74
 
63
75
  ```sh
64
76
  npx grapheme-conformance --module unicode-segmenter/grapheme \
65
- --export splitGraphemes --version 16.0.0
77
+ --export splitGraphemes
66
78
  ```
67
79
 
68
80
  ```
69
- unicode-segmenter/grapheme (splitGraphemes) GraphemeBreakTest 16.0.0
70
- passed 1092/1093 99.91%
71
- failed 1
72
-
73
- line input want got rule
74
- 1105 2701 200D 2701 1 2 -
81
+ unicode-segmenter/grapheme (splitGraphemes) GraphemeBreakTest 17.0.0
82
+ passed 766/766 100.00%
83
+ failed 0
75
84
  ```
76
85
 
77
86
  | flag | default | meaning |
78
87
  |---|---|---|
79
88
  | `--module` | required | module to load: a bare name, or a path relative to cwd |
80
89
  | `--export` | `default` | the export to score |
81
- | `--version` | `16.0.0` | vendored vectors: `15.0.0`, `15.1.0`, `16.0.0`, `17.0.0` |
90
+ | `--version` | `17.0.0` | vendored vectors: `15.0.0`, `15.1.0`, `16.0.0`, `17.0.0` |
82
91
  | `--min` | `1.0` | minimum pass rate before exiting non-zero |
83
92
  | `--limit` | `10` | failing cases to print |
84
93
 
@@ -94,13 +103,13 @@ import { parseBreakTest, score, vectors } from 'grapheme-conformance';
94
103
  const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
95
104
  const report = score(
96
105
  (s) => [...segmenter.segment(s)].map((part) => part.segment),
97
- vectors['16.0.0'],
106
+ vectors['17.0.0'],
98
107
  );
99
108
 
100
- report.passed; // 1092
101
- report.total; // 1093
102
- report.rate; // 0.999085...
103
- report.failures[0].inputHex; // '2701 200D 2701'
109
+ report.passed; // 766
110
+ report.total; // 766
111
+ report.rate; // 1
112
+ report.failures; // []
104
113
  ```
105
114
 
106
115
  ```ts
@@ -149,9 +158,12 @@ UAX #29 and does not prove which rule an implementation actually got wrong.
149
158
  ## Intl.Segmenter and the host ICU
150
159
 
151
160
  `Intl.Segmenter` is scored against whatever ICU the host Node ships, so its row
152
- moves with the runtime, and not monotonically: Node 18.20.8 scores higher than
153
- Node 22 because the `2701 200D 2701` deviation is a newer ICU behaviour. The
154
- pure-JS libraries are pinned to exact versions and score identically everywhere.
161
+ moves with the runtime. Against the older keys the movement is not even
162
+ monotonic: Node 18.20.8 scores `1187` and `1093` where Node 20 and 22 score
163
+ `1186` and `1092`, because newer ICU implements the Unicode 17.0 property
164
+ change that those keys predate. Against the `17.0.0` key every one of them is
165
+ correct. The pure-JS libraries are pinned to exact versions and score
166
+ identically everywhere.
155
167
 
156
168
  ## Verifying this build
157
169
 
package/VERIFY.md CHANGED
@@ -50,18 +50,25 @@ Expected output:
50
50
 
51
51
  `Intl.Segmenter` is not a fixed library. It is scored against whatever ICU the
52
52
  host Node ships, so its row moves with the runtime. Measured across the CI
53
- matrix:
53
+ matrix, against these two older keys:
54
54
 
55
55
  | runtime | 15.1.0 | 16.0.0 | `2701 200D 2701` |
56
56
  |---|---|---|---|
57
- | Node 22.22.2 (ICU 78.2) | 1186 | 1092 | split, the known deviation |
58
- | Node 20.x | 1186 | 1092 | split |
59
- | Node 18.20.8 | 1187 | 1093 | not split |
57
+ | Node 22.22.2 (ICU 78.2) | 1186 | 1092 | two clusters |
58
+ | Node 20.x | 1186 | 1092 | two clusters |
59
+ | Node 18.20.8 | 1187 | 1093 | one cluster |
60
60
 
61
61
  Run the command above on Node 22 or 20 to get the expected output above. On
62
- Node 18 the `Intl.Segmenter` value reads `1187` and `1093` instead: that ICU
63
- predates the deviation and passes every case. The other four values are
64
- pinned to exact library versions and hold identically on every Node.
62
+ Node 18 the `Intl.Segmenter` value reads `1187` and `1093` instead. The other
63
+ four values are pinned to exact library versions and hold identically on
64
+ every Node.
65
+
66
+ That single differing case is not an ICU bug. Unicode 17.0 removed U+2701
67
+ from `Extended_Pictographic`, so GB11 stopped applying and two clusters became
68
+ the correct answer; newer ICU implements that change and these two older keys
69
+ predate it. Scored against `17.0.0`, which is what the CLI uses by default,
70
+ ICU on Node 22 passes all 766 cases. Score an implementation against the key
71
+ it targets.
65
72
 
66
73
  The baseline was verified on Node 22.22.2 (ICU 78.2, Unicode 17.0).
67
74
 
package/dist/cli.cjs CHANGED
@@ -212,7 +212,7 @@ async function main() {
212
212
  console.log(USAGE);
213
213
  process.exit(args.module ? 0 : 2);
214
214
  }
215
- const version = args.version ?? "16.0.0";
215
+ const version = args.version ?? "17.0.0";
216
216
  const cases = vectors[version];
217
217
  if (!cases) fail(`Unknown version '${version}'. Have: ${Object.keys(vectors).join(", ")}`);
218
218
  const min = args.min === void 0 ? 1 : Number(args.min);
package/dist/cli.js CHANGED
@@ -207,7 +207,7 @@ async function main() {
207
207
  console.log(USAGE);
208
208
  process.exit(args.module ? 0 : 2);
209
209
  }
210
- const version = args.version ?? "16.0.0";
210
+ const version = args.version ?? "17.0.0";
211
211
  const cases = vectors[version];
212
212
  if (!cases) fail(`Unknown version '${version}'. Have: ${Object.keys(vectors).join(", ")}`);
213
213
  const min = args.min === void 0 ? 1 : Number(args.min);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grapheme-conformance",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Score any JavaScript grapheme segmenter against Unicode's official GraphemeBreakTest.txt. Find out which string splitters are wrong, and about what.",
5
5
  "keywords": [
6
6
  "unicode",