-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
131 lines (117 loc) · 3.33 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
131
const valueParser = require('postcss-value-parser');
// Copy pasted and adapted from https://stackoverflow.com/questions/36721830/convert-hsl-to-rgb-and-hex
// Transform an HSL value into its HEX counterpart
function hslToHex({ hue, saturation, lightness }) {
const a = (saturation * Math.min(lightness, 1 - lightness)) / 100;
function f(n) {
const k = (n + hue / 30) % 12;
const color = lightness - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
return Math.round(255 * color)
.toString(16)
.padStart(2, '0'); // Convert to Hex and prefix "0" if needed
}
return `#${f(0)}${f(8)}${f(4)}`;
}
// Figure out if a value contains hsl or hsla
function isHslDeclaration(value) {
return value.indexOf('hsl') !== -1;
}
// Create a Declaration parser
// According to the PostCSS API docs, this is the fastest
// https://github.com/postcss/postcss/blob/main/docs/guidelines/plugin.md#23-use-fast-nodes-scanning
// For each property in the list, it returns an object of the shape:
// ```
// color: (decl) => {
// ...
// },
// background: (decl) => {
// ...
// }
function declarationReplacers(properties, preserve) {
function replacer(decl) {
if (!decl.value || !isHslDeclaration(decl.value)) {
return;
}
// We don't need a fallback if the previous prop equals the current
if (decl.prev() && decl.prev().prop === decl.prop) {
return;
}
const parsedValue = valueParser(decl.value)
.walk((node) => {
const { nodes, value, type } = node;
if (type === 'function' && isHslDeclaration(value)) {
const hue = parseInt(nodes[0].value, 10);
const saturation = parseInt(nodes[2].value, 10);
const lightness = parseInt(nodes[4].value, 10);
if (
typeof hue === 'number' &&
typeof saturation === 'number' &&
typeof lightness === 'number'
) {
/* eslint-disable no-param-reassign */
node.type = 'word';
node.value = hslToHex({
hue,
saturation,
lightness: lightness / 100
});
/* eslint-enable no-param-reassign */
}
}
})
.toString();
if (parsedValue !== decl.value) {
decl.cloneBefore({ value: parsedValue });
if (!preserve) {
decl.remove();
}
}
}
return properties.reduce(
(replacers, property) => ({
...replacers,
[property]: replacer
}),
{}
);
}
const defaultProperties = [
'background-color',
'background',
'border-bottom-color',
'border-color',
'border-left-color',
'border-right-color',
'border-top-color',
'border',
'caret-color',
'color',
'column-rule-color',
'column-rule',
'filter',
'outline-color',
'outline',
'text-decoration-color',
'text-shadow'
];
const defaults = {
skipProperties: [],
preserve: true
};
/**
* PostCSS plugin to transform hsl() and hsla() to hexadecimal
*/
module.exports = (options = {}) => {
const {
preserve = defaults.preserve,
skipProperties = defaults.skipProperties
} = options;
const finalProperties = defaultProperties.filter(
(property) => !skipProperties.includes(property)
);
return {
postcssPlugin: 'postcss-color-hsla-fallback',
Declaration: declarationReplacers(finalProperties, preserve)
};
};
module.exports.postcss = true;