@projectwallace/css-analyzer 4.0.3 → 5.0.0-alpha.1

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 (95) hide show
  1. package/dist/analyzer.cjs +2 -0
  2. package/dist/analyzer.cjs.map +1 -0
  3. package/dist/analyzer.js +2 -0
  4. package/dist/analyzer.js.map +1 -0
  5. package/dist/analyzer.module.js +2 -0
  6. package/dist/analyzer.module.js.map +1 -0
  7. package/dist/analyzer.umd.js +2 -0
  8. package/dist/analyzer.umd.js.map +1 -0
  9. package/package.json +25 -63
  10. package/readme.md +369 -237
  11. package/src/aggregate-collection.js +113 -0
  12. package/src/aggregate-collection.test.js +47 -0
  13. package/src/atrules/atrules.js +76 -0
  14. package/src/atrules/atrules.test.js +289 -0
  15. package/src/context-collection.js +36 -0
  16. package/src/countable-collection.js +46 -0
  17. package/src/declarations/declarations.js +46 -0
  18. package/src/declarations/declarations.test.js +113 -0
  19. package/src/index.js +259 -0
  20. package/src/index.test.js +60 -0
  21. package/src/properties/properties.js +48 -0
  22. package/src/properties/properties.test.js +138 -0
  23. package/src/rules/rules.js +57 -0
  24. package/src/rules/rules.test.js +247 -0
  25. package/src/selectors/complexity.test.js +123 -0
  26. package/src/selectors/selectors.js +122 -0
  27. package/src/selectors/selectors.test.js +189 -0
  28. package/src/selectors/specificity.js +201 -0
  29. package/src/selectors/specificity.test.js +247 -0
  30. package/src/smoke.test.js +39 -0
  31. package/src/stylesheet/stylesheet.test.js +88 -0
  32. package/src/values/animations.js +53 -0
  33. package/src/values/animations.test.js +154 -0
  34. package/src/values/box-shadows.test.js +82 -0
  35. package/src/values/colors.js +192 -0
  36. package/src/values/colors.test.js +804 -0
  37. package/src/values/font-families.js +98 -0
  38. package/src/values/font-families.test.js +119 -0
  39. package/src/values/font-sizes.js +92 -0
  40. package/src/values/font-sizes.test.js +120 -0
  41. package/src/values/text-shadows.test.js +93 -0
  42. package/src/values/units.test.js +72 -0
  43. package/src/values/values.js +30 -0
  44. package/src/values/vendor-prefix.js +45 -0
  45. package/src/values/vendor-prefix.test.js +64 -0
  46. package/src/values/z-index.test.js +54 -0
  47. package/src/vendor-prefix.js +16 -0
  48. package/src/analyzer/atrules/charsets.js +0 -12
  49. package/src/analyzer/atrules/documents.js +0 -12
  50. package/src/analyzer/atrules/fontfaces.js +0 -30
  51. package/src/analyzer/atrules/imports.js +0 -12
  52. package/src/analyzer/atrules/index.js +0 -13
  53. package/src/analyzer/atrules/keyframes.js +0 -19
  54. package/src/analyzer/atrules/mediaqueries.js +0 -19
  55. package/src/analyzer/atrules/namespaces.js +0 -12
  56. package/src/analyzer/atrules/pages.js +0 -12
  57. package/src/analyzer/atrules/supports.js +0 -19
  58. package/src/analyzer/declarations/importants.js +0 -8
  59. package/src/analyzer/declarations/index.js +0 -13
  60. package/src/analyzer/index.js +0 -39
  61. package/src/analyzer/properties/browserhacks.js +0 -13
  62. package/src/analyzer/properties/index.js +0 -15
  63. package/src/analyzer/properties/prefixed.js +0 -13
  64. package/src/analyzer/rules/index.js +0 -53
  65. package/src/analyzer/selectors/accessibility.js +0 -14
  66. package/src/analyzer/selectors/browserhacks.js +0 -11
  67. package/src/analyzer/selectors/complexity.js +0 -44
  68. package/src/analyzer/selectors/id.js +0 -12
  69. package/src/analyzer/selectors/index.js +0 -23
  70. package/src/analyzer/selectors/js.js +0 -12
  71. package/src/analyzer/selectors/specificity.js +0 -42
  72. package/src/analyzer/selectors/universal.js +0 -13
  73. package/src/analyzer/stylesheets/browserhacks.js +0 -15
  74. package/src/analyzer/stylesheets/cohesion.js +0 -30
  75. package/src/analyzer/stylesheets/index.js +0 -33
  76. package/src/analyzer/stylesheets/lines-of-code.js +0 -14
  77. package/src/analyzer/stylesheets/simplicity.js +0 -7
  78. package/src/analyzer/stylesheets/size.js +0 -27
  79. package/src/analyzer/values/animations.js +0 -122
  80. package/src/analyzer/values/box-shadows.js +0 -17
  81. package/src/analyzer/values/browserhacks.js +0 -16
  82. package/src/analyzer/values/colors.js +0 -137
  83. package/src/analyzer/values/font-families.js +0 -30
  84. package/src/analyzer/values/font-sizes.js +0 -31
  85. package/src/analyzer/values/index.js +0 -26
  86. package/src/analyzer/values/prefixed.js +0 -22
  87. package/src/analyzer/values/text-shadows.js +0 -17
  88. package/src/analyzer/values/z-indexes.js +0 -20
  89. package/src/parser/atrules.js +0 -37
  90. package/src/parser/declarations.js +0 -15
  91. package/src/parser/index.js +0 -30
  92. package/src/parser/rules.js +0 -17
  93. package/src/parser/selectors.js +0 -26
  94. package/src/utils/css.js +0 -8
  95. package/src/utils/uniquer.js +0 -24
@@ -0,0 +1,804 @@
1
+ import { suite } from 'uvu';
2
+ import * as assert from 'uvu/assert';
3
+ import { analyze } from '../index.js'
4
+
5
+ const Colors = suite('Colors')
6
+
7
+ Colors('finds hex colors', () => {
8
+ const actual = analyze(`
9
+ test {
10
+ color: #000;
11
+ color: #1111;
12
+ color: #222222;
13
+ color: #33333333;
14
+ }
15
+
16
+ test-bg {
17
+ background: linear-gradient(90deg, #444 0, #444 100%);
18
+ }
19
+
20
+ test-box-shadow {
21
+ box-shadow: 0 0 0 #555;
22
+ }
23
+
24
+ test-outline-color {
25
+ outline-color: #666;
26
+ }
27
+
28
+ test-custom-property {
29
+ test: #777;
30
+ }
31
+
32
+ #not-a-color {
33
+ margin: 0;
34
+ }
35
+ `).values.colors;
36
+
37
+ const expected = {
38
+ total: 9,
39
+ totalUnique: 8,
40
+ unique: {
41
+ '#000': 1,
42
+ '#1111': 1,
43
+ '#222222': 1,
44
+ '#33333333': 1,
45
+ '#444': 2,
46
+ '#555': 1,
47
+ '#666': 1,
48
+ '#777': 1,
49
+ },
50
+ uniquenessRatio: 8 / 9,
51
+ itemsPerContext: {
52
+ color: {
53
+ total: 4,
54
+ totalUnique: 4,
55
+ uniquenessRatio: 1,
56
+ unique: {
57
+ '#000': 1,
58
+ '#1111': 1,
59
+ '#222222': 1,
60
+ '#33333333': 1,
61
+ },
62
+ },
63
+ background: {
64
+ total: 2,
65
+ totalUnique: 1,
66
+ uniquenessRatio: 1 / 2,
67
+ unique: {
68
+ '#444': 2,
69
+ },
70
+ },
71
+ 'box-shadow': {
72
+ total: 1,
73
+ totalUnique: 1,
74
+ uniquenessRatio: 1,
75
+ unique: {
76
+ '#555': 1,
77
+ },
78
+ },
79
+ 'outline-color': {
80
+ total: 1,
81
+ totalUnique: 1,
82
+ uniquenessRatio: 1,
83
+ unique: {
84
+ '#666': 1,
85
+ }
86
+ },
87
+ 'test': {
88
+ total: 1,
89
+ totalUnique: 1,
90
+ uniquenessRatio: 1,
91
+ unique: {
92
+ '#777': 1,
93
+ },
94
+ },
95
+ },
96
+ }
97
+ assert.equal(actual, expected)
98
+ })
99
+
100
+ Colors('finds hsl(a) colors', () => {
101
+ const actual = analyze(`
102
+ test {
103
+ color: hsl(1, 20%, 30%);
104
+ color: hsl(1 20% 30% / 1);
105
+ color: Hsla(2, 20%, 30%, 0.5);
106
+ }
107
+
108
+ test-bg {
109
+ background: linear-gradient(90deg, hsl(3, 20%, 30%) 0, hsl(3, 20%, 30%) 100%);
110
+ }
111
+
112
+ test-box-shadow {
113
+ box-shadow: 0 0 0 hsl(4, 20%, 30%);
114
+ }
115
+
116
+ test-outline-color {
117
+ outline-color: hsl(5, 20%, 30%);
118
+ }
119
+
120
+ test-custom-property {
121
+ test: hsl(6, 20%, 30%);
122
+ }
123
+
124
+ test-notations {
125
+ color: hsl(30, 100%, 50%, 0.6);
126
+ color: hsla(30, 100%, 50%, 0.6);
127
+ color: hsl(30 100% 50% / 0.6);
128
+ color: hsla(30 100% 50% / 0.6);
129
+ color: hsl(30.0 100% 50% / 60%);
130
+ color: hsla(30.2 100% 50% / 60%);
131
+
132
+ /* Source: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#hsl_syntax_variations */
133
+ /* These examples all specify the same color: a lavender. */
134
+ color: hsl(270,60%,70%);
135
+ color: hsl(270, 60%, 70%);
136
+ color: hsl(270 60% 70%);
137
+ color: hsl(270deg, 60%, 70%);
138
+ color: hsl(4.71239rad, 60%, 70%);
139
+ color: hsl(.75turn, 60%, 70%);
140
+
141
+ /* These examples all specify the same color: a lavender that is 15% opaque. */
142
+ color: hsl(270, 60%, 50%, .15);
143
+ color: hsl(270, 60%, 50%, 15%);
144
+ color: hsl(270 60% 50% / .15);
145
+ color: hsl(270 60% 50% / 15%);
146
+ }
147
+ `).values.colors;
148
+
149
+ const expected = {
150
+ total: 24,
151
+ totalUnique: 23,
152
+ unique: {
153
+ 'hsl(1, 20%, 30%)': 1,
154
+ 'hsl(1 20% 30% / 1)': 1,
155
+ 'Hsla(2, 20%, 30%, 0.5)': 1,
156
+ 'hsl(3, 20%, 30%)': 2,
157
+ 'hsl(4, 20%, 30%)': 1,
158
+ 'hsl(5, 20%, 30%)': 1,
159
+ 'hsl(6, 20%, 30%)': 1,
160
+ 'hsl(30, 100%, 50%, 0.6)': 1,
161
+ 'hsla(30, 100%, 50%, 0.6)': 1,
162
+ 'hsl(30 100% 50% / 0.6)': 1,
163
+ 'hsla(30 100% 50% / 0.6)': 1,
164
+ 'hsl(30.0 100% 50% / 60%)': 1,
165
+ 'hsla(30.2 100% 50% / 60%)': 1,
166
+ 'hsl(270,60%,70%)': 1,
167
+ 'hsl(270, 60%, 70%)': 1,
168
+ 'hsl(270 60% 70%)': 1,
169
+ 'hsl(270deg, 60%, 70%)': 1,
170
+ 'hsl(4.71239rad, 60%, 70%)': 1,
171
+ 'hsl(.75turn, 60%, 70%)': 1,
172
+ 'hsl(270, 60%, 50%, .15)': 1,
173
+ 'hsl(270, 60%, 50%, 15%)': 1,
174
+ 'hsl(270 60% 50% / .15)': 1,
175
+ 'hsl(270 60% 50% / 15%)': 1,
176
+ },
177
+ uniquenessRatio: 23 / 24,
178
+ itemsPerContext: {
179
+ color: {
180
+ total: 19,
181
+ totalUnique: 19,
182
+ unique: {
183
+ 'hsl(1, 20%, 30%)': 1,
184
+ 'hsl(1 20% 30% / 1)': 1,
185
+ 'Hsla(2, 20%, 30%, 0.5)': 1,
186
+ 'hsl(30, 100%, 50%, 0.6)': 1,
187
+ 'hsla(30, 100%, 50%, 0.6)': 1,
188
+ 'hsl(30 100% 50% / 0.6)': 1,
189
+ 'hsla(30 100% 50% / 0.6)': 1,
190
+ 'hsl(30.0 100% 50% / 60%)': 1,
191
+ 'hsla(30.2 100% 50% / 60%)': 1,
192
+ 'hsl(270,60%,70%)': 1,
193
+ 'hsl(270, 60%, 70%)': 1,
194
+ 'hsl(270 60% 70%)': 1,
195
+ 'hsl(270deg, 60%, 70%)': 1,
196
+ 'hsl(4.71239rad, 60%, 70%)': 1,
197
+ 'hsl(.75turn, 60%, 70%)': 1,
198
+ 'hsl(270, 60%, 50%, .15)': 1,
199
+ 'hsl(270, 60%, 50%, 15%)': 1,
200
+ 'hsl(270 60% 50% / .15)': 1,
201
+ 'hsl(270 60% 50% / 15%)': 1,
202
+ },
203
+ uniquenessRatio: 1,
204
+ },
205
+ background: {
206
+ total: 2,
207
+ totalUnique: 1,
208
+ unique: {
209
+ 'hsl(3, 20%, 30%)': 2
210
+ },
211
+ uniquenessRatio: 0.5
212
+ },
213
+ 'box-shadow': {
214
+ total: 1,
215
+ totalUnique: 1,
216
+ unique: {
217
+ 'hsl(4, 20%, 30%)': 1
218
+ },
219
+ uniquenessRatio: 1
220
+ },
221
+ 'outline-color': {
222
+ total: 1,
223
+ totalUnique: 1,
224
+ unique: {
225
+ 'hsl(5, 20%, 30%)': 1
226
+ },
227
+ uniquenessRatio: 1
228
+ },
229
+ 'test': {
230
+ total: 1,
231
+ totalUnique: 1,
232
+ unique: {
233
+ 'hsl(6, 20%, 30%)': 1
234
+ },
235
+ uniquenessRatio: 1
236
+ }
237
+ }
238
+ }
239
+ assert.equal(actual, expected)
240
+ })
241
+
242
+ Colors('finds rgb(a) colors', () => {
243
+ const actual = analyze(`
244
+ test {
245
+ color: rgb(1, 1, 1);
246
+ color: rgba(2, 2, 2, 0.2);
247
+ color: RGBA(3, 3, 3, .3);
248
+ color: rgba(4,4,4,.4)
249
+ }
250
+
251
+ test-bg {
252
+ background: linear-gradient(90deg, rgb(5, 5, 5) 0, rgb(5, 5, 5) 100%);
253
+ }
254
+
255
+ test-box-shadow {
256
+ box-shadow: 0 0 0 rgb(6,6,6);
257
+ }
258
+
259
+ test-outline-color {
260
+ outline-color: rgb(7,7,7);
261
+ }
262
+
263
+ test-custom-property {
264
+ test: rgb(8,8,8);
265
+ }
266
+
267
+ /* Source: https://developer.mozilla.org/en-US/docs/Web/CSS/color */
268
+ test-notations {
269
+ color: rgb(34, 12, 64, 0.6);
270
+ color: rgba(34, 12, 64, 0.6);
271
+ color: rgb(34 12 64 / 0.6);
272
+ color: rgba(34 12 64 / 0.3);
273
+ color: rgb(34.0 12 64 / 60%);
274
+ color: rgba(34.6 12 64 / 30%);
275
+ color: rgba(255, 0, 153.6, 1);
276
+ }
277
+
278
+ /* https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#rgb_transparency_variations */
279
+ test-exotic-numbers {
280
+ color: rgba(1e2, .5e1, .5e0, +.25e2%);
281
+ }
282
+ `).values.colors;
283
+
284
+ const expected = {
285
+ total: 17,
286
+ totalUnique: 16,
287
+ unique: {
288
+ 'rgb(1, 1, 1)': 1,
289
+ 'rgba(2, 2, 2, 0.2)': 1,
290
+ 'RGBA(3, 3, 3, .3)': 1,
291
+ 'rgba(4,4,4,.4)': 1,
292
+ 'rgb(5, 5, 5)': 2,
293
+ 'rgb(6,6,6)': 1,
294
+ 'rgb(7,7,7)': 1,
295
+ 'rgb(8,8,8)': 1,
296
+ 'rgb(34, 12, 64, 0.6)': 1,
297
+ 'rgba(34, 12, 64, 0.6)': 1,
298
+ 'rgb(34 12 64 / 0.6)': 1,
299
+ 'rgba(34 12 64 / 0.3)': 1,
300
+ 'rgb(34.0 12 64 / 60%)': 1,
301
+ 'rgba(34.6 12 64 / 30%)': 1,
302
+ 'rgba(255, 0, 153.6, 1)': 1,
303
+ 'rgba(1e2, .5e1, .5e0, +.25e2%)': 1,
304
+ },
305
+ uniquenessRatio: 16 / 17,
306
+ itemsPerContext: {
307
+ color: {
308
+ total: 12,
309
+ totalUnique: 12,
310
+ unique: {
311
+ 'rgb(1, 1, 1)': 1,
312
+ 'rgba(2, 2, 2, 0.2)': 1,
313
+ 'RGBA(3, 3, 3, .3)': 1,
314
+ 'rgba(4,4,4,.4)': 1,
315
+ 'rgb(34, 12, 64, 0.6)': 1,
316
+ 'rgba(34, 12, 64, 0.6)': 1,
317
+ 'rgb(34 12 64 / 0.6)': 1,
318
+ 'rgba(34 12 64 / 0.3)': 1,
319
+ 'rgb(34.0 12 64 / 60%)': 1,
320
+ 'rgba(34.6 12 64 / 30%)': 1,
321
+ 'rgba(255, 0, 153.6, 1)': 1,
322
+ 'rgba(1e2, .5e1, .5e0, +.25e2%)': 1
323
+ },
324
+ uniquenessRatio: 1,
325
+ },
326
+ background: {
327
+ total: 2,
328
+ totalUnique: 1,
329
+ unique: {
330
+ 'rgb(5, 5, 5)': 2
331
+ },
332
+ uniquenessRatio: 0.5,
333
+ },
334
+ 'box-shadow': {
335
+ total: 1,
336
+ totalUnique: 1,
337
+ unique: {
338
+ 'rgb(6,6,6)': 1
339
+ },
340
+ uniquenessRatio: 1,
341
+ },
342
+ 'outline-color': {
343
+ total: 1,
344
+ totalUnique: 1,
345
+ unique: {
346
+ 'rgb(7,7,7)': 1
347
+ },
348
+ uniquenessRatio: 1,
349
+ },
350
+ 'test': {
351
+ total: 1,
352
+ totalUnique: 1,
353
+ unique: {
354
+ 'rgb(8,8,8)': 1
355
+ },
356
+ uniquenessRatio: 1,
357
+ },
358
+ },
359
+ }
360
+ assert.equal(actual, expected)
361
+ })
362
+
363
+ Colors('finds LCH() colors', () => {
364
+ const actual = analyze(`
365
+ .lch {
366
+ /* Source: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/lch() */
367
+ color: lch(29.2345% 44.2 27);
368
+ color: lch(52.2345% 72.2 56.2);
369
+
370
+ /* Capitalisation */
371
+ color: LCH(29.2345% 44.2 27);
372
+ color: Lch(29.2345% 44.2 27);
373
+
374
+ /* Opacity notations */
375
+ color: lch(52.2345% 72.2 56.2 / 1);
376
+ color: lch(52.2345% 72.2 56.2 / .5);
377
+ color: lch(52.2345% 72.2 56.2 / 0.5);
378
+ color: lch(52.2345% 72.2 56.2 / 50%);
379
+ }
380
+ `)
381
+ const expected = {
382
+ total: 8,
383
+ totalUnique: 8,
384
+ unique: {
385
+ 'lch(29.2345% 44.2 27)': 1,
386
+ 'lch(52.2345% 72.2 56.2)': 1,
387
+
388
+ /* Capitalisation */
389
+ 'LCH(29.2345% 44.2 27)': 1,
390
+ 'Lch(29.2345% 44.2 27)': 1,
391
+
392
+ /* Opacity notations */
393
+ 'lch(52.2345% 72.2 56.2 / 1)': 1,
394
+ 'lch(52.2345% 72.2 56.2 / .5)': 1,
395
+ 'lch(52.2345% 72.2 56.2 / 0.5)': 1,
396
+ 'lch(52.2345% 72.2 56.2 / 50%)': 1,
397
+ },
398
+ uniquenessRatio: 1,
399
+ itemsPerContext: {
400
+ color: {
401
+ total: 8,
402
+ totalUnique: 8,
403
+ unique: {
404
+ 'lch(29.2345% 44.2 27)': 1,
405
+ 'lch(52.2345% 72.2 56.2)': 1,
406
+ 'LCH(29.2345% 44.2 27)': 1,
407
+ 'Lch(29.2345% 44.2 27)': 1,
408
+ 'lch(52.2345% 72.2 56.2 / 1)': 1,
409
+ 'lch(52.2345% 72.2 56.2 / .5)': 1,
410
+ 'lch(52.2345% 72.2 56.2 / 0.5)': 1,
411
+ 'lch(52.2345% 72.2 56.2 / 50%)': 1
412
+ },
413
+ uniquenessRatio: 1,
414
+ },
415
+ }
416
+ }
417
+
418
+ assert.equal(actual.values.colors, expected)
419
+ })
420
+
421
+ Colors('finds LAB() colors', () => {
422
+ const actual = analyze(`
423
+ .lch {
424
+ /* Source: https://drafts.csswg.org/css-color/#specifying-lab-lch */
425
+ color: lab(52.2345% 40.1645 59.9971);
426
+ color: lab(60.2345% -5.3654 58.956);
427
+ color: lab(62.2345% -34.9638 47.7721);
428
+ color: lab(67.5345% -8.6911 -41.6019);
429
+
430
+ /* Capitalisation */
431
+ color: lab(29.2345% 39.3825 20.0664);
432
+ color: Lab(29.2345% 39.3825 20.0664);
433
+ color: LAB(29.2345% 39.3825 20.0664);
434
+
435
+ /* Opacity notations */
436
+ color: lab(52.2345% 40.1645 59.9971 / 0);
437
+ color: lab(52.2345% 40.1645 59.9971 / .5);
438
+ color: lab(52.2345% 40.1645 59.9971 / 0.5);
439
+ color: lab(52.2345% 40.1645 59.9971 / 50%);
440
+ }
441
+ `)
442
+ const expected = {
443
+ total: 11,
444
+ totalUnique: 11,
445
+ unique: {
446
+ 'lab(52.2345% 40.1645 59.9971)': 1,
447
+ 'lab(60.2345% -5.3654 58.956)': 1,
448
+ 'lab(62.2345% -34.9638 47.7721)': 1,
449
+ 'lab(67.5345% -8.6911 -41.6019)': 1,
450
+
451
+ /* Capitalisation */
452
+ 'lab(29.2345% 39.3825 20.0664)': 1,
453
+ 'Lab(29.2345% 39.3825 20.0664)': 1,
454
+ 'LAB(29.2345% 39.3825 20.0664)': 1,
455
+
456
+ /* Opacity notations */
457
+ 'lab(52.2345% 40.1645 59.9971 / 0)': 1,
458
+ 'lab(52.2345% 40.1645 59.9971 / .5)': 1,
459
+ 'lab(52.2345% 40.1645 59.9971 / 0.5)': 1,
460
+ 'lab(52.2345% 40.1645 59.9971 / 50%)': 1,
461
+ },
462
+ uniquenessRatio: 1,
463
+ 'itemsPerContext': {
464
+ 'color': {
465
+ 'total': 11,
466
+ 'totalUnique': 11,
467
+ 'unique': {
468
+ 'lab(52.2345% 40.1645 59.9971)': 1,
469
+ 'lab(60.2345% -5.3654 58.956)': 1,
470
+ 'lab(62.2345% -34.9638 47.7721)': 1,
471
+ 'lab(67.5345% -8.6911 -41.6019)': 1,
472
+ 'lab(29.2345% 39.3825 20.0664)': 1,
473
+ 'Lab(29.2345% 39.3825 20.0664)': 1,
474
+ 'LAB(29.2345% 39.3825 20.0664)': 1,
475
+ 'lab(52.2345% 40.1645 59.9971 / 0)': 1,
476
+ 'lab(52.2345% 40.1645 59.9971 / .5)': 1,
477
+ 'lab(52.2345% 40.1645 59.9971 / 0.5)': 1,
478
+ 'lab(52.2345% 40.1645 59.9971 / 50%)': 1
479
+ },
480
+ uniquenessRatio: 1,
481
+ },
482
+ }
483
+ }
484
+
485
+ assert.equal(actual.values.colors, expected)
486
+ })
487
+
488
+ Colors('finds hwb() colors', () => {
489
+ const actual = analyze(`
490
+ .hwb {
491
+ color: hwb(194 0% 0%);
492
+ color: hwb(194, 0%, 0%);
493
+
494
+ color: Hwb(194 0% 0%);
495
+ color: HWB(194 0% 0%);
496
+
497
+ color: hwb(194 0% 0% / .5);
498
+ color: hwb(194 0% 0% / 0.5);
499
+ color: hwb(194 0% 0% / 50%);
500
+ }
501
+ `)
502
+ const expected = {
503
+ total: 7,
504
+ totalUnique: 7,
505
+ uniquenessRatio: 1,
506
+ unique: {
507
+ 'hwb(194 0% 0%)': 1,
508
+ 'hwb(194, 0%, 0%)': 1,
509
+
510
+ 'Hwb(194 0% 0%)': 1,
511
+ 'HWB(194 0% 0%)': 1,
512
+
513
+ 'hwb(194 0% 0% / .5)': 1,
514
+ 'hwb(194 0% 0% / 0.5)': 1,
515
+ 'hwb(194 0% 0% / 50%)': 1,
516
+ },
517
+ itemsPerContext: {
518
+ color: {
519
+ total: 7,
520
+ totalUnique: 7,
521
+ unique: {
522
+ 'hwb(194 0% 0%)': 1,
523
+ 'hwb(194, 0%, 0%)': 1,
524
+ 'Hwb(194 0% 0%)': 1,
525
+ 'HWB(194 0% 0%)': 1,
526
+ 'hwb(194 0% 0% / .5)': 1,
527
+ 'hwb(194 0% 0% / 0.5)': 1,
528
+ 'hwb(194 0% 0% / 50%)': 1
529
+ },
530
+ uniquenessRatio: 1,
531
+ }
532
+ }
533
+ }
534
+
535
+ assert.equal(actual.values.colors, expected)
536
+ })
537
+
538
+ Colors('finds color() colors', () => {
539
+ const actual = analyze(`
540
+ .colors {
541
+ color: color(rec2020 0.42053 0.979780 0.00579);
542
+ color: color(display-p3 -0.6112 1.0079 -0.2192);
543
+ color: color(profoto-rgb 0.4835 0.9167 0.2188);
544
+ color: color(xyz-d50 0.2005 0.14089 0.4472);
545
+ }
546
+ `)
547
+ const expected = {
548
+ total: 4,
549
+ totalUnique: 4,
550
+ unique: {
551
+ 'color(rec2020 0.42053 0.979780 0.00579)': 1,
552
+ 'color(display-p3 -0.6112 1.0079 -0.2192)': 1,
553
+ 'color(profoto-rgb 0.4835 0.9167 0.2188)': 1,
554
+ 'color(xyz-d50 0.2005 0.14089 0.4472)': 1,
555
+ },
556
+ uniquenessRatio: 1,
557
+ itemsPerContext: {
558
+ color: {
559
+ total: 4,
560
+ totalUnique: 4,
561
+ unique: {
562
+ 'color(rec2020 0.42053 0.979780 0.00579)': 1,
563
+ 'color(display-p3 -0.6112 1.0079 -0.2192)': 1,
564
+ 'color(profoto-rgb 0.4835 0.9167 0.2188)': 1,
565
+ 'color(xyz-d50 0.2005 0.14089 0.4472)': 1
566
+ },
567
+ uniquenessRatio: 1
568
+ },
569
+ },
570
+ }
571
+
572
+ assert.equal(actual.values.colors, expected)
573
+ })
574
+
575
+ Colors('finds color keywords', () => {
576
+ const actual = analyze(`
577
+ test {
578
+ outline: 1px solid tomato;
579
+ border-color: Aqua;
580
+ outline-color: whitesmoke;
581
+ background: linear-gradient(90deg, purple 0, purple 100%);
582
+ }
583
+ `).values.colors
584
+ const expected = {
585
+ total: 5,
586
+ totalUnique: 4,
587
+ unique: {
588
+ 'tomato': 1,
589
+ 'Aqua': 1,
590
+ 'whitesmoke': 1,
591
+ 'purple': 2,
592
+ },
593
+ uniquenessRatio: 4 / 5,
594
+ itemsPerContext: {
595
+ outline: {
596
+ total: 1,
597
+ totalUnique: 1,
598
+ unique: {
599
+ tomato: 1,
600
+ },
601
+ uniquenessRatio: 1,
602
+ },
603
+ 'border-color': {
604
+ total: 1,
605
+ totalUnique: 1,
606
+ unique: {
607
+ Aqua: 1,
608
+ },
609
+ uniquenessRatio: 1,
610
+ },
611
+ 'outline-color': {
612
+ total: 1,
613
+ totalUnique: 1,
614
+ unique: {
615
+ whitesmoke: 1
616
+ },
617
+ uniquenessRatio: 1,
618
+ },
619
+ background: {
620
+ total: 2,
621
+ totalUnique: 1,
622
+ unique: {
623
+ purple: 2
624
+ },
625
+ uniquenessRatio: 0.5,
626
+ },
627
+ },
628
+ }
629
+ assert.equal(actual, expected)
630
+ })
631
+
632
+ Colors('does not report false positives for color keywords', () => {
633
+ const actual = analyze(`
634
+ test {
635
+ /* Not a blue keyword */
636
+ background: url(icon-blue.png), url(blue-icon.png), url(blue_icon.png), url(icon_blue.png);
637
+
638
+ /* Not a hex color */
639
+ background-image: url(#footer-logo-text-linear-gradient);
640
+
641
+ /* Not the white color keyword */
642
+ white-space: nowrap;
643
+
644
+ /* Not the linen color keyword */
645
+ counter-increment: lineNo;
646
+
647
+ /* Not the gray keyword */
648
+ -moz-osx-font-smoothing: grayscale;
649
+
650
+ /* Not the black color keyword */
651
+ font-family: Arial Black, Arial Bold, Gadget, sans-serif;
652
+ }
653
+ `).values.colors
654
+ const expected = {
655
+ total: 0,
656
+ totalUnique: 0,
657
+ unique: {},
658
+ uniquenessRatio: 0,
659
+ itemsPerContext: {},
660
+ }
661
+
662
+ assert.equal(actual, expected)
663
+ })
664
+
665
+ // Source: https://gist.github.com/lahmatiy/84c7fd877a78e561cac57e59e142c9e3
666
+ Colors.skip('ignores color names that are not actual colors', () => {
667
+ const fixture = `
668
+ test {
669
+ animation: red; /* it's not a color but an animation name */
670
+ unknown-property: blue; /* don't match value as a color when property is unknown */
671
+ }
672
+ `
673
+ const actual = analyze(fixture).values.colors
674
+ const expected = {
675
+ total: 0,
676
+ totalUnique: 0,
677
+ unique: {},
678
+ uniquenessRatio: 0,
679
+ itemsPerContext: {},
680
+ }
681
+
682
+ assert.equal(actual, expected)
683
+ })
684
+
685
+ Colors('ignores CSS keywords', () => {
686
+ const fixture = `
687
+ testColorKeywords {
688
+ color: currentColor;
689
+ color: currentcolor;
690
+ color: inherit;
691
+ color: unset;
692
+ color: initial;
693
+ color: transparent;
694
+ }
695
+ `
696
+ const actual = analyze(fixture).values.colors
697
+ const expected = {
698
+ total: 0,
699
+ totalUnique: 0,
700
+ unique: {},
701
+ uniquenessRatio: 0,
702
+ itemsPerContext: {},
703
+ }
704
+
705
+ assert.equal(actual, expected)
706
+ })
707
+
708
+ // https://drafts.csswg.org/css-color/#css-system-colors
709
+ Colors('finds System Colors', () => {
710
+ const fixture = `
711
+ color: test-system-colors {
712
+ color: Canvas;
713
+ color: CanvasText;
714
+ color: Linktext;
715
+ color: VisitedText;
716
+ color: ActiveText;
717
+ color: ButtonFace;
718
+ color: ButtonText;
719
+ color: ButtonBorder;
720
+ color: Field;
721
+ color: FieldText;
722
+ color: Highlight;
723
+ color: HighlightText;
724
+ color: SelectedItem;
725
+ color: SelectedItemText;
726
+ color: Mark;
727
+ color: MarkText;
728
+ color: GrayText;
729
+ }
730
+
731
+ test-false-positives {
732
+ color: RandomWord;
733
+ }
734
+ `
735
+ const result = analyze(fixture)
736
+ const actual = result.values.colors
737
+ const expected = {
738
+ total: 17,
739
+ totalUnique: 17,
740
+ unique: {
741
+ Canvas: 1,
742
+ CanvasText: 1,
743
+ Linktext: 1,
744
+ VisitedText: 1,
745
+ ActiveText: 1,
746
+ ButtonFace: 1,
747
+ ButtonText: 1,
748
+ ButtonBorder: 1,
749
+ Field: 1,
750
+ FieldText: 1,
751
+ Highlight: 1,
752
+ HighlightText: 1,
753
+ SelectedItem: 1,
754
+ SelectedItemText: 1,
755
+ Mark: 1,
756
+ MarkText: 1,
757
+ GrayText: 1,
758
+ },
759
+ uniquenessRatio: 17 / 17,
760
+ itemsPerContext: {
761
+ color: {
762
+ total: 17,
763
+ totalUnique: 17,
764
+ unique: {
765
+ Canvas: 1,
766
+ CanvasText: 1,
767
+ Linktext: 1,
768
+ VisitedText: 1,
769
+ ActiveText: 1,
770
+ ButtonFace: 1,
771
+ ButtonText: 1,
772
+ ButtonBorder: 1,
773
+ Field: 1,
774
+ FieldText: 1,
775
+ Highlight: 1,
776
+ HighlightText: 1,
777
+ SelectedItem: 1,
778
+ SelectedItemText: 1,
779
+ Mark: 1,
780
+ MarkText: 1,
781
+ GrayText: 1,
782
+ },
783
+ uniquenessRatio: 19 / 19,
784
+ },
785
+ },
786
+ }
787
+
788
+ assert.equal(actual, expected)
789
+ })
790
+
791
+ Colors('insane color mode', () => {
792
+ const fixture = `
793
+ .color-insanity {
794
+ color: #7654CD;
795
+ color: rgb(46.27% 32.94% 80.39%);
796
+ color: lab(44.36% 36.05 -58.99);
797
+ color: color(xyz-d50 0.2005 0.14089 0.4472);
798
+ }
799
+ `
800
+
801
+ assert.not.throws(() => analyze(fixture))
802
+ })
803
+
804
+ Colors.run()