color-name-list 13.0.0 → 13.2.0

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 (47) hide show
  1. package/.github/workflows/build-and-release.yml +3 -3
  2. package/.github/workflows/build.yml +1 -1
  3. package/.husky/pre-commit +1 -1
  4. package/CONTRIBUTING.md +1 -1
  5. package/README.md +48 -2
  6. package/changes.svg +3 -3
  7. package/dist/colornames.bestof.csv +6 -2
  8. package/dist/colornames.bestof.esm.js +1 -1
  9. package/dist/colornames.bestof.esm.mjs +1 -1
  10. package/dist/colornames.bestof.html +1 -1
  11. package/dist/colornames.bestof.json +1 -1
  12. package/dist/colornames.bestof.min.json +1 -1
  13. package/dist/colornames.bestof.scss +1 -1
  14. package/dist/colornames.bestof.umd.js +1 -1
  15. package/dist/colornames.bestof.xml +18 -2
  16. package/dist/colornames.bestof.yaml +14 -2
  17. package/dist/colornames.csv +9 -6
  18. package/dist/colornames.esm.js +1 -1
  19. package/dist/colornames.esm.mjs +1 -1
  20. package/dist/colornames.html +1 -1
  21. package/dist/colornames.json +1 -1
  22. package/dist/colornames.min.json +1 -1
  23. package/dist/colornames.scss +1 -1
  24. package/dist/colornames.short.csv +4 -2
  25. package/dist/colornames.short.esm.js +1 -1
  26. package/dist/colornames.short.esm.mjs +1 -1
  27. package/dist/colornames.short.html +1 -1
  28. package/dist/colornames.short.json +1 -1
  29. package/dist/colornames.short.min.json +1 -1
  30. package/dist/colornames.short.scss +1 -1
  31. package/dist/colornames.short.umd.js +1 -1
  32. package/dist/colornames.short.xml +10 -2
  33. package/dist/colornames.short.yaml +8 -2
  34. package/dist/colornames.umd.js +1 -1
  35. package/dist/colornames.xml +27 -15
  36. package/dist/colornames.yaml +21 -12
  37. package/dist/history.json +1 -1
  38. package/package.json +2 -4
  39. package/scripts/lib.js +2 -1
  40. package/scripts/sortSrc.js +1 -1
  41. package/src/colornames.csv +10 -7
  42. package/tests/duplicate-allowlist.json +2 -1
  43. package/tests/duplicates.test.js +152 -21
  44. package/tests/title-case.test.js +384 -0
  45. package/vitest.config.js +5 -0
  46. /package/tests/{formats.test.js → formats.ci.test.js} +0 -0
  47. /package/tests/{imports.test.js → imports.ci.test.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "color-name-list",
3
- "version": "13.0.0",
3
+ "version": "13.2.0",
4
4
  "description": "long list of color names",
5
5
  "main": "dist/colornames.json",
6
6
  "browser": "dist/colornames.umd.js",
@@ -24,10 +24,8 @@
24
24
  "scripts": {
25
25
  "commit": "git-cz",
26
26
  "pull-colors": "curl -L 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQube6Y0wHyEtJnjg0eU3N7VseoxVnD4L9uDqvWZdl_tzzrHDVN10IPP7cdFipX8j70atNMLfPCB0Q6/pub?gid=40578722&single=true&output=csv' -o src/colornames.csv",
27
- "test": "npm run test:precommit && vitest run",
27
+ "test": "vitest run",
28
28
  "test:watch": "vitest",
29
- "test:precommit": "npm run build:test",
30
- "build:test": "node scripts/build.js --testonly",
31
29
  "build": "node scripts/build.js && npm run prettier",
32
30
  "prettier": "prettier --write ./dist/*",
33
31
  "lint": "npm run lint:scripts && npm run lint:markdown",
package/scripts/lib.js CHANGED
@@ -160,7 +160,8 @@ export const findNearDuplicateNameConflicts = (items, options = {}) => {
160
160
  .normalize('NFD')
161
161
  .replace(/[\u0300-\u036f]/g, '');
162
162
  const tokens = base.match(/[a-z0-9]+/g) || [];
163
- const filtered = foldStopwords && stopSet && stopSet.size ? tokens.filter((t) => !stopSet.has(t)) : tokens;
163
+ const filtered =
164
+ foldStopwords && stopSet && stopSet.size ? tokens.filter((t) => !stopSet.has(t)) : tokens;
164
165
  return filtered.length ? filtered.join('') : normalizeNameForDuplicates(name);
165
166
  };
166
167
 
@@ -45,7 +45,7 @@ const readAndSortCSV = () => {
45
45
  });
46
46
 
47
47
  // Combine header & sorted lines (no blank line). Ensure exactly one final newline.
48
- const sortedData = [header, ...sortedColorLines].join('\n') + '\n';
48
+ const sortedData = [header, ...sortedColorLines].join('\n');
49
49
 
50
50
  // Write back
51
51
  fs.writeFileSync(csvPath, sortedData, 'utf8');
@@ -215,8 +215,6 @@ Afternoon Stroll,#d9c5a1,
215
215
  Afternoon Tea,#594e40,
216
216
  Agapanthus,#bbc5de,
217
217
  Agate Brown,#956a60,
218
- Agate Green,#52928d,
219
- Agate Grey,#acaa99,
220
218
  Agate Violet,#5a5b74,
221
219
  Agave,#879d99,
222
220
  Agave Frond,#5a6e6a,
@@ -1014,7 +1012,7 @@ April Sunshine,#fbe198,
1014
1012
  April Tears,#b4cbd4,
1015
1013
  April Wedding,#c5cfb1,
1016
1014
  April Winds,#d5e2e5,
1017
- Aqua,#00ffff,x
1015
+ Aqua,#0ff0fe,x
1018
1016
  Aqua Bay,#b5dfc9,
1019
1017
  Aqua Belt,#7acad0,
1020
1018
  Aqua Bloom,#96d3d8,
@@ -1923,6 +1921,7 @@ Barberry Bush,#d2c61f,
1923
1921
  Barberry Sand,#e1d4bc,
1924
1922
  Barberry Yellow,#f3bd32,
1925
1923
  Barbie Pink,#fe46a5,
1924
+ Barbiecore,#e15acb,x
1926
1925
  Barcelona Beige,#c4b39c,
1927
1926
  Barcelona Brown,#926a46,
1928
1927
  Barcelona Orange,#ff9500,
@@ -6975,7 +6974,7 @@ Cuticle Pink,#e3a49a,
6975
6974
  Cutlery Polish,#f4dda5,
6976
6975
  Cuttlefish,#7fbbc2,x
6977
6976
  Cutty Sark,#5c8173,
6978
- Cyan,#0ff0fe,x
6977
+ Cyan,#00ffff,x
6979
6978
  Cyan Azure,#4e82b4,
6980
6979
  Cyan Blue,#14a3c7,
6981
6980
  Cyan Cobalt Blue,#28589c,
@@ -7792,6 +7791,7 @@ Dig It,#8e6e57,
7792
7791
  Digger’s Gold,#a37336,
7793
7792
  Digital,#636365,
7794
7793
  Digital Garage,#b7b3a4,
7794
+ Digital Lavender,#cac0e1,x
7795
7795
  Digital Violets,#aa00ff,
7796
7796
  Digital Yellow,#ffeb7e,
7797
7797
  Dignified,#3b695f,
@@ -10683,6 +10683,7 @@ Gemini,#b4d6cb,
10683
10683
  Gemini Mustard Momento,#fca750,
10684
10684
  Gemstone Blue,#004f6d,
10685
10685
  Gemstone Green,#4b6331,
10686
+ Gen Z Yellow,#f4e386,
10686
10687
  Generic Viridian,#007f66,
10687
10688
  Genestealer Purple,#7761ab,
10688
10689
  Genetic Code,#18515d,
@@ -11519,7 +11520,7 @@ Green,#00ff00,x
11519
11520
  Green 383,#3e3d29,
11520
11521
  Green Acres,#53a144,
11521
11522
  Green Adirondack,#688878,
11522
- Green Agate,#3f6253,
11523
+ Green Agate,#52928d,
11523
11524
  Green Alabaster,#c8ccba,
11524
11525
  Green Amazons,#98a893,
11525
11526
  Green Andara,#afc1a3,
@@ -11672,6 +11673,7 @@ Green Olive,#8d8b55,x
11672
11673
  Green Olive Pit,#bdaa89,
11673
11674
  Green Onion,#c1e089,
11674
11675
  Green Onyx,#989a82,
11676
+ Green Ooze,#e1fe52,x
11675
11677
  Green Papaya,#e5ce77,
11676
11678
  Green Parakeet,#7bd5bf,
11677
11679
  Green Parlor,#cfddb9,
@@ -15186,7 +15188,6 @@ Lima Green,#b1b787,
15186
15188
  Lima Sombrio,#7aac21,
15187
15189
  Limbert Leather,#988870,
15188
15190
  Lime,#aaff32,x
15189
- Lime Acid,#afff01,
15190
15191
  Lime Blossom,#f4f2d3,
15191
15192
  Lime Bright,#f1e4b0,
15192
15193
  Lime Cake,#dae3d0,
@@ -16808,6 +16809,7 @@ Milky Yellow,#f8dd74,
16808
16809
  Mill Creek,#876e59,
16809
16810
  Millbrook,#595648,
16810
16811
  Mille-Feuille,#efc87d,x
16812
+ Millennial Grey,#7b7b7d,
16811
16813
  Millennial Pink,#f6c8c1,x
16812
16814
  Millennium Silver,#8c9595,
16813
16815
  Million Grey,#999999,x
@@ -20832,6 +20834,7 @@ Popular Beige,#d4ccc3,
20832
20834
  Porcelain,#dddcdb,x
20833
20835
  Porcelain Basin,#d9d0c4,
20834
20836
  Porcelain Blue,#95c0cb,
20837
+ Porcelain Blush,#f0dbd5,x
20835
20838
  Porcelain Crab,#e9b7a8,
20836
20839
  Porcelain Earth,#eeffbb,
20837
20840
  Porcelain Figurines,#a9998c,
@@ -30117,4 +30120,4 @@ Zumthor,#cdd5d5,
30117
30120
  Zunda Green,#6bc026,x
30118
30121
  Zuni,#008996,
30119
30122
  Zürich Blue,#248bcc,
30120
- Zürich White,#e6e1d9,
30123
+ Zürich White,#e6e1d9,
@@ -35,5 +35,6 @@
35
35
  "In the Dark",
36
36
  "Dark as Night",
37
37
  "Green With Envy",
38
- "Lost in Space"
38
+ "Lost in Space",
39
+ "Fruit of Passion"
39
40
  ]
@@ -11,6 +11,109 @@ describe('Duplicate-like color names', () => {
11
11
  csvTestData.load();
12
12
  });
13
13
 
14
+ // Shared stopwords for normalization across tests
15
+ const STOPWORDS = [
16
+ 'of',
17
+ 'the',
18
+ 'and',
19
+ 'a',
20
+ 'an',
21
+ 'in',
22
+ 'on',
23
+ 'at',
24
+ 'to',
25
+ 'for',
26
+ 'by',
27
+ 'with',
28
+ 'from',
29
+ 'as',
30
+ 'is',
31
+ 'it',
32
+ 'this',
33
+ 'that',
34
+ 'these',
35
+ 'those',
36
+ 'be',
37
+ 'are',
38
+ 'was',
39
+ 'were',
40
+ 'or',
41
+ ];
42
+
43
+ // Helper: normalize a phrase similar to scripts/lib normalization but keeping token boundaries
44
+ const normalize = (s) =>
45
+ String(s)
46
+ .toLowerCase()
47
+ .normalize('NFD')
48
+ .replace(/[\u0300-\u036f]/g, '')
49
+ .replace(/[^a-z0-9]+/g, ' ')
50
+ .trim();
51
+
52
+ const tokenize = (name) => {
53
+ const base = normalize(name);
54
+ const tokens = base.match(/[a-z0-9]+/g) || [];
55
+ const stopSet = new Set(STOPWORDS.map((w) => normalize(w)));
56
+ return tokens.filter((t) => t && !stopSet.has(t));
57
+ };
58
+
59
+ // Detect two-word names that are exact reversals of each other (after stopword filtering)
60
+ function findTwoWordReversedPairs(items) {
61
+ const groups = new Map(); // key: sorted pair "a|b" -> list of { name, lineNumber, order: "a b" }
62
+
63
+ for (const item of items) {
64
+ if (!item || typeof item.name !== 'string') continue;
65
+ const tokens = tokenize(item.name);
66
+ if (tokens.length !== 2) continue; // only 2-token (after stopword removal)
67
+ const [a, b] = tokens;
68
+ if (!a || !b) continue;
69
+ if (a === b) continue; // "blue blue" reversed is the same – ignore
70
+
71
+ const key = [a, b].sort().join('|');
72
+ const order = `${a} ${b}`;
73
+ if (!groups.has(key)) groups.set(key, []);
74
+ groups.get(key).push({ name: item.name, lineNumber: item.lineNumber, order });
75
+ }
76
+
77
+ const conflicts = [];
78
+ for (const [key, entries] of groups.entries()) {
79
+ // We have a potential conflict if we see both orders "a b" and "b a"
80
+ const uniqOrders = [...new Set(entries.map((e) => e.order))];
81
+ if (uniqOrders.length < 2) continue;
82
+
83
+ const [t1, t2] = key.split('|');
84
+ const forward = `${t1} ${t2}`;
85
+ const backward = `${t2} ${t1}`;
86
+
87
+ const hasForward = uniqOrders.includes(forward);
88
+ const hasBackward = uniqOrders.includes(backward);
89
+
90
+ if (hasForward && hasBackward) {
91
+ // Keep entries unique by name@line and sorted by line number for stable output
92
+ const seen = new Set();
93
+ const unique = entries
94
+ .filter((e) => {
95
+ const k = `${e.name}@${e.lineNumber}`;
96
+ if (seen.has(k)) return false;
97
+ seen.add(k);
98
+ return true;
99
+ })
100
+ .sort((a, b) => a.lineNumber - b.lineNumber);
101
+
102
+ // Respect allowlist: if either direction string is allowlisted, skip
103
+ const allowSet = new Set(
104
+ (Array.isArray(allowlist) ? allowlist : [])
105
+ .filter((v) => typeof v === 'string' && v.trim().length)
106
+ .map((v) => normalize(v))
107
+ );
108
+ if (allowSet.has(forward) || allowSet.has(backward)) continue;
109
+
110
+ conflicts.push({ key, tokens: [t1, t2], entries: unique });
111
+ }
112
+ }
113
+
114
+ return conflicts;
115
+ }
116
+
14
117
  it('should not contain the same name twice', () => {
15
118
  expect(csvTestData.lineCount).toBeGreaterThan(1);
16
119
 
@@ -45,13 +148,7 @@ describe('Duplicate-like color names', () => {
45
148
  foldPlurals: true,
46
149
  pluralAllowlist,
47
150
  foldStopwords: true,
48
- stopwords: [
49
- 'of', 'the', 'and', 'a', 'an',
50
- 'in', 'on', 'at', 'to', 'for',
51
- 'by', 'with', 'from', 'as', 'is',
52
- 'it', 'this', 'that', 'these', 'those',
53
- 'be', 'are', 'was', 'were', 'or'
54
- ],
151
+ stopwords: STOPWORDS,
55
152
  });
56
153
 
57
154
  if (conflicts.length) {
@@ -81,7 +178,8 @@ describe('Duplicate-like color names', () => {
81
178
 
82
179
  throw new Error(
83
180
  buildFailureMessage({
84
- title: 'Found {n} duplicate-like {items} (case/accents/punctuation/stopwords-insensitive):',
181
+ title:
182
+ 'Found {n} duplicate-like {items} (case/accents/punctuation/stopwords-insensitive):',
85
183
  offenders: [...allOffendingNames],
86
184
  offenderLabel: 'name',
87
185
  details: [
@@ -142,24 +240,57 @@ describe('Duplicate-like color names', () => {
142
240
  });
143
241
 
144
242
  it('should detect names that only differ by stopwords when enabled', () => {
145
- const items = [
146
- { name: 'Heart Gold' },
147
- { name: 'Heart of Gold' },
148
- ];
149
- const stopwords = [
150
- 'of', 'the', 'and', 'a', 'an',
151
- 'in', 'on', 'at', 'to', 'for',
152
- 'by', 'with', 'from', 'as', 'is',
153
- 'it', 'this', 'that', 'these', 'those',
154
- 'be', 'are', 'was', 'were', 'or',
155
- ];
156
-
243
+ const items = [{ name: 'Heart Gold' }, { name: 'Heart of Gold' }];
157
244
  const conflicts = findNearDuplicateNameConflicts(items, {
158
245
  foldStopwords: true,
159
- stopwords,
246
+ stopwords: STOPWORDS,
160
247
  });
161
248
 
162
249
  expect(conflicts.length).toBe(1);
163
250
  expect(conflicts[0].entries.length).toBe(2);
164
251
  });
252
+
253
+ it.skip('should not contain two-word names that are exact reversals of each other', () => {
254
+ expect(csvTestData.lineCount).toBeGreaterThan(1);
255
+
256
+ const conflicts = findTwoWordReversedPairs(csvTestData.items);
257
+
258
+ if (conflicts.length) {
259
+ // Build helpful details with line numbers
260
+ const details = [];
261
+ const offenderNames = new Set();
262
+
263
+ conflicts
264
+ .sort((a, b) => a.tokens.join(' ').localeCompare(b.tokens.join(' ')))
265
+ .forEach(({ tokens, entries }) => {
266
+ const [a, b] = tokens;
267
+ details.push(` • ${a} / ${b}:`);
268
+ entries.forEach((e) => {
269
+ offenderNames.add(e.name);
270
+ details.push(` - line ${e.lineNumber}: "${e.name}"`);
271
+ });
272
+ details.push('');
273
+ });
274
+
275
+ throw new Error(
276
+ buildFailureMessage({
277
+ title: 'Found {n} word-order reversed {items}:',
278
+ offenders: [...offenderNames],
279
+ offenderLabel: 'name',
280
+ details: [
281
+ ...details,
282
+ 'Names that only differ by word order (e.g., "Beach Sand" vs "Sand Beach") should be unified.',
283
+ 'Please keep a single preferred order and remove the other.',
284
+ ],
285
+ tips: [
286
+ 'Edit src/colornames.csv and keep only one order for each two-word pair.',
287
+ 'After changes, run: npm run sort-colors',
288
+ ],
289
+ count: conflicts.length,
290
+ })
291
+ );
292
+ }
293
+
294
+ expect(conflicts.length).toBe(0);
295
+ });
165
296
  });