grapheme-conformance 0.1.2 → 0.1.3

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.
Files changed (3) hide show
  1. package/README.md +5 -0
  2. package/VERIFY.md +57 -29
  3. package/package.json +4 -2
package/README.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  **Your string splitter is probably wrong about Hindi.**
4
4
 
5
+ [![build](https://github.com/tamerkalla/grapheme-conformance/actions/workflows/release.yml/badge.svg)](https://github.com/tamerkalla/grapheme-conformance/actions/workflows/release.yml)
6
+ [![npm](https://img.shields.io/npm/v/grapheme-conformance.svg)](https://www.npmjs.com/package/grapheme-conformance)
7
+ [![license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
8
+ [![provenance](https://img.shields.io/badge/provenance-attested-brightgreen.svg)](https://www.npmjs.com/package/grapheme-conformance)
9
+
5
10
  Unicode publishes `GraphemeBreakTest.txt`: a machine-readable answer key that
6
11
  says exactly where a string may be split into user-perceived characters. This
7
12
  package scores any JavaScript grapheme segmenter against it, tells you which
package/VERIFY.md CHANGED
@@ -1,29 +1,50 @@
1
1
  # VERIFY
2
2
 
3
- Run one command:
3
+ This reproduces the scoreboard's two Unicode-16.0.0-and-15.1.0 columns from the
4
+ published package, in a clean directory. It does not require this repository
5
+ to be checked out.
4
6
 
5
- ```
6
- npm run scoreboard
7
- ```
7
+ ```bash
8
+ mkdir -p grapheme-conformance-verify && cd grapheme-conformance-verify
9
+ npm init -y >/dev/null 2>&1
10
+ npm install grapheme-conformance@latest grapheme-splitter@1.0.4 graphemer@1.4.0 runes2@1.1.4 unicode-segmenter@0.17.3 >/dev/null 2>&1
11
+ cat > verify.mjs <<'JS'
12
+ import { score, vectors } from 'grapheme-conformance';
13
+ import { splitGraphemes } from 'unicode-segmenter/grapheme';
14
+ import graphemerModule from 'graphemer';
15
+ import GraphemeSplitter from 'grapheme-splitter';
16
+ import { runes } from 'runes2';
8
17
 
9
- It builds the package, scores every library against every vendored copy of
10
- Unicode's `GraphemeBreakTest.txt`, writes `SCOREBOARD.md`, and prints the table
11
- below to stdout.
18
+ const Graphemer = graphemerModule.default ?? graphemerModule;
19
+ const intl = new Intl.Segmenter('en', { granularity: 'grapheme' });
20
+ const graphemer = new Graphemer();
21
+ const splitter = new GraphemeSplitter();
12
22
 
13
- ## Expected output
23
+ const impls = {
24
+ 'Intl.Segmenter': (s) => [...intl.segment(s)].map((p) => p.segment),
25
+ 'unicode-segmenter': (s) => [...splitGraphemes(s)],
26
+ graphemer: (s) => graphemer.splitGraphemes(s),
27
+ 'grapheme-splitter': (s) => splitter.splitGraphemes(s),
28
+ runes2: (s) => runes(s),
29
+ };
14
30
 
15
- Compare ten integers. These two columns are the specified baseline.
31
+ for (const version of ['15.1.0', '16.0.0']) {
32
+ const vecs = vectors[version];
33
+ const row = Object.entries(impls)
34
+ .map(([name, fn]) => `${name}=${score(fn, vecs).passed}`)
35
+ .join(' ');
36
+ console.log(`${version}: ${row}`);
37
+ }
38
+ JS
39
+ node verify.mjs
40
+ ```
16
41
 
17
- | implementation | U15.1.0 (1187) | U16.0.0 (1093) |
18
- |---|---|---|
19
- | `Intl.Segmenter` | 1186 | 1092 |
20
- | `unicode-segmenter` | 1186 | 1092 |
21
- | `graphemer` | 1180 | 1086 |
22
- | `grapheme-splitter` | 1175 | 1081 |
23
- | `runes2` | 730 | 695 |
42
+ Expected output:
24
43
 
25
- The printed table shows each cell as `passed/total (rate%)`; the integer to
26
- check is the one before the slash. If all ten match, the build is correct.
44
+ ```text
45
+ 15.1.0: Intl.Segmenter=1186 unicode-segmenter=1186 graphemer=1180 grapheme-splitter=1175 runes2=730
46
+ 16.0.0: Intl.Segmenter=1092 unicode-segmenter=1092 graphemer=1086 grapheme-splitter=1081 runes2=695
47
+ ```
27
48
 
28
49
  ## One caveat, and it is the only one
29
50
 
@@ -37,22 +58,29 @@ matrix:
37
58
  | Node 20.x | 1186 | 1092 | split |
38
59
  | Node 18.20.8 | 1187 | 1093 | not split |
39
60
 
40
- Run `npm run scoreboard` on Node 22 or 20 to get the two baseline numbers. On
41
- Node 18 the `Intl.Segmenter` row reads `1187` and `1093` instead: that ICU
42
- predates the deviation and passes every case. The other four rows are pinned to
43
- exact versions and hold identically on every Node.
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.
44
65
 
45
66
  The baseline was verified on Node 22.22.2 (ICU 78.2, Unicode 17.0).
46
67
 
47
- ## Everything else
68
+ ## Reproducing SCOREBOARD.md and the rest of the gates
48
69
 
49
- ```
50
- npm test # 104 assertions, offline, including all ten integers above
70
+ The committed `SCOREBOARD.md` (all four vendored Unicode versions, not just
71
+ the two above) is generated from this repository's own scripts, which are not
72
+ part of the published package, so reproducing it requires a checkout:
73
+
74
+ ```bash
75
+ git clone https://github.com/tamerkalla/grapheme-conformance.git && cd grapheme-conformance
76
+ npm ci
77
+ npm run scoreboard # regenerates SCOREBOARD.md and prints it
78
+ npm run scoreboard:check # asserts it is byte-identical to a fresh run
51
79
  npm run typecheck
80
+ npm test # includes all ten integers above
52
81
  npm run build
53
- npm run smoke # loads the ESM and CJS entry points and scores Intl.Segmenter
54
- npm run scoreboard:check # asserts SCOREBOARD.md is byte-identical to a fresh run
82
+ node scripts/smoke.mjs # loads the ESM and CJS entry points
55
83
  ```
56
84
 
57
- No network access is required by any of these. The vectors are committed under
58
- `vectors/` and are never fetched.
85
+ No network access is required by any of these. The vectors are committed
86
+ under `vectors/` and are never fetched.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "grapheme-conformance",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
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",
@@ -52,6 +52,7 @@
52
52
  "scripts": {
53
53
  "build": "tsup",
54
54
  "typecheck": "tsc --noEmit",
55
+ "pretest": "npm run build",
55
56
  "test": "vitest run",
56
57
  "prescoreboard": "npm run build",
57
58
  "scoreboard": "node scripts/scoreboard.mjs",
@@ -67,6 +68,7 @@
67
68
  "tsup": "8.3.5",
68
69
  "typescript": "5.7.2",
69
70
  "unicode-segmenter": "0.17.3",
70
- "vitest": "2.1.8"
71
+ "vitest": "2.1.8",
72
+ "yaml": "2.9.0"
71
73
  }
72
74
  }