@tbela99/css-parser 0.0.1-alpha3 → 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,9 +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 { getConfig } from './utils/config.js';
4
5
  import { render } from '../renderer/render.js';
5
6
 
6
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
+ }
7
56
  function deduplicate(ast, options = {}, recursive = false) {
8
57
  // @ts-ignore
9
58
  if (('chi' in ast) && ast.chi?.length > 0) {
@@ -19,6 +68,14 @@ function deduplicate(ast, options = {}, recursive = false) {
19
68
  }
20
69
  // @ts-ignore
21
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
+ }
22
79
  if (node.typ == 'AtRule' && node.nam == 'font-face') {
23
80
  continue;
24
81
  }
@@ -29,6 +86,111 @@ function deduplicate(ast, options = {}, recursive = false) {
29
86
  continue;
30
87
  }
31
88
  // @ts-ignore
89
+ if (node.typ == 'Rule') {
90
+ reduceRuleSelector(node);
91
+ let wrapper;
92
+ let match;
93
+ // @ts-ignore
94
+ if (options.nestingRules) {
95
+ // @ts-ignore
96
+ if (previous != null && previous.typ == 'Rule') {
97
+ reduceRuleSelector(previous);
98
+ // @ts-ignore
99
+ match = matchSelectors(previous.raw, node.raw, ast.typ);
100
+ // @ts-ignore
101
+ if (match != null) {
102
+ // @ts-ignore
103
+ wrapper = wrapNodes(previous, node, match, ast, i, nodeIndex);
104
+ nodeIndex = i - 1;
105
+ // @ts-ignore
106
+ previous = ast.chi[nodeIndex];
107
+ }
108
+ }
109
+ // @ts-ignore
110
+ if (wrapper != null) {
111
+ // @ts-ignore
112
+ while (i < ast.chi.length) {
113
+ // @ts-ignore
114
+ const nextNode = ast.chi[i];
115
+ // @ts-ignore
116
+ if (nextNode.typ != 'Rule') {
117
+ // i--;
118
+ // previous = wrapper;
119
+ // nodeIndex = i;
120
+ break;
121
+ }
122
+ reduceRuleSelector(nextNode);
123
+ // @ts-ignore
124
+ match = matchSelectors(wrapper.raw, nextNode.raw, ast.typ);
125
+ // @ts-ignore
126
+ if (match == null) {
127
+ break;
128
+ }
129
+ // @ts-ignore
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,
150
+ // @ts-ignore
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;
176
+ }
177
+ else {
178
+ curr.splice(0, 1);
179
+ }
180
+ }
181
+ }
182
+ else if (combinators.includes(curr[0])) {
183
+ curr.unshift('&');
184
+ }
185
+ // @ts-ignore
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(',')})`;
191
+ }
192
+ }
193
+ // @ts-ignore
32
194
  if (previous != null && 'chi' in previous && ('chi' in node)) {
33
195
  // @ts-ignore
34
196
  if (previous.typ == node.typ) {
@@ -48,13 +210,14 @@ function deduplicate(ast, options = {}, recursive = false) {
48
210
  // @ts-ignore
49
211
  if ((node.typ == 'Rule' && node.sel == previous.sel) ||
50
212
  // @ts-ignore
51
- (node.typ == 'AtRule') && node.val == previous.val) {
213
+ (node.typ == 'AtRule') && node.val != 'font-face' && node.val == previous.val) {
52
214
  // @ts-ignore
53
215
  node.chi.unshift(...previous.chi);
54
216
  // @ts-ignore
55
217
  ast.chi.splice(nodeIndex, 1);
56
218
  // @ts-ignore
57
219
  if (hasDeclaration(node)) {
220
+ // @ts-ignore
58
221
  deduplicateRule(node);
59
222
  }
60
223
  else {
@@ -70,19 +233,26 @@ function deduplicate(ast, options = {}, recursive = false) {
70
233
  if (intersect != null) {
71
234
  if (intersect.node1.chi.length == 0) {
72
235
  // @ts-ignore
73
- ast.chi.splice(i, 1);
236
+ ast.chi.splice(i--, 1);
237
+ // @ts-ignore
238
+ node = ast.chi[i];
74
239
  }
75
240
  else {
76
241
  // @ts-ignore
77
242
  ast.chi.splice(i, 1, intersect.node1);
243
+ node = intersect.node1;
78
244
  }
79
245
  if (intersect.node2.chi.length == 0) {
80
246
  // @ts-ignore
81
247
  ast.chi.splice(nodeIndex, 1, intersect.result);
248
+ previous = intersect.result;
82
249
  }
83
250
  else {
84
251
  // @ts-ignore
85
252
  ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2);
253
+ previous = intersect.result;
254
+ // @ts-ignore
255
+ i = nodeIndex;
86
256
  }
87
257
  }
88
258
  }
@@ -92,6 +262,7 @@ function deduplicate(ast, options = {}, recursive = false) {
92
262
  if (recursive && previous != node) {
93
263
  // @ts-ignore
94
264
  if (hasDeclaration(previous)) {
265
+ // @ts-ignore
95
266
  deduplicateRule(previous);
96
267
  }
97
268
  else {
@@ -109,12 +280,25 @@ function deduplicate(ast, options = {}, recursive = false) {
109
280
  deduplicateRule(node);
110
281
  }
111
282
  else {
112
- deduplicate(node, options, recursive);
283
+ // @ts-ignore
284
+ if (!(node.typ == 'AtRule' && node.nam != 'font-face')) {
285
+ deduplicate(node, options, recursive);
286
+ }
113
287
  }
114
288
  }
115
289
  }
116
290
  return ast;
117
291
  }
292
+ function hasOnlyDeclarations(node) {
293
+ let k = node.chi.length;
294
+ while (k--) {
295
+ if (node.chi[k].typ == 'Comment') {
296
+ continue;
297
+ }
298
+ return node.chi[k].typ == 'Declaration';
299
+ }
300
+ return true;
301
+ }
118
302
  function hasDeclaration(node) {
119
303
  // @ts-ignore
120
304
  for (let i = 0; i < node.chi?.length; i++) {
@@ -127,7 +311,7 @@ function hasDeclaration(node) {
127
311
  }
128
312
  return true;
129
313
  }
130
- function deduplicateRule(ast, options = {}) {
314
+ function deduplicateRule(ast) {
131
315
  // @ts-ignore
132
316
  if (!('chi' in ast) || ast.chi?.length <= 1) {
133
317
  return ast;
@@ -174,42 +358,42 @@ function deduplicateRule(ast, options = {}) {
174
358
  }
175
359
  // @ts-ignore
176
360
  ast.chi = children.concat(ast.chi?.slice(k));
177
- /*
178
- // @ts-ignore
179
-
180
- const properties: PropertyList = new PropertyList();
181
-
182
- for (; k < j; k++) {
183
-
184
- // @ts-ignore
185
- if ('Comment' == ast.chi[k].typ || 'Declaration' == ast.chi[k].typ) {
186
-
187
- // @ts-ignore
188
- properties.add(ast.chi[k]);
189
- continue;
190
- }
191
-
192
- break;
193
- }
194
-
195
- // @ts-ignore
196
- ast.chi = [...properties].concat(ast.chi.slice(k));
197
- */
198
- //
199
- // @ts-ignore
200
- // ast.chi.splice(0, k - 1, ...properties);
201
361
  return ast;
202
362
  }
203
363
  function splitRule(buffer) {
204
- const result = [];
364
+ const result = [[]];
205
365
  let str = '';
206
366
  for (let i = 0; i < buffer.length; i++) {
207
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
+ }
208
390
  if (chr == ',') {
209
391
  if (str !== '') {
210
- result.push(str);
392
+ // @ts-ignore
393
+ result.at(-1).push(str);
211
394
  str = '';
212
395
  }
396
+ result.push([]);
213
397
  continue;
214
398
  }
215
399
  str += chr;
@@ -256,14 +440,49 @@ function splitRule(buffer) {
256
440
  }
257
441
  }
258
442
  i = k;
259
- continue;
260
443
  }
261
444
  }
262
445
  if (str !== '') {
263
- result.push(str);
446
+ // @ts-ignore
447
+ result.at(-1).push(str);
264
448
  }
265
449
  return result;
266
450
  }
451
+ function reduceRuleSelector(node) {
452
+ if (node.raw == null) {
453
+ Object.defineProperty(node, 'raw', { enumerable: false, writable: true, value: splitRule(node.sel) });
454
+ }
455
+ // @ts-ignore
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 });
482
+ }
483
+ }
484
+ // }
485
+ }
267
486
  function diff(n1, n2, options = {}) {
268
487
  let node1 = n1;
269
488
  let node2 = n2;
@@ -280,8 +499,28 @@ function diff(n1, n2, options = {}) {
280
499
  // @ts-ignore
281
500
  return null;
282
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;
283
510
  node1 = { ...node1, chi: node1.chi.slice() };
284
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
+ // }
285
524
  const intersect = [];
286
525
  while (i--) {
287
526
  if (node1.chi[i].typ == 'Comment') {
@@ -309,7 +548,7 @@ function diff(n1, n2, options = {}) {
309
548
  const result = (intersect.length == 0 ? null : {
310
549
  ...node1,
311
550
  // @ts-ignore
312
- sel: [...new Set([...(n1.raw || splitRule(n1.sel)).concat(n2.raw || splitRule(n2.sel))])].join(),
551
+ sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(','),
313
552
  chi: intersect.reverse()
314
553
  });
315
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)) {
@@ -318,5 +557,261 @@ function diff(n1, n2, options = {}) {
318
557
  }
319
558
  return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node2 : node2 };
320
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
+ }
321
816
 
322
- export { deduplicate, deduplicateRule, hasDeclaration };
817
+ export { deduplicate, deduplicateRule, hasDeclaration, reduceSelector };