| [9af201e] | 1 | 'use strict';
|
|---|
| 2 | const valueParser = require('postcss-value-parser');
|
|---|
| 3 |
|
|---|
| 4 | const directionKeywords = new Set(['top', 'right', 'bottom', 'left', 'center']);
|
|---|
| 5 |
|
|---|
| 6 | const center = '50%';
|
|---|
| 7 | const horizontal = new Map([
|
|---|
| 8 | ['right', '100%'],
|
|---|
| 9 | ['left', '0'],
|
|---|
| 10 | ]);
|
|---|
| 11 | const verticalValue = new Map([
|
|---|
| 12 | ['bottom', '100%'],
|
|---|
| 13 | ['top', '0'],
|
|---|
| 14 | ]);
|
|---|
| 15 | const mathFunctions = new Set(['calc', 'min', 'max', 'clamp']);
|
|---|
| 16 | const variableFunctions = new Set(['var', 'env', 'constant']);
|
|---|
| 17 | /**
|
|---|
| 18 | * @param {valueParser.Node} node
|
|---|
| 19 | * @return {boolean}
|
|---|
| 20 | */
|
|---|
| 21 | function isCommaNode(node) {
|
|---|
| 22 | return node.type === 'div' && node.value === ',';
|
|---|
| 23 | }
|
|---|
| 24 |
|
|---|
| 25 | /**
|
|---|
| 26 | * @param {valueParser.Node} node
|
|---|
| 27 | * @return {boolean}
|
|---|
| 28 | */
|
|---|
| 29 | function 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 | */
|
|---|
| 41 | function 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 | */
|
|---|
| 52 | function 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 | */
|
|---|
| 66 | function 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 | */
|
|---|
| 84 | function 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 | */
|
|---|
| 215 | function 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 |
|
|---|
| 247 | pluginCreator.postcss = true;
|
|---|
| 248 | module.exports = pluginCreator;
|
|---|