@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.
@@ -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 && node.raw != null && previous?.raw != null && node.raw.length == 1 && previous.raw.length == 1) {
37
- const match = [];
94
+ if (options.nestingRules) {
38
95
  // @ts-ignore
39
- while (node.raw[0].length > 0 && previous.raw[0].length > 0) {
40
- // @ts-ignore
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
- const wrapper = { ...previous, chi: [] };
99
+ match = matchSelectors(previous.raw, node.raw, ast.typ);
52
100
  // @ts-ignore
53
- if (previous.raw[0].length == 0) {
54
- // @ts-ignore
55
- wrapper.chi.push(...previous.chi);
56
- }
57
- else {
101
+ if (match != null) {
58
102
  // @ts-ignore
59
- previous.sel = previous.raw.reduce((acc, curr) => {
60
- acc.push(curr.join(''));
61
- return acc;
62
- }, []).join(',');
103
+ wrapper = wrapNodes(previous, node, match, ast, i, nodeIndex);
104
+ nodeIndex = i - 1;
63
105
  // @ts-ignore
64
- wrapper.chi.push(previous);
106
+ previous = ast.chi[nodeIndex];
65
107
  }
66
- // @ts-ignore
67
- if (node.raw[0].length == 0) {
68
- // @ts-ignore
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' || nextNode.raw == null) {
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
- if (nextNode.raw.length != 1 || !eq(wrapper.raw[0], nextNode.raw[0].slice(0, wrapper.raw[0].length))) {
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
- nextNode.raw[0].splice(0, wrapper.raw[0].length);
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
- if (nextNode.raw[0].length == 0 ||
121
- // @ts-ignore
122
- (nextNode.raw.length == 1 && nextNode.raw[0] == '&')) {
123
- if (hasOnlyDeclarations(wrapper)) {
124
- wrapper.chi.push(...nextNode.chi);
125
- // @ts-ignore
126
- ast.chi.splice(i, 1);
127
- continue;
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
- // @ts-ignore
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
- deduplicateRule(wrapper);
144
- nodeIndex = --i;
182
+ else if (combinators.includes(curr[0])) {
183
+ curr.unshift('&');
184
+ }
145
185
  // @ts-ignore
146
- previous = ast.chi[i];
147
- continue;
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, options = {}) {
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
- result.push(str);
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
- result.push(str);
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
- // @ts-ignore
385
- let optimized = reduceSelector(node.raw);
386
- if (optimized != null) {
387
- Object.defineProperty(node, 'optimized', { enumerable: false, writable: true, value: optimized });
388
- }
389
- if (optimized != null && optimized.match && optimized.reducible) {
390
- const raw = [
391
- [
392
- optimized.optimized[0], ':is('
393
- ].concat(optimized.selector.reduce((acc, curr) => {
394
- if (acc.length > 0) {
395
- acc.push(',');
396
- }
397
- acc.push(...curr);
398
- return acc;
399
- }, [])).concat(')')
400
- ];
401
- const sel = raw[0].join('');
402
- if (sel.length < node.sel.length) {
403
- node.sel = sel;
404
- // node.raw = raw;
405
- Object.defineProperty(node, 'raw', { enumerable: false, writable: true, value: raw });
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
- Object.defineProperty(node1, 'optimized', { enumerable: false, writable: true, value: optimized1 });
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
- Object.defineProperty(node2, 'optimized', { enumerable: false, writable: true, value: optimized2 });
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 < 2) {
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
- if (optimized.at(-1) == ' ') {
508
- optimized.pop();
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.length == 0) {
512
- return { match: false, optimized, selector, reducible };
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
- const slice = curr.slice(optimized.length);
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 (slice.length > 0 && slice[0] == ' ') {
521
- slice.shift();
781
+ if (hasCompound && curr[0] == ' ') {
782
+ hasCompound = false;
783
+ curr.unshift('&');
522
784
  }
523
- if (slice.length == 0) {
524
- slice.push('&');
785
+ if (curr.length == 0) {
786
+ curr.push('&');
787
+ hasCompound = false;
525
788
  }
526
789
  if (reducible) {
527
- const chr = slice[0].charAt(0);
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(slice);
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
  }