1 | "use strict";
|
2 |
|
3 | const Collection = require("../lib/collection"),
|
4 | debug = require("debug")("analyze-css:duplicated"),
|
5 | format = require("util").format;
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | function rule(analyzer) {
|
11 | var selectors = new Collection(),
|
12 | mediaQueryStack = [],
|
13 | browserPrefixRegEx = /^-(moz|o|webkit|ms)-/;
|
14 |
|
15 | analyzer.setMetric("duplicatedSelectors");
|
16 | analyzer.setMetric("duplicatedProperties");
|
17 |
|
18 |
|
19 | analyzer.on("media", function (query) {
|
20 | mediaQueryStack.push(query);
|
21 | debug("push: %j", mediaQueryStack);
|
22 | });
|
23 |
|
24 | analyzer.on("mediaEnd", function (query) {
|
25 | mediaQueryStack.pop(query);
|
26 | debug("pop: %j", mediaQueryStack);
|
27 | });
|
28 |
|
29 |
|
30 | analyzer.on("rule", function (rule) {
|
31 | selectors.push(
|
32 |
|
33 | (mediaQueryStack.length > 0
|
34 | ? "@media " + mediaQueryStack.join(" @media ") + " "
|
35 | : "") +
|
36 |
|
37 | rule.selectors.join(", ")
|
38 | );
|
39 | });
|
40 |
|
41 |
|
42 | analyzer.on("rule", function (rule) {
|
43 | var propertiesHash = {};
|
44 |
|
45 |
|
46 | if (rule.declarations) {
|
47 | rule.declarations.forEach(function (declaration) {
|
48 | var propertyName;
|
49 |
|
50 | if (declaration.type === "declaration") {
|
51 | propertyName = declaration.property;
|
52 |
|
53 |
|
54 |
|
55 |
|
56 | if (browserPrefixRegEx.test(declaration.value) === true) {
|
57 | return;
|
58 | }
|
59 |
|
60 |
|
61 | if (propertiesHash[propertyName] === true) {
|
62 |
|
63 | analyzer.setCurrentPosition(declaration.position);
|
64 |
|
65 | analyzer.incrMetric("duplicatedProperties");
|
66 | analyzer.addOffender(
|
67 | "duplicatedProperties",
|
68 | format(
|
69 | "%s {%s: %s}",
|
70 | rule.selectors.join(", "),
|
71 | declaration.property,
|
72 | declaration.value
|
73 | )
|
74 | );
|
75 | } else {
|
76 |
|
77 | propertiesHash[propertyName] = true;
|
78 | }
|
79 | }
|
80 | });
|
81 | }
|
82 | });
|
83 |
|
84 |
|
85 |
|
86 | analyzer.on("font-face", function (rule) {
|
87 | rule.declarations.forEach(function (declaration) {
|
88 | if (declaration.property === "src") {
|
89 | selectors.push("@font-face src: " + declaration.value);
|
90 |
|
91 | debug(
|
92 | "special handling for @font-face, provided src: %s",
|
93 | declaration.value
|
94 | );
|
95 | return false;
|
96 | }
|
97 | });
|
98 | });
|
99 |
|
100 | analyzer.on("report", function () {
|
101 | analyzer.setCurrentPosition(undefined);
|
102 |
|
103 | selectors.sort().forEach((selector, cnt) => {
|
104 | if (cnt > 1) {
|
105 | analyzer.incrMetric("duplicatedSelectors");
|
106 | analyzer.addOffender(
|
107 | "duplicatedSelectors",
|
108 | format("%s (%d times)", selector, cnt)
|
109 | );
|
110 | }
|
111 | });
|
112 | });
|
113 | }
|
114 |
|
115 | rule.description = "Reports duplicated CSS selectors and properties";
|
116 | module.exports = rule;
|