@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.
@@ -1457,7 +1457,6 @@
1457
1457
  return values;
1458
1458
  }
1459
1459
 
1460
- const indents = [];
1461
1460
  function render(data, opt = {}) {
1462
1461
  const options = Object.assign(opt.compress ? {
1463
1462
  indent: '',
@@ -1475,27 +1474,12 @@
1475
1474
  return acc;
1476
1475
  }
1477
1476
  }
1478
- // if (options.compress && curr.typ == 'Whitespace') {
1479
- //
1480
- // if (original[index + 1]?.typ == 'Start-parens' ||
1481
- // (index > 0 && (original[index - 1].typ == 'Pseudo-class-func' ||
1482
- // original[index - 1].typ == 'End-parens' ||
1483
- // original[index - 1].typ == 'UrlFunc' ||
1484
- // original[index - 1].typ == 'Func' ||
1485
- // (
1486
- // original[index - 1].typ == 'Color' &&
1487
- // (<ColorToken>original[index - 1]).kin != 'hex' &&
1488
- // (<ColorToken>original[index - 1]).kin != 'lit')))) {
1489
- //
1490
- // return acc;
1491
- // }
1492
- // }
1493
1477
  return acc + renderToken(curr, options);
1494
1478
  }
1495
- return { code: doRender(data, options, reducer) };
1479
+ return { code: doRender(data, options, reducer, 0) };
1496
1480
  }
1497
1481
  // @ts-ignore
1498
- function doRender(data, options, reducer, level = 0) {
1482
+ function doRender(data, options, reducer, level = 0, indents = []) {
1499
1483
  if (indents.length < level + 1) {
1500
1484
  indents.push(options.indent.repeat(level));
1501
1485
  }
@@ -1509,7 +1493,7 @@
1509
1493
  return options.removeComments ? '' : data.val;
1510
1494
  case 'StyleSheet':
1511
1495
  return data.chi.reduce((css, node) => {
1512
- const str = doRender(node, options, reducer, level);
1496
+ const str = doRender(node, options, reducer, level, indents);
1513
1497
  if (str === '') {
1514
1498
  return css;
1515
1499
  }
@@ -1536,7 +1520,7 @@
1536
1520
  str = `@${node.nam} ${node.val};`;
1537
1521
  }
1538
1522
  else {
1539
- str = doRender(node, options, reducer, level + 1);
1523
+ str = doRender(node, options, reducer, level + 1, indents);
1540
1524
  }
1541
1525
  if (css === '') {
1542
1526
  return str;
@@ -1544,8 +1528,7 @@
1544
1528
  if (str === '') {
1545
1529
  return css;
1546
1530
  }
1547
- if (str !== '')
1548
- return `${css}${options.newLine}${indentSub}${str}`;
1531
+ return `${css}${options.newLine}${indentSub}${str}`;
1549
1532
  }, '');
1550
1533
  if (children.endsWith(';')) {
1551
1534
  children = children.slice(0, -1);
@@ -2257,6 +2240,54 @@
2257
2240
  }
2258
2241
 
2259
2242
  const configuration = getConfig();
2243
+ const combinators = ['+', '>', '~'];
2244
+ const notEndingWith = ['(', '['].concat(combinators);
2245
+ function wrapNodes(previous, node, match, ast, i, nodeIndex) {
2246
+ // @ts-ignore
2247
+ let pSel = match.selector1.reduce(reducer, []).join(',');
2248
+ // @ts-ignore
2249
+ let nSel = match.selector2.reduce(reducer, []).join(',');
2250
+ // @ts-ignore
2251
+ const wrapper = { ...previous, chi: [], sel: match.match.reduce(reducer, []).join(',') };
2252
+ // @ts-ignore
2253
+ Object.defineProperty(wrapper, 'raw', {
2254
+ enumerable: false,
2255
+ writable: true,
2256
+ // @ts-ignore
2257
+ value: match.match.map(t => t.slice())
2258
+ });
2259
+ if (pSel == '&' || pSel === '') {
2260
+ // @ts-ignore
2261
+ wrapper.chi.push(...previous.chi);
2262
+ // @ts-ignore
2263
+ if ((nSel == '&' || nSel === '') && hasOnlyDeclarations(previous)) {
2264
+ // @ts-ignore
2265
+ wrapper.chi.push(...node.chi);
2266
+ }
2267
+ else {
2268
+ // @ts-ignore
2269
+ wrapper.chi.push(node);
2270
+ }
2271
+ }
2272
+ else {
2273
+ // @ts-ignore
2274
+ wrapper.chi.push(previous, node);
2275
+ }
2276
+ // @ts-ignore
2277
+ ast.chi.splice(i, 1, wrapper);
2278
+ // @ts-ignore
2279
+ ast.chi.splice(nodeIndex, 1);
2280
+ // @ts-ignore
2281
+ previous.sel = pSel;
2282
+ // @ts-ignore
2283
+ previous.raw = match.selector1;
2284
+ // @ts-ignore
2285
+ node.sel = nSel;
2286
+ // @ts-ignore
2287
+ node.raw = match.selector2;
2288
+ reduceRuleSelector(wrapper);
2289
+ return wrapper;
2290
+ }
2260
2291
  function deduplicate(ast, options = {}, recursive = false) {
2261
2292
  // @ts-ignore
2262
2293
  if (('chi' in ast) && ast.chi?.length > 0) {
@@ -2272,6 +2303,14 @@
2272
2303
  }
2273
2304
  // @ts-ignore
2274
2305
  node = ast.chi[i];
2306
+ // @ts-ignore
2307
+ if (previous == node) {
2308
+ // console.error('idem!');
2309
+ // @ts-ignore
2310
+ ast.chi.splice(i, 1);
2311
+ i--;
2312
+ continue;
2313
+ }
2275
2314
  if (node.typ == 'AtRule' && node.nam == 'font-face') {
2276
2315
  continue;
2277
2316
  }
@@ -2284,120 +2323,106 @@
2284
2323
  // @ts-ignore
2285
2324
  if (node.typ == 'Rule') {
2286
2325
  reduceRuleSelector(node);
2326
+ let wrapper;
2327
+ let match;
2287
2328
  // @ts-ignore
2288
- if (options.nestingRules && node.raw != null && previous?.raw != null && node.raw.length == 1 && previous.raw.length == 1) {
2289
- const match = [];
2329
+ if (options.nestingRules) {
2290
2330
  // @ts-ignore
2291
- while (node.raw[0].length > 0 && previous.raw[0].length > 0) {
2331
+ if (previous != null && previous.typ == 'Rule') {
2332
+ reduceRuleSelector(previous);
2292
2333
  // @ts-ignore
2293
- if (node.raw[0][0] != previous.raw[0][0]) {
2294
- break;
2295
- }
2296
- // @ts-ignore
2297
- match.push(node.raw[0].shift());
2298
- // @ts-ignore
2299
- previous.raw[0].shift();
2300
- }
2301
- if (match.length > 0) {
2334
+ match = matchSelectors(previous.raw, node.raw, ast.typ);
2302
2335
  // @ts-ignore
2303
- const wrapper = { ...previous, chi: [] };
2304
- // @ts-ignore
2305
- if (previous.raw[0].length == 0) {
2336
+ if (match != null) {
2306
2337
  // @ts-ignore
2307
- wrapper.chi.push(...previous.chi);
2308
- }
2309
- else {
2338
+ wrapper = wrapNodes(previous, node, match, ast, i, nodeIndex);
2339
+ nodeIndex = i - 1;
2310
2340
  // @ts-ignore
2311
- previous.sel = previous.raw.reduce((acc, curr) => {
2312
- acc.push(curr.join(''));
2313
- return acc;
2314
- }, []).join(',');
2315
- // @ts-ignore
2316
- wrapper.chi.push(previous);
2341
+ previous = ast.chi[nodeIndex];
2317
2342
  }
2318
- // @ts-ignore
2319
- if (node.raw[0].length == 0) {
2320
- // @ts-ignore
2321
- if (previous.raw.length == 0) {
2322
- // @ts-ignore
2323
- wrapper.chi.push(...node.chi);
2324
- }
2325
- else {
2326
- if (hasOnlyDeclarations(wrapper)) {
2327
- wrapper.chi.push(...node.chi);
2328
- }
2329
- else {
2330
- // @ts-ignore
2331
- node.raw[0].push('&');
2332
- // @ts-ignore
2333
- node.sel = node.raw.reduce((acc, curr) => {
2334
- acc.push(curr.join(''));
2335
- return acc;
2336
- }, []).join(',');
2337
- // @ts-ignore
2338
- wrapper.chi.push(node);
2339
- }
2340
- }
2341
- }
2342
- else {
2343
- // @ts-ignore
2344
- node.sel = node.raw.reduce((acc, curr) => {
2345
- acc.push(curr.join(''));
2346
- return acc;
2347
- }, []).join(',');
2348
- // @ts-ignore
2349
- wrapper.chi.push(node);
2350
- }
2351
- Object.defineProperty(wrapper, 'raw', { enumerable: false, writable: true, value: [match] });
2352
- // @ts-ignore
2353
- ast.chi.splice(i, 1, wrapper);
2354
- // @ts-ignore
2355
- ast.chi.splice(nodeIndex, 1);
2343
+ }
2344
+ // @ts-ignore
2345
+ if (wrapper != null) {
2356
2346
  // @ts-ignore
2357
2347
  while (i < ast.chi.length) {
2358
2348
  // @ts-ignore
2359
2349
  const nextNode = ast.chi[i];
2360
2350
  // @ts-ignore
2361
- if (nextNode.typ != 'Rule' || nextNode.raw == null) {
2351
+ if (nextNode.typ != 'Rule') {
2352
+ // i--;
2353
+ // previous = wrapper;
2354
+ // nodeIndex = i;
2362
2355
  break;
2363
2356
  }
2364
2357
  reduceRuleSelector(nextNode);
2365
2358
  // @ts-ignore
2366
- if (nextNode.raw.length != 1 || !eq(wrapper.raw[0], nextNode.raw[0].slice(0, wrapper.raw[0].length))) {
2359
+ match = matchSelectors(wrapper.raw, nextNode.raw, ast.typ);
2360
+ // @ts-ignore
2361
+ if (match == null) {
2367
2362
  break;
2368
2363
  }
2369
2364
  // @ts-ignore
2370
- nextNode.raw[0].splice(0, wrapper.raw[0].length);
2365
+ wrapper = wrapNodes(wrapper, nextNode, match, ast, i, nodeIndex);
2366
+ }
2367
+ nodeIndex = --i;
2368
+ // @ts-ignore
2369
+ previous = ast.chi[nodeIndex];
2370
+ deduplicate(wrapper, options, recursive);
2371
+ continue;
2372
+ }
2373
+ // @ts-ignore
2374
+ else if (node.optimized != null &&
2375
+ // @ts-ignore
2376
+ node.optimized.match &&
2377
+ // @ts-ignore
2378
+ node.optimized.selector.length > 1) {
2379
+ // @ts-ignore
2380
+ wrapper = { ...node, chi: [], sel: node.optimized.optimized[0] };
2381
+ // @ts-ignore
2382
+ Object.defineProperty(wrapper, 'raw', {
2383
+ enumerable: false,
2384
+ writable: true,
2371
2385
  // @ts-ignore
2372
- if (nextNode.raw[0].length == 0 ||
2373
- // @ts-ignore
2374
- (nextNode.raw.length == 1 && nextNode.raw[0] == '&')) {
2375
- if (hasOnlyDeclarations(wrapper)) {
2376
- wrapper.chi.push(...nextNode.chi);
2377
- // @ts-ignore
2378
- ast.chi.splice(i, 1);
2379
- continue;
2386
+ value: [[node.optimized.optimized[0]]]
2387
+ });
2388
+ // @ts-ignore
2389
+ node.sel = node.optimized.selector.reduce(reducer, []).join(',');
2390
+ // @ts-ignore
2391
+ node.raw = node.optimized.selector.slice();
2392
+ // @ts-ignore
2393
+ wrapper.chi.push(node);
2394
+ // @ts-ignore
2395
+ ast.chi.splice(i, 1, wrapper);
2396
+ node = wrapper;
2397
+ }
2398
+ }
2399
+ // @ts-ignore
2400
+ else if (node.optimized?.match) {
2401
+ let wrap = true;
2402
+ // @ts-ignore
2403
+ const selector = node.optimized.selector.reduce((acc, curr) => {
2404
+ if (curr[0] == '&') {
2405
+ if (curr[1] == ' ') {
2406
+ curr.splice(0, 2);
2407
+ }
2408
+ else {
2409
+ if (ast.typ != 'Rule' && combinators.includes(curr[1])) {
2410
+ wrap = false;
2380
2411
  }
2381
2412
  else {
2382
- // @ts-ignore
2383
- nextNode.raw[0].push('&');
2413
+ curr.splice(0, 1);
2384
2414
  }
2385
2415
  }
2386
- // @ts-ignore
2387
- nextNode.sel = nextNode.raw.reduce((acc, curr) => {
2388
- acc.push(curr.join(''));
2389
- return acc;
2390
- }, []).join(',');
2391
- wrapper.chi.push(nextNode);
2392
- // @ts-ignore
2393
- ast.chi.splice(i, 1);
2394
2416
  }
2395
- deduplicateRule(wrapper);
2396
- nodeIndex = --i;
2417
+ else if (combinators.includes(curr[0])) {
2418
+ curr.unshift('&');
2419
+ }
2397
2420
  // @ts-ignore
2398
- previous = ast.chi[i];
2399
- continue;
2400
- }
2421
+ acc.push(curr.map(t => t.replaceAll('&', node.optimized.optimized[0])).join(''));
2422
+ return acc;
2423
+ }, []);
2424
+ // @ts-ignore
2425
+ node.sel = (wrap ? node.optimized.optimized[0] : '') + `:is(${selector.join(',')})`;
2401
2426
  }
2402
2427
  }
2403
2428
  // @ts-ignore
@@ -2427,6 +2452,7 @@
2427
2452
  ast.chi.splice(nodeIndex, 1);
2428
2453
  // @ts-ignore
2429
2454
  if (hasDeclaration(node)) {
2455
+ // @ts-ignore
2430
2456
  deduplicateRule(node);
2431
2457
  }
2432
2458
  else {
@@ -2471,6 +2497,7 @@
2471
2497
  if (recursive && previous != node) {
2472
2498
  // @ts-ignore
2473
2499
  if (hasDeclaration(previous)) {
2500
+ // @ts-ignore
2474
2501
  deduplicateRule(previous);
2475
2502
  }
2476
2503
  else {
@@ -2488,6 +2515,7 @@
2488
2515
  deduplicateRule(node);
2489
2516
  }
2490
2517
  else {
2518
+ // @ts-ignore
2491
2519
  if (!(node.typ == 'AtRule' && node.nam != 'font-face')) {
2492
2520
  deduplicate(node, options, recursive);
2493
2521
  }
@@ -2518,7 +2546,7 @@
2518
2546
  }
2519
2547
  return true;
2520
2548
  }
2521
- function deduplicateRule(ast, options = {}) {
2549
+ function deduplicateRule(ast) {
2522
2550
  // @ts-ignore
2523
2551
  if (!('chi' in ast) || ast.chi?.length <= 1) {
2524
2552
  return ast;
@@ -2568,15 +2596,39 @@
2568
2596
  return ast;
2569
2597
  }
2570
2598
  function splitRule(buffer) {
2571
- const result = [];
2599
+ const result = [[]];
2572
2600
  let str = '';
2573
2601
  for (let i = 0; i < buffer.length; i++) {
2574
2602
  let chr = buffer.charAt(i);
2603
+ if (isWhiteSpace(chr.charCodeAt(0))) {
2604
+ let k = i;
2605
+ while (k + 1 < buffer.length) {
2606
+ if (isWhiteSpace(buffer[k + 1].charCodeAt(0))) {
2607
+ k++;
2608
+ continue;
2609
+ }
2610
+ break;
2611
+ }
2612
+ if (str !== '') {
2613
+ // @ts-ignore
2614
+ result.at(-1).push(str);
2615
+ str = '';
2616
+ }
2617
+ // @ts-ignore
2618
+ if (result.at(-1).length > 0) {
2619
+ // @ts-ignore
2620
+ result.at(-1).push(' ');
2621
+ }
2622
+ i = k;
2623
+ continue;
2624
+ }
2575
2625
  if (chr == ',') {
2576
2626
  if (str !== '') {
2577
- result.push(str);
2627
+ // @ts-ignore
2628
+ result.at(-1).push(str);
2578
2629
  str = '';
2579
2630
  }
2631
+ result.push([]);
2580
2632
  continue;
2581
2633
  }
2582
2634
  str += chr;
@@ -2626,38 +2678,45 @@
2626
2678
  }
2627
2679
  }
2628
2680
  if (str !== '') {
2629
- result.push(str);
2681
+ // @ts-ignore
2682
+ result.at(-1).push(str);
2630
2683
  }
2631
2684
  return result;
2632
2685
  }
2633
2686
  function reduceRuleSelector(node) {
2687
+ if (node.raw == null) {
2688
+ Object.defineProperty(node, 'raw', { enumerable: false, writable: true, value: splitRule(node.sel) });
2689
+ }
2634
2690
  // @ts-ignore
2635
- if (node.raw != null) {
2636
- // @ts-ignore
2637
- let optimized = reduceSelector(node.raw);
2638
- if (optimized != null) {
2639
- Object.defineProperty(node, 'optimized', { enumerable: false, writable: true, value: optimized });
2640
- }
2641
- if (optimized != null && optimized.match && optimized.reducible) {
2642
- const raw = [
2643
- [
2644
- optimized.optimized[0], ':is('
2645
- ].concat(optimized.selector.reduce((acc, curr) => {
2646
- if (acc.length > 0) {
2647
- acc.push(',');
2648
- }
2649
- acc.push(...curr);
2650
- return acc;
2651
- }, [])).concat(')')
2652
- ];
2653
- const sel = raw[0].join('');
2654
- if (sel.length < node.sel.length) {
2655
- node.sel = sel;
2656
- // node.raw = raw;
2657
- Object.defineProperty(node, 'raw', { enumerable: false, writable: true, value: raw });
2658
- }
2691
+ // if (node.raw != null) {
2692
+ // @ts-ignore
2693
+ let optimized = reduceSelector(node.raw.reduce((acc, curr) => {
2694
+ acc.push(curr.slice());
2695
+ return acc;
2696
+ }, []));
2697
+ if (optimized != null) {
2698
+ Object.defineProperty(node, 'optimized', { enumerable: false, writable: true, value: optimized });
2699
+ }
2700
+ if (optimized != null && optimized.match && optimized.reducible && optimized.selector.length > 1) {
2701
+ const raw = [
2702
+ [
2703
+ optimized.optimized[0], ':is('
2704
+ ].concat(optimized.selector.reduce((acc, curr) => {
2705
+ if (acc.length > 0) {
2706
+ acc.push(',');
2707
+ }
2708
+ acc.push(...curr);
2709
+ return acc;
2710
+ }, [])).concat(')')
2711
+ ];
2712
+ const sel = raw[0].join('');
2713
+ if (sel.length < node.sel.length) {
2714
+ node.sel = sel;
2715
+ // node.raw = raw;
2716
+ Object.defineProperty(node, 'raw', { enumerable: false, writable: true, value: raw });
2659
2717
  }
2660
2718
  }
2719
+ // }
2661
2720
  }
2662
2721
  function diff(n1, n2, options = {}) {
2663
2722
  let node1 = n1;
@@ -2678,25 +2737,25 @@
2678
2737
  // @ts-ignore
2679
2738
  const raw1 = node1.raw;
2680
2739
  // @ts-ignore
2681
- const optimized1 = node1.optimized;
2740
+ // const optimized1 = node1.optimized;
2682
2741
  // @ts-ignore
2683
2742
  const raw2 = node2.raw;
2684
2743
  // @ts-ignore
2685
- const optimized2 = node2.optimized;
2744
+ // const optimized2 = node2.optimized;
2686
2745
  node1 = { ...node1, chi: node1.chi.slice() };
2687
2746
  node2 = { ...node2, chi: node2.chi.slice() };
2688
2747
  if (raw1 != null) {
2689
2748
  Object.defineProperty(node1, 'raw', { enumerable: false, writable: true, value: raw1 });
2690
2749
  }
2691
- if (optimized1 != null) {
2692
- Object.defineProperty(node1, 'optimized', { enumerable: false, writable: true, value: optimized1 });
2693
- }
2750
+ // if (optimized1 != null) {
2751
+ // Object.defineProperty(node1, 'optimized', {enumerable: false, writable: true, value: optimized1});
2752
+ // }
2694
2753
  if (raw2 != null) {
2695
2754
  Object.defineProperty(node2, 'raw', { enumerable: false, writable: true, value: raw2 });
2696
2755
  }
2697
- if (optimized2 != null) {
2698
- Object.defineProperty(node2, 'optimized', { enumerable: false, writable: true, value: optimized2 });
2699
- }
2756
+ // if (optimized2 != null) {
2757
+ // Object.defineProperty(node2, 'optimized', {enumerable: false, writable: true, value: optimized2});
2758
+ // }
2700
2759
  const intersect = [];
2701
2760
  while (i--) {
2702
2761
  if (node1.chi[i].typ == 'Comment') {
@@ -2724,7 +2783,7 @@
2724
2783
  const result = (intersect.length == 0 ? null : {
2725
2784
  ...node1,
2726
2785
  // @ts-ignore
2727
- sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(),
2786
+ sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(','),
2728
2787
  chi: intersect.reverse()
2729
2788
  });
2730
2789
  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)) {
@@ -2733,8 +2792,169 @@
2733
2792
  }
2734
2793
  return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node2 : node2 };
2735
2794
  }
2795
+ function matchSelectors(selector1, selector2, parentType) {
2796
+ let match = [[]];
2797
+ 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));
2798
+ let i = 0;
2799
+ let k;
2800
+ let l;
2801
+ let token;
2802
+ let matching = true;
2803
+ let matchFunction = 0;
2804
+ let inAttr = 0;
2805
+ for (; i < j; i++) {
2806
+ k = 0;
2807
+ token = selector1[0][i];
2808
+ for (; k < selector1.length; k++) {
2809
+ if (selector1[k][i] != token) {
2810
+ matching = false;
2811
+ break;
2812
+ }
2813
+ }
2814
+ if (matching) {
2815
+ l = 0;
2816
+ for (; l < selector2.length; l++) {
2817
+ if (selector2[l][i] != token) {
2818
+ matching = false;
2819
+ break;
2820
+ }
2821
+ }
2822
+ }
2823
+ if (!matching) {
2824
+ break;
2825
+ }
2826
+ if (token == ',') {
2827
+ match.push([]);
2828
+ }
2829
+ else {
2830
+ if (token.endsWith('(')) {
2831
+ matchFunction++;
2832
+ }
2833
+ if (token.endsWith('[')) {
2834
+ inAttr++;
2835
+ }
2836
+ else if (token == ')') {
2837
+ matchFunction--;
2838
+ }
2839
+ else if (token == ']') {
2840
+ inAttr--;
2841
+ }
2842
+ match.at(-1).push(token);
2843
+ }
2844
+ }
2845
+ // invalid function
2846
+ if (matchFunction != 0 || inAttr != 0) {
2847
+ return null;
2848
+ }
2849
+ if (parentType != 'Rule') {
2850
+ for (const part of match) {
2851
+ if (part.length > 0 && combinators.includes(part[0].charAt(0))) {
2852
+ return null;
2853
+ }
2854
+ }
2855
+ }
2856
+ if (match.length > 1) {
2857
+ console.error(`unsupported multilevel matching`);
2858
+ console.error({ match, selector1, selector2 });
2859
+ return null;
2860
+ }
2861
+ for (const part of match) {
2862
+ while (part.length > 0) {
2863
+ const token = part.at(-1);
2864
+ if (token == ' ' || combinators.includes(token) || notEndingWith.includes(token.at(-1))) {
2865
+ part.pop();
2866
+ continue;
2867
+ }
2868
+ break;
2869
+ }
2870
+ }
2871
+ if (match.every(t => t.length == 0)) {
2872
+ return null;
2873
+ }
2874
+ if (eq([['&']], match)) {
2875
+ return null;
2876
+ }
2877
+ function reduce(acc, curr) {
2878
+ if (acc === null) {
2879
+ return null;
2880
+ }
2881
+ let hasCompoundSelector = true;
2882
+ curr = curr.slice(match[0].length);
2883
+ while (curr.length > 0) {
2884
+ if (curr[0] == ' ') {
2885
+ hasCompoundSelector = false;
2886
+ curr.unshift('&');
2887
+ continue;
2888
+ }
2889
+ break;
2890
+ }
2891
+ // invalid function match
2892
+ if (curr.length > 0 && curr[0].endsWith('(') && curr.at(-1) != ')') {
2893
+ return null;
2894
+ }
2895
+ if (curr.length == 1 && combinators.includes(curr[0].charAt(0))) {
2896
+ return null;
2897
+ }
2898
+ if (hasCompoundSelector && curr.length > 0) {
2899
+ hasCompoundSelector = !['&'].concat(combinators).includes(curr[0].charAt(0));
2900
+ }
2901
+ if (curr[0] == ':is(') {
2902
+ let inFunction = 0;
2903
+ let canReduce = true;
2904
+ const isCompound = curr.reduce((acc, token, index) => {
2905
+ if (index == 0) {
2906
+ inFunction++;
2907
+ canReduce = curr[1] == '&';
2908
+ }
2909
+ else if (token.endsWith('(')) {
2910
+ if (inFunction == 0) {
2911
+ canReduce = false;
2912
+ }
2913
+ inFunction++;
2914
+ }
2915
+ else if (token == ')') {
2916
+ inFunction--;
2917
+ }
2918
+ else if (token == ',') {
2919
+ if (!canReduce) {
2920
+ canReduce = curr[index + 1] == '&';
2921
+ }
2922
+ acc.push([]);
2923
+ }
2924
+ else
2925
+ acc.at(-1)?.push(token);
2926
+ return acc;
2927
+ }, [[]]);
2928
+ if (inFunction > 0) {
2929
+ canReduce = false;
2930
+ }
2931
+ if (canReduce) {
2932
+ curr = isCompound.reduce((acc, curr) => {
2933
+ if (acc.length > 0) {
2934
+ acc.push(',');
2935
+ }
2936
+ acc.push(...curr);
2937
+ return acc;
2938
+ }, []);
2939
+ }
2940
+ }
2941
+ // @todo: check hasCompoundSelector && curr[0] == '&' && curr[1] == ' '
2942
+ acc.push(match.length == 0 ? ['&'] : (hasCompoundSelector && curr[0] != '&' && (curr.length == 0 || !combinators.includes(curr[0].charAt(0))) ? ['&'].concat(curr) : curr));
2943
+ return acc;
2944
+ }
2945
+ // @ts-ignore
2946
+ selector1 = selector1.reduce(reduce, []);
2947
+ // @ts-ignore
2948
+ selector2 = selector2.reduce(reduce, []);
2949
+ return selector1 == null || selector2 == null ? null : {
2950
+ eq: eq(selector1, selector2),
2951
+ match,
2952
+ selector1,
2953
+ selector2
2954
+ };
2955
+ }
2736
2956
  function reduceSelector(selector) {
2737
- if (selector.length < 2) {
2957
+ if (selector.length == 0) {
2738
2958
  return null;
2739
2959
  }
2740
2960
  const optimized = [];
@@ -2756,37 +2976,75 @@
2756
2976
  }
2757
2977
  optimized.push(item);
2758
2978
  }
2759
- if (optimized.at(-1) == ' ') {
2760
- optimized.pop();
2979
+ while (optimized.length > 0) {
2980
+ const last = optimized.at(-1);
2981
+ if ((last == ' ' || combinators.includes(last))) {
2982
+ optimized.pop();
2983
+ continue;
2984
+ }
2985
+ break;
2986
+ }
2987
+ selector.forEach((selector) => selector.splice(0, optimized.length));
2988
+ // combinator
2989
+ if (combinators.includes(optimized.at(-1))) {
2990
+ const combinator = optimized.pop();
2991
+ selector.forEach(selector => selector.unshift(combinator));
2761
2992
  }
2762
2993
  let reducible = optimized.length == 1;
2763
- if (optimized.length == 0) {
2764
- return { match: false, optimized, selector, reducible };
2994
+ if (optimized[0] == '&' && optimized[1] == ' ') {
2995
+ optimized.splice(0, 2);
2996
+ }
2997
+ if (optimized.length == 0 ||
2998
+ (optimized[0].charAt(0) == '&' ||
2999
+ selector.length == 1)) {
3000
+ return {
3001
+ match: false,
3002
+ optimized,
3003
+ selector: selector.map(selector => selector[0] == '&' && selector[1] == ' ' ? selector.slice(2) : selector),
3004
+ reducible: selector.length > 1 && selector.every((selector) => !combinators.includes(selector[0]))
3005
+ };
2765
3006
  }
2766
3007
  return {
2767
3008
  match: true,
2768
3009
  optimized,
2769
3010
  selector: selector.reduce((acc, curr) => {
2770
- const slice = curr.slice(optimized.length);
3011
+ let hasCompound = true;
3012
+ if (hasCompound && curr.length > 0) {
3013
+ hasCompound = !['&'].concat(combinators).includes(curr[0].charAt(0));
3014
+ }
2771
3015
  // @ts-ignore
2772
- if (slice.length > 0 && slice[0] == ' ') {
2773
- slice.shift();
3016
+ if (hasCompound && curr[0] == ' ') {
3017
+ hasCompound = false;
3018
+ curr.unshift('&');
2774
3019
  }
2775
- if (slice.length == 0) {
2776
- slice.push('&');
3020
+ if (curr.length == 0) {
3021
+ curr.push('&');
3022
+ hasCompound = false;
2777
3023
  }
2778
3024
  if (reducible) {
2779
- const chr = slice[0].charAt(0);
3025
+ const chr = curr[0].charAt(0);
2780
3026
  // @ts-ignore
2781
3027
  reducible = chr == '.' || chr == ':' || isIdentStart(chr.codePointAt(0));
2782
3028
  }
2783
- acc.push(slice);
3029
+ acc.push(hasCompound ? ['&'].concat(curr) : curr);
2784
3030
  return acc;
2785
3031
  }, []),
2786
- reducible
3032
+ reducible: selector.every((selector) => !['>', '+', '~', '&'].includes(selector[0]))
2787
3033
  };
2788
3034
  }
2789
- function reducer(acc, curr) {
3035
+ function reducer(acc, curr, index, array) {
3036
+ // trim :is()
3037
+ if (array.length == 1 && array[0][0] == ':is(' && array[0].at(-1) == ')') {
3038
+ curr = curr.slice(1, -1);
3039
+ }
3040
+ if (curr[0] == '&') {
3041
+ if (curr[1] == ' ') {
3042
+ curr.splice(0, 2);
3043
+ }
3044
+ else if (combinators.includes(curr[1])) {
3045
+ curr.splice(0, 1);
3046
+ }
3047
+ }
2790
3048
  acc.push(curr.join(''));
2791
3049
  return acc;
2792
3050
  }
@@ -3094,7 +3352,7 @@
3094
3352
  // https://www.w3.org/TR/css-nesting-1/#conditionals
3095
3353
  // allowed nesting at-rules
3096
3354
  // there must be a top level rule in the stack
3097
- const raw = tokens.reduce((acc, curr, index, array) => {
3355
+ const raw = tokens.reduce((acc, curr) => {
3098
3356
  acc.push(renderToken(curr, { removeComments: true }));
3099
3357
  return acc;
3100
3358
  }, []);
@@ -3129,7 +3387,12 @@
3129
3387
  }
3130
3388
  }
3131
3389
  const uniq = new Map;
3132
- parseTokens(tokens, { compress: options.compress }).reduce((acc, curr) => {
3390
+ parseTokens(tokens, 'Rule', { compress: options.compress }).reduce((acc, curr, index, array) => {
3391
+ if (curr.typ == 'Whitespace') {
3392
+ if (array[index - 1]?.val == '+' || array[index + 1]?.val == '+') {
3393
+ return acc;
3394
+ }
3395
+ }
3133
3396
  let t = renderToken(curr, { compress: true });
3134
3397
  if (t == ',') {
3135
3398
  acc.push([]);
@@ -3173,7 +3436,7 @@
3173
3436
  }
3174
3437
  if (tokens[i].typ == 'Colon') {
3175
3438
  name = tokens.slice(0, i);
3176
- value = parseTokens(tokens.slice(i + 1), {
3439
+ value = parseTokens(tokens.slice(i + 1), 'Declaration', {
3177
3440
  parseColor: true,
3178
3441
  src: options.src,
3179
3442
  resolveUrls: options.resolveUrls,
@@ -3262,7 +3525,6 @@
3262
3525
  position.ind = ind;
3263
3526
  position.lin = lin;
3264
3527
  position.col = col == 0 ? 1 : col;
3265
- // }
3266
3528
  }
3267
3529
  function consumeWhiteSpace() {
3268
3530
  let count = 0;
@@ -3494,6 +3756,9 @@
3494
3756
  break;
3495
3757
  case '~':
3496
3758
  case '|':
3759
+ if (tokens.at(-1)?.typ == 'Whitespace') {
3760
+ tokens.pop();
3761
+ }
3497
3762
  if (buffer.length > 0) {
3498
3763
  pushToken(getType(buffer));
3499
3764
  buffer = '';
@@ -3515,19 +3780,32 @@
3515
3780
  break;
3516
3781
  }
3517
3782
  pushToken(getType(buffer));
3783
+ while (isWhiteSpace(value.charCodeAt(0))) {
3784
+ value = next();
3785
+ }
3518
3786
  buffer = value;
3519
3787
  break;
3520
3788
  case '>':
3521
- if (tokens[tokens.length - 1]?.typ == 'Whitespace') {
3522
- tokens.pop();
3523
- }
3524
3789
  if (buffer !== '') {
3525
3790
  pushToken(getType(buffer));
3526
3791
  buffer = '';
3527
3792
  }
3793
+ if (tokens[tokens.length - 1]?.typ == 'Whitespace') {
3794
+ tokens.pop();
3795
+ }
3528
3796
  pushToken({ typ: 'Gt' });
3529
3797
  consumeWhiteSpace();
3530
3798
  break;
3799
+ case '.':
3800
+ const codepoint = peek().charCodeAt(0);
3801
+ if (!isDigit(codepoint) && buffer !== '') {
3802
+ pushToken(getType(buffer));
3803
+ buffer = value;
3804
+ break;
3805
+ }
3806
+ buffer += value;
3807
+ break;
3808
+ case '+':
3531
3809
  case ':':
3532
3810
  case ',':
3533
3811
  case '=':
@@ -3541,6 +3819,9 @@
3541
3819
  }
3542
3820
  pushToken(getType(value));
3543
3821
  buffer = '';
3822
+ if (value == '+' && isWhiteSpace(peek().charCodeAt(0))) {
3823
+ pushToken(getType(next()));
3824
+ }
3544
3825
  while (isWhiteSpace(peek().charCodeAt(0))) {
3545
3826
  next();
3546
3827
  }
@@ -3718,13 +3999,14 @@
3718
3999
  }
3719
4000
  return { ast, errors, bytesIn };
3720
4001
  }
3721
- function parseTokens(tokens, options = {}) {
4002
+ function parseTokens(tokens, nodeType, options = {}) {
3722
4003
  for (let i = 0; i < tokens.length; i++) {
3723
4004
  const t = tokens[i];
3724
4005
  if (t.typ == 'Whitespace' && ((i == 0 ||
3725
4006
  i + 1 == tokens.length ||
3726
4007
  ['Comma'].includes(tokens[i + 1].typ) ||
3727
4008
  (i > 0 &&
4009
+ tokens[i + 1]?.typ != 'Literal' &&
3728
4010
  funcLike.includes(tokens[i - 1].typ) &&
3729
4011
  !['var', 'calc'].includes(tokens[i - 1].val))))) {
3730
4012
  tokens.splice(i--, 1);
@@ -3771,7 +4053,7 @@
3771
4053
  if (t.chi.length > 1) {
3772
4054
  /*(<AttrToken>t).chi =*/
3773
4055
  // @ts-ignore
3774
- parseTokens(t.chi, options);
4056
+ parseTokens(t.chi, t.typ, options);
3775
4057
  }
3776
4058
  // @ts-ignore
3777
4059
  t.chi.forEach(val => {
@@ -3883,7 +4165,7 @@
3883
4165
  // @ts-ignore
3884
4166
  if (t.chi.length > 0) {
3885
4167
  // @ts-ignore
3886
- parseTokens(t.chi, options);
4168
+ parseTokens(t.chi, t.typ, options);
3887
4169
  if (t.typ == 'Pseudo-class-func' && t.val == ':is' && options.compress) {
3888
4170
  //
3889
4171
  const count = t.chi.filter(t => t.typ != 'Comment').length;
@@ -3949,7 +4231,7 @@
3949
4231
  yield { node, parent, root };
3950
4232
  if ('chi' in node) {
3951
4233
  for (const child of node.chi) {
3952
- yield* doWalk(child, node, (root == null ? node : root));
4234
+ yield* doWalk(child, node, (root ?? node));
3953
4235
  }
3954
4236
  }
3955
4237
  }