source: frontend/node_modules/postcss-normalize-positions/src/index.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 5.3 KB
Line 
1'use strict';
2const valueParser = require('postcss-value-parser');
3
4const directionKeywords = new Set(['top', 'right', 'bottom', 'left', 'center']);
5
6const center = '50%';
7const horizontal = new Map([
8 ['right', '100%'],
9 ['left', '0'],
10]);
11const verticalValue = new Map([
12 ['bottom', '100%'],
13 ['top', '0'],
14]);
15const mathFunctions = new Set(['calc', 'min', 'max', 'clamp']);
16const variableFunctions = new Set(['var', 'env', 'constant']);
17/**
18 * @param {valueParser.Node} node
19 * @return {boolean}
20 */
21function isCommaNode(node) {
22 return node.type === 'div' && node.value === ',';
23}
24
25/**
26 * @param {valueParser.Node} node
27 * @return {boolean}
28 */
29function isVariableFunctionNode(node) {
30 if (node.type !== 'function') {
31 return false;
32 }
33
34 return variableFunctions.has(node.value.toLowerCase());
35}
36
37/**
38 * @param {valueParser.Node} node
39 * @return {boolean}
40 */
41function isMathFunctionNode(node) {
42 if (node.type !== 'function') {
43 return false;
44 }
45 return mathFunctions.has(node.value.toLowerCase());
46}
47
48/**
49 * @param {valueParser.Node} node
50 * @return {boolean}
51 */
52function isNumberNode(node) {
53 if (node.type !== 'word') {
54 return false;
55 }
56
57 const value = parseFloat(node.value);
58
59 return !isNaN(value);
60}
61
62/**
63 * @param {valueParser.Node} node
64 * @return {boolean}
65 */
66function isDimensionNode(node) {
67 if (node.type !== 'word') {
68 return false;
69 }
70
71 const parsed = valueParser.unit(node.value);
72
73 if (!parsed) {
74 return false;
75 }
76
77 return parsed.unit !== '';
78}
79
80/**
81 * @param {string} value
82 * @return {string}
83 */
84function transform(value) {
85 const parsed = valueParser(value);
86 /** @type {({start: number, end: number} | {start: null, end: null})[]} */
87 const ranges = [];
88 let rangeIndex = 0;
89 let shouldContinue = true;
90
91 parsed.nodes.forEach((node, index) => {
92 // After comma (`,`) follows next background
93 if (isCommaNode(node)) {
94 rangeIndex += 1;
95 shouldContinue = true;
96
97 return;
98 }
99
100 if (!shouldContinue) {
101 return;
102 }
103
104 // After separator (`/`) follows `background-size` values
105 // Avoid them
106 if (node.type === 'div' && node.value === '/') {
107 shouldContinue = false;
108
109 return;
110 }
111
112 if (!ranges[rangeIndex]) {
113 ranges[rangeIndex] = {
114 start: null,
115 end: null,
116 };
117 }
118
119 // Do not try to be processed `var and `env` function inside background
120 if (isVariableFunctionNode(node)) {
121 shouldContinue = false;
122 ranges[rangeIndex].start = null;
123 ranges[rangeIndex].end = null;
124
125 return;
126 }
127
128 const isPositionKeyword =
129 (node.type === 'word' &&
130 directionKeywords.has(node.value.toLowerCase())) ||
131 isDimensionNode(node) ||
132 isNumberNode(node) ||
133 isMathFunctionNode(node);
134
135 if (ranges[rangeIndex].start === null && isPositionKeyword) {
136 ranges[rangeIndex].start = index;
137 ranges[rangeIndex].end = index;
138
139 return;
140 }
141
142 if (ranges[rangeIndex].start !== null) {
143 if (node.type === 'space') {
144 return;
145 } else if (isPositionKeyword) {
146 ranges[rangeIndex].end = index;
147
148 return;
149 }
150
151 return;
152 }
153 });
154
155 ranges.forEach((range) => {
156 if (range.start === null) {
157 return;
158 }
159
160 const nodes = parsed.nodes.slice(range.start, range.end + 1);
161
162 if (nodes.length > 3) {
163 return;
164 }
165
166 const firstNode = nodes[0].value.toLowerCase();
167 const secondNode =
168 nodes[2] && nodes[2].value ? nodes[2].value.toLowerCase() : null;
169
170 if (nodes.length === 1 || secondNode === 'center') {
171 if (secondNode) {
172 nodes[2].value = nodes[1].value = '';
173 }
174
175 const map = new Map([...horizontal, ['center', center]]);
176
177 if (map.has(firstNode)) {
178 nodes[0].value = /** @type {string}*/ (map.get(firstNode));
179 }
180
181 return;
182 }
183
184 if (secondNode !== null) {
185 if (firstNode === 'center' && directionKeywords.has(secondNode)) {
186 nodes[0].value = nodes[1].value = '';
187
188 if (horizontal.has(secondNode)) {
189 nodes[2].value = /** @type {string} */ (horizontal.get(secondNode));
190 }
191 return;
192 }
193
194 if (horizontal.has(firstNode) && verticalValue.has(secondNode)) {
195 nodes[0].value = /** @type {string} */ (horizontal.get(firstNode));
196 nodes[2].value = /** @type {string} */ (verticalValue.get(secondNode));
197
198 return;
199 } else if (verticalValue.has(firstNode) && horizontal.has(secondNode)) {
200 nodes[0].value = /** @type {string} */ (horizontal.get(secondNode));
201 nodes[2].value = /** @type {string} */ (verticalValue.get(firstNode));
202
203 return;
204 }
205 }
206 });
207
208 return parsed.toString();
209}
210
211/**
212 * @type {import('postcss').PluginCreator<void>}
213 * @return {import('postcss').Plugin}
214 */
215function pluginCreator() {
216 return {
217 postcssPlugin: 'postcss-normalize-positions',
218
219 OnceExit(css) {
220 const cache = new Map();
221
222 css.walkDecls(
223 /^(background(-position)?|(-\w+-)?perspective-origin)$/i,
224 (decl) => {
225 const value = decl.value;
226
227 if (!value) {
228 return;
229 }
230
231 if (cache.has(value)) {
232 decl.value = cache.get(value);
233
234 return;
235 }
236
237 const result = transform(value);
238
239 decl.value = result;
240 cache.set(value, result);
241 }
242 );
243 },
244 };
245}
246
247pluginCreator.postcss = true;
248module.exports = pluginCreator;
Note: See TracBrowser for help on using the repository browser.