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

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 (38) hide show
  1. package/package.json +2 -5
  2. package/src/aggregate-collection.js +0 -113
  3. package/src/aggregate-collection.test.js +0 -47
  4. package/src/atrules/atrules.js +0 -76
  5. package/src/atrules/atrules.test.js +0 -289
  6. package/src/context-collection.js +0 -36
  7. package/src/countable-collection.js +0 -46
  8. package/src/declarations/declarations.js +0 -46
  9. package/src/declarations/declarations.test.js +0 -113
  10. package/src/index.js +0 -259
  11. package/src/index.test.js +0 -60
  12. package/src/properties/properties.js +0 -48
  13. package/src/properties/properties.test.js +0 -138
  14. package/src/rules/rules.js +0 -57
  15. package/src/rules/rules.test.js +0 -247
  16. package/src/selectors/complexity.test.js +0 -123
  17. package/src/selectors/selectors.js +0 -122
  18. package/src/selectors/selectors.test.js +0 -189
  19. package/src/selectors/specificity.js +0 -201
  20. package/src/selectors/specificity.test.js +0 -247
  21. package/src/smoke.test.js +0 -39
  22. package/src/stylesheet/stylesheet.test.js +0 -88
  23. package/src/values/animations.js +0 -53
  24. package/src/values/animations.test.js +0 -154
  25. package/src/values/box-shadows.test.js +0 -82
  26. package/src/values/colors.js +0 -192
  27. package/src/values/colors.test.js +0 -804
  28. package/src/values/font-families.js +0 -98
  29. package/src/values/font-families.test.js +0 -119
  30. package/src/values/font-sizes.js +0 -92
  31. package/src/values/font-sizes.test.js +0 -120
  32. package/src/values/text-shadows.test.js +0 -93
  33. package/src/values/units.test.js +0 -72
  34. package/src/values/values.js +0 -30
  35. package/src/values/vendor-prefix.js +0 -45
  36. package/src/values/vendor-prefix.test.js +0 -64
  37. package/src/values/z-index.test.js +0 -54
  38. package/src/vendor-prefix.js +0 -16
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@projectwallace/css-analyzer",
3
- "version": "5.0.0-alpha.1",
3
+ "version": "5.0.0-alpha.2",
4
4
  "author": "Bart Veneman",
5
5
  "repository": {
6
6
  "type": "git",
@@ -37,10 +37,7 @@
37
37
  "metrics"
38
38
  ],
39
39
  "files": [
40
- "dist",
41
- "src",
42
- "!src/__fixtures__",
43
- "!*.test.js"
40
+ "dist"
44
41
  ],
45
42
  "dependencies": {
46
43
  "css-tree": "^2.0.1"
@@ -1,113 +0,0 @@
1
- /**
2
- * Find the mode (most occurring value) in an array of Numbers
3
- * Takes the mean/average of multiple values if multiple values occur the same amount of times.
4
- *
5
- * @see https://github.com/angus-c/just/blob/684af9ca0c7808bc78543ec89379b1fdfce502b1/packages/array-mode/index.js
6
- * @param {Array} arr - Array to find the mode value for
7
- * @returns {Number} mode - The `mode` value of `arr`
8
- */
9
- function Mode(arr) {
10
- const frequencies = Object.create(null)
11
- let maxOccurrences = -1
12
- let maxOccurenceCount = 0
13
- let sum = 0
14
-
15
- for (let i = 0; i < arr.length; i++) {
16
- const element = arr[i]
17
- const updatedCount = (frequencies[element] || 0) + 1
18
- frequencies[element] = updatedCount
19
-
20
- if (updatedCount > maxOccurrences) {
21
- maxOccurrences = updatedCount
22
- maxOccurenceCount = 0
23
- sum = 0
24
- }
25
-
26
- if (updatedCount >= maxOccurrences) {
27
- maxOccurenceCount++
28
- sum += element
29
- }
30
- }
31
-
32
- return sum / maxOccurenceCount
33
- }
34
-
35
- /**
36
- * Find the middle number in an Array of Numbers
37
- * Returns the average of 2 numbers if the Array length is an even number
38
- * @see https://github.com/angus-c/just/blob/684af9ca0c7808bc78543ec89379b1fdfce502b1/packages/array-median/index.js
39
- * @param {Array} arr - A sorted Array
40
- * @returns {Number} - The array's Median
41
- */
42
- function Median(arr) {
43
- const middle = arr.length / 2
44
- const lowerMiddleRank = Math.floor(middle)
45
-
46
- if (middle !== lowerMiddleRank) {
47
- return arr[lowerMiddleRank]
48
- }
49
- return (arr[lowerMiddleRank] + arr[lowerMiddleRank - 1]) / 2
50
- }
51
-
52
- class AggregateCollection {
53
- constructor(size) {
54
- /** @type Number[] */
55
- this.items = new Uint8Array(size)
56
- this.sum = 0
57
- this.cursor = 0
58
- }
59
-
60
- /**
61
- * Add a new Integer to the AggregateCollection
62
- * @param {number} item - The item to add to the AggregateCollection
63
- */
64
- add(item) {
65
- this.items[this.cursor] = (item)
66
- this.sum += item
67
- this.cursor++
68
- }
69
-
70
- aggregate() {
71
- if (this.cursor === 0) {
72
- return {
73
- min: 0,
74
- max: 0,
75
- mean: 0,
76
- mode: 0,
77
- median: 0,
78
- range: 0,
79
- sum: 0,
80
- }
81
- }
82
-
83
- /** @type Number[] */
84
- const sorted = new Uint8Array(
85
- this.items.slice(0, this.cursor)
86
- ).sort((a, b) => a - b)
87
- const min = sorted[0]
88
- const max = sorted[sorted.length - 1]
89
-
90
- const mode = Mode(sorted)
91
- const median = Median(sorted)
92
-
93
- return {
94
- min,
95
- max,
96
- mean: this.sum / this.cursor,
97
- mode,
98
- median,
99
- range: max - min,
100
- sum: this.sum,
101
- }
102
- }
103
-
104
- toArray() {
105
- return Array.from(
106
- this.items.subarray(0, this.cursor)
107
- )
108
- }
109
- }
110
-
111
- export {
112
- AggregateCollection
113
- }
@@ -1,47 +0,0 @@
1
- import { suite } from 'uvu';
2
- import * as assert from 'uvu/assert';
3
- import { AggregateCollection } from './aggregate-collection.js'
4
-
5
- const CollectionSuite = suite('AggregateCollection')
6
-
7
- CollectionSuite('aggregates correctly', () => {
8
- const fixture = new AggregateCollection(6)
9
- fixture.add(1)
10
- fixture.add(2)
11
- fixture.add(25)
12
- fixture.add(3)
13
- fixture.add(4)
14
- fixture.add(4)
15
- const actual = fixture.aggregate()
16
- const expected = {
17
- max: 25,
18
- min: 1,
19
- range: 24,
20
- mean: 39 / 6,
21
- median: 3.5,
22
- mode: 4,
23
- sum: 39,
24
- }
25
-
26
- assert.equal(actual, expected)
27
- assert.equal(fixture.toArray(), [1, 2, 25, 3, 4, 4])
28
- })
29
-
30
- CollectionSuite('handles collections without values', () => {
31
- const fixture = new AggregateCollection(0)
32
- const aggregate = fixture.aggregate()
33
- const items = fixture.toArray()
34
-
35
- assert.equal(aggregate, {
36
- max: 0,
37
- min: 0,
38
- range: 0,
39
- mean: 0,
40
- median: 0,
41
- mode: 0,
42
- sum: 0,
43
- })
44
- assert.equal(items, [])
45
- })
46
-
47
- CollectionSuite.run()
@@ -1,76 +0,0 @@
1
- import { CountableCollection } from '../countable-collection.js'
2
- import { hasVendorPrefix } from '../vendor-prefix.js'
3
-
4
- const analyzeAtRules = ({ atrules, stringifyNode }) => {
5
- const fontfaces = []
6
- const imports = new CountableCollection()
7
- const medias = new CountableCollection()
8
- const charsets = new CountableCollection()
9
- const supports = new CountableCollection()
10
- const keyframes = new CountableCollection()
11
- const prefixedKeyframes = new CountableCollection()
12
- const containers = new CountableCollection()
13
-
14
- const machine = {
15
- 'font-face': (node) => {
16
- const descriptors = {}
17
-
18
- node.block.children.forEach(descriptor => {
19
- descriptors[descriptor.property] = stringifyNode(descriptor.value)
20
- })
21
-
22
- fontfaces.push(descriptors)
23
- },
24
- 'media': node => medias.push(node.prelude.value),
25
- 'supports': node => supports.push(node.prelude.value),
26
- 'keyframes': node => keyframes.push(`@${node.name} ${node.prelude.value}`),
27
- 'import': node => imports.push(node.prelude.value),
28
- 'charset': node => charsets.push(node.prelude.value),
29
- 'container': node => containers.push(node.prelude.value),
30
- }
31
-
32
- for (let i = 0; i < atrules.length; i++) {
33
- const node = atrules[i]
34
- const nodeName = node.name
35
- const action = machine[nodeName]
36
- if (action) {
37
- action(node)
38
- continue
39
- }
40
-
41
- if (nodeName.endsWith('keyframes')) {
42
- const name = `@${nodeName} ${node.prelude.value}`
43
- keyframes.push(name)
44
-
45
- if (hasVendorPrefix(nodeName)) {
46
- prefixedKeyframes.push(name)
47
- }
48
- continue
49
- }
50
- }
51
-
52
- return {
53
- fontface: {
54
- total: fontfaces.length,
55
- totalUnique: fontfaces.length,
56
- unique: fontfaces,
57
- uniquenessRatio: 1
58
- },
59
- import: imports.count(),
60
- media: medias.count(),
61
- charset: charsets.count(),
62
- supports: supports.count(),
63
- keyframes: {
64
- ...keyframes.count(),
65
- prefixed: {
66
- ...prefixedKeyframes.count(),
67
- ratio: prefixedKeyframes.size() / keyframes.size()
68
- }
69
- },
70
- container: containers.count(),
71
- }
72
- }
73
-
74
- export {
75
- analyzeAtRules
76
- }
@@ -1,289 +0,0 @@
1
- import { suite } from 'uvu'
2
- import * as assert from 'uvu/assert'
3
- import { analyze } from '../index.js'
4
-
5
- const AtRules = suite('at-rules')
6
-
7
- AtRules('finds @font-face', () => {
8
- const fixture = `
9
- @font-face {
10
- font-family: Arial;
11
- src: url("https://url-to-arial.woff");
12
- }
13
-
14
- @font-face {
15
- font-display: swap;
16
- font-family: Test;
17
- font-stretch: condensed;
18
- font-style: italic;
19
- font-weight: 700;
20
- font-variant: no-common-ligatures proportional-nums;
21
- font-feature-settings: "liga" 0;
22
- font-variation-settings: "xhgt" 0.7;
23
- src: local("Input Mono");
24
- unicode-range: U+0025-00FF;
25
- }
26
-
27
- @font-face {
28
- font-family: 'Input Mono';
29
- src: local('Input Mono') url("https://url-to-input-mono.woff");
30
- }
31
-
32
- @font-face {
33
- font-family: MyHelvetica;
34
- src: local("Helvetica Neue Bold"), local("HelveticaNeue-Bold"), url(MgOpenModernaBold.ttf);
35
- font-weight: bold;
36
- }
37
-
38
- /* Duplicate @font-face in Media Query */
39
- @media (min-width: 1000px) {
40
- @font-face {
41
- font-family: 'Input Mono';
42
- src: local('Input Mono') url("https://url-to-input-mono.woff");
43
- }
44
- }`
45
- const actual = analyze(fixture).atrules.fontface
46
- const expected = {
47
- total: 5,
48
- totalUnique: 5,
49
- unique: [
50
- {
51
- "font-family": "Arial",
52
- "src": "url(\"https://url-to-arial.woff\")"
53
- },
54
- {
55
- "font-display": `swap`,
56
- "font-family": `Test`,
57
- "font-stretch": `condensed`,
58
- "font-style": `italic`,
59
- "font-weight": `700`,
60
- "font-variant": `no-common-ligatures proportional-nums`,
61
- "font-feature-settings": `"liga" 0`,
62
- "font-variation-settings": `"xhgt" 0.7`,
63
- "src": `local("Input Mono")`,
64
- "unicode-range": `U+0025-00FF`,
65
- },
66
- {
67
- "font-family": "'Input Mono'",
68
- "src": "local('Input Mono') url(\"https://url-to-input-mono.woff\")"
69
- },
70
- {
71
- 'font-family': 'MyHelvetica',
72
- 'src': 'local("Helvetica Neue Bold"), local("HelveticaNeue-Bold"), url(MgOpenModernaBold.ttf)',
73
- 'font-weight': 'bold',
74
- },
75
- {
76
- "font-family": "'Input Mono'",
77
- "src": "local('Input Mono') url(\"https://url-to-input-mono.woff\")"
78
- }
79
- ],
80
- uniquenessRatio: 1
81
- }
82
-
83
- assert.equal(actual, expected)
84
- })
85
-
86
- AtRules('finds @imports', () => {
87
- const fixture = `
88
- @import "https://example.com/without-url";
89
- @import url("https://example.com/with-url");
90
- @import url("https://example.com/with-media-query") screen and (min-width: 33em);
91
- @import url("https://example.com/with-multiple-media-queries") screen, projection;
92
- `
93
- const actual = analyze(fixture).atrules.import
94
- const expected = {
95
- total: 4,
96
- totalUnique: 4,
97
- unique: {
98
- '"https://example.com/without-url"': 1,
99
- 'url("https://example.com/with-url")': 1,
100
- 'url("https://example.com/with-media-query") screen and (min-width: 33em)': 1,
101
- 'url("https://example.com/with-multiple-media-queries") screen, projection': 1,
102
- },
103
- uniquenessRatio: 4 / 4
104
- }
105
-
106
- assert.equal(actual, expected)
107
- })
108
-
109
- AtRules('finds @charsets', () => {
110
- const fixture = `
111
- @charset "UTF-8";
112
- @charset "UTF-16";
113
- `
114
- const actual = analyze(fixture).atrules.charset
115
- const expected = {
116
- total: 2,
117
- totalUnique: 2,
118
- unique: {
119
- '"UTF-8"': 1,
120
- '"UTF-16"': 1,
121
- },
122
- uniquenessRatio: 2 / 2
123
- }
124
-
125
- assert.equal(actual, expected)
126
- })
127
-
128
- AtRules('finds @supports', () => {
129
- const fixture = `
130
- @supports (filter: blur(5px)) {}
131
- @supports (display: table-cell) and (display: list-item) {}
132
- @supports (-webkit-appearance: none) {}
133
-
134
- @media (min-width: 0) {
135
- @supports (-webkit-appearance: none) {}
136
- }
137
- `
138
- const actual = analyze(fixture).atrules.supports
139
- const expected = {
140
- total: 4,
141
- totalUnique: 3,
142
- unique: {
143
- '(filter: blur(5px))': 1,
144
- '(display: table-cell) and (display: list-item)': 1,
145
- '(-webkit-appearance: none)': 2,
146
- },
147
- uniquenessRatio: 3 / 4
148
- }
149
-
150
- assert.equal(actual, expected)
151
- })
152
-
153
- AtRules('finds @media', () => {
154
- const fixture = `
155
- @media screen {}
156
- @media screen and (min-width: 33em) {}
157
- @media (min-width: 20px) {}
158
- @media (max-width: 200px) {}
159
- @media screen or print {}
160
- @media \\0 all {}
161
-
162
- @supports (-webkit-appearance: none) {
163
- @media (min-width: 0) {}
164
- }
165
- `
166
- const actual = analyze(fixture).atrules.media
167
- const expected = {
168
- total: 7,
169
- totalUnique: 7,
170
- unique: {
171
- 'screen': 1,
172
- 'screen and (min-width: 33em)': 1,
173
- '(min-width: 20px)': 1,
174
- '(max-width: 200px)': 1,
175
- 'screen or print': 1,
176
- '\\0 all': 1,
177
- '(min-width: 0)': 1,
178
- },
179
- uniquenessRatio: 7 / 7
180
- }
181
-
182
- assert.equal(actual, expected)
183
- })
184
-
185
- AtRules('analyzes @keyframes', () => {
186
- const fixture = `
187
- @keyframes one {}
188
- @keyframes one {}
189
- @keyframes TWO {}
190
-
191
- /* Prefixes */
192
- @-webkit-keyframes animation {}
193
- @-moz-keyframes animation {}
194
- @-o-keyframes animation {}
195
- `
196
- const actual = analyze(fixture).atrules.keyframes
197
- const expected = {
198
- total: 6,
199
- totalUnique: 5,
200
- unique: {
201
- '@keyframes one': 2,
202
- '@keyframes TWO': 1,
203
- '@-webkit-keyframes animation': 1,
204
- '@-moz-keyframes animation': 1,
205
- '@-o-keyframes animation': 1,
206
- },
207
- uniquenessRatio: 5 / 6,
208
- prefixed: {
209
- total: 3,
210
- totalUnique: 3,
211
- unique: {
212
- '@-webkit-keyframes animation': 1,
213
- '@-moz-keyframes animation': 1,
214
- '@-o-keyframes animation': 1,
215
- },
216
- uniquenessRatio: 3 / 3,
217
- ratio: 3 / 6
218
- }
219
- }
220
-
221
- assert.equal(actual, expected)
222
- })
223
-
224
- AtRules('analyzes container queries', () => {
225
- // Fixture contains examples from the spec.
226
- // https://drafts.csswg.org/css-contain-3/
227
- const fixture = `
228
- /* Example 2 */
229
- @container (inline-size > 45em) {
230
- .media-object {
231
- grid-template: 'img content' auto / auto 1fr;
232
- }
233
- }
234
-
235
- /* Example 3 */
236
- @container (width > 40em) {
237
- h2 { font-size: 1.5em; }
238
- }
239
-
240
- /* Example 4 */
241
- @container (--cards) {
242
- article {
243
- border: thin solid silver;
244
- border-radius: 0.5em;
245
- padding: 1em;
246
- }
247
- }
248
-
249
- /* Example 5 */
250
- @container page-layout (block-size > 12em) {
251
- .card { margin-block: 2em; }
252
- }
253
-
254
- @container component-library (inline-size > 30em) {
255
- .card { margin-inline: 2em; }
256
- }
257
-
258
- /* Example 8 */
259
- @container card (inline-size > 30em) and (--responsive = true) {
260
- /* styles */
261
- }
262
-
263
- /* Example 11 */
264
- @container type(inline-size) {
265
- /* only applies when an inline-size container is available */
266
- h2 { font-size: calc(1.2em + 1cqi); }
267
- }
268
- `
269
- const result = analyze(fixture)
270
- const actual = result.atrules.container
271
- const expected = {
272
- total: 7,
273
- totalUnique: 7,
274
- unique: {
275
- '(inline-size > 45em)': 1,
276
- '(width > 40em)': 1,
277
- '(--cards)': 1,
278
- 'page-layout (block-size > 12em)': 1,
279
- 'component-library (inline-size > 30em)': 1,
280
- 'card (inline-size > 30em) and (--responsive = true)': 1,
281
- 'type(inline-size)': 1,
282
- },
283
- uniquenessRatio: 7 / 7
284
- }
285
-
286
- assert.equal(actual, expected)
287
- })
288
-
289
- AtRules.run()
@@ -1,36 +0,0 @@
1
- import { CountableCollection } from './countable-collection.js'
2
-
3
- class ContextCollection {
4
- constructor() {
5
- this.list = new CountableCollection()
6
- this.contexts = {}
7
- this.contextCount = 0
8
- }
9
-
10
- push(item, context) {
11
- this.list.push(item)
12
-
13
- if (!this.contexts[context]) {
14
- this.contexts[context] = new CountableCollection()
15
- this.contextCount++
16
- }
17
-
18
- this.contexts[context].push(item)
19
- }
20
-
21
- count() {
22
- const itemsPerContext = {}
23
-
24
- for (let context in this.contexts) {
25
- itemsPerContext[context] = this.contexts[context].count()
26
- }
27
-
28
- return Object.assign(this.list.count(), {
29
- itemsPerContext
30
- })
31
- }
32
- }
33
-
34
- export {
35
- ContextCollection
36
- }
@@ -1,46 +0,0 @@
1
- class CountableCollection {
2
- constructor(initial) {
3
- this.items = {}
4
- this.total = 0
5
- this.totalUnique = 0
6
-
7
- if (initial) {
8
- for (let index = 0; index < initial.length; index++) {
9
- this.push(initial[index])
10
- }
11
- }
12
- }
13
-
14
- /**
15
- *
16
- * @param {string} item
17
- */
18
- push(item) {
19
- this.total++
20
-
21
- if (this.items[item]) {
22
- this.items[item]++
23
- return
24
- }
25
-
26
- this.items[item] = 1
27
- this.totalUnique++
28
- }
29
-
30
- size() {
31
- return this.total
32
- }
33
-
34
- count() {
35
- return {
36
- total: this.total,
37
- totalUnique: this.totalUnique,
38
- unique: this.items,
39
- uniquenessRatio: this.total === 0 ? 0 : this.totalUnique / this.total,
40
- }
41
- }
42
- }
43
-
44
- export {
45
- CountableCollection
46
- }
@@ -1,46 +0,0 @@
1
- const analyzeDeclarations = ({ stringifyNode, declarations }) => {
2
- const total = declarations.length
3
- const cache = Object.create(null)
4
- let importants = 0
5
- let totalUnique = 0
6
- let totalInKeyframes = 0
7
-
8
- for (let i = 0; i < total; i++) {
9
- const declaration = declarations[i]
10
-
11
- if (declaration.important === true) {
12
- importants++
13
-
14
- if (declaration.inKeyframe) {
15
- totalInKeyframes++
16
- }
17
- }
18
-
19
- const stringified = stringifyNode(declaration)
20
-
21
- if (!cache[stringified]) {
22
- cache[stringified] = 1
23
- totalUnique++
24
- }
25
- }
26
-
27
- return {
28
- total,
29
- unique: {
30
- total: totalUnique,
31
- ratio: total === 0 ? 0 : totalUnique / total,
32
- },
33
- importants: {
34
- total: importants,
35
- ratio: total === 0 ? 0 : importants / total,
36
- inKeyframes: {
37
- total: totalInKeyframes,
38
- ratio: importants === 0 ? 0 : totalInKeyframes / importants,
39
- },
40
- },
41
- }
42
- }
43
-
44
- export {
45
- analyzeDeclarations
46
- }