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.
- package/.github/workflows/build-and-release.yml +3 -3
- package/.github/workflows/build.yml +1 -1
- package/.husky/pre-commit +1 -1
- package/CONTRIBUTING.md +1 -1
- package/README.md +48 -2
- package/changes.svg +3 -3
- package/dist/colornames.bestof.csv +6 -2
- package/dist/colornames.bestof.esm.js +1 -1
- package/dist/colornames.bestof.esm.mjs +1 -1
- package/dist/colornames.bestof.html +1 -1
- package/dist/colornames.bestof.json +1 -1
- package/dist/colornames.bestof.min.json +1 -1
- package/dist/colornames.bestof.scss +1 -1
- package/dist/colornames.bestof.umd.js +1 -1
- package/dist/colornames.bestof.xml +18 -2
- package/dist/colornames.bestof.yaml +14 -2
- package/dist/colornames.csv +9 -6
- package/dist/colornames.esm.js +1 -1
- package/dist/colornames.esm.mjs +1 -1
- package/dist/colornames.html +1 -1
- package/dist/colornames.json +1 -1
- package/dist/colornames.min.json +1 -1
- package/dist/colornames.scss +1 -1
- package/dist/colornames.short.csv +4 -2
- package/dist/colornames.short.esm.js +1 -1
- package/dist/colornames.short.esm.mjs +1 -1
- package/dist/colornames.short.html +1 -1
- package/dist/colornames.short.json +1 -1
- package/dist/colornames.short.min.json +1 -1
- package/dist/colornames.short.scss +1 -1
- package/dist/colornames.short.umd.js +1 -1
- package/dist/colornames.short.xml +10 -2
- package/dist/colornames.short.yaml +8 -2
- package/dist/colornames.umd.js +1 -1
- package/dist/colornames.xml +27 -15
- package/dist/colornames.yaml +21 -12
- package/dist/history.json +1 -1
- package/package.json +2 -4
- package/scripts/lib.js +2 -1
- package/scripts/sortSrc.js +1 -1
- package/src/colornames.csv +10 -7
- package/tests/duplicate-allowlist.json +2 -1
- package/tests/duplicates.test.js +152 -21
- package/tests/title-case.test.js +384 -0
- package/vitest.config.js +5 -0
- /package/tests/{formats.test.js → formats.ci.test.js} +0 -0
- /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.
|
|
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": "
|
|
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 =
|
|
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
|
|
package/scripts/sortSrc.js
CHANGED
|
@@ -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')
|
|
48
|
+
const sortedData = [header, ...sortedColorLines].join('\n');
|
|
49
49
|
|
|
50
50
|
// Write back
|
|
51
51
|
fs.writeFileSync(csvPath, sortedData, 'utf8');
|
package/src/colornames.csv
CHANGED
|
@@ -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,#
|
|
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,#
|
|
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,#
|
|
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,
|
package/tests/duplicates.test.js
CHANGED
|
@@ -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:
|
|
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
|
});
|