@tbela99/css-parser 0.0.1-alpha4 → 0.0.1-alpha5
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 +2 -0
- package/README.md +2 -0
- package/dist/index-umd-web.js +454 -172
- package/dist/index.cjs +454 -172
- package/dist/index.d.ts +18 -7
- package/dist/lib/parser/deduplicate.js +416 -141
- package/dist/lib/parser/parse.js +35 -11
- package/dist/lib/renderer/render.js +5 -22
- package/dist/lib/walker/walk.js +1 -1
- package/package.json +5 -4
|
@@ -1,10 +1,58 @@
|
|
|
1
|
+
import { isIdentStart, isWhiteSpace } from './utils/syntax.js';
|
|
2
|
+
import { getConfig } from './utils/config.js';
|
|
1
3
|
import { PropertyList } from './declaration/list.js';
|
|
2
4
|
import { eq } from './utils/eq.js';
|
|
3
|
-
import { isIdentStart } from './utils/syntax.js';
|
|
4
|
-
import { getConfig } from './utils/config.js';
|
|
5
5
|
import { render } from '../renderer/render.js';
|
|
6
6
|
|
|
7
7
|
const configuration = getConfig();
|
|
8
|
+
const combinators = ['+', '>', '~'];
|
|
9
|
+
const notEndingWith = ['(', '['].concat(combinators);
|
|
10
|
+
function wrapNodes(previous, node, match, ast, i, nodeIndex) {
|
|
11
|
+
// @ts-ignore
|
|
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,
|
|
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);
|
|
27
|
+
// @ts-ignore
|
|
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
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// @ts-ignore
|
|
39
|
+
wrapper.chi.push(previous, node);
|
|
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
|
+
}
|
|
8
56
|
function deduplicate(ast, options = {}, recursive = false) {
|
|
9
57
|
// @ts-ignore
|
|
10
58
|
if (('chi' in ast) && ast.chi?.length > 0) {
|
|
@@ -20,6 +68,14 @@ function deduplicate(ast, options = {}, recursive = false) {
|
|
|
20
68
|
}
|
|
21
69
|
// @ts-ignore
|
|
22
70
|
node = ast.chi[i];
|
|
71
|
+
// @ts-ignore
|
|
72
|
+
if (previous == node) {
|
|
73
|
+
// console.error('idem!');
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
ast.chi.splice(i, 1);
|
|
76
|
+
i--;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
23
79
|
if (node.typ == 'AtRule' && node.nam == 'font-face') {
|
|
24
80
|
continue;
|
|
25
81
|
}
|
|
@@ -32,120 +88,106 @@ function deduplicate(ast, options = {}, recursive = false) {
|
|
|
32
88
|
// @ts-ignore
|
|
33
89
|
if (node.typ == 'Rule') {
|
|
34
90
|
reduceRuleSelector(node);
|
|
91
|
+
let wrapper;
|
|
92
|
+
let match;
|
|
35
93
|
// @ts-ignore
|
|
36
|
-
if (options.nestingRules
|
|
37
|
-
const match = [];
|
|
94
|
+
if (options.nestingRules) {
|
|
38
95
|
// @ts-ignore
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (node.raw[0][0] != previous.raw[0][0]) {
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
// @ts-ignore
|
|
45
|
-
match.push(node.raw[0].shift());
|
|
46
|
-
// @ts-ignore
|
|
47
|
-
previous.raw[0].shift();
|
|
48
|
-
}
|
|
49
|
-
if (match.length > 0) {
|
|
96
|
+
if (previous != null && previous.typ == 'Rule') {
|
|
97
|
+
reduceRuleSelector(previous);
|
|
50
98
|
// @ts-ignore
|
|
51
|
-
|
|
99
|
+
match = matchSelectors(previous.raw, node.raw, ast.typ);
|
|
52
100
|
// @ts-ignore
|
|
53
|
-
if (
|
|
54
|
-
// @ts-ignore
|
|
55
|
-
wrapper.chi.push(...previous.chi);
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
101
|
+
if (match != null) {
|
|
58
102
|
// @ts-ignore
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return acc;
|
|
62
|
-
}, []).join(',');
|
|
103
|
+
wrapper = wrapNodes(previous, node, match, ast, i, nodeIndex);
|
|
104
|
+
nodeIndex = i - 1;
|
|
63
105
|
// @ts-ignore
|
|
64
|
-
|
|
106
|
+
previous = ast.chi[nodeIndex];
|
|
65
107
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (previous.raw.length == 0) {
|
|
70
|
-
// @ts-ignore
|
|
71
|
-
wrapper.chi.push(...node.chi);
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
if (hasOnlyDeclarations(wrapper)) {
|
|
75
|
-
wrapper.chi.push(...node.chi);
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
// @ts-ignore
|
|
79
|
-
node.raw[0].push('&');
|
|
80
|
-
// @ts-ignore
|
|
81
|
-
node.sel = node.raw.reduce((acc, curr) => {
|
|
82
|
-
acc.push(curr.join(''));
|
|
83
|
-
return acc;
|
|
84
|
-
}, []).join(',');
|
|
85
|
-
// @ts-ignore
|
|
86
|
-
wrapper.chi.push(node);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
// @ts-ignore
|
|
92
|
-
node.sel = node.raw.reduce((acc, curr) => {
|
|
93
|
-
acc.push(curr.join(''));
|
|
94
|
-
return acc;
|
|
95
|
-
}, []).join(',');
|
|
96
|
-
// @ts-ignore
|
|
97
|
-
wrapper.chi.push(node);
|
|
98
|
-
}
|
|
99
|
-
Object.defineProperty(wrapper, 'raw', { enumerable: false, writable: true, value: [match] });
|
|
100
|
-
// @ts-ignore
|
|
101
|
-
ast.chi.splice(i, 1, wrapper);
|
|
102
|
-
// @ts-ignore
|
|
103
|
-
ast.chi.splice(nodeIndex, 1);
|
|
108
|
+
}
|
|
109
|
+
// @ts-ignore
|
|
110
|
+
if (wrapper != null) {
|
|
104
111
|
// @ts-ignore
|
|
105
112
|
while (i < ast.chi.length) {
|
|
106
113
|
// @ts-ignore
|
|
107
114
|
const nextNode = ast.chi[i];
|
|
108
115
|
// @ts-ignore
|
|
109
|
-
if (nextNode.typ != 'Rule'
|
|
116
|
+
if (nextNode.typ != 'Rule') {
|
|
117
|
+
// i--;
|
|
118
|
+
// previous = wrapper;
|
|
119
|
+
// nodeIndex = i;
|
|
110
120
|
break;
|
|
111
121
|
}
|
|
112
122
|
reduceRuleSelector(nextNode);
|
|
113
123
|
// @ts-ignore
|
|
114
|
-
|
|
124
|
+
match = matchSelectors(wrapper.raw, nextNode.raw, ast.typ);
|
|
125
|
+
// @ts-ignore
|
|
126
|
+
if (match == null) {
|
|
115
127
|
break;
|
|
116
128
|
}
|
|
117
129
|
// @ts-ignore
|
|
118
|
-
|
|
130
|
+
wrapper = wrapNodes(wrapper, nextNode, match, ast, i, nodeIndex);
|
|
131
|
+
}
|
|
132
|
+
nodeIndex = --i;
|
|
133
|
+
// @ts-ignore
|
|
134
|
+
previous = ast.chi[nodeIndex];
|
|
135
|
+
deduplicate(wrapper, options, recursive);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
// @ts-ignore
|
|
139
|
+
else if (node.optimized != null &&
|
|
140
|
+
// @ts-ignore
|
|
141
|
+
node.optimized.match &&
|
|
142
|
+
// @ts-ignore
|
|
143
|
+
node.optimized.selector.length > 1) {
|
|
144
|
+
// @ts-ignore
|
|
145
|
+
wrapper = { ...node, chi: [], sel: node.optimized.optimized[0] };
|
|
146
|
+
// @ts-ignore
|
|
147
|
+
Object.defineProperty(wrapper, 'raw', {
|
|
148
|
+
enumerable: false,
|
|
149
|
+
writable: true,
|
|
119
150
|
// @ts-ignore
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
151
|
+
value: [[node.optimized.optimized[0]]]
|
|
152
|
+
});
|
|
153
|
+
// @ts-ignore
|
|
154
|
+
node.sel = node.optimized.selector.reduce(reducer, []).join(',');
|
|
155
|
+
// @ts-ignore
|
|
156
|
+
node.raw = node.optimized.selector.slice();
|
|
157
|
+
// @ts-ignore
|
|
158
|
+
wrapper.chi.push(node);
|
|
159
|
+
// @ts-ignore
|
|
160
|
+
ast.chi.splice(i, 1, wrapper);
|
|
161
|
+
node = wrapper;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// @ts-ignore
|
|
165
|
+
else if (node.optimized?.match) {
|
|
166
|
+
let wrap = true;
|
|
167
|
+
// @ts-ignore
|
|
168
|
+
const selector = node.optimized.selector.reduce((acc, curr) => {
|
|
169
|
+
if (curr[0] == '&') {
|
|
170
|
+
if (curr[1] == ' ') {
|
|
171
|
+
curr.splice(0, 2);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
if (ast.typ != 'Rule' && combinators.includes(curr[1])) {
|
|
175
|
+
wrap = false;
|
|
128
176
|
}
|
|
129
177
|
else {
|
|
130
|
-
|
|
131
|
-
nextNode.raw[0].push('&');
|
|
178
|
+
curr.splice(0, 1);
|
|
132
179
|
}
|
|
133
180
|
}
|
|
134
|
-
// @ts-ignore
|
|
135
|
-
nextNode.sel = nextNode.raw.reduce((acc, curr) => {
|
|
136
|
-
acc.push(curr.join(''));
|
|
137
|
-
return acc;
|
|
138
|
-
}, []).join(',');
|
|
139
|
-
wrapper.chi.push(nextNode);
|
|
140
|
-
// @ts-ignore
|
|
141
|
-
ast.chi.splice(i, 1);
|
|
142
181
|
}
|
|
143
|
-
|
|
144
|
-
|
|
182
|
+
else if (combinators.includes(curr[0])) {
|
|
183
|
+
curr.unshift('&');
|
|
184
|
+
}
|
|
145
185
|
// @ts-ignore
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
186
|
+
acc.push(curr.map(t => t.replaceAll('&', node.optimized.optimized[0])).join(''));
|
|
187
|
+
return acc;
|
|
188
|
+
}, []);
|
|
189
|
+
// @ts-ignore
|
|
190
|
+
node.sel = (wrap ? node.optimized.optimized[0] : '') + `:is(${selector.join(',')})`;
|
|
149
191
|
}
|
|
150
192
|
}
|
|
151
193
|
// @ts-ignore
|
|
@@ -175,6 +217,7 @@ function deduplicate(ast, options = {}, recursive = false) {
|
|
|
175
217
|
ast.chi.splice(nodeIndex, 1);
|
|
176
218
|
// @ts-ignore
|
|
177
219
|
if (hasDeclaration(node)) {
|
|
220
|
+
// @ts-ignore
|
|
178
221
|
deduplicateRule(node);
|
|
179
222
|
}
|
|
180
223
|
else {
|
|
@@ -219,6 +262,7 @@ function deduplicate(ast, options = {}, recursive = false) {
|
|
|
219
262
|
if (recursive && previous != node) {
|
|
220
263
|
// @ts-ignore
|
|
221
264
|
if (hasDeclaration(previous)) {
|
|
265
|
+
// @ts-ignore
|
|
222
266
|
deduplicateRule(previous);
|
|
223
267
|
}
|
|
224
268
|
else {
|
|
@@ -236,6 +280,7 @@ function deduplicate(ast, options = {}, recursive = false) {
|
|
|
236
280
|
deduplicateRule(node);
|
|
237
281
|
}
|
|
238
282
|
else {
|
|
283
|
+
// @ts-ignore
|
|
239
284
|
if (!(node.typ == 'AtRule' && node.nam != 'font-face')) {
|
|
240
285
|
deduplicate(node, options, recursive);
|
|
241
286
|
}
|
|
@@ -266,7 +311,7 @@ function hasDeclaration(node) {
|
|
|
266
311
|
}
|
|
267
312
|
return true;
|
|
268
313
|
}
|
|
269
|
-
function deduplicateRule(ast
|
|
314
|
+
function deduplicateRule(ast) {
|
|
270
315
|
// @ts-ignore
|
|
271
316
|
if (!('chi' in ast) || ast.chi?.length <= 1) {
|
|
272
317
|
return ast;
|
|
@@ -316,15 +361,39 @@ function deduplicateRule(ast, options = {}) {
|
|
|
316
361
|
return ast;
|
|
317
362
|
}
|
|
318
363
|
function splitRule(buffer) {
|
|
319
|
-
const result = [];
|
|
364
|
+
const result = [[]];
|
|
320
365
|
let str = '';
|
|
321
366
|
for (let i = 0; i < buffer.length; i++) {
|
|
322
367
|
let chr = buffer.charAt(i);
|
|
368
|
+
if (isWhiteSpace(chr.charCodeAt(0))) {
|
|
369
|
+
let k = i;
|
|
370
|
+
while (k + 1 < buffer.length) {
|
|
371
|
+
if (isWhiteSpace(buffer[k + 1].charCodeAt(0))) {
|
|
372
|
+
k++;
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
if (str !== '') {
|
|
378
|
+
// @ts-ignore
|
|
379
|
+
result.at(-1).push(str);
|
|
380
|
+
str = '';
|
|
381
|
+
}
|
|
382
|
+
// @ts-ignore
|
|
383
|
+
if (result.at(-1).length > 0) {
|
|
384
|
+
// @ts-ignore
|
|
385
|
+
result.at(-1).push(' ');
|
|
386
|
+
}
|
|
387
|
+
i = k;
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
323
390
|
if (chr == ',') {
|
|
324
391
|
if (str !== '') {
|
|
325
|
-
|
|
392
|
+
// @ts-ignore
|
|
393
|
+
result.at(-1).push(str);
|
|
326
394
|
str = '';
|
|
327
395
|
}
|
|
396
|
+
result.push([]);
|
|
328
397
|
continue;
|
|
329
398
|
}
|
|
330
399
|
str += chr;
|
|
@@ -374,38 +443,45 @@ function splitRule(buffer) {
|
|
|
374
443
|
}
|
|
375
444
|
}
|
|
376
445
|
if (str !== '') {
|
|
377
|
-
|
|
446
|
+
// @ts-ignore
|
|
447
|
+
result.at(-1).push(str);
|
|
378
448
|
}
|
|
379
449
|
return result;
|
|
380
450
|
}
|
|
381
451
|
function reduceRuleSelector(node) {
|
|
452
|
+
if (node.raw == null) {
|
|
453
|
+
Object.defineProperty(node, 'raw', { enumerable: false, writable: true, value: splitRule(node.sel) });
|
|
454
|
+
}
|
|
382
455
|
// @ts-ignore
|
|
383
|
-
if (node.raw != null) {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
456
|
+
// if (node.raw != null) {
|
|
457
|
+
// @ts-ignore
|
|
458
|
+
let optimized = reduceSelector(node.raw.reduce((acc, curr) => {
|
|
459
|
+
acc.push(curr.slice());
|
|
460
|
+
return acc;
|
|
461
|
+
}, []));
|
|
462
|
+
if (optimized != null) {
|
|
463
|
+
Object.defineProperty(node, 'optimized', { enumerable: false, writable: true, value: optimized });
|
|
464
|
+
}
|
|
465
|
+
if (optimized != null && optimized.match && optimized.reducible && optimized.selector.length > 1) {
|
|
466
|
+
const raw = [
|
|
467
|
+
[
|
|
468
|
+
optimized.optimized[0], ':is('
|
|
469
|
+
].concat(optimized.selector.reduce((acc, curr) => {
|
|
470
|
+
if (acc.length > 0) {
|
|
471
|
+
acc.push(',');
|
|
472
|
+
}
|
|
473
|
+
acc.push(...curr);
|
|
474
|
+
return acc;
|
|
475
|
+
}, [])).concat(')')
|
|
476
|
+
];
|
|
477
|
+
const sel = raw[0].join('');
|
|
478
|
+
if (sel.length < node.sel.length) {
|
|
479
|
+
node.sel = sel;
|
|
480
|
+
// node.raw = raw;
|
|
481
|
+
Object.defineProperty(node, 'raw', { enumerable: false, writable: true, value: raw });
|
|
407
482
|
}
|
|
408
483
|
}
|
|
484
|
+
// }
|
|
409
485
|
}
|
|
410
486
|
function diff(n1, n2, options = {}) {
|
|
411
487
|
let node1 = n1;
|
|
@@ -426,25 +502,25 @@ function diff(n1, n2, options = {}) {
|
|
|
426
502
|
// @ts-ignore
|
|
427
503
|
const raw1 = node1.raw;
|
|
428
504
|
// @ts-ignore
|
|
429
|
-
const optimized1 = node1.optimized;
|
|
505
|
+
// const optimized1 = node1.optimized;
|
|
430
506
|
// @ts-ignore
|
|
431
507
|
const raw2 = node2.raw;
|
|
432
508
|
// @ts-ignore
|
|
433
|
-
const optimized2 = node2.optimized;
|
|
509
|
+
// const optimized2 = node2.optimized;
|
|
434
510
|
node1 = { ...node1, chi: node1.chi.slice() };
|
|
435
511
|
node2 = { ...node2, chi: node2.chi.slice() };
|
|
436
512
|
if (raw1 != null) {
|
|
437
513
|
Object.defineProperty(node1, 'raw', { enumerable: false, writable: true, value: raw1 });
|
|
438
514
|
}
|
|
439
|
-
if (optimized1 != null) {
|
|
440
|
-
|
|
441
|
-
}
|
|
515
|
+
// if (optimized1 != null) {
|
|
516
|
+
// Object.defineProperty(node1, 'optimized', {enumerable: false, writable: true, value: optimized1});
|
|
517
|
+
// }
|
|
442
518
|
if (raw2 != null) {
|
|
443
519
|
Object.defineProperty(node2, 'raw', { enumerable: false, writable: true, value: raw2 });
|
|
444
520
|
}
|
|
445
|
-
if (optimized2 != null) {
|
|
446
|
-
|
|
447
|
-
}
|
|
521
|
+
// if (optimized2 != null) {
|
|
522
|
+
// Object.defineProperty(node2, 'optimized', {enumerable: false, writable: true, value: optimized2});
|
|
523
|
+
// }
|
|
448
524
|
const intersect = [];
|
|
449
525
|
while (i--) {
|
|
450
526
|
if (node1.chi[i].typ == 'Comment') {
|
|
@@ -472,7 +548,7 @@ function diff(n1, n2, options = {}) {
|
|
|
472
548
|
const result = (intersect.length == 0 ? null : {
|
|
473
549
|
...node1,
|
|
474
550
|
// @ts-ignore
|
|
475
|
-
sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(),
|
|
551
|
+
sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(','),
|
|
476
552
|
chi: intersect.reverse()
|
|
477
553
|
});
|
|
478
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)) {
|
|
@@ -481,8 +557,169 @@ function diff(n1, n2, options = {}) {
|
|
|
481
557
|
}
|
|
482
558
|
return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node2 : node2 };
|
|
483
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
|
+
}
|
|
484
721
|
function reduceSelector(selector) {
|
|
485
|
-
if (selector.length
|
|
722
|
+
if (selector.length == 0) {
|
|
486
723
|
return null;
|
|
487
724
|
}
|
|
488
725
|
const optimized = [];
|
|
@@ -504,37 +741,75 @@ function reduceSelector(selector) {
|
|
|
504
741
|
}
|
|
505
742
|
optimized.push(item);
|
|
506
743
|
}
|
|
507
|
-
|
|
508
|
-
optimized.
|
|
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));
|
|
509
757
|
}
|
|
510
758
|
let reducible = optimized.length == 1;
|
|
511
|
-
if (optimized
|
|
512
|
-
|
|
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
|
+
};
|
|
513
771
|
}
|
|
514
772
|
return {
|
|
515
773
|
match: true,
|
|
516
774
|
optimized,
|
|
517
775
|
selector: selector.reduce((acc, curr) => {
|
|
518
|
-
|
|
776
|
+
let hasCompound = true;
|
|
777
|
+
if (hasCompound && curr.length > 0) {
|
|
778
|
+
hasCompound = !['&'].concat(combinators).includes(curr[0].charAt(0));
|
|
779
|
+
}
|
|
519
780
|
// @ts-ignore
|
|
520
|
-
if (
|
|
521
|
-
|
|
781
|
+
if (hasCompound && curr[0] == ' ') {
|
|
782
|
+
hasCompound = false;
|
|
783
|
+
curr.unshift('&');
|
|
522
784
|
}
|
|
523
|
-
if (
|
|
524
|
-
|
|
785
|
+
if (curr.length == 0) {
|
|
786
|
+
curr.push('&');
|
|
787
|
+
hasCompound = false;
|
|
525
788
|
}
|
|
526
789
|
if (reducible) {
|
|
527
|
-
const chr =
|
|
790
|
+
const chr = curr[0].charAt(0);
|
|
528
791
|
// @ts-ignore
|
|
529
792
|
reducible = chr == '.' || chr == ':' || isIdentStart(chr.codePointAt(0));
|
|
530
793
|
}
|
|
531
|
-
acc.push(
|
|
794
|
+
acc.push(hasCompound ? ['&'].concat(curr) : curr);
|
|
532
795
|
return acc;
|
|
533
796
|
}, []),
|
|
534
|
-
reducible
|
|
797
|
+
reducible: selector.every((selector) => !['>', '+', '~', '&'].includes(selector[0]))
|
|
535
798
|
};
|
|
536
799
|
}
|
|
537
|
-
function reducer(acc, curr) {
|
|
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
|
+
}
|
|
538
813
|
acc.push(curr.join(''));
|
|
539
814
|
return acc;
|
|
540
815
|
}
|