-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathindex.js
117 lines (104 loc) · 3.2 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
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
*/
function removeDupProperties(selector) {
// 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) {
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) {
removeDupProperties(destination);
}
} else {
if (options.removeDuplicatedProperties) {
removeDupProperties(rule);
}
// add new selector to symbol table
map.set(selector, rule);
}
});
};
});