diff --git a/src/__fixtures__/bol-com-20231008.json b/src/__fixtures__/bol-com-20231008.json index 6655514..f0bc75c 100644 --- a/src/__fixtures__/bol-com-20231008.json +++ b/src/__fixtures__/bol-com-20231008.json @@ -3,6 +3,7 @@ "sourceLinesOfCode": 18136, "linesOfCode": 29247, "size": 543848, + "complexity": 47099, "comments": { "total": 0, "size": 0 @@ -262,6 +263,16 @@ "totalUnique": 0, "unique": {}, "uniquenessRatio": 0 + }, + "total": 1061, + "complexity": { + "min": 1, + "max": 1, + "mean": 1, + "mode": 1, + "median": 1, + "range": 0, + "sum": 1061 } }, "rules": { @@ -55076,6 +55087,15 @@ "total": 0, "ratio": 0 } + }, + "complexity": { + "min": 1, + "max": 1, + "mean": 1, + "mode": 1, + "median": 1, + "range": 0, + "sum": 10441 } }, "properties": { @@ -56558,6 +56578,15 @@ "uniquenessRatio": 1 } } + }, + "complexity": { + "min": 1, + "max": 2, + "mean": 1.0010535389330524, + "mode": 1, + "median": 1, + "range": 1, + "sum": 10452 } } } \ No newline at end of file diff --git a/src/__fixtures__/bootstrap-5.3.2.json b/src/__fixtures__/bootstrap-5.3.2.json index 6bdd6cf..0997c05 100644 --- a/src/__fixtures__/bootstrap-5.3.2.json +++ b/src/__fixtures__/bootstrap-5.3.2.json @@ -3,6 +3,7 @@ "sourceLinesOfCode": 8640, "linesOfCode": 13899, "size": 262482, + "complexity": 23488, "comments": { "total": 3, "size": 225 @@ -149,6 +150,16 @@ "totalUnique": 0, "unique": {}, "uniquenessRatio": 0 + }, + "total": 115, + "complexity": { + "min": 1, + "max": 1, + "mean": 1, + "mode": 1, + "median": 1, + "range": 0, + "sum": 115 } }, "rules": { @@ -25986,6 +25997,15 @@ "total": 0, "ratio": 0 } + }, + "complexity": { + "min": 1, + "max": 1, + "mean": 1, + "mode": 1, + "median": 1, + "range": 0, + "sum": 5547 } }, "properties": { @@ -30627,6 +30647,15 @@ "uniquenessRatio": 0.03333333333333333 } } + }, + "complexity": { + "min": 1, + "max": 2, + "mean": 1.0030216850337719, + "mode": 1, + "median": 1, + "range": 1, + "sum": 5643 } } } \ No newline at end of file diff --git a/src/__fixtures__/cnn-20231008.json b/src/__fixtures__/cnn-20231008.json index 68e5457..4302e5f 100644 --- a/src/__fixtures__/cnn-20231008.json +++ b/src/__fixtures__/cnn-20231008.json @@ -3,6 +3,7 @@ "sourceLinesOfCode": 10127, "linesOfCode": 14690, "size": 821224, + "complexity": 41021, "comments": { "total": 0, "size": 0 @@ -160,6 +161,16 @@ "totalUnique": 0, "unique": {}, "uniquenessRatio": 0 + }, + "total": 120, + "complexity": { + "min": 1, + "max": 1, + "mean": 1, + "mode": 1, + "median": 1, + "range": 0, + "sum": 120 } }, "rules": { @@ -31333,6 +31344,15 @@ "total": 0, "ratio": 0 } + }, + "complexity": { + "min": 1, + "max": 1, + "mean": 1, + "mode": 1, + "median": 1, + "range": 0, + "sum": 5959 } }, "properties": { @@ -33685,6 +33705,15 @@ "uniquenessRatio": 1 } } + }, + "complexity": { + "min": 1, + "max": 2, + "mean": 1.0008390669575433, + "mode": 1, + "median": 1, + "range": 1, + "sum": 5964 } } } \ No newline at end of file diff --git a/src/__fixtures__/css-tricks-20231008.json b/src/__fixtures__/css-tricks-20231008.json index e72b0b0..a1b09d3 100644 --- a/src/__fixtures__/css-tricks-20231008.json +++ b/src/__fixtures__/css-tricks-20231008.json @@ -3,6 +3,7 @@ "sourceLinesOfCode": 4292, "linesOfCode": 6354, "size": 115415, + "complexity": 13339, "comments": { "total": 2, "size": 173 @@ -158,6 +159,16 @@ "totalUnique": 0, "unique": {}, "uniquenessRatio": 0 + }, + "total": 77, + "complexity": { + "min": 1, + "max": 2, + "mean": 1.025974025974026, + "mode": 1, + "median": 1, + "range": 1, + "sum": 79 } }, "rules": { @@ -12596,6 +12607,15 @@ "total": 0, "ratio": 0 } + }, + "complexity": { + "min": 1, + "max": 1, + "mean": 1, + "mode": 1, + "median": 1, + "range": 0, + "sum": 2683 } }, "properties": { @@ -14847,6 +14867,15 @@ "uniquenessRatio": 0.3333333333333333 } } + }, + "complexity": { + "min": 1, + "max": 2, + "mean": 1.0067089079388745, + "mode": 1, + "median": 1, + "range": 1, + "sum": 2701 } } } \ No newline at end of file diff --git a/src/__fixtures__/gazelle-20231008.json b/src/__fixtures__/gazelle-20231008.json index ada1f35..e75e49a 100644 --- a/src/__fixtures__/gazelle-20231008.json +++ b/src/__fixtures__/gazelle-20231008.json @@ -3,6 +3,7 @@ "sourceLinesOfCode": 29224, "linesOfCode": 45124, "size": 846671, + "complexity": 97705, "comments": { "total": 4, "size": 1678 @@ -269,6 +270,16 @@ "totalUnique": 0, "unique": {}, "uniquenessRatio": 0 + }, + "total": 931, + "complexity": { + "min": 1, + "max": 2, + "mean": 1.0139634801288937, + "mode": 1, + "median": 1, + "range": 1, + "sum": 944 } }, "rules": { @@ -87501,6 +87512,15 @@ "total": 0, "ratio": 0 } + }, + "complexity": { + "min": 1, + "max": 1, + "mean": 1, + "mode": 1, + "median": 1, + "range": 0, + "sum": 17586 } }, "properties": { @@ -90316,6 +90336,15 @@ "uniquenessRatio": 0.5 } } + }, + "complexity": { + "min": 1, + "max": 2, + "mean": 1.0304617662130615, + "mode": 1, + "median": 1, + "range": 1, + "sum": 18098 } } } \ No newline at end of file diff --git a/src/__fixtures__/github-20231008.json b/src/__fixtures__/github-20231008.json index eeba813..e9f16ac 100644 --- a/src/__fixtures__/github-20231008.json +++ b/src/__fixtures__/github-20231008.json @@ -3,6 +3,7 @@ "sourceLinesOfCode": 33278, "linesOfCode": 51852, "size": 1206900, + "complexity": 104774, "comments": { "total": 4, "size": 528 @@ -296,6 +297,16 @@ "totalUnique": 0, "unique": {}, "uniquenessRatio": 0 + }, + "total": 677, + "complexity": { + "min": 1, + "max": 2, + "mean": 1.0014771048744462, + "mode": 1, + "median": 1, + "range": 1, + "sum": 678 } }, "rules": { @@ -92207,6 +92218,15 @@ "total": 0, "ratio": 0 } + }, + "complexity": { + "min": 1, + "max": 1, + "mean": 1, + "mode": 1, + "median": 1, + "range": 0, + "sum": 21677 } }, "properties": { @@ -102091,6 +102111,15 @@ "uniquenessRatio": 1 } } + }, + "complexity": { + "min": 1, + "max": 2, + "mean": 1.00175118014314, + "mode": 1, + "median": 1, + "range": 1, + "sum": 26314 } } } \ No newline at end of file diff --git a/src/__fixtures__/indiatimes-20231008.json b/src/__fixtures__/indiatimes-20231008.json index 21c402c..40e1caf 100644 --- a/src/__fixtures__/indiatimes-20231008.json +++ b/src/__fixtures__/indiatimes-20231008.json @@ -3,6 +3,7 @@ "sourceLinesOfCode": 14331, "linesOfCode": 19092, "size": 437264, + "complexity": 54056, "comments": { "total": 0, "size": 0 @@ -107,6 +108,16 @@ "totalUnique": 0, "unique": {}, "uniquenessRatio": 0 + }, + "total": 8, + "complexity": { + "min": 1, + "max": 1, + "mean": 1, + "mode": 1, + "median": 1, + "range": 0, + "sum": 8 } }, "rules": { @@ -56610,6 +56621,15 @@ "total": 0, "ratio": 0 } + }, + "complexity": { + "min": 1, + "max": 1, + "mean": 1, + "mode": 1, + "median": 1, + "range": 0, + "sum": 6144 } }, "properties": { @@ -58238,6 +58258,15 @@ "uniquenessRatio": 1 } } + }, + "complexity": { + "min": 1, + "max": 2, + "mean": 1.002277533756304, + "mode": 1, + "median": 1, + "range": 1, + "sum": 6161 } } } \ No newline at end of file diff --git a/src/__fixtures__/smashing-magazine-20231008.json b/src/__fixtures__/smashing-magazine-20231008.json index c49b444..227e0f7 100644 --- a/src/__fixtures__/smashing-magazine-20231008.json +++ b/src/__fixtures__/smashing-magazine-20231008.json @@ -3,6 +3,7 @@ "sourceLinesOfCode": 18393, "linesOfCode": 28097, "size": 544284, + "complexity": 53769, "comments": { "total": 4, "size": 418 @@ -423,6 +424,16 @@ "totalUnique": 0, "unique": {}, "uniquenessRatio": 0 + }, + "total": 677, + "complexity": { + "min": 1, + "max": 2, + "mean": 1.035450516986706, + "mode": 1, + "median": 1, + "range": 1, + "sum": 701 } }, "rules": { @@ -49076,6 +49087,15 @@ "total": 0, "ratio": 0 } + }, + "complexity": { + "min": 1, + "max": 1, + "mean": 1, + "mode": 1, + "median": 1, + "range": 0, + "sum": 11757 } }, "properties": { @@ -51522,6 +51542,15 @@ "uniquenessRatio": 1 } } + }, + "complexity": { + "min": 1, + "max": 2, + "mean": 1.013728813559322, + "mode": 1, + "median": 1, + "range": 1, + "sum": 11962 } } } \ No newline at end of file diff --git a/src/__fixtures__/trello-20231008.json b/src/__fixtures__/trello-20231008.json index 9c1cee9..9be60c0 100644 --- a/src/__fixtures__/trello-20231008.json +++ b/src/__fixtures__/trello-20231008.json @@ -3,6 +3,7 @@ "sourceLinesOfCode": 4978, "linesOfCode": 9025, "size": 150901, + "complexity": 15470, "comments": { "total": 912, "size": 2736 @@ -294,6 +295,16 @@ "totalUnique": 0, "unique": {}, "uniquenessRatio": 0 + }, + "total": 194, + "complexity": { + "min": 1, + "max": 2, + "mean": 1.0051546391752577, + "mode": 1, + "median": 1, + "range": 1, + "sum": 195 } }, "rules": { @@ -10498,6 +10509,15 @@ "total": 0, "ratio": 0 } + }, + "complexity": { + "min": 1, + "max": 1, + "mean": 1, + "mode": 1, + "median": 1, + "range": 0, + "sum": 3629 } }, "properties": { @@ -11627,6 +11647,15 @@ "uniquenessRatio": 1 } } + }, + "complexity": { + "min": 1, + "max": 2, + "mean": 1.0511129431162407, + "mode": 1, + "median": 1, + "range": 1, + "sum": 3825 } } } \ No newline at end of file diff --git a/src/atrules/atrules.test.js b/src/atrules/atrules.test.js index 3dc06f4..f986d08 100644 --- a/src/atrules/atrules.test.js +++ b/src/atrules/atrules.test.js @@ -4,6 +4,66 @@ import { analyze } from '../index.js' const AtRules = suite('at-rules') +AtRules('counts total atrules', () => { + let css = ` + @import url(test.css); + + @layer a, b, c; + + @media all { + a {} + } + + @supports (display: grid) { + a {} + } + ` + let actual = analyze(css).atrules.total + assert.is(actual, 4) +}) + +AtRules('calculates complexity', () => { + let css = ` + /* 1 */ + @import url(test.css); + + /* 1 */ + @layer a, b, c; + /* 2 */ + @layer {} + + /* 1 */ + @supports (display: grid) {} + /* 2 */ + @supports (-webkit-appearance: none) {} + + /* 1 */ + @media all {} + /* 2 */ + @media (min-resolution: .001dpcm) {} + + /* 1 */ + @font-face { + font-family: Arial; + } + + /* 1 */ + @keyframes one {} + /* 2 */ + @-webkit-keyframes one {} + ` + let actual = analyze(css).atrules.complexity + assert.equal(actual, { + min: 1, + max: 2, + mean: 14 / 10, + mode: 1, + median: 1, + range: 1, + sum: 14, + }) +}) + AtRules('finds @layer', () => { // Fixture is pretty much a straight copy from all code examples from // https://css-tricks.com/css-cascade-layers/ diff --git a/src/declarations/declarations.test.js b/src/declarations/declarations.test.js index 74e7e6d..b966d91 100644 --- a/src/declarations/declarations.test.js +++ b/src/declarations/declarations.test.js @@ -121,4 +121,30 @@ Declarations('should calculate !importants within @keyframes', () => { }) }) +Declarations('should count complexity', () => { + const css = ` + a { + color: green; + color: green !important; + } + + @keyframes test { + from { + opacity: 1 !important; + } + } + ` + const actual = analyze(css).declarations.complexity + const expected = { + min: 1, + max: 3, + mean: 2, + mode: 2, + median: 2, + range: 2, + sum: 6, + } + assert.equal(actual, expected) +}) + Declarations.run() \ No newline at end of file diff --git a/src/index.js b/src/index.js index 336d8d8..ffe3fdb 100644 --- a/src/index.js +++ b/src/index.js @@ -98,6 +98,7 @@ export function analyze(css, options = {}) { // Atrules let totalAtRules = 0 + let atRuleComplexities = new AggregateCollection() /** @type {Record}[]} */ let fontfaces = [] let fontfaces_with_loc = new Collection(useLocations) @@ -146,6 +147,7 @@ export function analyze(css, options = {}) { // Declarations let uniqueDeclarations = new Set() let totalDeclarations = 0 + let declarationComplexities = new AggregateCollection() let importantDeclarations = 0 let importantsInKeyframes = 0 let importantCustomProperties = new Collection(useLocations) @@ -158,6 +160,7 @@ export function analyze(css, options = {}) { let propertyComplexities = new AggregateCollection() // Values + let valueComplexities = new AggregateCollection() let vendorPrefixedValues = new Collection(useLocations) let valueBrowserhacks = new Collection(useLocations) let zindex = new Collection(useLocations) @@ -177,6 +180,7 @@ export function analyze(css, options = {}) { switch (node.type) { case Atrule: { totalAtRules++ + let atRuleName = node.name if (atRuleName === 'font-face') { @@ -194,9 +198,12 @@ export function analyze(css, options = {}) { }) fontfaces.push(descriptors) + atRuleComplexities.push(1) break } + let complexity = 1 + // All the AtRules in here MUST have a prelude, so we can count their names if (node.prelude !== null) { let prelude = node.prelude @@ -207,52 +214,47 @@ export function analyze(css, options = {}) { medias.p(preludeStr, loc) if (isMediaBrowserhack(prelude)) { mediaBrowserhacks.p(preludeStr, loc) + complexity++ } - break - } - if (atRuleName === 'supports') { + } else if (atRuleName === 'supports') { supports.p(preludeStr, loc) + // TODO: analyze vendor prefixes in @supports + // TODO: analyze complexity of @supports 'declaration' if (isSupportsBrowserhack(prelude)) { supportsBrowserhacks.p(preludeStr, loc) + complexity++ } - break - } - if (endsWith('keyframes', atRuleName)) { + } else if (endsWith('keyframes', atRuleName)) { let name = '@' + atRuleName + ' ' + preludeStr if (hasVendorPrefix(atRuleName)) { prefixedKeyframes.p(name, loc) + complexity++ } keyframes.p(name, loc) - break - } - if (atRuleName === 'import') { + } else if (atRuleName === 'import') { imports.p(preludeStr, loc) - break - } - if (atRuleName === 'charset') { + // TODO: analyze complexity of media queries, layers and supports in @import + // see https://github.com/projectwallace/css-analyzer/issues/326 + } else if (atRuleName === 'charset') { charsets.p(preludeStr, loc) - break - } - if (atRuleName === 'container') { + } else if (atRuleName === 'container') { containers.p(preludeStr, loc) - break - } - if (atRuleName === 'layer') { + // TODO: calculate complexity of container 'declaration' + } else if (atRuleName === 'layer') { preludeStr .split(',') .forEach(name => layers.p(name.trim(), loc)) - break - } - if (atRuleName === 'property') { + } else if (atRuleName === 'property') { registeredProperties.p(preludeStr, loc) - break + // TODO: add complexity for descriptors } } else { if (atRuleName === 'layer') { layers.p('', node.loc) - break + complexity++ } } + atRuleComplexities.push(complexity) break } case Rule: { @@ -391,29 +393,37 @@ export function analyze(css, options = {}) { } case Value: { if (isValueKeyword(node)) { + valueComplexities.push(1) break } let declaration = this.declaration let { property, important } = declaration + let complexity = 1 if (isValuePrefixed(node)) { vendorPrefixedValues.p(stringifyNode(node), node.loc) + complexity++ } // i.e. `property: value !ie` if (typeof important === 'string') { valueBrowserhacks.p(stringifyNodePlain(node) + '!' + important, node.loc) + complexity++ } // i.e. `property: value\9` if (isIe9Hack(node)) { valueBrowserhacks.p(stringifyNode(node), node.loc) + complexity++ } let children = node.children let loc = node.loc + // TODO: should shorthands be counted towards complexity? + valueComplexities.push(complexity) + // Process properties first that don't have colors, // so we can avoid further walking them; if (isProperty('z-index', property)) { @@ -566,17 +576,22 @@ export function analyze(css, options = {}) { } totalDeclarations++ + let complexity = 1 uniqueDeclarations.add(stringifyNode(node)) if (node.important === true) { importantDeclarations++ + complexity++ if (this.atrule && endsWith('keyframes', this.atrule.name)) { importantsInKeyframes++ + complexity++ } } + declarationComplexities.push(complexity) + let { property, loc: { start } } = node let propertyLoc = { start: { @@ -599,13 +614,15 @@ export function analyze(css, options = {}) { propertyComplexities.push(2) } else if (isCustom(property)) { customProperties.p(property, propertyLoc) - propertyComplexities.push(2) + propertyComplexities.push(node.important ? 3 : 2) + if (node.important === true) { importantCustomProperties.p(property, propertyLoc) } } else { propertyComplexities.push(1) } + break } } @@ -623,12 +640,18 @@ export function analyze(css, options = {}) { let assign = Object.assign let cssLen = css.length let fontFacesCount = fontfaces.length + let atRuleComplexity = atRuleComplexities.aggregate() + let selectorComplexity = selectorComplexities.aggregate() + let declarationComplexity = declarationComplexities.aggregate() + let propertyComplexity = propertyComplexities.aggregate() + let valueComplexity = valueComplexities.aggregate() return { stylesheet: { sourceLinesOfCode: totalAtRules + totalSelectors + totalDeclarations + keyframeSelectors.size(), linesOfCode, size: cssLen, + complexity: atRuleComplexity.sum + selectorComplexity.sum + declarationComplexity.sum + propertyComplexity.sum + valueComplexity.sum, comments: { total: totalComments, size: commentsSize, @@ -679,6 +702,8 @@ export function analyze(css, options = {}) { container: containers.c(), layer: layers.c(), property: registeredProperties.c(), + total: totalAtRules, + complexity: atRuleComplexity }, rules: { total: totalRules, @@ -731,7 +756,7 @@ export function analyze(css, options = {}) { uniqueSpecificities.c(), ), complexity: assign( - selectorComplexities.aggregate(), + selectorComplexity, uniqueSelectorComplexities.c(), { items: selectorComplexities.toArray(), @@ -771,6 +796,7 @@ export function analyze(css, options = {}) { ratio: ratio(importantsInKeyframes, importantDeclarations), }, }, + complexity: declarationComplexity, }, properties: assign( properties.c(), @@ -797,7 +823,7 @@ export function analyze(css, options = {}) { propertyHacks.c(), { ratio: ratio(propertyHacks.size(), properties.size()), }), - complexity: propertyComplexities.aggregate(), + complexity: propertyComplexity, }), values: { colors: assign( @@ -820,6 +846,7 @@ export function analyze(css, options = {}) { prefixes: vendorPrefixedValues.c(), browserhacks: valueBrowserhacks.c(), units: units.count(), + complexity: valueComplexity, }, __meta__: { parseTime: startAnalysis - startParse, diff --git a/src/index.test.js b/src/index.test.js index 853b4d9..bf07e02 100644 --- a/src/index.test.js +++ b/src/index.test.js @@ -1,5 +1,5 @@ -import { suite } from "uvu"; -import * as assert from "uvu/assert"; +import { suite } from "uvu" +import * as assert from "uvu/assert" import { analyze, compareSpecificity, @@ -12,7 +12,7 @@ import { isPropertyHack, isValuePrefixed, hasVendorPrefix, -} from "./index.js"; +} from "./index.js" const Api = suite("Public API") @@ -67,7 +67,7 @@ Api("does not break on CSS Syntax Errors", () => { Api("handles empty input gracefully", () => { const actual = analyze("") - delete actual.__meta__; + delete actual.__meta__ const expected = { stylesheet: { sourceLinesOfCode: 0, @@ -93,6 +93,7 @@ Api("handles empty input gracefully", () => { unique: {}, }, }, + complexity: 0, }, atrules: { fontface: { @@ -168,6 +169,16 @@ Api("handles empty input gracefully", () => { unique: {}, uniquenessRatio: 0, }, + total: 0, + complexity: { + min: 0, + max: 0, + mean: 0, + mode: 0, + median: 0, + range: 0, + sum: 0, + } }, rules: { total: 0, @@ -218,85 +229,69 @@ Api("handles empty input gracefully", () => { uniquenessRatio: 0, }, }, - "selectors": { - "total": 0, - "totalUnique": 0, - "uniquenessRatio": 0, - "specificity": { - "min": [0, 0, 0], - "max": [0, 0, 0], - "sum": [ - 0, - 0, - 0, - ], - "mean": [ - 0, - 0, - 0, - ], - "mode": [ - 0, - 0, - 0, - ], - "median": [ - 0, - 0, - 0, - ], - "items": [], - "unique": {}, - "total": 0, - "totalUnique": 0, - "uniquenessRatio": 0, - }, - "complexity": { - "min": 0, - "max": 0, - "mean": 0, - "mode": 0, - "median": 0, - "range": 0, - "sum": 0, - "total": 0, - "totalUnique": 0, - "unique": {}, - "uniquenessRatio": 0, - "items": [] - }, - "id": { - "total": 0, - "totalUnique": 0, - "unique": {}, - "uniquenessRatio": 0, - "ratio": 0 - }, - "accessibility": { - "total": 0, - "totalUnique": 0, - "unique": {}, - "uniquenessRatio": 0, - "ratio": 0 - }, - "keyframes": { - "total": 0, - "totalUnique": 0, - "unique": {}, - "uniquenessRatio": 0 - }, - "prefixed": { - "total": 0, - "totalUnique": 0, - "unique": {}, - "uniquenessRatio": 0, - "ratio": 0 - }, - "combinators": { - "total": 0, - "totalUnique": 0, - "unique": {}, - "uniquenessRatio": 0, + selectors: { + total: 0, + totalUnique: 0, + uniquenessRatio: 0, + specificity: { + min: [0, 0, 0], + max: [0, 0, 0], + sum: [0, 0, 0], + mean: [0, 0, 0], + mode: [0, 0, 0], + median: [0, 0, 0], + items: [], + unique: {}, + total: 0, + totalUnique: 0, + uniquenessRatio: 0, + }, + complexity: { + min: 0, + max: 0, + mean: 0, + mode: 0, + median: 0, + range: 0, + sum: 0, + total: 0, + totalUnique: 0, + unique: {}, + uniquenessRatio: 0, + items: [], + }, + id: { + total: 0, + totalUnique: 0, + unique: {}, + uniquenessRatio: 0, + ratio: 0, + }, + accessibility: { + total: 0, + totalUnique: 0, + unique: {}, + uniquenessRatio: 0, + ratio: 0, + }, + keyframes: { + total: 0, + totalUnique: 0, + unique: {}, + uniquenessRatio: 0, + }, + prefixed: { + total: 0, + totalUnique: 0, + unique: {}, + uniquenessRatio: 0, + ratio: 0, + }, + combinators: { + total: 0, + totalUnique: 0, + unique: {}, + uniquenessRatio: 0, }, }, declarations: { @@ -315,6 +310,15 @@ Api("handles empty input gracefully", () => { ratio: 0, }, }, + complexity: { + min: 0, + max: 0, + mean: 0, + mode: 0, + median: 0, + range: 0, + sum: 0, + } }, properties: { total: 0, @@ -448,8 +452,17 @@ Api("handles empty input gracefully", () => { uniquenessRatio: 0, itemsPerContext: {}, }, + complexity: { + min: 0, + max: 0, + mean: 0, + mode: 0, + median: 0, + range: 0, + sum: 0, + } }, - }; + } assert.equal(actual, expected) }) @@ -460,7 +473,7 @@ Api("has metadata", () => { (_) => ` html { font: 1em/1 sans-serif; - color: rgb(0 0 0 / 0.5) + color: rgb(0 0 0 / 0.5); } @media screen { @@ -476,7 +489,7 @@ Api("has metadata", () => { .join("") const result = analyze(fixture) - const actual = result.__meta__; + const actual = result.__meta__ assert.type(actual.parseTime, "number") assert.ok( diff --git a/src/values/complexity.test.js b/src/values/complexity.test.js new file mode 100644 index 0000000..2816ed9 --- /dev/null +++ b/src/values/complexity.test.js @@ -0,0 +1,31 @@ +import { suite } from 'uvu'; +import * as assert from 'uvu/assert'; +import { analyze } from '../index.js' + +const test = suite('Value complexity') + +test('calculates value complexity', () => { + const fixture = ` + a { + color: green; /* 1 */ + color: green !ie; /* 2 */ + width: -webkit-max-content; /* 2 */ + width: -webkit-max-content !ie; /* 3 */ + color: green\\9; /* 2 */ + } + ` + const actual = analyze(fixture).values.complexity + const expected = { + min: 1, + max: 3, + mean: 10 / 5, + mode: 2, + median: 2, + range: 2, + sum: 10, + } + + assert.equal(actual, expected) +}) + +test.run()