@tbela99/css-parser 0.0.1-alpha5 → 0.0.1-rc1
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/.gitattributes +1 -0
- package/README.md +18 -6
- package/dist/config.json.js +95 -4
- package/dist/index-umd-web.js +1609 -1391
- package/dist/index.cjs +1609 -1391
- package/dist/index.d.ts +317 -9
- package/dist/index.js +6 -2
- package/dist/lib/{parser/deduplicate.js → ast/minify.js} +393 -414
- package/dist/lib/parser/declaration/list.js +38 -9
- package/dist/lib/parser/declaration/map.js +203 -145
- package/dist/lib/parser/declaration/set.js +26 -34
- package/dist/lib/parser/parse.js +237 -683
- package/dist/lib/parser/tokenize.js +452 -0
- package/dist/lib/parser/utils/eq.js +29 -5
- package/dist/lib/parser/utils/syntax.js +24 -7
- package/dist/lib/renderer/render.js +9 -6
- package/dist/lib/transform.js +1 -1
- package/dist/node/index.js +1 -0
- package/dist/web/index.js +6 -2
- package/package.json +11 -5
- /package/dist/lib/{walker → ast}/walk.js +0 -0
|
@@ -1,59 +1,303 @@
|
|
|
1
|
-
import { isIdentStart, isWhiteSpace } from '
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { eq } from './utils/eq.js';
|
|
1
|
+
import { isIdentStart, isIdent, isFunction, isWhiteSpace } from '../parser/utils/syntax.js';
|
|
2
|
+
import { PropertyList } from '../parser/declaration/list.js';
|
|
3
|
+
import { eq } from '../parser/utils/eq.js';
|
|
5
4
|
import { render } from '../renderer/render.js';
|
|
5
|
+
import '../renderer/utils/color.js';
|
|
6
6
|
|
|
7
|
-
const configuration = getConfig();
|
|
8
7
|
const combinators = ['+', '>', '~'];
|
|
9
8
|
const notEndingWith = ['(', '['].concat(combinators);
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
let pSel = match.selector1.reduce(reducer, []).join(',');
|
|
13
|
-
// @ts-ignore
|
|
14
|
-
let nSel = match.selector2.reduce(reducer, []).join(',');
|
|
15
|
-
// @ts-ignore
|
|
16
|
-
const wrapper = { ...previous, chi: [], sel: match.match.reduce(reducer, []).join(',') };
|
|
17
|
-
// @ts-ignore
|
|
18
|
-
Object.defineProperty(wrapper, 'raw', {
|
|
19
|
-
enumerable: false,
|
|
20
|
-
writable: true,
|
|
9
|
+
function minify(ast, options = {}, recursive = false) {
|
|
10
|
+
function wrapNodes(previous, node, match, ast, i, nodeIndex) {
|
|
21
11
|
// @ts-ignore
|
|
22
|
-
|
|
23
|
-
});
|
|
24
|
-
if (pSel == '&' || pSel === '') {
|
|
12
|
+
let pSel = match.selector1.reduce(reducer, []).join(',');
|
|
25
13
|
// @ts-ignore
|
|
26
|
-
|
|
14
|
+
let nSel = match.selector2.reduce(reducer, []).join(',');
|
|
27
15
|
// @ts-ignore
|
|
28
|
-
|
|
16
|
+
const wrapper = { ...previous, chi: [], sel: match.match.reduce(reducer, []).join(',') };
|
|
17
|
+
// @ts-ignore
|
|
18
|
+
Object.defineProperty(wrapper, 'raw', {
|
|
19
|
+
enumerable: false,
|
|
20
|
+
writable: true,
|
|
21
|
+
// @ts-ignore
|
|
22
|
+
value: match.match.map(t => t.slice())
|
|
23
|
+
});
|
|
24
|
+
if (pSel == '&' || pSel === '') {
|
|
25
|
+
// @ts-ignore
|
|
26
|
+
wrapper.chi.push(...previous.chi);
|
|
29
27
|
// @ts-ignore
|
|
30
|
-
|
|
28
|
+
if ((nSel == '&' || nSel === '') && hasOnlyDeclarations(previous)) {
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
wrapper.chi.push(...node.chi);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
// @ts-ignore
|
|
34
|
+
wrapper.chi.push(node);
|
|
35
|
+
}
|
|
31
36
|
}
|
|
32
37
|
else {
|
|
33
38
|
// @ts-ignore
|
|
34
|
-
wrapper.chi.push(node);
|
|
39
|
+
wrapper.chi.push(previous, node);
|
|
35
40
|
}
|
|
41
|
+
// @ts-ignore
|
|
42
|
+
ast.chi.splice(i, 1, wrapper);
|
|
43
|
+
// @ts-ignore
|
|
44
|
+
ast.chi.splice(nodeIndex, 1);
|
|
45
|
+
// @ts-ignore
|
|
46
|
+
previous.sel = pSel;
|
|
47
|
+
// @ts-ignore
|
|
48
|
+
previous.raw = match.selector1;
|
|
49
|
+
// @ts-ignore
|
|
50
|
+
node.sel = nSel;
|
|
51
|
+
// @ts-ignore
|
|
52
|
+
node.raw = match.selector2;
|
|
53
|
+
reduceRuleSelector(wrapper);
|
|
54
|
+
return wrapper;
|
|
55
|
+
}
|
|
56
|
+
function reducer(acc, curr, index, array) {
|
|
57
|
+
// trim :is()
|
|
58
|
+
if (array.length == 1 && array[0][0] == ':is(' && array[0].at(-1) == ')') {
|
|
59
|
+
curr = curr.slice(1, -1);
|
|
60
|
+
}
|
|
61
|
+
if (curr[0] == '&') {
|
|
62
|
+
if (curr[1] == ' ' && !isIdent(curr[2]) && !isFunction(curr[2])) {
|
|
63
|
+
curr.splice(0, 2);
|
|
64
|
+
}
|
|
65
|
+
else if (combinators.includes(curr[1])) {
|
|
66
|
+
curr.splice(0, 1);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else if (ast.typ == 'Rule' && (isIdent(curr[0]) || isFunction(curr[0]))) {
|
|
70
|
+
curr.unshift('&', ' ');
|
|
71
|
+
}
|
|
72
|
+
acc.push(curr.join(''));
|
|
73
|
+
return acc;
|
|
36
74
|
}
|
|
37
|
-
|
|
75
|
+
function diff(n1, n2, options = {}) {
|
|
76
|
+
let node1 = n1;
|
|
77
|
+
let node2 = n2;
|
|
78
|
+
let exchanged = false;
|
|
79
|
+
if (node1.chi.length > node2.chi.length) {
|
|
80
|
+
const t = node1;
|
|
81
|
+
node1 = node2;
|
|
82
|
+
node2 = t;
|
|
83
|
+
exchanged = true;
|
|
84
|
+
}
|
|
85
|
+
let i = node1.chi.length;
|
|
86
|
+
let j = node2.chi.length;
|
|
87
|
+
if (i == 0 || j == 0) {
|
|
88
|
+
// @ts-ignore
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
// @ts-ignore
|
|
92
|
+
const raw1 = node1.raw;
|
|
38
93
|
// @ts-ignore
|
|
39
|
-
|
|
94
|
+
const raw2 = node2.raw;
|
|
95
|
+
// @ts-ignore
|
|
96
|
+
node1 = { ...node1, chi: node1.chi.slice() };
|
|
97
|
+
node2 = { ...node2, chi: node2.chi.slice() };
|
|
98
|
+
if (raw1 != null) {
|
|
99
|
+
Object.defineProperty(node1, 'raw', { enumerable: false, writable: true, value: raw1 });
|
|
100
|
+
}
|
|
101
|
+
if (raw2 != null) {
|
|
102
|
+
Object.defineProperty(node2, 'raw', { enumerable: false, writable: true, value: raw2 });
|
|
103
|
+
}
|
|
104
|
+
const intersect = [];
|
|
105
|
+
while (i--) {
|
|
106
|
+
if (node1.chi[i].typ == 'Comment') {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
j = node2.chi.length;
|
|
110
|
+
if (j == 0) {
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
while (j--) {
|
|
114
|
+
if (node2.chi[j].typ == 'Comment') {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (node1.chi[i].nam == node2.chi[j].nam) {
|
|
118
|
+
if (eq(node1.chi[i], node2.chi[j])) {
|
|
119
|
+
intersect.push(node1.chi[i]);
|
|
120
|
+
node1.chi.splice(i, 1);
|
|
121
|
+
node2.chi.splice(j, 1);
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// @ts-ignore
|
|
128
|
+
const result = (intersect.length == 0 ? null : {
|
|
129
|
+
...node1,
|
|
130
|
+
// @ts-ignore
|
|
131
|
+
sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(','),
|
|
132
|
+
chi: intersect.reverse()
|
|
133
|
+
});
|
|
134
|
+
if (result == null || [n1, n2].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + render(curr, options).code.length, 0) <= [node1, node2, result].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + render(curr, options).code.length, 0)) {
|
|
135
|
+
// @ts-ignore
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node2 : node2 };
|
|
139
|
+
}
|
|
140
|
+
function matchSelectors(selector1, selector2, parentType) {
|
|
141
|
+
let match = [[]];
|
|
142
|
+
const j = Math.min(selector1.reduce((acc, curr) => Math.min(acc, curr.length), selector1.length > 0 ? selector1[0].length : 0), selector2.reduce((acc, curr) => Math.min(acc, curr.length), selector2.length > 0 ? selector2[0].length : 0));
|
|
143
|
+
let i = 0;
|
|
144
|
+
let k;
|
|
145
|
+
let l;
|
|
146
|
+
let token;
|
|
147
|
+
let matching = true;
|
|
148
|
+
let matchFunction = 0;
|
|
149
|
+
let inAttr = 0;
|
|
150
|
+
for (; i < j; i++) {
|
|
151
|
+
k = 0;
|
|
152
|
+
token = selector1[0][i];
|
|
153
|
+
for (; k < selector1.length; k++) {
|
|
154
|
+
if (selector1[k][i] != token) {
|
|
155
|
+
matching = false;
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (matching) {
|
|
160
|
+
l = 0;
|
|
161
|
+
for (; l < selector2.length; l++) {
|
|
162
|
+
if (selector2[l][i] != token) {
|
|
163
|
+
matching = false;
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (!matching) {
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
if (token == ',') {
|
|
172
|
+
match.push([]);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
if (token.endsWith('(')) {
|
|
176
|
+
matchFunction++;
|
|
177
|
+
}
|
|
178
|
+
if (token.endsWith('[')) {
|
|
179
|
+
inAttr++;
|
|
180
|
+
}
|
|
181
|
+
else if (token == ')') {
|
|
182
|
+
matchFunction--;
|
|
183
|
+
}
|
|
184
|
+
else if (token == ']') {
|
|
185
|
+
inAttr--;
|
|
186
|
+
}
|
|
187
|
+
match.at(-1).push(token);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// invalid function
|
|
191
|
+
if (matchFunction != 0 || inAttr != 0) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
if (parentType != 'Rule') {
|
|
195
|
+
for (const part of match) {
|
|
196
|
+
if (part.length > 0 && combinators.includes(part[0].charAt(0))) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (match.length > 1) {
|
|
202
|
+
console.error(`unsupported multilevel matching`);
|
|
203
|
+
console.error({ match, selector1, selector2 });
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
for (const part of match) {
|
|
207
|
+
while (part.length > 0) {
|
|
208
|
+
const token = part.at(-1);
|
|
209
|
+
if (token == ' ' || combinators.includes(token) || notEndingWith.includes(token.at(-1))) {
|
|
210
|
+
part.pop();
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (match.every(t => t.length == 0)) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
if (eq([['&']], match)) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
function reduce(acc, curr) {
|
|
223
|
+
if (acc === null) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
let hasCompoundSelector = true;
|
|
227
|
+
curr = curr.slice(match[0].length);
|
|
228
|
+
while (curr.length > 0) {
|
|
229
|
+
if (curr[0] == ' ') {
|
|
230
|
+
hasCompoundSelector = false;
|
|
231
|
+
curr.unshift('&');
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
// invalid function match
|
|
237
|
+
if (curr.length > 0 && curr[0].endsWith('(') && curr.at(-1) != ')') {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
if (curr.length == 1 && combinators.includes(curr[0].charAt(0))) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
if (hasCompoundSelector && curr.length > 0) {
|
|
244
|
+
hasCompoundSelector = !['&'].concat(combinators).includes(curr[0].charAt(0));
|
|
245
|
+
}
|
|
246
|
+
if (curr[0] == ':is(') {
|
|
247
|
+
let inFunction = 0;
|
|
248
|
+
let canReduce = true;
|
|
249
|
+
const isCompound = curr.reduce((acc, token, index) => {
|
|
250
|
+
if (index == 0) {
|
|
251
|
+
inFunction++;
|
|
252
|
+
canReduce = curr[1] == '&';
|
|
253
|
+
}
|
|
254
|
+
else if (token.endsWith('(')) {
|
|
255
|
+
if (inFunction == 0) {
|
|
256
|
+
canReduce = false;
|
|
257
|
+
}
|
|
258
|
+
inFunction++;
|
|
259
|
+
}
|
|
260
|
+
else if (token == ')') {
|
|
261
|
+
inFunction--;
|
|
262
|
+
}
|
|
263
|
+
else if (token == ',') {
|
|
264
|
+
if (!canReduce) {
|
|
265
|
+
canReduce = curr[index + 1] == '&';
|
|
266
|
+
}
|
|
267
|
+
acc.push([]);
|
|
268
|
+
}
|
|
269
|
+
else
|
|
270
|
+
acc.at(-1)?.push(token);
|
|
271
|
+
return acc;
|
|
272
|
+
}, [[]]);
|
|
273
|
+
if (inFunction > 0) {
|
|
274
|
+
canReduce = false;
|
|
275
|
+
}
|
|
276
|
+
if (canReduce) {
|
|
277
|
+
curr = isCompound.reduce((acc, curr) => {
|
|
278
|
+
if (acc.length > 0) {
|
|
279
|
+
acc.push(',');
|
|
280
|
+
}
|
|
281
|
+
acc.push(...curr);
|
|
282
|
+
return acc;
|
|
283
|
+
}, []);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// @todo: check hasCompoundSelector && curr[0] == '&' && curr[1] == ' '
|
|
287
|
+
acc.push(match.length == 0 ? ['&'] : (hasCompoundSelector && curr[0] != '&' && (curr.length == 0 || !combinators.includes(curr[0].charAt(0))) ? ['&'].concat(curr) : curr));
|
|
288
|
+
return acc;
|
|
289
|
+
}
|
|
290
|
+
// @ts-ignore
|
|
291
|
+
selector1 = selector1.reduce(reduce, []);
|
|
292
|
+
// @ts-ignore
|
|
293
|
+
selector2 = selector2.reduce(reduce, []);
|
|
294
|
+
return selector1 == null || selector2 == null ? null : {
|
|
295
|
+
eq: eq(selector1, selector2),
|
|
296
|
+
match,
|
|
297
|
+
selector1,
|
|
298
|
+
selector2
|
|
299
|
+
};
|
|
40
300
|
}
|
|
41
|
-
// @ts-ignore
|
|
42
|
-
ast.chi.splice(i, 1, wrapper);
|
|
43
|
-
// @ts-ignore
|
|
44
|
-
ast.chi.splice(nodeIndex, 1);
|
|
45
|
-
// @ts-ignore
|
|
46
|
-
previous.sel = pSel;
|
|
47
|
-
// @ts-ignore
|
|
48
|
-
previous.raw = match.selector1;
|
|
49
|
-
// @ts-ignore
|
|
50
|
-
node.sel = nSel;
|
|
51
|
-
// @ts-ignore
|
|
52
|
-
node.raw = match.selector2;
|
|
53
|
-
reduceRuleSelector(wrapper);
|
|
54
|
-
return wrapper;
|
|
55
|
-
}
|
|
56
|
-
function deduplicate(ast, options = {}, recursive = false) {
|
|
57
301
|
// @ts-ignore
|
|
58
302
|
if (('chi' in ast) && ast.chi?.length > 0) {
|
|
59
303
|
let i = 0;
|
|
@@ -93,7 +337,8 @@ function deduplicate(ast, options = {}, recursive = false) {
|
|
|
93
337
|
// @ts-ignore
|
|
94
338
|
if (options.nestingRules) {
|
|
95
339
|
// @ts-ignore
|
|
96
|
-
if (previous
|
|
340
|
+
if (previous?.typ == 'Rule') {
|
|
341
|
+
// @ts-ignore
|
|
97
342
|
reduceRuleSelector(previous);
|
|
98
343
|
// @ts-ignore
|
|
99
344
|
match = matchSelectors(previous.raw, node.raw, ast.typ);
|
|
@@ -132,7 +377,7 @@ function deduplicate(ast, options = {}, recursive = false) {
|
|
|
132
377
|
nodeIndex = --i;
|
|
133
378
|
// @ts-ignore
|
|
134
379
|
previous = ast.chi[nodeIndex];
|
|
135
|
-
|
|
380
|
+
minify(wrapper, options, recursive);
|
|
136
381
|
continue;
|
|
137
382
|
}
|
|
138
383
|
// @ts-ignore
|
|
@@ -181,13 +426,24 @@ function deduplicate(ast, options = {}, recursive = false) {
|
|
|
181
426
|
}
|
|
182
427
|
else if (combinators.includes(curr[0])) {
|
|
183
428
|
curr.unshift('&');
|
|
429
|
+
wrap = false;
|
|
184
430
|
}
|
|
185
431
|
// @ts-ignore
|
|
186
|
-
acc.push(curr
|
|
432
|
+
acc.push(curr);
|
|
187
433
|
return acc;
|
|
188
434
|
}, []);
|
|
435
|
+
if (!wrap) {
|
|
436
|
+
wrap = selector.some(s => s[0] != '&');
|
|
437
|
+
}
|
|
438
|
+
const rule = selector.map(s => {
|
|
439
|
+
if (s[0] == '&') {
|
|
440
|
+
// @ts-ignore
|
|
441
|
+
s[0] = node.optimized.optimized[0];
|
|
442
|
+
}
|
|
443
|
+
return s.join('');
|
|
444
|
+
}).join(',');
|
|
189
445
|
// @ts-ignore
|
|
190
|
-
node.sel =
|
|
446
|
+
node.sel = wrap ? node.optimized.optimized[0] + `:is(${rule})` : rule;
|
|
191
447
|
}
|
|
192
448
|
}
|
|
193
449
|
// @ts-ignore
|
|
@@ -218,10 +474,10 @@ function deduplicate(ast, options = {}, recursive = false) {
|
|
|
218
474
|
// @ts-ignore
|
|
219
475
|
if (hasDeclaration(node)) {
|
|
220
476
|
// @ts-ignore
|
|
221
|
-
|
|
477
|
+
minifyRule(node);
|
|
222
478
|
}
|
|
223
479
|
else {
|
|
224
|
-
|
|
480
|
+
minify(node, options, recursive);
|
|
225
481
|
}
|
|
226
482
|
i--;
|
|
227
483
|
previous = node;
|
|
@@ -263,10 +519,10 @@ function deduplicate(ast, options = {}, recursive = false) {
|
|
|
263
519
|
// @ts-ignore
|
|
264
520
|
if (hasDeclaration(previous)) {
|
|
265
521
|
// @ts-ignore
|
|
266
|
-
|
|
522
|
+
minifyRule(previous);
|
|
267
523
|
}
|
|
268
524
|
else {
|
|
269
|
-
|
|
525
|
+
minify(previous, options, recursive);
|
|
270
526
|
}
|
|
271
527
|
}
|
|
272
528
|
}
|
|
@@ -277,18 +533,97 @@ function deduplicate(ast, options = {}, recursive = false) {
|
|
|
277
533
|
if (recursive && node != null && ('chi' in node)) {
|
|
278
534
|
// @ts-ignore
|
|
279
535
|
if (node.chi.some(n => n.typ == 'Declaration')) {
|
|
280
|
-
|
|
536
|
+
minifyRule(node);
|
|
281
537
|
}
|
|
282
538
|
else {
|
|
283
539
|
// @ts-ignore
|
|
284
540
|
if (!(node.typ == 'AtRule' && node.nam != 'font-face')) {
|
|
285
|
-
|
|
541
|
+
minify(node, options, recursive);
|
|
286
542
|
}
|
|
287
543
|
}
|
|
288
544
|
}
|
|
289
545
|
}
|
|
290
546
|
return ast;
|
|
291
547
|
}
|
|
548
|
+
function reduceSelector(selector) {
|
|
549
|
+
if (selector.length == 0) {
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
const optimized = [];
|
|
553
|
+
const k = selector.reduce((acc, curr) => acc == 0 ? curr.length : (curr.length == 0 ? acc : Math.min(acc, curr.length)), 0);
|
|
554
|
+
let i = 0;
|
|
555
|
+
let j;
|
|
556
|
+
let match;
|
|
557
|
+
for (; i < k; i++) {
|
|
558
|
+
const item = selector[0][i];
|
|
559
|
+
match = true;
|
|
560
|
+
for (j = 1; j < selector.length; j++) {
|
|
561
|
+
if (item != selector[j][i]) {
|
|
562
|
+
match = false;
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
if (!match) {
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
optimized.push(item);
|
|
570
|
+
}
|
|
571
|
+
while (optimized.length > 0) {
|
|
572
|
+
const last = optimized.at(-1);
|
|
573
|
+
if ((last == ' ' || combinators.includes(last))) {
|
|
574
|
+
optimized.pop();
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
579
|
+
selector.forEach((selector) => selector.splice(0, optimized.length));
|
|
580
|
+
// combinator
|
|
581
|
+
if (combinators.includes(optimized.at(-1))) {
|
|
582
|
+
const combinator = optimized.pop();
|
|
583
|
+
selector.forEach(selector => selector.unshift(combinator));
|
|
584
|
+
}
|
|
585
|
+
let reducible = optimized.length == 1;
|
|
586
|
+
if (optimized[0] == '&' && optimized[1] == ' ') {
|
|
587
|
+
optimized.splice(0, 2);
|
|
588
|
+
}
|
|
589
|
+
if (optimized.length == 0 ||
|
|
590
|
+
(optimized[0].charAt(0) == '&' ||
|
|
591
|
+
selector.length == 1)) {
|
|
592
|
+
return {
|
|
593
|
+
match: false,
|
|
594
|
+
optimized,
|
|
595
|
+
selector: selector.map(selector => selector[0] == '&' && selector[1] == ' ' ? selector.slice(2) : selector),
|
|
596
|
+
reducible: selector.length > 1 && selector.every((selector) => !combinators.includes(selector[0]))
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
return {
|
|
600
|
+
match: true,
|
|
601
|
+
optimized,
|
|
602
|
+
selector: selector.reduce((acc, curr) => {
|
|
603
|
+
let hasCompound = true;
|
|
604
|
+
if (hasCompound && curr.length > 0) {
|
|
605
|
+
hasCompound = !['&'].concat(combinators).includes(curr[0].charAt(0));
|
|
606
|
+
}
|
|
607
|
+
// @ts-ignore
|
|
608
|
+
if (hasCompound && curr[0] == ' ') {
|
|
609
|
+
hasCompound = false;
|
|
610
|
+
curr.unshift('&');
|
|
611
|
+
}
|
|
612
|
+
if (curr.length == 0) {
|
|
613
|
+
curr.push('&');
|
|
614
|
+
hasCompound = false;
|
|
615
|
+
}
|
|
616
|
+
if (reducible) {
|
|
617
|
+
const chr = curr[0].charAt(0);
|
|
618
|
+
// @ts-ignore
|
|
619
|
+
reducible = chr == '.' || chr == ':' || isIdentStart(chr.codePointAt(0));
|
|
620
|
+
}
|
|
621
|
+
acc.push(hasCompound ? ['&'].concat(curr) : curr);
|
|
622
|
+
return acc;
|
|
623
|
+
}, []),
|
|
624
|
+
reducible: selector.every((selector) => !['>', '+', '~', '&'].includes(selector[0]))
|
|
625
|
+
};
|
|
626
|
+
}
|
|
292
627
|
function hasOnlyDeclarations(node) {
|
|
293
628
|
let k = node.chi.length;
|
|
294
629
|
while (k--) {
|
|
@@ -311,7 +646,7 @@ function hasDeclaration(node) {
|
|
|
311
646
|
}
|
|
312
647
|
return true;
|
|
313
648
|
}
|
|
314
|
-
function
|
|
649
|
+
function minifyRule(ast) {
|
|
315
650
|
// @ts-ignore
|
|
316
651
|
if (!('chi' in ast) || ast.chi?.length <= 1) {
|
|
317
652
|
return ast;
|
|
@@ -319,45 +654,19 @@ function deduplicateRule(ast) {
|
|
|
319
654
|
// @ts-ignore
|
|
320
655
|
const j = ast.chi.length;
|
|
321
656
|
let k = 0;
|
|
322
|
-
let
|
|
657
|
+
let properties = new PropertyList();
|
|
323
658
|
// @ts-ignore
|
|
324
659
|
for (; k < j; k++) {
|
|
325
660
|
// @ts-ignore
|
|
326
661
|
const node = ast.chi[k];
|
|
327
|
-
if (node.typ == 'Comment') {
|
|
328
|
-
|
|
329
|
-
map.set(node, node);
|
|
662
|
+
if (node.typ == 'Comment' || node.typ == 'Declaration') {
|
|
663
|
+
properties.add(node);
|
|
330
664
|
continue;
|
|
331
665
|
}
|
|
332
|
-
|
|
333
|
-
break;
|
|
334
|
-
}
|
|
335
|
-
if (node.nam in configuration.map ||
|
|
336
|
-
node.nam in configuration.properties) {
|
|
337
|
-
// @ts-ignore
|
|
338
|
-
const shorthand = node.nam in configuration.map ? configuration.map[node.nam].shorthand : configuration.properties[node.nam].shorthand;
|
|
339
|
-
if (!map.has(shorthand)) {
|
|
340
|
-
map.set(shorthand, new PropertyList());
|
|
341
|
-
}
|
|
342
|
-
map.get(shorthand).add(node);
|
|
343
|
-
}
|
|
344
|
-
else {
|
|
345
|
-
map.set(node.nam, node);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
const children = [];
|
|
349
|
-
for (let child of map.values()) {
|
|
350
|
-
if (child instanceof PropertyList) {
|
|
351
|
-
// @ts-ignore
|
|
352
|
-
children.push(...child);
|
|
353
|
-
}
|
|
354
|
-
else {
|
|
355
|
-
// @ts-ignore
|
|
356
|
-
children.push(child);
|
|
357
|
-
}
|
|
666
|
+
break;
|
|
358
667
|
}
|
|
359
668
|
// @ts-ignore
|
|
360
|
-
ast.chi =
|
|
669
|
+
ast.chi = [...properties].concat(ast.chi.slice(k));
|
|
361
670
|
return ast;
|
|
362
671
|
}
|
|
363
672
|
function splitRule(buffer) {
|
|
@@ -483,335 +792,5 @@ function reduceRuleSelector(node) {
|
|
|
483
792
|
}
|
|
484
793
|
// }
|
|
485
794
|
}
|
|
486
|
-
function diff(n1, n2, options = {}) {
|
|
487
|
-
let node1 = n1;
|
|
488
|
-
let node2 = n2;
|
|
489
|
-
let exchanged = false;
|
|
490
|
-
if (node1.chi.length > node2.chi.length) {
|
|
491
|
-
const t = node1;
|
|
492
|
-
node1 = node2;
|
|
493
|
-
node2 = t;
|
|
494
|
-
exchanged = true;
|
|
495
|
-
}
|
|
496
|
-
let i = node1.chi.length;
|
|
497
|
-
let j = node2.chi.length;
|
|
498
|
-
if (i == 0 || j == 0) {
|
|
499
|
-
// @ts-ignore
|
|
500
|
-
return null;
|
|
501
|
-
}
|
|
502
|
-
// @ts-ignore
|
|
503
|
-
const raw1 = node1.raw;
|
|
504
|
-
// @ts-ignore
|
|
505
|
-
// const optimized1 = node1.optimized;
|
|
506
|
-
// @ts-ignore
|
|
507
|
-
const raw2 = node2.raw;
|
|
508
|
-
// @ts-ignore
|
|
509
|
-
// const optimized2 = node2.optimized;
|
|
510
|
-
node1 = { ...node1, chi: node1.chi.slice() };
|
|
511
|
-
node2 = { ...node2, chi: node2.chi.slice() };
|
|
512
|
-
if (raw1 != null) {
|
|
513
|
-
Object.defineProperty(node1, 'raw', { enumerable: false, writable: true, value: raw1 });
|
|
514
|
-
}
|
|
515
|
-
// if (optimized1 != null) {
|
|
516
|
-
// Object.defineProperty(node1, 'optimized', {enumerable: false, writable: true, value: optimized1});
|
|
517
|
-
// }
|
|
518
|
-
if (raw2 != null) {
|
|
519
|
-
Object.defineProperty(node2, 'raw', { enumerable: false, writable: true, value: raw2 });
|
|
520
|
-
}
|
|
521
|
-
// if (optimized2 != null) {
|
|
522
|
-
// Object.defineProperty(node2, 'optimized', {enumerable: false, writable: true, value: optimized2});
|
|
523
|
-
// }
|
|
524
|
-
const intersect = [];
|
|
525
|
-
while (i--) {
|
|
526
|
-
if (node1.chi[i].typ == 'Comment') {
|
|
527
|
-
continue;
|
|
528
|
-
}
|
|
529
|
-
j = node2.chi.length;
|
|
530
|
-
if (j == 0) {
|
|
531
|
-
break;
|
|
532
|
-
}
|
|
533
|
-
while (j--) {
|
|
534
|
-
if (node2.chi[j].typ == 'Comment') {
|
|
535
|
-
continue;
|
|
536
|
-
}
|
|
537
|
-
if (node1.chi[i].nam == node2.chi[j].nam) {
|
|
538
|
-
if (eq(node1.chi[i], node2.chi[j])) {
|
|
539
|
-
intersect.push(node1.chi[i]);
|
|
540
|
-
node1.chi.splice(i, 1);
|
|
541
|
-
node2.chi.splice(j, 1);
|
|
542
|
-
break;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
// @ts-ignore
|
|
548
|
-
const result = (intersect.length == 0 ? null : {
|
|
549
|
-
...node1,
|
|
550
|
-
// @ts-ignore
|
|
551
|
-
sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(','),
|
|
552
|
-
chi: intersect.reverse()
|
|
553
|
-
});
|
|
554
|
-
if (result == null || [n1, n2].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + render(curr, options).code.length, 0) <= [node1, node2, result].reduce((acc, curr) => curr.chi.length == 0 ? acc : acc + render(curr, options).code.length, 0)) {
|
|
555
|
-
// @ts-ignore
|
|
556
|
-
return null;
|
|
557
|
-
}
|
|
558
|
-
return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node2 : node2 };
|
|
559
|
-
}
|
|
560
|
-
function matchSelectors(selector1, selector2, parentType) {
|
|
561
|
-
let match = [[]];
|
|
562
|
-
const j = Math.min(selector1.reduce((acc, curr) => Math.min(acc, curr.length), selector1.length > 0 ? selector1[0].length : 0), selector2.reduce((acc, curr) => Math.min(acc, curr.length), selector2.length > 0 ? selector2[0].length : 0));
|
|
563
|
-
let i = 0;
|
|
564
|
-
let k;
|
|
565
|
-
let l;
|
|
566
|
-
let token;
|
|
567
|
-
let matching = true;
|
|
568
|
-
let matchFunction = 0;
|
|
569
|
-
let inAttr = 0;
|
|
570
|
-
for (; i < j; i++) {
|
|
571
|
-
k = 0;
|
|
572
|
-
token = selector1[0][i];
|
|
573
|
-
for (; k < selector1.length; k++) {
|
|
574
|
-
if (selector1[k][i] != token) {
|
|
575
|
-
matching = false;
|
|
576
|
-
break;
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
if (matching) {
|
|
580
|
-
l = 0;
|
|
581
|
-
for (; l < selector2.length; l++) {
|
|
582
|
-
if (selector2[l][i] != token) {
|
|
583
|
-
matching = false;
|
|
584
|
-
break;
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
if (!matching) {
|
|
589
|
-
break;
|
|
590
|
-
}
|
|
591
|
-
if (token == ',') {
|
|
592
|
-
match.push([]);
|
|
593
|
-
}
|
|
594
|
-
else {
|
|
595
|
-
if (token.endsWith('(')) {
|
|
596
|
-
matchFunction++;
|
|
597
|
-
}
|
|
598
|
-
if (token.endsWith('[')) {
|
|
599
|
-
inAttr++;
|
|
600
|
-
}
|
|
601
|
-
else if (token == ')') {
|
|
602
|
-
matchFunction--;
|
|
603
|
-
}
|
|
604
|
-
else if (token == ']') {
|
|
605
|
-
inAttr--;
|
|
606
|
-
}
|
|
607
|
-
match.at(-1).push(token);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
// invalid function
|
|
611
|
-
if (matchFunction != 0 || inAttr != 0) {
|
|
612
|
-
return null;
|
|
613
|
-
}
|
|
614
|
-
if (parentType != 'Rule') {
|
|
615
|
-
for (const part of match) {
|
|
616
|
-
if (part.length > 0 && combinators.includes(part[0].charAt(0))) {
|
|
617
|
-
return null;
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
if (match.length > 1) {
|
|
622
|
-
console.error(`unsupported multilevel matching`);
|
|
623
|
-
console.error({ match, selector1, selector2 });
|
|
624
|
-
return null;
|
|
625
|
-
}
|
|
626
|
-
for (const part of match) {
|
|
627
|
-
while (part.length > 0) {
|
|
628
|
-
const token = part.at(-1);
|
|
629
|
-
if (token == ' ' || combinators.includes(token) || notEndingWith.includes(token.at(-1))) {
|
|
630
|
-
part.pop();
|
|
631
|
-
continue;
|
|
632
|
-
}
|
|
633
|
-
break;
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
if (match.every(t => t.length == 0)) {
|
|
637
|
-
return null;
|
|
638
|
-
}
|
|
639
|
-
if (eq([['&']], match)) {
|
|
640
|
-
return null;
|
|
641
|
-
}
|
|
642
|
-
function reduce(acc, curr) {
|
|
643
|
-
if (acc === null) {
|
|
644
|
-
return null;
|
|
645
|
-
}
|
|
646
|
-
let hasCompoundSelector = true;
|
|
647
|
-
curr = curr.slice(match[0].length);
|
|
648
|
-
while (curr.length > 0) {
|
|
649
|
-
if (curr[0] == ' ') {
|
|
650
|
-
hasCompoundSelector = false;
|
|
651
|
-
curr.unshift('&');
|
|
652
|
-
continue;
|
|
653
|
-
}
|
|
654
|
-
break;
|
|
655
|
-
}
|
|
656
|
-
// invalid function match
|
|
657
|
-
if (curr.length > 0 && curr[0].endsWith('(') && curr.at(-1) != ')') {
|
|
658
|
-
return null;
|
|
659
|
-
}
|
|
660
|
-
if (curr.length == 1 && combinators.includes(curr[0].charAt(0))) {
|
|
661
|
-
return null;
|
|
662
|
-
}
|
|
663
|
-
if (hasCompoundSelector && curr.length > 0) {
|
|
664
|
-
hasCompoundSelector = !['&'].concat(combinators).includes(curr[0].charAt(0));
|
|
665
|
-
}
|
|
666
|
-
if (curr[0] == ':is(') {
|
|
667
|
-
let inFunction = 0;
|
|
668
|
-
let canReduce = true;
|
|
669
|
-
const isCompound = curr.reduce((acc, token, index) => {
|
|
670
|
-
if (index == 0) {
|
|
671
|
-
inFunction++;
|
|
672
|
-
canReduce = curr[1] == '&';
|
|
673
|
-
}
|
|
674
|
-
else if (token.endsWith('(')) {
|
|
675
|
-
if (inFunction == 0) {
|
|
676
|
-
canReduce = false;
|
|
677
|
-
}
|
|
678
|
-
inFunction++;
|
|
679
|
-
}
|
|
680
|
-
else if (token == ')') {
|
|
681
|
-
inFunction--;
|
|
682
|
-
}
|
|
683
|
-
else if (token == ',') {
|
|
684
|
-
if (!canReduce) {
|
|
685
|
-
canReduce = curr[index + 1] == '&';
|
|
686
|
-
}
|
|
687
|
-
acc.push([]);
|
|
688
|
-
}
|
|
689
|
-
else
|
|
690
|
-
acc.at(-1)?.push(token);
|
|
691
|
-
return acc;
|
|
692
|
-
}, [[]]);
|
|
693
|
-
if (inFunction > 0) {
|
|
694
|
-
canReduce = false;
|
|
695
|
-
}
|
|
696
|
-
if (canReduce) {
|
|
697
|
-
curr = isCompound.reduce((acc, curr) => {
|
|
698
|
-
if (acc.length > 0) {
|
|
699
|
-
acc.push(',');
|
|
700
|
-
}
|
|
701
|
-
acc.push(...curr);
|
|
702
|
-
return acc;
|
|
703
|
-
}, []);
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
// @todo: check hasCompoundSelector && curr[0] == '&' && curr[1] == ' '
|
|
707
|
-
acc.push(match.length == 0 ? ['&'] : (hasCompoundSelector && curr[0] != '&' && (curr.length == 0 || !combinators.includes(curr[0].charAt(0))) ? ['&'].concat(curr) : curr));
|
|
708
|
-
return acc;
|
|
709
|
-
}
|
|
710
|
-
// @ts-ignore
|
|
711
|
-
selector1 = selector1.reduce(reduce, []);
|
|
712
|
-
// @ts-ignore
|
|
713
|
-
selector2 = selector2.reduce(reduce, []);
|
|
714
|
-
return selector1 == null || selector2 == null ? null : {
|
|
715
|
-
eq: eq(selector1, selector2),
|
|
716
|
-
match,
|
|
717
|
-
selector1,
|
|
718
|
-
selector2
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
function reduceSelector(selector) {
|
|
722
|
-
if (selector.length == 0) {
|
|
723
|
-
return null;
|
|
724
|
-
}
|
|
725
|
-
const optimized = [];
|
|
726
|
-
const k = selector.reduce((acc, curr) => acc == 0 ? curr.length : (curr.length == 0 ? acc : Math.min(acc, curr.length)), 0);
|
|
727
|
-
let i = 0;
|
|
728
|
-
let j;
|
|
729
|
-
let match;
|
|
730
|
-
for (; i < k; i++) {
|
|
731
|
-
const item = selector[0][i];
|
|
732
|
-
match = true;
|
|
733
|
-
for (j = 1; j < selector.length; j++) {
|
|
734
|
-
if (item != selector[j][i]) {
|
|
735
|
-
match = false;
|
|
736
|
-
break;
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
if (!match) {
|
|
740
|
-
break;
|
|
741
|
-
}
|
|
742
|
-
optimized.push(item);
|
|
743
|
-
}
|
|
744
|
-
while (optimized.length > 0) {
|
|
745
|
-
const last = optimized.at(-1);
|
|
746
|
-
if ((last == ' ' || combinators.includes(last))) {
|
|
747
|
-
optimized.pop();
|
|
748
|
-
continue;
|
|
749
|
-
}
|
|
750
|
-
break;
|
|
751
|
-
}
|
|
752
|
-
selector.forEach((selector) => selector.splice(0, optimized.length));
|
|
753
|
-
// combinator
|
|
754
|
-
if (combinators.includes(optimized.at(-1))) {
|
|
755
|
-
const combinator = optimized.pop();
|
|
756
|
-
selector.forEach(selector => selector.unshift(combinator));
|
|
757
|
-
}
|
|
758
|
-
let reducible = optimized.length == 1;
|
|
759
|
-
if (optimized[0] == '&' && optimized[1] == ' ') {
|
|
760
|
-
optimized.splice(0, 2);
|
|
761
|
-
}
|
|
762
|
-
if (optimized.length == 0 ||
|
|
763
|
-
(optimized[0].charAt(0) == '&' ||
|
|
764
|
-
selector.length == 1)) {
|
|
765
|
-
return {
|
|
766
|
-
match: false,
|
|
767
|
-
optimized,
|
|
768
|
-
selector: selector.map(selector => selector[0] == '&' && selector[1] == ' ' ? selector.slice(2) : selector),
|
|
769
|
-
reducible: selector.length > 1 && selector.every((selector) => !combinators.includes(selector[0]))
|
|
770
|
-
};
|
|
771
|
-
}
|
|
772
|
-
return {
|
|
773
|
-
match: true,
|
|
774
|
-
optimized,
|
|
775
|
-
selector: selector.reduce((acc, curr) => {
|
|
776
|
-
let hasCompound = true;
|
|
777
|
-
if (hasCompound && curr.length > 0) {
|
|
778
|
-
hasCompound = !['&'].concat(combinators).includes(curr[0].charAt(0));
|
|
779
|
-
}
|
|
780
|
-
// @ts-ignore
|
|
781
|
-
if (hasCompound && curr[0] == ' ') {
|
|
782
|
-
hasCompound = false;
|
|
783
|
-
curr.unshift('&');
|
|
784
|
-
}
|
|
785
|
-
if (curr.length == 0) {
|
|
786
|
-
curr.push('&');
|
|
787
|
-
hasCompound = false;
|
|
788
|
-
}
|
|
789
|
-
if (reducible) {
|
|
790
|
-
const chr = curr[0].charAt(0);
|
|
791
|
-
// @ts-ignore
|
|
792
|
-
reducible = chr == '.' || chr == ':' || isIdentStart(chr.codePointAt(0));
|
|
793
|
-
}
|
|
794
|
-
acc.push(hasCompound ? ['&'].concat(curr) : curr);
|
|
795
|
-
return acc;
|
|
796
|
-
}, []),
|
|
797
|
-
reducible: selector.every((selector) => !['>', '+', '~', '&'].includes(selector[0]))
|
|
798
|
-
};
|
|
799
|
-
}
|
|
800
|
-
function reducer(acc, curr, index, array) {
|
|
801
|
-
// trim :is()
|
|
802
|
-
if (array.length == 1 && array[0][0] == ':is(' && array[0].at(-1) == ')') {
|
|
803
|
-
curr = curr.slice(1, -1);
|
|
804
|
-
}
|
|
805
|
-
if (curr[0] == '&') {
|
|
806
|
-
if (curr[1] == ' ') {
|
|
807
|
-
curr.splice(0, 2);
|
|
808
|
-
}
|
|
809
|
-
else if (combinators.includes(curr[1])) {
|
|
810
|
-
curr.splice(0, 1);
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
acc.push(curr.join(''));
|
|
814
|
-
return acc;
|
|
815
|
-
}
|
|
816
795
|
|
|
817
|
-
export {
|
|
796
|
+
export { combinators, hasDeclaration, minify, minifyRule, reduceSelector };
|