@thejaredwilcurt/csslop 0.0.17 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -9
- package/src/rules/custom-properties.js +89 -0
- package/src/rules/selectors.js +304 -0
- package/src/rules/stringify.js +36 -380
- package/src/value/color-mix.js +500 -0
- package/src/value/colors.js +58 -466
- package/src/value/minify.js +33 -1
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@thejaredwilcurt/csslop",
|
|
3
3
|
"main": "index.js",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.19",
|
|
6
6
|
"description": "Experimental CSS minification",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"prestart": "node ./scripts/prestart.js",
|
|
@@ -17,8 +17,7 @@
|
|
|
17
17
|
"real": "node ./tests/realworld.test.js",
|
|
18
18
|
"lint": "eslint *.js scripts src tests --fix",
|
|
19
19
|
"fix": "npm run lint",
|
|
20
|
-
"bump": "npx --yes -- @jsdevtools/version-bump-prompt && npm i"
|
|
21
|
-
"publisho": "npm publish --access=public"
|
|
20
|
+
"bump": "npx --yes -- @jsdevtools/version-bump-prompt && npm i"
|
|
22
21
|
},
|
|
23
22
|
"dependencies": {
|
|
24
23
|
"@csstools/css-calc": "^3.3.0",
|
|
@@ -27,7 +26,7 @@
|
|
|
27
26
|
"devDependencies": {
|
|
28
27
|
"@codemirror/autocomplete": "^6.20.3",
|
|
29
28
|
"@codemirror/lang-css": "^6.3.1",
|
|
30
|
-
"@codemirror/view": "^6.43.
|
|
29
|
+
"@codemirror/view": "^6.43.7",
|
|
31
30
|
"@eslint/js": "^10.0.1",
|
|
32
31
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
33
32
|
"codemirror": "^6.0.2",
|
|
@@ -37,12 +36,12 @@
|
|
|
37
36
|
"eslint-config-tjw-import-x": "^1.0.1",
|
|
38
37
|
"eslint-config-tjw-jsdoc": "^2.0.1",
|
|
39
38
|
"eslint-plugin-import-x": "^4.17.0",
|
|
40
|
-
"eslint-plugin-jsdoc": "^63.
|
|
39
|
+
"eslint-plugin-jsdoc": "^63.3.2",
|
|
41
40
|
"fflate": "^0.8.3",
|
|
42
|
-
"globals": "^17.
|
|
41
|
+
"globals": "^17.8.0",
|
|
43
42
|
"pretty-ms": "^9.3.0",
|
|
44
43
|
"real-world-css-libraries": "^1.0.5",
|
|
45
|
-
"vite": "^8.
|
|
44
|
+
"vite": "^8.2.0"
|
|
46
45
|
},
|
|
47
46
|
"author": "The Jared Wilcurt",
|
|
48
47
|
"homepage": "https://github.com/TheJaredWilcurt/csslop#readme",
|
|
@@ -59,11 +58,11 @@
|
|
|
59
58
|
"devEngines": {
|
|
60
59
|
"runtime": {
|
|
61
60
|
"name": "node",
|
|
62
|
-
"version": "26.5.
|
|
61
|
+
"version": "26.5.1"
|
|
63
62
|
},
|
|
64
63
|
"packageManager": {
|
|
65
64
|
"name": "npm",
|
|
66
|
-
"version": "
|
|
65
|
+
"version": "12.0.2"
|
|
67
66
|
}
|
|
68
67
|
}
|
|
69
68
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Custom property value whitespace and comment processing for CSS minification.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Removes spaces after commas only inside parenthesized groups (function
|
|
7
|
+
* calls like `var()`, `calc()`), leaving top-level comma spacing intact.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} value The whitespace-collapsed custom property value.
|
|
10
|
+
* @return {string} The value with post-comma spaces removed inside function calls only.
|
|
11
|
+
*/
|
|
12
|
+
function removeSpacesAfterCommasInsideFunctions (value) {
|
|
13
|
+
let result = '';
|
|
14
|
+
let parenthesisDepth = 0;
|
|
15
|
+
for (let index = 0; index < value.length; index++) {
|
|
16
|
+
const character = value[index];
|
|
17
|
+
if (character === '(') {
|
|
18
|
+
parenthesisDepth++;
|
|
19
|
+
}
|
|
20
|
+
if (character === ')') {
|
|
21
|
+
parenthesisDepth--;
|
|
22
|
+
}
|
|
23
|
+
if (character === ',' && parenthesisDepth > 0) {
|
|
24
|
+
result += ',';
|
|
25
|
+
// Skip whitespace after the comma inside function calls
|
|
26
|
+
while (index + 1 < value.length && value[index + 1] === ' ') {
|
|
27
|
+
index++;
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
result += character;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Strips leading zeros from decimal numbers in a custom property value
|
|
38
|
+
* (e.g. `0.5` becomes `.5`, `-0.02em` becomes `-.02em`).
|
|
39
|
+
*
|
|
40
|
+
* @param {string} value The custom property value string.
|
|
41
|
+
* @return {string} The value with leading zeros removed from decimals.
|
|
42
|
+
*/
|
|
43
|
+
function stripLeadingZerosFromDecimals (value) {
|
|
44
|
+
// Match a boundary (start, whitespace, comma, open-paren), optional sign, then leading zeros before a decimal
|
|
45
|
+
return value.replace(/(^|\s|,|\()(-?)0+(\.\d+)/g, '$1$2$3');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Processes CSS comments within a custom property value. If the value
|
|
50
|
+
* consists entirely of a comment, the comment is removed (producing an
|
|
51
|
+
* empty value). If comments appear between other tokens, their content
|
|
52
|
+
* is stripped but empty comment delimiters are kept as zero-width
|
|
53
|
+
* token separators to preserve the token sequence.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} value The raw custom property value string.
|
|
56
|
+
* @return {string} The value with comments processed.
|
|
57
|
+
*/
|
|
58
|
+
function processCustomPropertyComments (value) {
|
|
59
|
+
// Match values that are entirely a comment (with optional surrounding whitespace)
|
|
60
|
+
const commentOnlyPattern = /^\s*\/\*.*?\*\/\s*$/s;
|
|
61
|
+
if (commentOnlyPattern.test(value)) {
|
|
62
|
+
return '';
|
|
63
|
+
}
|
|
64
|
+
// Strip comment content but keep empty markers as token separators
|
|
65
|
+
return value.replace(/\/\*.*?\*\//g, '/**/');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Collapses whitespace in a custom property value while preserving
|
|
70
|
+
* token boundaries. Each whitespace sequence is reduced to a single
|
|
71
|
+
* space, spaces after commas inside function calls are removed, and
|
|
72
|
+
* leading zeros on decimal numbers are stripped.
|
|
73
|
+
*
|
|
74
|
+
* @param {string} value The raw custom property value string.
|
|
75
|
+
* @return {string} The minified custom property value.
|
|
76
|
+
*/
|
|
77
|
+
function collapseCustomPropertyWhitespace (value) {
|
|
78
|
+
// Collapse all whitespace sequences (newlines, tabs, multiple spaces) to a single space
|
|
79
|
+
let collapsed = value.replace(/\s+/g, ' ');
|
|
80
|
+
// Remove spaces after commas only inside function calls (e.g. var(--bar, 1.5) → var(--bar,1.5))
|
|
81
|
+
collapsed = removeSpacesAfterCommasInsideFunctions(collapsed);
|
|
82
|
+
// Strip leading zeros from decimals (e.g. 0.5 → .5, -0.02em → -.02em)
|
|
83
|
+
collapsed = stripLeadingZerosFromDecimals(collapsed);
|
|
84
|
+
return collapsed;
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
collapseCustomPropertyWhitespace,
|
|
88
|
+
processCustomPropertyComments
|
|
89
|
+
};
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Selector minification utilities for CSS rule stringification.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Splits a parameter string by commas while respecting nested parentheses,
|
|
7
|
+
* so commas inside function calls within default values are not treated as separators.
|
|
8
|
+
*
|
|
9
|
+
* @param {string} parameterString The comma-separated parameter string to split.
|
|
10
|
+
* @return {Array} An array of individual parameter strings.
|
|
11
|
+
*/
|
|
12
|
+
function splitParametersByComma (parameterString) {
|
|
13
|
+
const parameters = [];
|
|
14
|
+
let currentParameter = '';
|
|
15
|
+
let parenthesisDepth = 0;
|
|
16
|
+
for (const character of parameterString) {
|
|
17
|
+
if (character === '(') {
|
|
18
|
+
parenthesisDepth++;
|
|
19
|
+
} else if (character === ')') {
|
|
20
|
+
parenthesisDepth--;
|
|
21
|
+
}
|
|
22
|
+
if (character === ',' && parenthesisDepth === 0) {
|
|
23
|
+
parameters.push(currentParameter);
|
|
24
|
+
currentParameter = '';
|
|
25
|
+
} else {
|
|
26
|
+
currentParameter += character;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
parameters.push(currentParameter);
|
|
30
|
+
return parameters;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Finds the index of the closing parenthesis that matches the opening
|
|
35
|
+
* parenthesis at the given position in the string.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} text The string to search within.
|
|
38
|
+
* @param {number} openIndex The index of the opening parenthesis.
|
|
39
|
+
* @return {number} The index of the matching closing parenthesis, or -1 if not found.
|
|
40
|
+
*/
|
|
41
|
+
function findMatchingCloseParenthesis (text, openIndex) {
|
|
42
|
+
let depth = 0;
|
|
43
|
+
for (let index = openIndex; index < text.length; index++) {
|
|
44
|
+
if (text[index] === '(') {
|
|
45
|
+
depth++;
|
|
46
|
+
} else if (text[index] === ')') {
|
|
47
|
+
depth--;
|
|
48
|
+
if (depth === 0) {
|
|
49
|
+
return index;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return -1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Extracts the type selector or universal selector from the beginning of a
|
|
58
|
+
* compound selector string, if one is present. A type selector is a bare
|
|
59
|
+
* element name (e.g. `div`, `a`); the universal selector is `*`.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} compoundSelector A single compound CSS selector string.
|
|
62
|
+
* @return {string|null} The type or universal selector, or null if none is present.
|
|
63
|
+
*/
|
|
64
|
+
function extractTypeSelector (compoundSelector) {
|
|
65
|
+
// Match universal selector (*) or type selector (letter followed by alphanumeric chars or hyphens)
|
|
66
|
+
const match = compoundSelector.match(/^(\*|[a-zA-Z][a-zA-Z0-9-]*)/);
|
|
67
|
+
if (match) {
|
|
68
|
+
return match[0];
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Merges two simple/compound selectors into a single compound selector,
|
|
75
|
+
* ensuring any type or universal selector appears first. Returns null when
|
|
76
|
+
* merging is invalid because both sides contain a type or universal selector.
|
|
77
|
+
*
|
|
78
|
+
* @param {string} left The first selector to merge.
|
|
79
|
+
* @param {string} right The second selector to merge.
|
|
80
|
+
* @return {string|null} The merged compound selector, or null if the merge is invalid.
|
|
81
|
+
*/
|
|
82
|
+
function mergeCompoundSelectors (left, right) {
|
|
83
|
+
const leftTypeSelector = extractTypeSelector(left);
|
|
84
|
+
const rightTypeSelector = extractTypeSelector(right);
|
|
85
|
+
if (leftTypeSelector && rightTypeSelector) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
// When the right side has a type selector, it must come first in the compound
|
|
89
|
+
if (rightTypeSelector) {
|
|
90
|
+
return right + left;
|
|
91
|
+
}
|
|
92
|
+
return left + right;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Builds the cartesian product of two selector lists by merging every
|
|
97
|
+
* combination of left and right selectors into compound selectors.
|
|
98
|
+
* Returns null if any combination produces an invalid merge.
|
|
99
|
+
*
|
|
100
|
+
* @param {Array} leftParts Selectors from the first `:where()`.
|
|
101
|
+
* @param {Array} rightParts Selectors from the second `:where()`.
|
|
102
|
+
* @return {Array|null} The array of merged compound selectors, or null if any merge is invalid.
|
|
103
|
+
*/
|
|
104
|
+
function buildWhereCartesianProduct (leftParts, rightParts) {
|
|
105
|
+
const products = [];
|
|
106
|
+
for (const leftSelector of leftParts) {
|
|
107
|
+
for (const rightSelector of rightParts) {
|
|
108
|
+
const merged = mergeCompoundSelectors(leftSelector.trim(), rightSelector.trim());
|
|
109
|
+
if (merged === null) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
products.push(merged);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return products;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Scans a selector string for adjacent `:where(A):where(B)` patterns and
|
|
120
|
+
* merges them into a single `:where(AB)` (or `:where()` with the cartesian
|
|
121
|
+
* product of their selector lists) when the merged form is strictly shorter.
|
|
122
|
+
* Type selectors are correctly repositioned to the front of each merged
|
|
123
|
+
* compound, and merges that would produce invalid compound selectors (two
|
|
124
|
+
* type selectors) are skipped.
|
|
125
|
+
*
|
|
126
|
+
* @param {string} selector A minified CSS selector string.
|
|
127
|
+
* @return {string} The selector with beneficial adjacent `:where()` merges applied.
|
|
128
|
+
*/
|
|
129
|
+
function mergeAdjacentWherePseudoClasses (selector) {
|
|
130
|
+
let result = selector;
|
|
131
|
+
let position = 0;
|
|
132
|
+
while (position < result.length) {
|
|
133
|
+
const whereIndex = result.indexOf(':where(', position);
|
|
134
|
+
if (whereIndex === -1) {
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
// Index of the '(' in the first ':where('
|
|
138
|
+
const firstOpenParenthesis = whereIndex + 6;
|
|
139
|
+
const firstCloseParenthesis = findMatchingCloseParenthesis(result, firstOpenParenthesis);
|
|
140
|
+
if (firstCloseParenthesis === -1) {
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
const adjacentStart = firstCloseParenthesis + 1;
|
|
144
|
+
const adjacentWhereTag = ':where(';
|
|
145
|
+
if (result.slice(adjacentStart, adjacentStart + adjacentWhereTag.length) !== adjacentWhereTag) {
|
|
146
|
+
position = firstCloseParenthesis + 1;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
// Index of the '(' in the second ':where('
|
|
150
|
+
const secondOpenParenthesis = adjacentStart + 6;
|
|
151
|
+
const secondCloseParenthesis = findMatchingCloseParenthesis(result, secondOpenParenthesis);
|
|
152
|
+
if (secondCloseParenthesis === -1) {
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
const firstInnerContent = result.slice(firstOpenParenthesis + 1, firstCloseParenthesis);
|
|
156
|
+
const secondInnerContent = result.slice(secondOpenParenthesis + 1, secondCloseParenthesis);
|
|
157
|
+
const leftParts = splitParametersByComma(firstInnerContent);
|
|
158
|
+
const rightParts = splitParametersByComma(secondInnerContent);
|
|
159
|
+
const mergedParts = buildWhereCartesianProduct(leftParts, rightParts);
|
|
160
|
+
if (mergedParts === null) {
|
|
161
|
+
position = firstCloseParenthesis + 1;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const originalFragment = result.slice(whereIndex, secondCloseParenthesis + 1);
|
|
165
|
+
const mergedFragment = ':where(' + mergedParts.join(',') + ')';
|
|
166
|
+
if (mergedFragment.length < originalFragment.length) {
|
|
167
|
+
result = result.slice(0, whereIndex) + mergedFragment + result.slice(secondCloseParenthesis + 1);
|
|
168
|
+
// Don't advance position; the merged result may be adjacent to another :where()
|
|
169
|
+
} else {
|
|
170
|
+
position = firstCloseParenthesis + 1;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Processes a bare `:is()` selector by merging `:link`+`:visited` into `:any-link`,
|
|
178
|
+
* de-duplicating, sorting alphabetically, and conditionally expanding into individual
|
|
179
|
+
* selectors when all parts are simple type/universal selectors with no modifications.
|
|
180
|
+
*
|
|
181
|
+
* @param {string} selector A minified CSS selector string.
|
|
182
|
+
* @return {Array} An array of one or more processed selector strings.
|
|
183
|
+
*/
|
|
184
|
+
function processIsSelector (selector) {
|
|
185
|
+
// Replace :is(:link,:visited) and :is(:visited,:link) with :any-link
|
|
186
|
+
selector = selector.replace(/:is\(:link,:visited\)/g, ':any-link');
|
|
187
|
+
selector = selector.replace(/:is\(:visited,:link\)/g, ':any-link');
|
|
188
|
+
// Only process bare :is() selectors (where :is() is the entire selector)
|
|
189
|
+
if (!selector.startsWith(':is(')) {
|
|
190
|
+
return [selector];
|
|
191
|
+
}
|
|
192
|
+
let depth = 0;
|
|
193
|
+
let closingIndex = -1;
|
|
194
|
+
for (let index = 4; index < selector.length; index++) {
|
|
195
|
+
if (selector[index] === '(') {
|
|
196
|
+
depth++;
|
|
197
|
+
} else if (selector[index] === ')') {
|
|
198
|
+
if (depth === 0) {
|
|
199
|
+
closingIndex = index;
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
depth--;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (closingIndex !== selector.length - 1) {
|
|
206
|
+
return [selector];
|
|
207
|
+
}
|
|
208
|
+
const content = selector.slice(4, -1);
|
|
209
|
+
let parts = [];
|
|
210
|
+
let currentPart = '';
|
|
211
|
+
let parenDepth = 0;
|
|
212
|
+
for (const character of content) {
|
|
213
|
+
if (character === '(') {
|
|
214
|
+
parenDepth++;
|
|
215
|
+
} else if (character === ')') {
|
|
216
|
+
parenDepth--;
|
|
217
|
+
}
|
|
218
|
+
if (character === ',' && parenDepth === 0) {
|
|
219
|
+
parts.push(currentPart);
|
|
220
|
+
currentPart = '';
|
|
221
|
+
} else {
|
|
222
|
+
currentPart += character;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
parts.push(currentPart);
|
|
226
|
+
const originalCount = parts.length;
|
|
227
|
+
// Replace :link + :visited with :any-link
|
|
228
|
+
const hasLink = parts.includes(':link');
|
|
229
|
+
const hasVisited = parts.includes(':visited');
|
|
230
|
+
if (hasLink && hasVisited) {
|
|
231
|
+
parts = parts.filter((part) => {
|
|
232
|
+
return part !== ':link' && part !== ':visited';
|
|
233
|
+
});
|
|
234
|
+
if (!parts.includes(':any-link')) {
|
|
235
|
+
parts.push(':any-link');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// De-duplicate
|
|
239
|
+
parts = [...new Set(parts)];
|
|
240
|
+
// Sort alphabetically
|
|
241
|
+
parts.sort();
|
|
242
|
+
// Unwrap :is() with a single selector
|
|
243
|
+
if (parts.length === 1) {
|
|
244
|
+
return parts;
|
|
245
|
+
}
|
|
246
|
+
// Expand if all parts are simple type/universal selectors and no dedup/replacement occurred
|
|
247
|
+
const allSimple = parts.every((part) => {
|
|
248
|
+
return /^[a-z*][a-z0-9-]*$/i.test(part);
|
|
249
|
+
});
|
|
250
|
+
if (allSimple && parts.length === originalCount) {
|
|
251
|
+
return parts;
|
|
252
|
+
}
|
|
253
|
+
return [':is(' + parts.join(',') + ')'];
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Flattens a top-level `:is()` selector into its individual parts when the rule
|
|
258
|
+
* acts as a nesting parent. A nesting parent's entire selector list is treated
|
|
259
|
+
* as `:is()` when computing the specificity of its nested children, so lifting
|
|
260
|
+
* the parts out of an inner `:is()` does not change specificity. The `:is()` is
|
|
261
|
+
* kept when any part contains a pseudo (`:`), since such selectors may be
|
|
262
|
+
* unsupported and rely on `:is()` for forgiving parsing.
|
|
263
|
+
*
|
|
264
|
+
* @param {string} selector A minified CSS selector string.
|
|
265
|
+
* @return {Array} The flattened selector parts, or the original selector.
|
|
266
|
+
*/
|
|
267
|
+
function flattenNestingParentIsSelector (selector) {
|
|
268
|
+
if (!selector.startsWith(':is(')) {
|
|
269
|
+
return [selector];
|
|
270
|
+
}
|
|
271
|
+
let depth = 0;
|
|
272
|
+
let closingIndex = -1;
|
|
273
|
+
for (let index = 4; index < selector.length; index++) {
|
|
274
|
+
if (selector[index] === '(') {
|
|
275
|
+
depth++;
|
|
276
|
+
} else if (selector[index] === ')') {
|
|
277
|
+
if (depth === 0) {
|
|
278
|
+
closingIndex = index;
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
depth--;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// The :is() must span the entire selector to be safely liftable
|
|
285
|
+
if (closingIndex !== selector.length - 1) {
|
|
286
|
+
return [selector];
|
|
287
|
+
}
|
|
288
|
+
const parts = splitParametersByComma(selector.slice(4, -1)).map((part) => {
|
|
289
|
+
return part.trim();
|
|
290
|
+
});
|
|
291
|
+
const hasPotentiallyUnsupportedPart = parts.some((part) => {
|
|
292
|
+
return part.includes(':');
|
|
293
|
+
});
|
|
294
|
+
if (hasPotentiallyUnsupportedPart) {
|
|
295
|
+
return [selector];
|
|
296
|
+
}
|
|
297
|
+
return parts;
|
|
298
|
+
}
|
|
299
|
+
export {
|
|
300
|
+
flattenNestingParentIsSelector,
|
|
301
|
+
mergeAdjacentWherePseudoClasses,
|
|
302
|
+
processIsSelector,
|
|
303
|
+
splitParametersByComma
|
|
304
|
+
};
|