-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathindex.js
More file actions
156 lines (139 loc) · 4.48 KB
/
Copy pathindex.js
File metadata and controls
156 lines (139 loc) · 4.48 KB
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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) {
if (!exact) { // Remove duplicated properties, regardless of value
const retainedProps = new Set();
for (let actIndex = selector.nodes.length - 1; actIndex >= 1; actIndex--) {
const prop = selector.nodes[actIndex].prop;
if (prop !== undefined) {
if (!retainedProps.has(prop)) {
// Mark the prop as retained, all other occurrences must be removed
retainedProps.add(prop);
} else {
// This occurrence of the prop must be removed
selector.nodes[actIndex].remove();
}
}
}
} else {
// 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[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 = (options) => {
options = Object.assign({}, defaultOptions, options);
return {
postcssPlugin: name,
prepare() {
// Create a map to store maps
const mapTable = new Map();
// root map to store root selectors
mapTable.set('root', new Map());
return {
Rule: (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');
}
// create a uniform selector
const selector = uniformStyle.processSync(rule.selector, {
lossless: false,
});
if (map.has(selector)) {
// store original rule as destination
const destination = map.get(selector);
// check if node has already been processed
if (destination === rule) return;
// 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);
}
},
};
},
};
};
module.exports.postcss = true;