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