@projectwallace/css-analyzer 4.0.3 → 5.0.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 (58) hide show
  1. package/dist/analyzer.cjs +2 -0
  2. package/dist/analyzer.cjs.map +1 -0
  3. package/dist/analyzer.modern.js +2 -0
  4. package/dist/analyzer.modern.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 +27 -65
  10. package/readme.md +369 -237
  11. package/src/analyzer/atrules/charsets.js +0 -12
  12. package/src/analyzer/atrules/documents.js +0 -12
  13. package/src/analyzer/atrules/fontfaces.js +0 -30
  14. package/src/analyzer/atrules/imports.js +0 -12
  15. package/src/analyzer/atrules/index.js +0 -13
  16. package/src/analyzer/atrules/keyframes.js +0 -19
  17. package/src/analyzer/atrules/mediaqueries.js +0 -19
  18. package/src/analyzer/atrules/namespaces.js +0 -12
  19. package/src/analyzer/atrules/pages.js +0 -12
  20. package/src/analyzer/atrules/supports.js +0 -19
  21. package/src/analyzer/declarations/importants.js +0 -8
  22. package/src/analyzer/declarations/index.js +0 -13
  23. package/src/analyzer/index.js +0 -39
  24. package/src/analyzer/properties/browserhacks.js +0 -13
  25. package/src/analyzer/properties/index.js +0 -15
  26. package/src/analyzer/properties/prefixed.js +0 -13
  27. package/src/analyzer/rules/index.js +0 -53
  28. package/src/analyzer/selectors/accessibility.js +0 -14
  29. package/src/analyzer/selectors/browserhacks.js +0 -11
  30. package/src/analyzer/selectors/complexity.js +0 -44
  31. package/src/analyzer/selectors/id.js +0 -12
  32. package/src/analyzer/selectors/index.js +0 -23
  33. package/src/analyzer/selectors/js.js +0 -12
  34. package/src/analyzer/selectors/specificity.js +0 -42
  35. package/src/analyzer/selectors/universal.js +0 -13
  36. package/src/analyzer/stylesheets/browserhacks.js +0 -15
  37. package/src/analyzer/stylesheets/cohesion.js +0 -30
  38. package/src/analyzer/stylesheets/index.js +0 -33
  39. package/src/analyzer/stylesheets/lines-of-code.js +0 -14
  40. package/src/analyzer/stylesheets/simplicity.js +0 -7
  41. package/src/analyzer/stylesheets/size.js +0 -27
  42. package/src/analyzer/values/animations.js +0 -122
  43. package/src/analyzer/values/box-shadows.js +0 -17
  44. package/src/analyzer/values/browserhacks.js +0 -16
  45. package/src/analyzer/values/colors.js +0 -137
  46. package/src/analyzer/values/font-families.js +0 -30
  47. package/src/analyzer/values/font-sizes.js +0 -31
  48. package/src/analyzer/values/index.js +0 -26
  49. package/src/analyzer/values/prefixed.js +0 -22
  50. package/src/analyzer/values/text-shadows.js +0 -17
  51. package/src/analyzer/values/z-indexes.js +0 -20
  52. package/src/parser/atrules.js +0 -37
  53. package/src/parser/declarations.js +0 -15
  54. package/src/parser/index.js +0 -30
  55. package/src/parser/rules.js +0 -17
  56. package/src/parser/selectors.js +0 -26
  57. package/src/utils/css.js +0 -8
  58. package/src/utils/uniquer.js +0 -24
@@ -1,122 +0,0 @@
1
- const {parse} = require('postcss-values-parser')
2
- const splitValue = require('split-css-value')
3
- const {sortFn: timeSortFn} = require('css-time-sort')
4
- const uniquer = require('../../utils/uniquer')
5
- const {KEYWORDS} = require('../../utils/css')
6
-
7
- function getSingleDuration(animation) {
8
- let duration
9
-
10
- parse(animation).walkNumerics(node => {
11
- // The first time-value is always the duration, as per spec
12
- if (duration) {
13
- return
14
- }
15
-
16
- duration = node.toString()
17
- })
18
-
19
- return duration
20
- }
21
-
22
- function getDuration(animation) {
23
- return splitValue(animation)
24
- .map(animation => getSingleDuration(animation))
25
- .filter(Boolean)
26
- }
27
-
28
- function getSingleTimingFunction(animation) {
29
- let timingFunction
30
-
31
- parse(animation).walk(node => {
32
- // There should only be one timing function per shorthand
33
- if (timingFunction) {
34
- return
35
- }
36
-
37
- // Look for timing keywords
38
- if (
39
- node.type === 'word' &&
40
- [
41
- 'ease',
42
- 'ease-in',
43
- 'ease-in-out',
44
- 'ease-out',
45
- 'linear',
46
- 'step-start',
47
- 'step-end'
48
- ].includes(node.toString())
49
- ) {
50
- timingFunction = node.toString()
51
- return
52
- }
53
-
54
- // If there's no keyword, it should either be cubic-bezier() or steps()
55
- if (node.type === 'func' && ['cubic-bezier', 'steps'].includes(node.name)) {
56
- timingFunction = node.toString()
57
- }
58
- })
59
-
60
- return timingFunction
61
- }
62
-
63
- function getTimingFunction(animation) {
64
- return splitValue(animation)
65
- .map(animation => getSingleTimingFunction(animation))
66
- .filter(Boolean)
67
- }
68
-
69
- module.exports = declarations => {
70
- const all = declarations.filter(({value}) => !KEYWORDS.includes(value))
71
-
72
- const durations = all
73
- // First, find all durations directly
74
- .filter(({property}) =>
75
- ['animation-duration', 'transition-duration'].includes(property)
76
- )
77
- .map(declaration => declaration.value)
78
- // Then, find more through the shorthand declarations
79
- .concat(
80
- ...all
81
- .filter(({property}) => ['animation', 'transition'].includes(property))
82
- .map(({value}) => getDuration(value))
83
- )
84
-
85
- const {unique: uniqueDurations, totalUnique: totalUniqueDurations} = uniquer(
86
- durations,
87
- timeSortFn
88
- )
89
-
90
- const timingFunctions = all
91
- // First, find all timing-functions directly
92
- .filter(({property}) =>
93
- ['animation-timing-function', 'transition-timing-function'].includes(
94
- property
95
- )
96
- )
97
- .map(declaration => declaration.value)
98
- // Then, find more through the shorthand declarations
99
- .concat(
100
- ...all
101
- .filter(({property}) => ['animation', 'transition'].includes(property))
102
- .map(({value}) => getTimingFunction(value))
103
- )
104
-
105
- const {
106
- unique: uniqueTimingFunctions,
107
- totalUnique: totalUniqueTimingFunctions
108
- } = uniquer(timingFunctions)
109
-
110
- return {
111
- durations: {
112
- total: durations.length,
113
- unique: uniqueDurations,
114
- totalUnique: totalUniqueDurations
115
- },
116
- timingFunctions: {
117
- total: timingFunctions.length,
118
- unique: uniqueTimingFunctions,
119
- totalUnique: totalUniqueTimingFunctions
120
- }
121
- }
122
- }
@@ -1,17 +0,0 @@
1
- const uniquer = require('../../utils/uniquer')
2
- const {KEYWORDS} = require('../../utils/css')
3
-
4
- module.exports = declarations => {
5
- const all = declarations
6
- .filter(({property}) => property === 'box-shadow')
7
- .filter(({value}) => !KEYWORDS.includes(value))
8
- .map(declaration => declaration.value)
9
-
10
- const {unique, totalUnique} = uniquer(all)
11
-
12
- return {
13
- total: all.length,
14
- unique,
15
- totalUnique
16
- }
17
- }
@@ -1,16 +0,0 @@
1
- const isBrowserHack = require('css-value-browser-h4cks-analyzer')
2
- const uniquer = require('../../utils/uniquer')
3
-
4
- module.exports = declarations => {
5
- const all = declarations
6
- .filter(declaration => isBrowserHack(declaration.value))
7
- .map(declaration => declaration.value)
8
-
9
- const {unique, totalUnique} = uniquer(all)
10
-
11
- return {
12
- total: all.length,
13
- unique,
14
- totalUnique
15
- }
16
- }
@@ -1,137 +0,0 @@
1
- const {parse} = require('postcss-values-parser')
2
- const tinycolor = require('tinycolor2')
3
- const colorSorter = require('color-sorter')
4
-
5
- const uniquer = require('../../utils/uniquer')
6
-
7
- function extractColorsFromDeclaration(declaration) {
8
- const colors = []
9
-
10
- // Try-catch to ignore values that cannot be parsed with postcss-values-parser
11
- // Examples:
12
- // - Base64-encoded images
13
- // - startColorstr=\'#5243AA\', endColorstr=\'#0079bf\', GradientType=1
14
- // - opacity=50
15
- // - 1.5deg
16
- try {
17
- parse(declaration.value, {loose: true}).walk(node => {
18
- if (node.isColor) {
19
- return colors.push(node)
20
- }
21
- })
22
- } catch (_) {}
23
-
24
- if (colors.length > 0) {
25
- declaration.colors = colors.map(color => color.toString().trim())
26
- }
27
-
28
- return declaration
29
- }
30
-
31
- const addCount = color => {
32
- return {
33
- ...color,
34
- count: color.notations.reduce((total, {count}) => {
35
- return total + count
36
- }, 0)
37
- }
38
- }
39
-
40
- const addMostCommonNotation = color => {
41
- return {
42
- ...color,
43
- value: [...color.notations]
44
- .sort((a, b) => {
45
- // If counts are the same, get the shortest notation
46
- if (a.count === b.count) {
47
- return a.value.length - b.value.length
48
- }
49
-
50
- return b.count - a.count
51
- })
52
- .shift().value
53
- }
54
- }
55
-
56
- const addNotations = (acc, curr) => {
57
- if (!acc[curr.normalized]) {
58
- acc[curr.normalized] = {
59
- notations: []
60
- }
61
- }
62
-
63
- acc[curr.normalized] = {
64
- notations: [...acc[curr.normalized].notations, curr]
65
- }
66
-
67
- return acc
68
- }
69
-
70
- const filterDuplicateColors = color => {
71
- // Filter out the actual duplicate colors
72
- return color.notations.length > 1
73
- }
74
-
75
- const validateColor = color => {
76
- return tinycolor(color.value).isValid()
77
- }
78
-
79
- const normalizeColors = color => {
80
- // Add a normalized value
81
-
82
- // Avoid using TinyColor's toHslString() because it rounds
83
- // the numbers and incorrectly reports duplicates
84
- const {h, s, l, a} = tinycolor(color.value).toHsl()
85
- const normalized = a === 0 ? 0 : `h${h}s${s}l${l}a${a}`
86
-
87
- return {
88
- ...color,
89
- normalized
90
- }
91
- }
92
-
93
- const removeTemporaryProperties = color => {
94
- // Remove temporary props that were needed for analysis
95
- return {
96
- ...color,
97
- notations: color.notations.map(notation => {
98
- const {normalized, ...colorProps} = notation
99
- return colorProps
100
- })
101
- }
102
- }
103
-
104
- const withDuplicateNotations = colors =>
105
- Object.values(
106
- colors
107
- .filter(validateColor)
108
- .map(normalizeColors)
109
- .reduce(addNotations, {})
110
- )
111
- .filter(filterDuplicateColors)
112
- .map(addCount)
113
- .map(addMostCommonNotation)
114
- .map(removeTemporaryProperties)
115
-
116
- module.exports = declarations => {
117
- const all = declarations
118
- .map(extractColorsFromDeclaration)
119
- .filter(declaration => declaration.colors && declaration.colors.length > 0)
120
- .map(declaration => declaration.colors)
121
- .reduce((allColors, declarationColors) => {
122
- return [...allColors, ...declarationColors]
123
- }, [])
124
- const {totalUnique, unique} = uniquer(all, colorSorter.sortFn)
125
- const duplicates = withDuplicateNotations(unique)
126
-
127
- return {
128
- total: all.length,
129
- unique,
130
- totalUnique,
131
- duplicates: {
132
- unique: duplicates,
133
- totalUnique: duplicates.length,
134
- total: duplicates.length
135
- }
136
- }
137
- }
@@ -1,30 +0,0 @@
1
- const expand = require('css-shorthand-expand')
2
- const uniquer = require('../../utils/uniquer')
3
- const {KEYWORDS} = require('../../utils/css')
4
-
5
- module.exports = declarations => {
6
- const all = declarations.reduce((previous, {property, value}) => {
7
- if (KEYWORDS.includes(value)) {
8
- return previous
9
- }
10
-
11
- if (property === 'font-family') {
12
- previous = [...previous, value]
13
- }
14
-
15
- if (property === 'font') {
16
- const expanded = expand('font', value)
17
-
18
- if (expanded) {
19
- previous = [...previous, expanded['font-family']]
20
- }
21
- }
22
-
23
- return previous
24
- }, [])
25
-
26
- return {
27
- total: all.length,
28
- ...uniquer(all)
29
- }
30
- }
@@ -1,31 +0,0 @@
1
- const expand = require('css-shorthand-expand')
2
- const unitSort = require('css-unit-sort')
3
- const uniquer = require('../../utils/uniquer')
4
- const {KEYWORDS} = require('../../utils/css')
5
-
6
- module.exports = declarations => {
7
- const all = declarations.reduce((previous, {property, value}) => {
8
- if (KEYWORDS.includes(value)) {
9
- return previous
10
- }
11
-
12
- if (property === 'font-size') {
13
- previous = [...previous, value]
14
- }
15
-
16
- if (property === 'font') {
17
- const expanded = expand('font', value)
18
-
19
- if (expanded) {
20
- previous = [...previous, expanded['font-size']]
21
- }
22
- }
23
-
24
- return previous
25
- }, [])
26
-
27
- return {
28
- total: all.length,
29
- ...uniquer(all, unitSort.sortFn)
30
- }
31
- }
@@ -1,26 +0,0 @@
1
- module.exports = declarations => {
2
- const all = declarations.map(({value}) => value)
3
-
4
- const prefixed = require('./prefixed.js')(all)
5
- const fontsizes = require('./font-sizes.js')(declarations)
6
- const fontfamilies = require('./font-families.js')(declarations)
7
- const colors = require('./colors.js')(declarations)
8
- const browserhacks = require('./browserhacks.js')(declarations)
9
- const boxshadows = require('./box-shadows.js')(declarations)
10
- const textshadows = require('./text-shadows.js')(declarations)
11
- const zindexes = require('./z-indexes.js')(declarations)
12
- const animations = require('./animations.js')(declarations)
13
-
14
- return {
15
- total: all.length,
16
- prefixed,
17
- fontsizes,
18
- fontfamilies,
19
- colors,
20
- browserhacks,
21
- boxshadows,
22
- textshadows,
23
- zindexes,
24
- animations
25
- }
26
- }
@@ -1,22 +0,0 @@
1
- const vendorPrefixes = require('vendor-prefixes')()
2
- const uniquer = require('../../utils/uniquer')
3
-
4
- const PREFIX_REGEX = new RegExp(`^${vendorPrefixes.join('|')}`)
5
-
6
- module.exports = values => {
7
- const all = values.filter(property => PREFIX_REGEX.test(property))
8
-
9
- const share = (() => {
10
- if (values.length === 0) {
11
- return 0
12
- }
13
-
14
- return all.length / values.length
15
- })()
16
-
17
- return {
18
- total: all.length,
19
- ...uniquer(all),
20
- share
21
- }
22
- }
@@ -1,17 +0,0 @@
1
- const uniquer = require('../../utils/uniquer')
2
- const {KEYWORDS} = require('../../utils/css')
3
-
4
- module.exports = declarations => {
5
- const all = declarations
6
- .filter(({property}) => property === 'text-shadow')
7
- .filter(({value}) => !KEYWORDS.includes(value))
8
- .map(declaration => declaration.value)
9
-
10
- const {unique, totalUnique} = uniquer(all)
11
-
12
- return {
13
- total: all.length,
14
- unique,
15
- totalUnique
16
- }
17
- }
@@ -1,20 +0,0 @@
1
- const uniquer = require('../../utils/uniquer')
2
- const {KEYWORDS} = require('../../utils/css')
3
-
4
- module.exports = declarations => {
5
- const all = declarations
6
- // Ignore all declarations that are not z-index
7
- .filter(({property}) => property === 'z-index')
8
- // Ignore all CSS keywords and globals
9
- .filter(({value}) => !KEYWORDS.includes(value))
10
- // Create a list of integers
11
- .map(({value}) => parseInt(value, 10))
12
-
13
- const {unique, totalUnique} = uniquer(all, (a, b) => a - b)
14
-
15
- return {
16
- total: all.length,
17
- unique,
18
- totalUnique
19
- }
20
- }
@@ -1,37 +0,0 @@
1
- const declarations = require('./declarations')
2
-
3
- const AT_RULES_WITH_DECLARATIONS = ['font-face']
4
-
5
- function addDeclarations(atRule, tree) {
6
- if (!AT_RULES_WITH_DECLARATIONS.includes(atRule.type)) {
7
- return atRule
8
- }
9
-
10
- return {
11
- ...atRule,
12
- declarations: declarations(tree)
13
- }
14
- }
15
-
16
- module.exports = tree => {
17
- const atRules = []
18
-
19
- tree.walkAtRules(rule => {
20
- const atRule = {
21
- type: rule.name.trim(),
22
- params: rule.params.trim()
23
- }
24
-
25
- return atRules.push(addDeclarations(atRule, rule))
26
- })
27
-
28
- return atRules
29
- }
30
-
31
- module.exports.isKeyframes = rule => {
32
- return (
33
- rule.parent &&
34
- rule.parent.type === 'atrule' &&
35
- rule.parent.name.includes('keyframes')
36
- )
37
- }
@@ -1,15 +0,0 @@
1
- module.exports = tree => {
2
- const declarations = []
3
-
4
- tree.walkDecls(declaration => {
5
- declarations.push({
6
- // Need to prefix with the 'before', otherwise PostCSS will
7
- // trim off any browser hacks prefixes like * or _
8
- property: declaration.raws.before.trim() + declaration.prop,
9
- value: declaration.value,
10
- important: Boolean(declaration.important)
11
- })
12
- })
13
-
14
- return declarations
15
- }
@@ -1,30 +0,0 @@
1
- const {parse} = require('postcss')
2
- const atRules = require('./atrules')
3
- const rules = require('./rules')
4
- const selectors = require('./selectors')
5
- const declarations = require('./declarations')
6
-
7
- function processNodes(tree) {
8
- return {
9
- atRules: atRules(tree),
10
- rules: rules(tree),
11
- selectors: selectors(tree),
12
- declarations: declarations(tree)
13
- }
14
- }
15
-
16
- module.exports = async css => {
17
- try {
18
- const result = await parse(css)
19
- const rootNode = result.toResult().root
20
-
21
- return Promise.resolve(processNodes(rootNode))
22
- } catch (error) {
23
- const {line, column, reason, source} = error
24
- const message = source && line && reason && column
25
- ? `${reason} at line ${line}, column ${column}:\n\n${source}`
26
- : error.message
27
-
28
- return Promise.reject(new SyntaxError(message))
29
- }
30
- }
@@ -1,17 +0,0 @@
1
- const {isKeyframes} = require('./atrules')
2
- const getDeclarationsFromRule = require('./declarations')
3
- const {getSelectorsFromRule} = require('./selectors')
4
-
5
- module.exports = tree => {
6
- const rules = []
7
-
8
- tree.walkRules(rule => {
9
- const declarations = getDeclarationsFromRule(rule)
10
- // Don't include the 'selectors' (from, 50%, to, etc.) inside @keyframes
11
- const selectors = isKeyframes(rule) ? [] : getSelectorsFromRule(rule)
12
-
13
- rules.push({declarations, selectors})
14
- })
15
-
16
- return rules
17
- }
@@ -1,26 +0,0 @@
1
- const {isKeyframes} = require('./atrules')
2
-
3
- module.exports = tree => {
4
- const selectors = []
5
-
6
- tree.walkRules(rule => {
7
- // Don't include the 'selectors' (from, 50%, to, etc.) inside @keyframes
8
- if (isKeyframes(rule)) {
9
- return
10
- }
11
-
12
- // Get selectors: flatten the list, split each by ',' and trim the results
13
- selectors.push(...getSelectorsFromRule(rule))
14
- })
15
-
16
- return selectors
17
- }
18
-
19
- const getSelectorsFromRule = rule => {
20
- return rule.selector
21
- .split(',')
22
- .map(s => s.trim())
23
- .filter(Boolean)
24
- }
25
-
26
- module.exports.getSelectorsFromRule = getSelectorsFromRule
package/src/utils/css.js DELETED
@@ -1,8 +0,0 @@
1
- module.exports.KEYWORDS = [
2
- 'auto',
3
- 'inherit',
4
- 'initial',
5
- 'none',
6
- 'revert',
7
- 'unset'
8
- ]
@@ -1,24 +0,0 @@
1
- const stringSortFn = require('string-natural-compare')
2
-
3
- module.exports = (values, sortFn) => {
4
- sortFn =
5
- sortFn ||
6
- function(a, b) {
7
- return stringSortFn(String(a), String(b), {caseInsensitive: true})
8
- }
9
-
10
- // Create a Map of unique values and their counts
11
- const reduced = [
12
- ...values.reduce((map, value) => {
13
- return map.set(value, map.get(value) + 1 || 1)
14
- }, new Map())
15
- ].map(([value, count]) => ({value, count}))
16
-
17
- const sorted = reduced.map(element => element.value).sort(sortFn)
18
- const unique = sorted.map(value => reduced.find(r => r.value === value))
19
-
20
- return {
21
- unique,
22
- totalUnique: unique.length
23
- }
24
- }