main.mjs
3.84 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
import { shorthandData } from './shorthand-data.mjs';
const builtInOrders = [
'alphabetical',
'concentric-css',
'smacss',
];
export const cssDeclarationSorter = ({ order = 'alphabetical', keepOverrides = false } = {}) => ({
postcssPlugin: 'css-declaration-sorter',
OnceExit (css) {
let withKeepOverrides = comparator => comparator;
if (keepOverrides) {
withKeepOverrides = withOverridesComparator(shorthandData);
}
if (typeof order === 'function') {
return processCss({ css, comparator: withKeepOverrides(order) });
}
if (!builtInOrders.includes(order))
return Promise.reject(
Error([
`Invalid built-in order '${order}' provided.`,
`Available built-in orders are: ${builtInOrders}`,
].join('\n'))
);
return import(`../orders/${order}.mjs`)
.then(({ properties }) => processCss({
css,
comparator: withKeepOverrides(orderComparator(properties)),
}));
},
});
cssDeclarationSorter.postcss = true;
// Kept for backward compatibility
export default cssDeclarationSorter;
function processCss ({ css, comparator }) {
const comments = [];
const rulesCache = [];
css.walk(node => {
const nodes = node.nodes;
const type = node.type;
if (type === 'comment') {
// Don't do anything to root comments or the last newline comment
const isNewlineNode = node.raws.before && node.raws.before.includes('\n');
const lastNewlineNode = isNewlineNode && !node.next();
const onlyNode = !node.prev() && !node.next() || !node.parent;
if (lastNewlineNode || onlyNode || node.parent.type === 'root') {
return;
}
if (isNewlineNode) {
const pairedNode = node.next() || node.prev();
if (pairedNode) {
comments.unshift({
'comment': node,
'pairedNode': pairedNode,
'insertPosition': node.next() ? 'Before' : 'After',
});
node.remove();
}
} else {
const pairedNode = node.prev() || node.next();
if (pairedNode) {
comments.push({
'comment': node,
'pairedNode': pairedNode,
'insertPosition': 'After',
});
node.remove();
}
}
return;
}
// Add rule-like nodes to a cache so that we can remove all
// comment nodes before we start sorting.
const isRule = type === 'rule' || type === 'atrule';
if (isRule && nodes && nodes.length > 1) {
rulesCache.push(nodes);
}
});
// Perform a sort once all comment nodes are removed
rulesCache.forEach(nodes => {
sortCssDeclarations({ nodes, comparator });
});
// Add comments back to the nodes they are paired with
comments.forEach(node => {
const pairedNode = node.pairedNode;
node.comment.remove();
pairedNode.parent && pairedNode.parent['insert' + node.insertPosition](pairedNode, node.comment);
});
}
function sortCssDeclarations ({ nodes, comparator }) {
nodes.sort((a, b) => {
if (a.type === 'decl' && b.type === 'decl') {
return comparator(a.prop, b.prop);
} else {
return compareDifferentType(a, b);
}
});
}
function withOverridesComparator (shorthandData) {
return function (comparator) {
return function (a, b) {
a = removeVendorPrefix(a);
b = removeVendorPrefix(b);
if (shorthandData[a] && shorthandData[a].includes(b)) return 0;
if (shorthandData[b] && shorthandData[b].includes(a)) return 0;
return comparator(a, b);
};
};
}
function orderComparator (order) {
return function (a, b) {
return order.indexOf(a) - order.indexOf(b);
};
}
function compareDifferentType (a, b) {
if (b.type === 'atrule') {
return 0;
}
return a.type === 'decl' ? -1 : b.type === 'decl' ? 1 : 0;
}
function removeVendorPrefix (property) {
return property.replace(/^-\w+-/, '');
}