-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
130 lines (117 loc) · 3.59 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
const postcss = require('postcss');
const parser = require('postcss-selector-parser');
const {name} = require('../package.json');
/**
* Ensure that attributes with different quotes match.
* @param {Object} selector - postcss selector node
*/
function normalizeAttributes(selector) {
selector.walkAttributes((node) => {
if (node.value) {
// remove quotes
node.value = node.value.replace(/'|\\'|"|\\"/g, '');
}
});
}
/**
* Sort class and id groups alphabetically
* @param {Object} selector - postcss selector node
*/
function sortGroups(selector) {
selector.each((subSelector) => {
subSelector.nodes.sort((a, b) => {
// different types cannot be sorted
if (a.type !== b.type) {
return 0;
}
// sort alphabetically
return a.value < b.value ? -1 : 1;
});
});
selector.sort((a, b) => (a.nodes.join('') < b.nodes.join('') ? -1 : 1));
}
/**
* Remove duplicated properties
* @param {Object} selector - postcss selector node
* @param {Boolean} exact
*/
function removeDupProperties(selector, exact) {
// Remove duplicated properties from bottom to top ()
for (let actIndex = selector.nodes.length - 1; actIndex >= 1; actIndex--) {
for (let befIndex = actIndex - 1; befIndex >= 0; befIndex--) {
if (selector.nodes[actIndex].prop === selector.nodes[befIndex].prop) {
if (
!exact ||
(exact &&
selector.nodes[actIndex].value === selector.nodes[befIndex].value)
) {
selector.nodes[befIndex].remove();
actIndex--;
}
}
}
}
}
const uniformStyle = parser((selector) => {
normalizeAttributes(selector);
sortGroups(selector);
});
const defaultOptions = {
removeDuplicatedProperties: false,
};
module.exports = postcss.plugin(name, (options) => {
options = Object.assign({}, defaultOptions, options);
return (css) => {
// Create a map to store maps
const mapTable = new Map();
// root map to store root selectors
mapTable.set('root', new Map());
css.walkRules((rule) => {
let map;
// Check selector parent for any at rule
if (rule.parent.type === 'atrule') {
// Use name and query params as the key
const query =
rule.parent.name.toLowerCase() +
rule.parent.params.replace(/\s+/g, '');
// See if this query key is already in the map table
map = mapTable.has(query) ? // If it is use it
mapTable.get(query) : // if not set it and get it
mapTable.set(query, new Map()).get(query);
} else {
// Otherwise we are dealing with a selector in the root
map = mapTable.get('root');
}
const selector = uniformStyle.processSync(rule.selector, {
lossless: false,
});
if (map.has(selector)) {
// store original rule as destination
const destination = map.get(selector);
// move declarations to original rule
while (rule.nodes.length > 0) {
destination.append(rule.nodes[0]);
}
// remove duplicated rule
rule.remove();
if (options.removeDuplicatedProperties ||
options.removeDuplicatedValues) {
removeDupProperties(
destination,
options.removeDuplicatedValues,
);
}
} else {
if (options.removeDuplicatedProperties ||
options.removeDuplicatedValues) {
removeDupProperties(
rule,
options.removeDuplicatedValues,
);
}
// add new selector to symbol table
map.set(selector, rule);
}
});
};
});