• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

david-luna / csset / 19246794314

10 Nov 2025 09:32PM UTC coverage: 95.408%. Remained the same
19246794314

push

github

david-luna
Merge branch 'main' of github.com:david-luna/csset

386 of 409 branches covered (94.38%)

Branch coverage included in aggregate %.

176 of 176 new or added lines in 8 files covered. (100.0%)

35 existing lines in 3 files now uncovered.

1754 of 1834 relevant lines covered (95.64%)

128.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.42
/lib/parser/specificity.js
1
// MIT License
1✔
2

1✔
3
// Copyright (c) 2020 Lea Verou
1✔
4

1✔
5
// Permission is hereby granted, free of charge, to any person obtaining a copy
1✔
6
// of this software and associated documentation files (the "Software"), to deal
1✔
7
// in the Software without restriction, including without limitation the rights
1✔
8
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1✔
9
// copies of the Software, and to permit persons to whom the Software is
1✔
10
// furnished to do so, subject to the following conditions:
1✔
11

1✔
12
// The above copyright notice and this permission notice shall be included in all
1✔
13
// copies or substantial portions of the Software.
1✔
14

1✔
15
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1✔
16
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1✔
17
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1✔
18
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1✔
19
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1✔
20
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1✔
21
// SOFTWARE.
1✔
22

1✔
23
import { parse } from './parse.js';
1✔
24
import { RECURSIVE_PSEUDO_CLASSES } from './tokens.js';
1✔
25
import { walk } from './walk.js';
1✔
26

1✔
27
/**
1✔
28
 *
1✔
29
 * @param {Array<number>} arr
1✔
30
 * @returns {number}
1✔
31
 */
1✔
UNCOV
32
function maxIndexOf(arr) {
×
UNCOV
33
  let max = arr[0];
×
UNCOV
34
  let ret = 0;
×
UNCOV
35

×
UNCOV
36
  for (let i = 0; i < arr.length; i++) {
×
UNCOV
37
    if (arr[i] > max) {
×
UNCOV
38
      ret = i;
×
UNCOV
39
      max = arr[i];
×
UNCOV
40
    }
×
UNCOV
41
  }
×
UNCOV
42

×
UNCOV
43
  return arr.length === 0 ? -1 : ret;
×
UNCOV
44
}
×
45

1✔
46
/**
1✔
47
 * Converts the specificity array to a number
1✔
48
 * @param {[number, number, number]} specificityArr array if specificity weights
1✔
49
 * @param {number} [base] base to calculate the number
1✔
50
 * @returns {number}
1✔
51
 */
1✔
52
export function specificityToNumber(specificityArr, base) {
1✔
UNCOV
53
  const b = base || Math.max(...specificityArr) + 1;
×
54

×
55
  return specificityArr[0] * b ** 2 + specificityArr[1] * b + specificityArr[2];
×
56
}
×
57

1✔
58
/**
1✔
59
 * @param {string | AST} selector
1✔
60
 * @param {*} param1
1✔
61
 * @returns {[number, number, number] | null}
1✔
62
 */
1✔
63
// eslint-disable-next-line no-unused-vars
1✔
64
export function specificity(selector, { format = 'array' } = {}) {
1✔
65
  const ast = typeof selector === 'object' ? selector : parse(selector, { recursive: true });
2✔
66

2✔
67
  if (!ast) {
2!
UNCOV
68
    return null;
×
UNCOV
69
  }
×
70

2✔
71
  if (ast.type === 'list') {
2!
UNCOV
72
    // Return max specificity
×
UNCOV
73
    let base = 10;
×
UNCOV
74
    const specificities = ast.list.map((s) => {
×
75
      const sp = specificity(s) || [0, 0, 0];
×
76
      base = Math.max(base, ...sp);
×
77
      return sp;
×
78
    });
×
UNCOV
79
    const numbers = specificities.map((s) => specificityToNumber(s, base));
×
UNCOV
80
    const i = maxIndexOf(numbers);
×
UNCOV
81
    return specificities[i];
×
UNCOV
82
  }
×
83

2✔
84
  /** @type {[number, number, number]} */
2✔
85
  const ret = [0, 0, 0];
2✔
86

2✔
87
  walk(ast, (node) => {
2✔
88
    if (node.type === 'id') {
15✔
89
      ret[0]++;
1✔
90
    } else if (node.type === 'class' || node.type === 'attribute') {
15✔
91
      ret[1]++;
4✔
92
    } else if ((node.type === 'type' && node.content !== '*') || node.type === 'pseudo-element') {
14✔
93
      ret[2]++;
2✔
94
    } else if (node.type === 'pseudo-class' && node.name !== 'where') {
10✔
95
      if (RECURSIVE_PSEUDO_CLASSES.has(node.name) && node.subtree) {
2✔
96
        // Max of argument list
1✔
97
        const sub = specificity(node.subtree) || [];
1!
98
        sub.forEach((s, i) => (ret[i] += s));
1✔
99
      } else {
2✔
100
        ret[1]++;
1✔
101
      }
1✔
102
    }
2✔
103
  });
2✔
104

2✔
105
  return ret;
2✔
106
}
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc