@tbela99/css-parser 0.0.1-alpha4 → 0.0.1-rc1

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.
@@ -8,13 +8,14 @@
8
8
  // https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/#typedef-ident-token
9
9
  // '\\'
10
10
  const REVERSE_SOLIDUS = 0x5c;
11
+ const dimensionUnits = [
12
+ 'q', 'cap', 'ch', 'cm', 'cqb', 'cqh', 'cqi', 'cqmax', 'cqmin', 'cqw', 'dvb',
13
+ 'dvh', 'dvi', 'dvmax', 'dvmin', 'dvw', 'em', 'ex', 'ic', 'in', 'lh', 'lvb',
14
+ 'lvh', 'lvi', 'lvmax', 'lvw', 'mm', 'pc', 'pt', 'px', 'rem', 'rlh', 'svb',
15
+ 'svh', 'svi', 'svmin', 'svw', 'vb', 'vh', 'vi', 'vmax', 'vmin', 'vw'
16
+ ];
11
17
  function isLength(dimension) {
12
- return 'unit' in dimension && [
13
- 'q', 'cap', 'ch', 'cm', 'cqb', 'cqh', 'cqi', 'cqmax', 'cqmin', 'cqw', 'dvb',
14
- 'dvh', 'dvi', 'dvmax', 'dvmin', 'dvw', 'em', 'ex', 'ic', 'in', 'lh', 'lvb',
15
- 'lvh', 'lvi', 'lvmax', 'lvw', 'mm', 'pc', 'pt', 'px', 'rem', 'rlh', 'svb',
16
- 'svh', 'svi', 'svmin', 'svw', 'vb', 'vh', 'vi', 'vmax', 'vmin', 'vw'
17
- ].includes(dimension.unit.toLowerCase());
18
+ return 'unit' in dimension && dimensionUnits.includes(dimension.unit.toLowerCase());
18
19
  }
19
20
  function isResolution(dimension) {
20
21
  return 'unit' in dimension && ['dpi', 'dpcm', 'dppx', 'x'].includes(dimension.unit.toLowerCase());
@@ -239,6 +240,22 @@
239
240
  }
240
241
  return true;
241
242
  }
243
+ function isHexDigit(name) {
244
+ if (name.length || name.length > 6) {
245
+ return false;
246
+ }
247
+ for (let chr of name) {
248
+ let codepoint = chr.charCodeAt(0);
249
+ if (!isDigit(codepoint) &&
250
+ // A F
251
+ !(codepoint >= 0x41 && codepoint <= 0x46) &&
252
+ // a f
253
+ !(codepoint >= 0x61 && codepoint <= 0x66)) {
254
+ return false;
255
+ }
256
+ }
257
+ return true;
258
+ }
242
259
  function isFunction(name) {
243
260
  return name.endsWith('(') && isIdent(name.slice(0, -1));
244
261
  }
@@ -374,6 +391,7 @@
374
391
  },
375
392
  "border-width": {
376
393
  shorthand: "border-width",
394
+ map: "border",
377
395
  properties: [
378
396
  "border-top-width",
379
397
  "border-right-width",
@@ -384,6 +402,9 @@
384
402
  "Length",
385
403
  "Perc"
386
404
  ],
405
+ "default": [
406
+ "medium"
407
+ ],
387
408
  keywords: [
388
409
  "thin",
389
410
  "medium",
@@ -391,19 +412,24 @@
391
412
  ]
392
413
  },
393
414
  "border-top-width": {
415
+ map: "border",
394
416
  shorthand: "border-width"
395
417
  },
396
418
  "border-right-width": {
419
+ map: "border",
397
420
  shorthand: "border-width"
398
421
  },
399
422
  "border-bottom-width": {
423
+ map: "border",
400
424
  shorthand: "border-width"
401
425
  },
402
426
  "border-left-width": {
427
+ map: "border",
403
428
  shorthand: "border-width"
404
429
  },
405
430
  "border-style": {
406
431
  shorthand: "border-style",
432
+ map: "border",
407
433
  properties: [
408
434
  "border-top-style",
409
435
  "border-right-style",
@@ -412,6 +438,9 @@
412
438
  ],
413
439
  types: [
414
440
  ],
441
+ "default": [
442
+ "none"
443
+ ],
415
444
  keywords: [
416
445
  "none",
417
446
  "hidden",
@@ -426,19 +455,24 @@
426
455
  ]
427
456
  },
428
457
  "border-top-style": {
458
+ map: "border",
429
459
  shorthand: "border-style"
430
460
  },
431
461
  "border-right-style": {
462
+ map: "border",
432
463
  shorthand: "border-style"
433
464
  },
434
465
  "border-bottom-style": {
466
+ map: "border",
435
467
  shorthand: "border-style"
436
468
  },
437
469
  "border-left-style": {
470
+ map: "border",
438
471
  shorthand: "border-style"
439
472
  },
440
473
  "border-color": {
441
474
  shorthand: "border-color",
475
+ map: "border",
442
476
  properties: [
443
477
  "border-top-color",
444
478
  "border-right-color",
@@ -448,23 +482,95 @@
448
482
  types: [
449
483
  "Color"
450
484
  ],
485
+ "default": [
486
+ "currentcolor"
487
+ ],
451
488
  keywords: [
452
489
  ]
453
490
  },
454
491
  "border-top-color": {
492
+ map: "border",
455
493
  shorthand: "border-color"
456
494
  },
457
495
  "border-right-color": {
496
+ map: "border",
458
497
  shorthand: "border-color"
459
498
  },
460
499
  "border-bottom-color": {
500
+ map: "border",
461
501
  shorthand: "border-color"
462
502
  },
463
503
  "border-left-color": {
504
+ map: "border",
464
505
  shorthand: "border-color"
465
506
  }
466
507
  };
467
508
  var map = {
509
+ border: {
510
+ shorthand: "border",
511
+ pattern: "border-color border-style border-width",
512
+ keywords: [
513
+ "none"
514
+ ],
515
+ "default": [
516
+ "0",
517
+ "none"
518
+ ],
519
+ properties: {
520
+ "border-color": {
521
+ types: [
522
+ "Color"
523
+ ],
524
+ "default": [
525
+ "currentcolor"
526
+ ],
527
+ keywords: [
528
+ ]
529
+ },
530
+ "border-style": {
531
+ types: [
532
+ ],
533
+ "default": [
534
+ "none"
535
+ ],
536
+ keywords: [
537
+ "none",
538
+ "hidden",
539
+ "dotted",
540
+ "dashed",
541
+ "solid",
542
+ "double",
543
+ "groove",
544
+ "ridge",
545
+ "inset",
546
+ "outset"
547
+ ]
548
+ },
549
+ "border-width": {
550
+ types: [
551
+ "Length",
552
+ "Perc"
553
+ ],
554
+ "default": [
555
+ "medium"
556
+ ],
557
+ keywords: [
558
+ "thin",
559
+ "medium",
560
+ "thick"
561
+ ]
562
+ }
563
+ }
564
+ },
565
+ "border-color": {
566
+ shorthand: "border"
567
+ },
568
+ "border-style": {
569
+ shorthand: "border"
570
+ },
571
+ "border-width": {
572
+ shorthand: "border"
573
+ },
468
574
  outline: {
469
575
  shorthand: "outline",
470
576
  pattern: "outline-color outline-style outline-width",
@@ -481,12 +587,10 @@
481
587
  "Color"
482
588
  ],
483
589
  "default": [
484
- "currentColor",
485
- "invert"
590
+ "currentColor"
486
591
  ],
487
592
  keywords: [
488
- "currentColor",
489
- "invert"
593
+ "currentColor"
490
594
  ]
491
595
  },
492
596
  "outline-style": {
@@ -824,6 +928,7 @@
824
928
  "default": [
825
929
  "transparent"
826
930
  ],
931
+ multiple: true,
827
932
  keywords: [
828
933
  ]
829
934
  },
@@ -844,6 +949,7 @@
844
949
  "default": [
845
950
  "scroll"
846
951
  ],
952
+ multiple: true,
847
953
  keywords: [
848
954
  "scroll",
849
955
  "fixed",
@@ -856,6 +962,7 @@
856
962
  "default": [
857
963
  "border-box"
858
964
  ],
965
+ multiple: true,
859
966
  keywords: [
860
967
  "border-box",
861
968
  "padding-box",
@@ -869,6 +976,7 @@
869
976
  "default": [
870
977
  "padding-box"
871
978
  ],
979
+ multiple: true,
872
980
  keywords: [
873
981
  "border-box",
874
982
  "padding-box",
@@ -1457,9 +1565,8 @@
1457
1565
  return values;
1458
1566
  }
1459
1567
 
1460
- const indents = [];
1461
1568
  function render(data, opt = {}) {
1462
- const options = Object.assign(opt.compress ? {
1569
+ const options = Object.assign(opt.minify ?? true ? {
1463
1570
  indent: '',
1464
1571
  newLine: '',
1465
1572
  removeComments: true
@@ -1475,27 +1582,12 @@
1475
1582
  return acc;
1476
1583
  }
1477
1584
  }
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
1585
  return acc + renderToken(curr, options);
1494
1586
  }
1495
- return { code: doRender(data, options, reducer) };
1587
+ return { code: doRender(data, options, reducer, 0) };
1496
1588
  }
1497
1589
  // @ts-ignore
1498
- function doRender(data, options, reducer, level = 0) {
1590
+ function doRender(data, options, reducer, level = 0, indents = []) {
1499
1591
  if (indents.length < level + 1) {
1500
1592
  indents.push(options.indent.repeat(level));
1501
1593
  }
@@ -1509,7 +1601,7 @@
1509
1601
  return options.removeComments ? '' : data.val;
1510
1602
  case 'StyleSheet':
1511
1603
  return data.chi.reduce((css, node) => {
1512
- const str = doRender(node, options, reducer, level);
1604
+ const str = doRender(node, options, reducer, level, indents);
1513
1605
  if (str === '') {
1514
1606
  return css;
1515
1607
  }
@@ -1536,7 +1628,7 @@
1536
1628
  str = `@${node.nam} ${node.val};`;
1537
1629
  }
1538
1630
  else {
1539
- str = doRender(node, options, reducer, level + 1);
1631
+ str = doRender(node, options, reducer, level + 1, indents);
1540
1632
  }
1541
1633
  if (css === '') {
1542
1634
  return str;
@@ -1544,8 +1636,7 @@
1544
1636
  if (str === '') {
1545
1637
  return css;
1546
1638
  }
1547
- if (str !== '')
1548
- return `${css}${options.newLine}${indentSub}${str}`;
1639
+ return `${css}${options.newLine}${indentSub}${str}`;
1549
1640
  }, '');
1550
1641
  if (children.endsWith(';')) {
1551
1642
  children = children.slice(0, -1);
@@ -1560,8 +1651,11 @@
1560
1651
  function renderToken(token, options = {}) {
1561
1652
  switch (token.typ) {
1562
1653
  case 'Color':
1563
- if (options.compress || options.colorConvert) {
1564
- let value = token.kin == 'hex' ? token.val.toLowerCase() : '';
1654
+ if (options.minify || options.colorConvert) {
1655
+ if (token.kin == 'lit' && token.val.toLowerCase() == 'currentcolor') {
1656
+ return 'currentcolor';
1657
+ }
1658
+ let value = token.kin == 'hex' ? token.val.toLowerCase() : (token.kin == 'lit' ? COLORS_NAMES[token.val.toLowerCase()] : '');
1565
1659
  if (token.val == 'rgb' || token.val == 'rgba') {
1566
1660
  value = rgb2Hex(token);
1567
1661
  }
@@ -1605,7 +1699,7 @@
1605
1699
  case 'UrlFunc':
1606
1700
  case 'Pseudo-class-func':
1607
1701
  // @ts-ignore
1608
- return ( /* options.compress && 'Pseudo-class-func' == token.typ && token.val.slice(0, 2) == '::' ? token.val.slice(1) :*/token.val ?? '') + '(' + token.chi.reduce((acc, curr) => {
1702
+ return ( /* options.minify && 'Pseudo-class-func' == token.typ && token.val.slice(0, 2) == '::' ? token.val.slice(1) :*/token.val ?? '') + '(' + token.chi.reduce((acc, curr) => {
1609
1703
  if (options.removeComments && curr.typ == 'Comment') {
1610
1704
  if (!options.preserveLicense || !curr.val.startsWith('/*!')) {
1611
1705
  return acc;
@@ -1696,21 +1790,45 @@
1696
1790
  case 'String':
1697
1791
  case 'Iden':
1698
1792
  case 'Delim':
1699
- return /* options.compress && 'Pseudo-class' == token.typ && '::' == token.val.slice(0, 2) ? token.val.slice(1) : */ token.val;
1793
+ return /* options.minify && 'Pseudo-class' == token.typ && '::' == token.val.slice(0, 2) ? token.val.slice(1) : */ token.val;
1700
1794
  }
1701
1795
  throw new Error(`unexpected token ${JSON.stringify(token, null, 1)}`);
1702
1796
  }
1703
1797
 
1704
1798
  function eq(a, b) {
1705
- if ((typeof a != 'object') || typeof b != 'object') {
1799
+ if (a == null || b == null) {
1800
+ return a == b;
1801
+ }
1802
+ if (typeof a != 'object' || typeof b != 'object') {
1706
1803
  return a === b;
1707
1804
  }
1805
+ if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) {
1806
+ return false;
1807
+ }
1808
+ if (Array.isArray(a)) {
1809
+ if (a.length != b.length) {
1810
+ return false;
1811
+ }
1812
+ let i = 0;
1813
+ for (; i < a.length; i++) {
1814
+ if (!eq(a[i], b[i])) {
1815
+ return false;
1816
+ }
1817
+ }
1818
+ return true;
1819
+ }
1708
1820
  const k1 = Object.keys(a);
1709
1821
  const k2 = Object.keys(b);
1710
- return k1.length == k2.length &&
1711
- k1.every((key) => {
1712
- return eq(a[key], b[key]);
1713
- });
1822
+ if (k1.length != k2.length) {
1823
+ return false;
1824
+ }
1825
+ let key;
1826
+ for (key of k1) {
1827
+ if (!eq(a[key], b[key])) {
1828
+ return false;
1829
+ }
1830
+ }
1831
+ return true;
1714
1832
  }
1715
1833
 
1716
1834
  class PropertySet {
@@ -1722,8 +1840,7 @@
1722
1840
  }
1723
1841
  add(declaration) {
1724
1842
  if (declaration.nam == this.config.shorthand) {
1725
- this.declarations.clear();
1726
- this.declarations.set(declaration.nam, declaration);
1843
+ this.declarations = new Map;
1727
1844
  }
1728
1845
  else {
1729
1846
  // expand shorthand
@@ -1746,6 +1863,10 @@
1746
1863
  }
1747
1864
  if (token.typ != 'Whitespace' && token.typ != 'Comment') {
1748
1865
  if (token.typ == 'Iden' && this.config.keywords.includes(token.val)) {
1866
+ if (tokens.length == 0) {
1867
+ tokens.push([]);
1868
+ current++;
1869
+ }
1749
1870
  tokens[current].push(token);
1750
1871
  }
1751
1872
  if (token.typ == 'Literal' && token.val == this.config.separator) {
@@ -1761,10 +1882,6 @@
1761
1882
  this.declarations.delete(this.config.shorthand);
1762
1883
  for (const values of tokens) {
1763
1884
  this.config.properties.forEach((property, index) => {
1764
- // if (property == declaration.nam) {
1765
- //
1766
- // return;
1767
- // }
1768
1885
  if (!this.declarations.has(property)) {
1769
1886
  this.declarations.set(property, {
1770
1887
  typ: 'Declaration',
@@ -1793,30 +1910,20 @@
1793
1910
  this.declarations.set(declaration.nam, declaration);
1794
1911
  return this;
1795
1912
  }
1796
- // declaration.chi = declaration.chi.reduce((acc: Token[], token: Token) => {
1797
- //
1798
- // if (this.config.types.includes(token.typ) || ('0' == (<DimensionToken>token).chi && (
1799
- // this.config.types.includes('Length') ||
1800
- // this.config.types.includes('Angle') ||
1801
- // this.config.types.includes('Dimension'))) || (token.typ == 'Iden' && this.config.keywords.includes(token.chi))) {
1802
- //
1803
- // acc.push(token);
1804
- // }
1805
- //
1806
- // return acc;
1807
- // }, <Token[]>[]);
1808
- this.declarations.set(declaration.nam, declaration);
1809
1913
  }
1914
+ this.declarations.set(declaration.nam, declaration);
1810
1915
  return this;
1811
1916
  }
1917
+ isShortHand() {
1918
+ if (this.declarations.has(this.config.shorthand)) {
1919
+ return this.declarations.size == 1;
1920
+ }
1921
+ return this.config.properties.length == this.declarations.size;
1922
+ }
1812
1923
  [Symbol.iterator]() {
1813
1924
  let iterator;
1814
1925
  const declarations = this.declarations;
1815
- if (declarations.size < this.config.properties.length || this.config.properties.some((property, index) => {
1816
- return !declarations.has(property) || (index > 0 &&
1817
- // @ts-ignore
1818
- declarations.get(property).val.length != declarations.get(this.config.properties[Math.floor(index / 2)]).val.length);
1819
- })) {
1926
+ if (declarations.size < this.config.properties.length) {
1820
1927
  iterator = declarations.values();
1821
1928
  }
1822
1929
  else {
@@ -1874,17 +1981,20 @@
1874
1981
  return acc;
1875
1982
  }, [])
1876
1983
  }][Symbol.iterator]();
1877
- return {
1878
- next() {
1879
- return iterator.next();
1880
- }
1881
- };
1984
+ // return {
1985
+ // next() {
1986
+ //
1987
+ // return iterator.next();
1988
+ // }
1989
+ // }
1882
1990
  }
1883
- return {
1884
- next() {
1885
- return iterator.next();
1886
- }
1887
- };
1991
+ return iterator;
1992
+ // return {
1993
+ // next() {
1994
+ //
1995
+ // return iterator.next();
1996
+ // }
1997
+ // }
1888
1998
  }
1889
1999
  }
1890
2000
 
@@ -1899,34 +2009,7 @@
1899
2009
  return false;
1900
2010
  }
1901
2011
 
1902
- function getTokenType(val) {
1903
- if (val == 'transparent' || val == 'currentcolor') {
1904
- return {
1905
- typ: 'Color',
1906
- val,
1907
- kin: 'lit'
1908
- };
1909
- }
1910
- if (val.endsWith('%')) {
1911
- return {
1912
- typ: 'Perc',
1913
- val: val.slice(0, -1)
1914
- };
1915
- }
1916
- return {
1917
- typ: isNumber(val) ? 'Number' : 'Iden',
1918
- val
1919
- };
1920
- }
1921
- function parseString(val) {
1922
- return val.split(/\s/).map(getTokenType).reduce((acc, curr) => {
1923
- if (acc.length > 0) {
1924
- acc.push({ typ: 'Whitespace' });
1925
- }
1926
- acc.push(curr);
1927
- return acc;
1928
- }, []);
1929
- }
2012
+ const propertiesConfig = getConfig();
1930
2013
  class PropertyMap {
1931
2014
  config;
1932
2015
  declarations;
@@ -1941,7 +2024,7 @@
1941
2024
  }
1942
2025
  add(declaration) {
1943
2026
  if (declaration.nam == this.config.shorthand) {
1944
- this.declarations.clear();
2027
+ this.declarations = new Map;
1945
2028
  this.declarations.set(declaration.nam, declaration);
1946
2029
  }
1947
2030
  else {
@@ -2018,8 +2101,7 @@
2018
2101
  const defaults = parseString(props.default[0]);
2019
2102
  if (!(property in tokens)) {
2020
2103
  tokens[property] = [
2021
- [...defaults
2022
- ]
2104
+ [...defaults]
2023
2105
  ];
2024
2106
  }
2025
2107
  else {
@@ -2053,145 +2135,229 @@
2053
2135
  }, new Map);
2054
2136
  }
2055
2137
  }
2056
- this.declarations.set(declaration.nam, declaration);
2138
+ // @ts-ignore
2139
+ const config = propertiesConfig.properties[declaration.nam];
2140
+ let property = declaration.nam;
2141
+ if (config != null) {
2142
+ property = config.shorthand;
2143
+ let value = this.declarations.get(property);
2144
+ if (!(value instanceof PropertySet)) {
2145
+ // @ts-ignore
2146
+ this.declarations.set(property, new PropertySet(propertiesConfig.properties[config.shorthand]));
2147
+ // Token[]
2148
+ if (value != null) {
2149
+ // @ts-ignore
2150
+ this.declarations.get(property).add(value);
2151
+ }
2152
+ }
2153
+ this.declarations.get(property).add(declaration);
2154
+ }
2155
+ else {
2156
+ this.declarations.set(declaration.nam, declaration);
2157
+ }
2057
2158
  }
2058
2159
  return this;
2059
2160
  }
2060
2161
  [Symbol.iterator]() {
2061
- let requiredCount = Object.keys(this.config.properties).reduce((acc, curr) => this.declarations.has(curr) && this.config.properties[curr].required ? ++acc : acc, 0);
2062
- if (requiredCount == 0) {
2063
- requiredCount = this.declarations.size;
2064
- }
2065
- if (requiredCount < this.requiredCount) {
2066
- // if (this.declarations.size == 1 && this.declarations.has(this.config.shorthand)) {
2067
- //
2068
- // this.declarations
2069
- // }
2070
- return this.declarations.values();
2071
- }
2072
- let count = 0;
2073
- const separator = this.config.separator;
2074
- const tokens = {};
2075
- // @ts-ignore
2076
- const valid = Object.entries(this.config.properties).reduce((acc, curr) => {
2077
- if (!this.declarations.has(curr[0])) {
2078
- if (curr[1].required) {
2079
- acc.push(curr[0]);
2162
+ let iterable;
2163
+ let requiredCount = 0;
2164
+ let property;
2165
+ let isShorthand = true;
2166
+ for (property of Object.keys(this.config.properties)) {
2167
+ if (this.config.properties[property].required) {
2168
+ if (!this.declarations.has(property)) {
2169
+ isShorthand = false;
2170
+ break;
2080
2171
  }
2081
- return acc;
2082
- }
2083
- let current = 0;
2084
- const props = this.config.properties[curr[0]];
2085
- // @ts-ignore
2086
- for (const val of this.declarations.get(curr[0]).val) {
2087
- if (separator != null && separator.typ == val.typ && eq(separator, val)) {
2088
- current++;
2089
- if (tokens[curr[0]].length == current) {
2090
- tokens[curr[0]].push([]);
2172
+ else {
2173
+ const val = this.declarations.get(property);
2174
+ if (val instanceof PropertySet && !val.isShortHand()) {
2175
+ isShorthand = false;
2176
+ break;
2091
2177
  }
2092
- continue;
2093
- }
2094
- if (val.typ == 'Whitespace' || val.typ == 'Comment') {
2095
- continue;
2096
- }
2097
- if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(val, props.separator)) {
2098
- continue;
2099
- }
2100
- if (matchType(val, curr[1])) {
2101
- if (!(curr[0] in tokens)) {
2102
- tokens[curr[0]] = [[]];
2178
+ else {
2179
+ requiredCount++;
2103
2180
  }
2104
- // is default value
2105
- tokens[curr[0]][current].push(val);
2106
- continue;
2107
2181
  }
2108
- acc.push(curr[0]);
2109
- break;
2110
- }
2111
- if (count == 0) {
2112
- count = current;
2113
2182
  }
2114
- return acc;
2115
- }, []);
2116
- if (valid.length > 0 || Object.values(tokens).every(v => v.every(v => v.length == count))) {
2117
- return this.declarations.values();
2118
2183
  }
2119
- const values = Object.entries(tokens).reduce((acc, curr) => {
2120
- const props = this.config.properties[curr[0]];
2121
- for (let i = 0; i < curr[1].length; i++) {
2122
- if (acc.length == i) {
2123
- acc.push([]);
2124
- }
2125
- let values = curr[1][i].reduce((acc, curr) => {
2126
- if (acc.length > 0) {
2127
- acc.push({ typ: 'Whitespace' });
2184
+ if (requiredCount == 0) {
2185
+ requiredCount = this.declarations.size;
2186
+ }
2187
+ if (!isShorthand || requiredCount < this.requiredCount) {
2188
+ // @ts-ignore
2189
+ iterable = this.declarations.values();
2190
+ }
2191
+ else {
2192
+ let count = 0;
2193
+ const separator = this.config.separator;
2194
+ const tokens = {};
2195
+ // @ts-ignore
2196
+ /* const valid: string[] =*/ Object.entries(this.config.properties).reduce((acc, curr) => {
2197
+ if (!this.declarations.has(curr[0])) {
2198
+ if (curr[1].required) {
2199
+ acc.push(curr[0]);
2128
2200
  }
2129
- acc.push(curr);
2130
2201
  return acc;
2131
- }, []);
2132
- if (props.default.includes(curr[1][i].reduce((acc, curr) => acc + renderToken(curr) + ' ', '').trimEnd())) {
2133
- continue;
2134
2202
  }
2135
- values = values.filter((val) => {
2203
+ let current = 0;
2204
+ const props = this.config.properties[curr[0]];
2205
+ const declaration = this.declarations.get(curr[0]);
2206
+ // @ts-ignore
2207
+ for (const val of (declaration instanceof PropertySet ? [...declaration][0] : declaration).val) {
2208
+ if (separator != null && separator.typ == val.typ && eq(separator, val)) {
2209
+ current++;
2210
+ if (tokens[curr[0]].length == current) {
2211
+ tokens[curr[0]].push([]);
2212
+ }
2213
+ continue;
2214
+ }
2136
2215
  if (val.typ == 'Whitespace' || val.typ == 'Comment') {
2137
- return false;
2216
+ continue;
2138
2217
  }
2139
- return !(val.typ == 'Iden' && props.default.includes(val.val));
2140
- });
2141
- if (values.length > 0) {
2142
- if ('mapping' in props) {
2143
- // @ts-ignore
2144
- if (!('constraints' in props) || !('max' in props.constraints) || values.length <= props.constraints.mapping.max) {
2145
- let i = values.length;
2146
- while (i--) {
2218
+ if (props.multiple && props.separator != null && props.separator.typ == val.typ && eq(props.separator, val)) {
2219
+ continue;
2220
+ }
2221
+ if (matchType(val, curr[1])) {
2222
+ if (!(curr[0] in tokens)) {
2223
+ tokens[curr[0]] = [[]];
2224
+ }
2225
+ // is default value
2226
+ tokens[curr[0]][current].push(val);
2227
+ // continue;
2228
+ }
2229
+ else {
2230
+ acc.push(curr[0]);
2231
+ break;
2232
+ }
2233
+ }
2234
+ if (count == 0) {
2235
+ count = current;
2236
+ }
2237
+ return acc;
2238
+ }, []);
2239
+ count++;
2240
+ if (!Object.values(tokens).every(v => v.length == count)) {
2241
+ // @ts-ignore
2242
+ iterable = this.declarations.values();
2243
+ }
2244
+ else {
2245
+ const values = Object.entries(tokens).reduce((acc, curr) => {
2246
+ const props = this.config.properties[curr[0]];
2247
+ for (let i = 0; i < curr[1].length; i++) {
2248
+ if (acc.length == i) {
2249
+ acc.push([]);
2250
+ }
2251
+ let values = curr[1][i].reduce((acc, curr) => {
2252
+ if (acc.length > 0) {
2253
+ acc.push({ typ: 'Whitespace' });
2254
+ }
2255
+ acc.push(curr);
2256
+ return acc;
2257
+ }, []);
2258
+ // @todo remove renderToken call
2259
+ if (props.default.includes(curr[1][i].reduce((acc, curr) => acc + renderToken(curr) + ' ', '').trimEnd())) {
2260
+ continue;
2261
+ }
2262
+ let doFilterDefault = true;
2263
+ if (curr[0] in propertiesConfig.properties) {
2264
+ for (let v of values) {
2265
+ if (!['Whitespace', 'Comment', 'Iden'].includes(v.typ)
2266
+ || (v.typ == 'Iden' && !this.config.properties[curr[0]].default.includes(v.val))) {
2267
+ doFilterDefault = false;
2268
+ break;
2269
+ }
2270
+ }
2271
+ }
2272
+ // remove default values
2273
+ values = values.filter((val) => {
2274
+ if (val.typ == 'Whitespace' || val.typ == 'Comment') {
2275
+ return false;
2276
+ }
2277
+ return !doFilterDefault || !(val.typ == 'Iden' && props.default.includes(val.val));
2278
+ });
2279
+ if (values.length > 0) {
2280
+ if ('mapping' in props) {
2147
2281
  // @ts-ignore
2148
- if (values[i].typ == 'Iden' && values[i].val in props.mapping) {
2149
- // @ts-ignore
2150
- values.splice(i, 1, ...parseString(props.mapping[values[i].val]));
2282
+ if (!('constraints' in props) || !('max' in props.constraints) || values.length <= props.constraints.mapping.max) {
2283
+ let i = values.length;
2284
+ while (i--) {
2285
+ // @ts-ignore
2286
+ if (values[i].typ == 'Iden' && values[i].val in props.mapping) {
2287
+ // @ts-ignore
2288
+ values.splice(i, 1, ...parseString(props.mapping[values[i].val]));
2289
+ }
2290
+ }
2151
2291
  }
2152
2292
  }
2293
+ if ('prefix' in props) {
2294
+ // @ts-ignore
2295
+ acc[i].push({ ...props.prefix });
2296
+ }
2297
+ else if (acc[i].length > 0) {
2298
+ acc[i].push({ typ: 'Whitespace' });
2299
+ }
2300
+ acc[i].push(...values.reduce((acc, curr) => {
2301
+ if (acc.length > 0) {
2302
+ // @ts-ignore
2303
+ acc.push({ ...(props.separator ?? { typ: 'Whitespace' }) });
2304
+ }
2305
+ // @ts-ignore
2306
+ acc.push(curr);
2307
+ return acc;
2308
+ }, []));
2153
2309
  }
2154
2310
  }
2155
- if ('prefix' in props) {
2156
- // @ts-ignore
2157
- acc[i].push({ ...props.prefix });
2311
+ return acc;
2312
+ }, []).reduce((acc, curr) => {
2313
+ if (acc.length > 0) {
2314
+ acc.push({ ...separator });
2158
2315
  }
2159
- else if (acc[i].length > 0) {
2160
- acc[i].push({ typ: 'Whitespace' });
2316
+ if (curr.length == 0 && this.config.default.length > 0) {
2317
+ curr.push(...parseString(this.config.default[0]).reduce((acc, curr) => {
2318
+ if (acc.length > 0) {
2319
+ acc.push({ typ: 'Whitespace' });
2320
+ }
2321
+ acc.push(curr);
2322
+ return acc;
2323
+ }, []));
2161
2324
  }
2162
- acc[i].push(...values.reduce((acc, curr) => {
2163
- if (acc.length > 0) {
2325
+ acc.push(...curr);
2326
+ return acc;
2327
+ }, []);
2328
+ iterable = [{
2329
+ typ: 'Declaration',
2330
+ nam: this.config.shorthand,
2331
+ val: values
2332
+ }][Symbol.iterator]();
2333
+ }
2334
+ }
2335
+ const iterators = [];
2336
+ return {
2337
+ // @ts-ignore
2338
+ next() {
2339
+ let v = iterable.next();
2340
+ while (v.done || v.value instanceof PropertySet) {
2341
+ if (v.value instanceof PropertySet) {
2342
+ // @ts-ignore
2343
+ iterators.push(iterable);
2344
+ iterable = v.value[Symbol.iterator]();
2345
+ v = iterable.next();
2346
+ }
2347
+ if (v.done) {
2348
+ if (iterators.length > 0) {
2164
2349
  // @ts-ignore
2165
- acc.push({ ...(props.separator ?? { typ: 'Whitespace' }) });
2350
+ iterable = iterators.pop();
2351
+ v = iterable.next();
2166
2352
  }
2167
- // @ts-ignore
2168
- acc.push(curr);
2169
- return acc;
2170
- }, []));
2353
+ if (v.done && iterators.length == 0) {
2354
+ break;
2355
+ }
2356
+ }
2171
2357
  }
2358
+ return v;
2172
2359
  }
2173
- return acc;
2174
- }, []).reduce((acc, curr) => {
2175
- if (acc.length > 0) {
2176
- acc.push({ ...separator });
2177
- }
2178
- if (curr.length == 0) {
2179
- curr.push(...this.config.default[0].split(/\s/).map(getTokenType).reduce((acc, curr) => {
2180
- if (acc.length > 0) {
2181
- acc.push({ typ: 'Whitespace' });
2182
- }
2183
- acc.push(curr);
2184
- return acc;
2185
- }, []));
2186
- }
2187
- acc.push(...curr);
2188
- return acc;
2189
- }, []);
2190
- return [{
2191
- typ: 'Declaration',
2192
- nam: this.config.shorthand,
2193
- val: values
2194
- }][Symbol.iterator]();
2360
+ };
2195
2361
  }
2196
2362
  }
2197
2363
 
@@ -2201,33 +2367,61 @@
2201
2367
  constructor() {
2202
2368
  this.declarations = new Map;
2203
2369
  }
2370
+ set(nam, value) {
2371
+ return this.add({ typ: 'Declaration', nam, val: Array.isArray(value) ? value : parseString(String(value)) });
2372
+ }
2204
2373
  add(declaration) {
2205
2374
  if (declaration.typ != 'Declaration') {
2206
2375
  this.declarations.set(Number(Math.random().toString().slice(2)).toString(36), declaration);
2207
2376
  return this;
2208
2377
  }
2209
- const propertyName = declaration.nam;
2378
+ let propertyName = declaration.nam;
2379
+ let shortHandType;
2380
+ let shorthand;
2210
2381
  if (propertyName in config.properties) {
2211
2382
  // @ts-ignore
2212
- const shorthand = config.properties[propertyName].shorthand;
2383
+ if ('map' in config.properties[propertyName]) {
2384
+ shortHandType = 'map';
2385
+ // @ts-ignore
2386
+ shorthand = config.properties[propertyName].map;
2387
+ }
2388
+ else {
2389
+ shortHandType = 'set';
2390
+ // @ts-ignore
2391
+ shorthand = config.properties[propertyName].shorthand;
2392
+ }
2393
+ }
2394
+ else if (propertyName in config.map) {
2395
+ shortHandType = 'map';
2396
+ // @ts-ignore
2397
+ shorthand = config.map[propertyName].shorthand;
2398
+ }
2399
+ // @ts-ignore
2400
+ if (shortHandType == 'map') {
2401
+ // @ts-ignore
2213
2402
  if (!this.declarations.has(shorthand)) {
2214
2403
  // @ts-ignore
2215
- this.declarations.set(shorthand, new PropertySet(config.properties[shorthand]));
2404
+ this.declarations.set(shorthand, new PropertyMap(config.map[shorthand]));
2216
2405
  }
2406
+ // @ts-ignore
2217
2407
  this.declarations.get(shorthand).add(declaration);
2218
- return this;
2408
+ // return this;
2219
2409
  }
2220
- if (propertyName in config.map) {
2410
+ // @ts-ignore
2411
+ else if (shortHandType == 'set') {
2221
2412
  // @ts-ignore
2222
- const shorthand = config.map[propertyName].shorthand;
2413
+ // const shorthand: string = <string>config.properties[propertyName].shorthand;
2223
2414
  if (!this.declarations.has(shorthand)) {
2224
2415
  // @ts-ignore
2225
- this.declarations.set(shorthand, new PropertyMap(config.map[shorthand]));
2416
+ this.declarations.set(shorthand, new PropertySet(config.properties[shorthand]));
2226
2417
  }
2418
+ // @ts-ignore
2227
2419
  this.declarations.get(shorthand).add(declaration);
2228
- return this;
2420
+ // return this;
2421
+ }
2422
+ else {
2423
+ this.declarations.set(propertyName, declaration);
2229
2424
  }
2230
- this.declarations.set(propertyName, declaration);
2231
2425
  return this;
2232
2426
  }
2233
2427
  [Symbol.iterator]() {
@@ -2256,8 +2450,300 @@
2256
2450
  }
2257
2451
  }
2258
2452
 
2259
- const configuration = getConfig();
2260
- function deduplicate(ast, options = {}, recursive = false) {
2453
+ const combinators = ['+', '>', '~'];
2454
+ const notEndingWith = ['(', '['].concat(combinators);
2455
+ function minify(ast, options = {}, recursive = false) {
2456
+ function wrapNodes(previous, node, match, ast, i, nodeIndex) {
2457
+ // @ts-ignore
2458
+ let pSel = match.selector1.reduce(reducer, []).join(',');
2459
+ // @ts-ignore
2460
+ let nSel = match.selector2.reduce(reducer, []).join(',');
2461
+ // @ts-ignore
2462
+ const wrapper = { ...previous, chi: [], sel: match.match.reduce(reducer, []).join(',') };
2463
+ // @ts-ignore
2464
+ Object.defineProperty(wrapper, 'raw', {
2465
+ enumerable: false,
2466
+ writable: true,
2467
+ // @ts-ignore
2468
+ value: match.match.map(t => t.slice())
2469
+ });
2470
+ if (pSel == '&' || pSel === '') {
2471
+ // @ts-ignore
2472
+ wrapper.chi.push(...previous.chi);
2473
+ // @ts-ignore
2474
+ if ((nSel == '&' || nSel === '') && hasOnlyDeclarations(previous)) {
2475
+ // @ts-ignore
2476
+ wrapper.chi.push(...node.chi);
2477
+ }
2478
+ else {
2479
+ // @ts-ignore
2480
+ wrapper.chi.push(node);
2481
+ }
2482
+ }
2483
+ else {
2484
+ // @ts-ignore
2485
+ wrapper.chi.push(previous, node);
2486
+ }
2487
+ // @ts-ignore
2488
+ ast.chi.splice(i, 1, wrapper);
2489
+ // @ts-ignore
2490
+ ast.chi.splice(nodeIndex, 1);
2491
+ // @ts-ignore
2492
+ previous.sel = pSel;
2493
+ // @ts-ignore
2494
+ previous.raw = match.selector1;
2495
+ // @ts-ignore
2496
+ node.sel = nSel;
2497
+ // @ts-ignore
2498
+ node.raw = match.selector2;
2499
+ reduceRuleSelector(wrapper);
2500
+ return wrapper;
2501
+ }
2502
+ function reducer(acc, curr, index, array) {
2503
+ // trim :is()
2504
+ if (array.length == 1 && array[0][0] == ':is(' && array[0].at(-1) == ')') {
2505
+ curr = curr.slice(1, -1);
2506
+ }
2507
+ if (curr[0] == '&') {
2508
+ if (curr[1] == ' ' && !isIdent(curr[2]) && !isFunction(curr[2])) {
2509
+ curr.splice(0, 2);
2510
+ }
2511
+ else if (combinators.includes(curr[1])) {
2512
+ curr.splice(0, 1);
2513
+ }
2514
+ }
2515
+ else if (ast.typ == 'Rule' && (isIdent(curr[0]) || isFunction(curr[0]))) {
2516
+ curr.unshift('&', ' ');
2517
+ }
2518
+ acc.push(curr.join(''));
2519
+ return acc;
2520
+ }
2521
+ function diff(n1, n2, options = {}) {
2522
+ let node1 = n1;
2523
+ let node2 = n2;
2524
+ let exchanged = false;
2525
+ if (node1.chi.length > node2.chi.length) {
2526
+ const t = node1;
2527
+ node1 = node2;
2528
+ node2 = t;
2529
+ exchanged = true;
2530
+ }
2531
+ let i = node1.chi.length;
2532
+ let j = node2.chi.length;
2533
+ if (i == 0 || j == 0) {
2534
+ // @ts-ignore
2535
+ return null;
2536
+ }
2537
+ // @ts-ignore
2538
+ const raw1 = node1.raw;
2539
+ // @ts-ignore
2540
+ const raw2 = node2.raw;
2541
+ // @ts-ignore
2542
+ node1 = { ...node1, chi: node1.chi.slice() };
2543
+ node2 = { ...node2, chi: node2.chi.slice() };
2544
+ if (raw1 != null) {
2545
+ Object.defineProperty(node1, 'raw', { enumerable: false, writable: true, value: raw1 });
2546
+ }
2547
+ if (raw2 != null) {
2548
+ Object.defineProperty(node2, 'raw', { enumerable: false, writable: true, value: raw2 });
2549
+ }
2550
+ const intersect = [];
2551
+ while (i--) {
2552
+ if (node1.chi[i].typ == 'Comment') {
2553
+ continue;
2554
+ }
2555
+ j = node2.chi.length;
2556
+ if (j == 0) {
2557
+ break;
2558
+ }
2559
+ while (j--) {
2560
+ if (node2.chi[j].typ == 'Comment') {
2561
+ continue;
2562
+ }
2563
+ if (node1.chi[i].nam == node2.chi[j].nam) {
2564
+ if (eq(node1.chi[i], node2.chi[j])) {
2565
+ intersect.push(node1.chi[i]);
2566
+ node1.chi.splice(i, 1);
2567
+ node2.chi.splice(j, 1);
2568
+ break;
2569
+ }
2570
+ }
2571
+ }
2572
+ }
2573
+ // @ts-ignore
2574
+ const result = (intersect.length == 0 ? null : {
2575
+ ...node1,
2576
+ // @ts-ignore
2577
+ sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(','),
2578
+ chi: intersect.reverse()
2579
+ });
2580
+ 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)) {
2581
+ // @ts-ignore
2582
+ return null;
2583
+ }
2584
+ return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node2 : node2 };
2585
+ }
2586
+ function matchSelectors(selector1, selector2, parentType) {
2587
+ let match = [[]];
2588
+ 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));
2589
+ let i = 0;
2590
+ let k;
2591
+ let l;
2592
+ let token;
2593
+ let matching = true;
2594
+ let matchFunction = 0;
2595
+ let inAttr = 0;
2596
+ for (; i < j; i++) {
2597
+ k = 0;
2598
+ token = selector1[0][i];
2599
+ for (; k < selector1.length; k++) {
2600
+ if (selector1[k][i] != token) {
2601
+ matching = false;
2602
+ break;
2603
+ }
2604
+ }
2605
+ if (matching) {
2606
+ l = 0;
2607
+ for (; l < selector2.length; l++) {
2608
+ if (selector2[l][i] != token) {
2609
+ matching = false;
2610
+ break;
2611
+ }
2612
+ }
2613
+ }
2614
+ if (!matching) {
2615
+ break;
2616
+ }
2617
+ if (token == ',') {
2618
+ match.push([]);
2619
+ }
2620
+ else {
2621
+ if (token.endsWith('(')) {
2622
+ matchFunction++;
2623
+ }
2624
+ if (token.endsWith('[')) {
2625
+ inAttr++;
2626
+ }
2627
+ else if (token == ')') {
2628
+ matchFunction--;
2629
+ }
2630
+ else if (token == ']') {
2631
+ inAttr--;
2632
+ }
2633
+ match.at(-1).push(token);
2634
+ }
2635
+ }
2636
+ // invalid function
2637
+ if (matchFunction != 0 || inAttr != 0) {
2638
+ return null;
2639
+ }
2640
+ if (parentType != 'Rule') {
2641
+ for (const part of match) {
2642
+ if (part.length > 0 && combinators.includes(part[0].charAt(0))) {
2643
+ return null;
2644
+ }
2645
+ }
2646
+ }
2647
+ if (match.length > 1) {
2648
+ console.error(`unsupported multilevel matching`);
2649
+ console.error({ match, selector1, selector2 });
2650
+ return null;
2651
+ }
2652
+ for (const part of match) {
2653
+ while (part.length > 0) {
2654
+ const token = part.at(-1);
2655
+ if (token == ' ' || combinators.includes(token) || notEndingWith.includes(token.at(-1))) {
2656
+ part.pop();
2657
+ continue;
2658
+ }
2659
+ break;
2660
+ }
2661
+ }
2662
+ if (match.every(t => t.length == 0)) {
2663
+ return null;
2664
+ }
2665
+ if (eq([['&']], match)) {
2666
+ return null;
2667
+ }
2668
+ function reduce(acc, curr) {
2669
+ if (acc === null) {
2670
+ return null;
2671
+ }
2672
+ let hasCompoundSelector = true;
2673
+ curr = curr.slice(match[0].length);
2674
+ while (curr.length > 0) {
2675
+ if (curr[0] == ' ') {
2676
+ hasCompoundSelector = false;
2677
+ curr.unshift('&');
2678
+ continue;
2679
+ }
2680
+ break;
2681
+ }
2682
+ // invalid function match
2683
+ if (curr.length > 0 && curr[0].endsWith('(') && curr.at(-1) != ')') {
2684
+ return null;
2685
+ }
2686
+ if (curr.length == 1 && combinators.includes(curr[0].charAt(0))) {
2687
+ return null;
2688
+ }
2689
+ if (hasCompoundSelector && curr.length > 0) {
2690
+ hasCompoundSelector = !['&'].concat(combinators).includes(curr[0].charAt(0));
2691
+ }
2692
+ if (curr[0] == ':is(') {
2693
+ let inFunction = 0;
2694
+ let canReduce = true;
2695
+ const isCompound = curr.reduce((acc, token, index) => {
2696
+ if (index == 0) {
2697
+ inFunction++;
2698
+ canReduce = curr[1] == '&';
2699
+ }
2700
+ else if (token.endsWith('(')) {
2701
+ if (inFunction == 0) {
2702
+ canReduce = false;
2703
+ }
2704
+ inFunction++;
2705
+ }
2706
+ else if (token == ')') {
2707
+ inFunction--;
2708
+ }
2709
+ else if (token == ',') {
2710
+ if (!canReduce) {
2711
+ canReduce = curr[index + 1] == '&';
2712
+ }
2713
+ acc.push([]);
2714
+ }
2715
+ else
2716
+ acc.at(-1)?.push(token);
2717
+ return acc;
2718
+ }, [[]]);
2719
+ if (inFunction > 0) {
2720
+ canReduce = false;
2721
+ }
2722
+ if (canReduce) {
2723
+ curr = isCompound.reduce((acc, curr) => {
2724
+ if (acc.length > 0) {
2725
+ acc.push(',');
2726
+ }
2727
+ acc.push(...curr);
2728
+ return acc;
2729
+ }, []);
2730
+ }
2731
+ }
2732
+ // @todo: check hasCompoundSelector && curr[0] == '&' && curr[1] == ' '
2733
+ acc.push(match.length == 0 ? ['&'] : (hasCompoundSelector && curr[0] != '&' && (curr.length == 0 || !combinators.includes(curr[0].charAt(0))) ? ['&'].concat(curr) : curr));
2734
+ return acc;
2735
+ }
2736
+ // @ts-ignore
2737
+ selector1 = selector1.reduce(reduce, []);
2738
+ // @ts-ignore
2739
+ selector2 = selector2.reduce(reduce, []);
2740
+ return selector1 == null || selector2 == null ? null : {
2741
+ eq: eq(selector1, selector2),
2742
+ match,
2743
+ selector1,
2744
+ selector2
2745
+ };
2746
+ }
2261
2747
  // @ts-ignore
2262
2748
  if (('chi' in ast) && ast.chi?.length > 0) {
2263
2749
  let i = 0;
@@ -2272,6 +2758,14 @@
2272
2758
  }
2273
2759
  // @ts-ignore
2274
2760
  node = ast.chi[i];
2761
+ // @ts-ignore
2762
+ if (previous == node) {
2763
+ // console.error('idem!');
2764
+ // @ts-ignore
2765
+ ast.chi.splice(i, 1);
2766
+ i--;
2767
+ continue;
2768
+ }
2275
2769
  if (node.typ == 'AtRule' && node.nam == 'font-face') {
2276
2770
  continue;
2277
2771
  }
@@ -2284,120 +2778,118 @@
2284
2778
  // @ts-ignore
2285
2779
  if (node.typ == 'Rule') {
2286
2780
  reduceRuleSelector(node);
2781
+ let wrapper;
2782
+ let match;
2287
2783
  // @ts-ignore
2288
- if (options.nestingRules && node.raw != null && previous?.raw != null && node.raw.length == 1 && previous.raw.length == 1) {
2289
- const match = [];
2784
+ if (options.nestingRules) {
2290
2785
  // @ts-ignore
2291
- while (node.raw[0].length > 0 && previous.raw[0].length > 0) {
2292
- // @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) {
2786
+ if (previous?.typ == 'Rule') {
2302
2787
  // @ts-ignore
2303
- const wrapper = { ...previous, chi: [] };
2788
+ reduceRuleSelector(previous);
2304
2789
  // @ts-ignore
2305
- if (previous.raw[0].length == 0) {
2306
- // @ts-ignore
2307
- wrapper.chi.push(...previous.chi);
2308
- }
2309
- else {
2310
- // @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);
2317
- }
2790
+ match = matchSelectors(previous.raw, node.raw, ast.typ);
2318
2791
  // @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 {
2792
+ if (match != null) {
2343
2793
  // @ts-ignore
2344
- node.sel = node.raw.reduce((acc, curr) => {
2345
- acc.push(curr.join(''));
2346
- return acc;
2347
- }, []).join(',');
2794
+ wrapper = wrapNodes(previous, node, match, ast, i, nodeIndex);
2795
+ nodeIndex = i - 1;
2348
2796
  // @ts-ignore
2349
- wrapper.chi.push(node);
2797
+ previous = ast.chi[nodeIndex];
2350
2798
  }
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);
2799
+ }
2800
+ // @ts-ignore
2801
+ if (wrapper != null) {
2356
2802
  // @ts-ignore
2357
2803
  while (i < ast.chi.length) {
2358
2804
  // @ts-ignore
2359
2805
  const nextNode = ast.chi[i];
2360
2806
  // @ts-ignore
2361
- if (nextNode.typ != 'Rule' || nextNode.raw == null) {
2807
+ if (nextNode.typ != 'Rule') {
2808
+ // i--;
2809
+ // previous = wrapper;
2810
+ // nodeIndex = i;
2362
2811
  break;
2363
2812
  }
2364
2813
  reduceRuleSelector(nextNode);
2365
2814
  // @ts-ignore
2366
- if (nextNode.raw.length != 1 || !eq(wrapper.raw[0], nextNode.raw[0].slice(0, wrapper.raw[0].length))) {
2815
+ match = matchSelectors(wrapper.raw, nextNode.raw, ast.typ);
2816
+ // @ts-ignore
2817
+ if (match == null) {
2367
2818
  break;
2368
2819
  }
2369
2820
  // @ts-ignore
2370
- nextNode.raw[0].splice(0, wrapper.raw[0].length);
2821
+ wrapper = wrapNodes(wrapper, nextNode, match, ast, i, nodeIndex);
2822
+ }
2823
+ nodeIndex = --i;
2824
+ // @ts-ignore
2825
+ previous = ast.chi[nodeIndex];
2826
+ minify(wrapper, options, recursive);
2827
+ continue;
2828
+ }
2829
+ // @ts-ignore
2830
+ else if (node.optimized != null &&
2831
+ // @ts-ignore
2832
+ node.optimized.match &&
2833
+ // @ts-ignore
2834
+ node.optimized.selector.length > 1) {
2835
+ // @ts-ignore
2836
+ wrapper = { ...node, chi: [], sel: node.optimized.optimized[0] };
2837
+ // @ts-ignore
2838
+ Object.defineProperty(wrapper, 'raw', {
2839
+ enumerable: false,
2840
+ writable: true,
2371
2841
  // @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;
2842
+ value: [[node.optimized.optimized[0]]]
2843
+ });
2844
+ // @ts-ignore
2845
+ node.sel = node.optimized.selector.reduce(reducer, []).join(',');
2846
+ // @ts-ignore
2847
+ node.raw = node.optimized.selector.slice();
2848
+ // @ts-ignore
2849
+ wrapper.chi.push(node);
2850
+ // @ts-ignore
2851
+ ast.chi.splice(i, 1, wrapper);
2852
+ node = wrapper;
2853
+ }
2854
+ }
2855
+ // @ts-ignore
2856
+ else if (node.optimized?.match) {
2857
+ let wrap = true;
2858
+ // @ts-ignore
2859
+ const selector = node.optimized.selector.reduce((acc, curr) => {
2860
+ if (curr[0] == '&') {
2861
+ if (curr[1] == ' ') {
2862
+ curr.splice(0, 2);
2863
+ }
2864
+ else {
2865
+ if (ast.typ != 'Rule' && combinators.includes(curr[1])) {
2866
+ wrap = false;
2380
2867
  }
2381
2868
  else {
2382
- // @ts-ignore
2383
- nextNode.raw[0].push('&');
2869
+ curr.splice(0, 1);
2384
2870
  }
2385
2871
  }
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
2872
  }
2395
- deduplicateRule(wrapper);
2396
- nodeIndex = --i;
2873
+ else if (combinators.includes(curr[0])) {
2874
+ curr.unshift('&');
2875
+ wrap = false;
2876
+ }
2397
2877
  // @ts-ignore
2398
- previous = ast.chi[i];
2399
- continue;
2878
+ acc.push(curr);
2879
+ return acc;
2880
+ }, []);
2881
+ if (!wrap) {
2882
+ wrap = selector.some(s => s[0] != '&');
2400
2883
  }
2884
+ const rule = selector.map(s => {
2885
+ if (s[0] == '&') {
2886
+ // @ts-ignore
2887
+ s[0] = node.optimized.optimized[0];
2888
+ }
2889
+ return s.join('');
2890
+ }).join(',');
2891
+ // @ts-ignore
2892
+ node.sel = wrap ? node.optimized.optimized[0] + `:is(${rule})` : rule;
2401
2893
  }
2402
2894
  }
2403
2895
  // @ts-ignore
@@ -2427,10 +2919,11 @@
2427
2919
  ast.chi.splice(nodeIndex, 1);
2428
2920
  // @ts-ignore
2429
2921
  if (hasDeclaration(node)) {
2430
- deduplicateRule(node);
2922
+ // @ts-ignore
2923
+ minifyRule(node);
2431
2924
  }
2432
2925
  else {
2433
- deduplicate(node, options, recursive);
2926
+ minify(node, options, recursive);
2434
2927
  }
2435
2928
  i--;
2436
2929
  previous = node;
@@ -2471,10 +2964,11 @@
2471
2964
  if (recursive && previous != node) {
2472
2965
  // @ts-ignore
2473
2966
  if (hasDeclaration(previous)) {
2474
- deduplicateRule(previous);
2967
+ // @ts-ignore
2968
+ minifyRule(previous);
2475
2969
  }
2476
2970
  else {
2477
- deduplicate(previous, options, recursive);
2971
+ minify(previous, options, recursive);
2478
2972
  }
2479
2973
  }
2480
2974
  }
@@ -2485,98 +2979,176 @@
2485
2979
  if (recursive && node != null && ('chi' in node)) {
2486
2980
  // @ts-ignore
2487
2981
  if (node.chi.some(n => n.typ == 'Declaration')) {
2488
- deduplicateRule(node);
2982
+ minifyRule(node);
2489
2983
  }
2490
2984
  else {
2985
+ // @ts-ignore
2491
2986
  if (!(node.typ == 'AtRule' && node.nam != 'font-face')) {
2492
- deduplicate(node, options, recursive);
2987
+ minify(node, options, recursive);
2493
2988
  }
2494
2989
  }
2495
2990
  }
2496
2991
  }
2497
2992
  return ast;
2498
2993
  }
2499
- function hasOnlyDeclarations(node) {
2500
- let k = node.chi.length;
2501
- while (k--) {
2502
- if (node.chi[k].typ == 'Comment') {
2503
- continue;
2504
- }
2505
- return node.chi[k].typ == 'Declaration';
2506
- }
2507
- return true;
2508
- }
2509
- function hasDeclaration(node) {
2510
- // @ts-ignore
2511
- for (let i = 0; i < node.chi?.length; i++) {
2512
- // @ts-ignore
2513
- if (node.chi[i].typ == 'Comment') {
2514
- continue;
2515
- }
2516
- // @ts-ignore
2517
- return node.chi[i].typ == 'Declaration';
2994
+ function reduceSelector(selector) {
2995
+ if (selector.length == 0) {
2996
+ return null;
2518
2997
  }
2519
- return true;
2520
- }
2521
- function deduplicateRule(ast, options = {}) {
2522
- // @ts-ignore
2998
+ const optimized = [];
2999
+ const k = selector.reduce((acc, curr) => acc == 0 ? curr.length : (curr.length == 0 ? acc : Math.min(acc, curr.length)), 0);
3000
+ let i = 0;
3001
+ let j;
3002
+ let match;
3003
+ for (; i < k; i++) {
3004
+ const item = selector[0][i];
3005
+ match = true;
3006
+ for (j = 1; j < selector.length; j++) {
3007
+ if (item != selector[j][i]) {
3008
+ match = false;
3009
+ break;
3010
+ }
3011
+ }
3012
+ if (!match) {
3013
+ break;
3014
+ }
3015
+ optimized.push(item);
3016
+ }
3017
+ while (optimized.length > 0) {
3018
+ const last = optimized.at(-1);
3019
+ if ((last == ' ' || combinators.includes(last))) {
3020
+ optimized.pop();
3021
+ continue;
3022
+ }
3023
+ break;
3024
+ }
3025
+ selector.forEach((selector) => selector.splice(0, optimized.length));
3026
+ // combinator
3027
+ if (combinators.includes(optimized.at(-1))) {
3028
+ const combinator = optimized.pop();
3029
+ selector.forEach(selector => selector.unshift(combinator));
3030
+ }
3031
+ let reducible = optimized.length == 1;
3032
+ if (optimized[0] == '&' && optimized[1] == ' ') {
3033
+ optimized.splice(0, 2);
3034
+ }
3035
+ if (optimized.length == 0 ||
3036
+ (optimized[0].charAt(0) == '&' ||
3037
+ selector.length == 1)) {
3038
+ return {
3039
+ match: false,
3040
+ optimized,
3041
+ selector: selector.map(selector => selector[0] == '&' && selector[1] == ' ' ? selector.slice(2) : selector),
3042
+ reducible: selector.length > 1 && selector.every((selector) => !combinators.includes(selector[0]))
3043
+ };
3044
+ }
3045
+ return {
3046
+ match: true,
3047
+ optimized,
3048
+ selector: selector.reduce((acc, curr) => {
3049
+ let hasCompound = true;
3050
+ if (hasCompound && curr.length > 0) {
3051
+ hasCompound = !['&'].concat(combinators).includes(curr[0].charAt(0));
3052
+ }
3053
+ // @ts-ignore
3054
+ if (hasCompound && curr[0] == ' ') {
3055
+ hasCompound = false;
3056
+ curr.unshift('&');
3057
+ }
3058
+ if (curr.length == 0) {
3059
+ curr.push('&');
3060
+ hasCompound = false;
3061
+ }
3062
+ if (reducible) {
3063
+ const chr = curr[0].charAt(0);
3064
+ // @ts-ignore
3065
+ reducible = chr == '.' || chr == ':' || isIdentStart(chr.codePointAt(0));
3066
+ }
3067
+ acc.push(hasCompound ? ['&'].concat(curr) : curr);
3068
+ return acc;
3069
+ }, []),
3070
+ reducible: selector.every((selector) => !['>', '+', '~', '&'].includes(selector[0]))
3071
+ };
3072
+ }
3073
+ function hasOnlyDeclarations(node) {
3074
+ let k = node.chi.length;
3075
+ while (k--) {
3076
+ if (node.chi[k].typ == 'Comment') {
3077
+ continue;
3078
+ }
3079
+ return node.chi[k].typ == 'Declaration';
3080
+ }
3081
+ return true;
3082
+ }
3083
+ function hasDeclaration(node) {
3084
+ // @ts-ignore
3085
+ for (let i = 0; i < node.chi?.length; i++) {
3086
+ // @ts-ignore
3087
+ if (node.chi[i].typ == 'Comment') {
3088
+ continue;
3089
+ }
3090
+ // @ts-ignore
3091
+ return node.chi[i].typ == 'Declaration';
3092
+ }
3093
+ return true;
3094
+ }
3095
+ function minifyRule(ast) {
3096
+ // @ts-ignore
2523
3097
  if (!('chi' in ast) || ast.chi?.length <= 1) {
2524
3098
  return ast;
2525
3099
  }
2526
3100
  // @ts-ignore
2527
3101
  const j = ast.chi.length;
2528
3102
  let k = 0;
2529
- let map = new Map;
3103
+ let properties = new PropertyList();
2530
3104
  // @ts-ignore
2531
3105
  for (; k < j; k++) {
2532
3106
  // @ts-ignore
2533
3107
  const node = ast.chi[k];
2534
- if (node.typ == 'Comment') {
2535
- // @ts-ignore
2536
- map.set(node, node);
3108
+ if (node.typ == 'Comment' || node.typ == 'Declaration') {
3109
+ properties.add(node);
2537
3110
  continue;
2538
3111
  }
2539
- else if (node.typ != 'Declaration') {
2540
- break;
2541
- }
2542
- if (node.nam in configuration.map ||
2543
- node.nam in configuration.properties) {
2544
- // @ts-ignore
2545
- const shorthand = node.nam in configuration.map ? configuration.map[node.nam].shorthand : configuration.properties[node.nam].shorthand;
2546
- if (!map.has(shorthand)) {
2547
- map.set(shorthand, new PropertyList());
2548
- }
2549
- map.get(shorthand).add(node);
2550
- }
2551
- else {
2552
- map.set(node.nam, node);
2553
- }
2554
- }
2555
- const children = [];
2556
- for (let child of map.values()) {
2557
- if (child instanceof PropertyList) {
2558
- // @ts-ignore
2559
- children.push(...child);
2560
- }
2561
- else {
2562
- // @ts-ignore
2563
- children.push(child);
2564
- }
3112
+ break;
2565
3113
  }
2566
3114
  // @ts-ignore
2567
- ast.chi = children.concat(ast.chi?.slice(k));
3115
+ ast.chi = [...properties].concat(ast.chi.slice(k));
2568
3116
  return ast;
2569
3117
  }
2570
3118
  function splitRule(buffer) {
2571
- const result = [];
3119
+ const result = [[]];
2572
3120
  let str = '';
2573
3121
  for (let i = 0; i < buffer.length; i++) {
2574
3122
  let chr = buffer.charAt(i);
3123
+ if (isWhiteSpace(chr.charCodeAt(0))) {
3124
+ let k = i;
3125
+ while (k + 1 < buffer.length) {
3126
+ if (isWhiteSpace(buffer[k + 1].charCodeAt(0))) {
3127
+ k++;
3128
+ continue;
3129
+ }
3130
+ break;
3131
+ }
3132
+ if (str !== '') {
3133
+ // @ts-ignore
3134
+ result.at(-1).push(str);
3135
+ str = '';
3136
+ }
3137
+ // @ts-ignore
3138
+ if (result.at(-1).length > 0) {
3139
+ // @ts-ignore
3140
+ result.at(-1).push(' ');
3141
+ }
3142
+ i = k;
3143
+ continue;
3144
+ }
2575
3145
  if (chr == ',') {
2576
3146
  if (str !== '') {
2577
- result.push(str);
3147
+ // @ts-ignore
3148
+ result.at(-1).push(str);
2578
3149
  str = '';
2579
3150
  }
3151
+ result.push([]);
2580
3152
  continue;
2581
3153
  }
2582
3154
  str += chr;
@@ -2626,198 +3198,64 @@
2626
3198
  }
2627
3199
  }
2628
3200
  if (str !== '') {
2629
- result.push(str);
3201
+ // @ts-ignore
3202
+ result.at(-1).push(str);
2630
3203
  }
2631
3204
  return result;
2632
3205
  }
2633
3206
  function reduceRuleSelector(node) {
2634
- // @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
- }
2659
- }
2660
- }
2661
- }
2662
- function diff(n1, n2, options = {}) {
2663
- let node1 = n1;
2664
- let node2 = n2;
2665
- let exchanged = false;
2666
- if (node1.chi.length > node2.chi.length) {
2667
- const t = node1;
2668
- node1 = node2;
2669
- node2 = t;
2670
- exchanged = true;
2671
- }
2672
- let i = node1.chi.length;
2673
- let j = node2.chi.length;
2674
- if (i == 0 || j == 0) {
2675
- // @ts-ignore
2676
- return null;
3207
+ if (node.raw == null) {
3208
+ Object.defineProperty(node, 'raw', { enumerable: false, writable: true, value: splitRule(node.sel) });
2677
3209
  }
2678
3210
  // @ts-ignore
2679
- const raw1 = node1.raw;
2680
- // @ts-ignore
2681
- const optimized1 = node1.optimized;
3211
+ // if (node.raw != null) {
2682
3212
  // @ts-ignore
2683
- const raw2 = node2.raw;
2684
- // @ts-ignore
2685
- const optimized2 = node2.optimized;
2686
- node1 = { ...node1, chi: node1.chi.slice() };
2687
- node2 = { ...node2, chi: node2.chi.slice() };
2688
- if (raw1 != null) {
2689
- Object.defineProperty(node1, 'raw', { enumerable: false, writable: true, value: raw1 });
2690
- }
2691
- if (optimized1 != null) {
2692
- Object.defineProperty(node1, 'optimized', { enumerable: false, writable: true, value: optimized1 });
2693
- }
2694
- if (raw2 != null) {
2695
- Object.defineProperty(node2, 'raw', { enumerable: false, writable: true, value: raw2 });
2696
- }
2697
- if (optimized2 != null) {
2698
- Object.defineProperty(node2, 'optimized', { enumerable: false, writable: true, value: optimized2 });
3213
+ let optimized = reduceSelector(node.raw.reduce((acc, curr) => {
3214
+ acc.push(curr.slice());
3215
+ return acc;
3216
+ }, []));
3217
+ if (optimized != null) {
3218
+ Object.defineProperty(node, 'optimized', { enumerable: false, writable: true, value: optimized });
2699
3219
  }
2700
- const intersect = [];
2701
- while (i--) {
2702
- if (node1.chi[i].typ == 'Comment') {
2703
- continue;
2704
- }
2705
- j = node2.chi.length;
2706
- if (j == 0) {
2707
- break;
2708
- }
2709
- while (j--) {
2710
- if (node2.chi[j].typ == 'Comment') {
2711
- continue;
2712
- }
2713
- if (node1.chi[i].nam == node2.chi[j].nam) {
2714
- if (eq(node1.chi[i], node2.chi[j])) {
2715
- intersect.push(node1.chi[i]);
2716
- node1.chi.splice(i, 1);
2717
- node2.chi.splice(j, 1);
2718
- break;
3220
+ if (optimized != null && optimized.match && optimized.reducible && optimized.selector.length > 1) {
3221
+ const raw = [
3222
+ [
3223
+ optimized.optimized[0], ':is('
3224
+ ].concat(optimized.selector.reduce((acc, curr) => {
3225
+ if (acc.length > 0) {
3226
+ acc.push(',');
2719
3227
  }
2720
- }
3228
+ acc.push(...curr);
3229
+ return acc;
3230
+ }, [])).concat(')')
3231
+ ];
3232
+ const sel = raw[0].join('');
3233
+ if (sel.length < node.sel.length) {
3234
+ node.sel = sel;
3235
+ // node.raw = raw;
3236
+ Object.defineProperty(node, 'raw', { enumerable: false, writable: true, value: raw });
2721
3237
  }
2722
3238
  }
3239
+ // }
3240
+ }
3241
+
3242
+ function* walk(node) {
2723
3243
  // @ts-ignore
2724
- const result = (intersect.length == 0 ? null : {
2725
- ...node1,
2726
- // @ts-ignore
2727
- sel: [...new Set([...(n1?.raw?.reduce(reducer, []) || splitRule(n1.sel)).concat(n2?.raw?.reduce(reducer, []) || splitRule(n2.sel))])].join(),
2728
- chi: intersect.reverse()
2729
- });
2730
- 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
- // @ts-ignore
2732
- return null;
2733
- }
2734
- return { result, node1: exchanged ? node2 : node1, node2: exchanged ? node2 : node2 };
3244
+ yield* doWalk(node, null, null);
2735
3245
  }
2736
- function reduceSelector(selector) {
2737
- if (selector.length < 2) {
2738
- return null;
2739
- }
2740
- const optimized = [];
2741
- const k = selector.reduce((acc, curr) => acc == 0 ? curr.length : (curr.length == 0 ? acc : Math.min(acc, curr.length)), 0);
2742
- let i = 0;
2743
- let j;
2744
- let match;
2745
- for (; i < k; i++) {
2746
- const item = selector[0][i];
2747
- match = true;
2748
- for (j = 1; j < selector.length; j++) {
2749
- if (item != selector[j][i]) {
2750
- match = false;
2751
- break;
2752
- }
2753
- }
2754
- if (!match) {
2755
- break;
3246
+ function* doWalk(node, parent, root) {
3247
+ yield { node, parent, root };
3248
+ if ('chi' in node) {
3249
+ for (const child of node.chi) {
3250
+ yield* doWalk(child, node, (root ?? node));
2756
3251
  }
2757
- optimized.push(item);
2758
- }
2759
- if (optimized.at(-1) == ' ') {
2760
- optimized.pop();
2761
3252
  }
2762
- let reducible = optimized.length == 1;
2763
- if (optimized.length == 0) {
2764
- return { match: false, optimized, selector, reducible };
2765
- }
2766
- return {
2767
- match: true,
2768
- optimized,
2769
- selector: selector.reduce((acc, curr) => {
2770
- const slice = curr.slice(optimized.length);
2771
- // @ts-ignore
2772
- if (slice.length > 0 && slice[0] == ' ') {
2773
- slice.shift();
2774
- }
2775
- if (slice.length == 0) {
2776
- slice.push('&');
2777
- }
2778
- if (reducible) {
2779
- const chr = slice[0].charAt(0);
2780
- // @ts-ignore
2781
- reducible = chr == '.' || chr == ':' || isIdentStart(chr.codePointAt(0));
2782
- }
2783
- acc.push(slice);
2784
- return acc;
2785
- }, []),
2786
- reducible
2787
- };
2788
- }
2789
- function reducer(acc, curr) {
2790
- acc.push(curr.join(''));
2791
- return acc;
2792
3253
  }
2793
3254
 
2794
- const urlTokenMatcher = /^(["']?)[a-zA-Z0-9_/.-][a-zA-Z0-9_/:.#?-]+(\1)$/;
2795
- const funcLike = ['Start-parens', 'Func', 'UrlFunc', 'Pseudo-class-func'];
2796
- async function parse$1(iterator, opt = {}) {
2797
- const errors = [];
2798
- const options = {
2799
- src: '',
2800
- sourcemap: false,
2801
- compress: false,
2802
- nestingRules: false,
2803
- resolveImport: false,
2804
- resolveUrls: false,
2805
- removeEmpty: true,
2806
- ...opt
2807
- };
2808
- if (options.resolveImport) {
2809
- options.resolveUrls = true;
2810
- }
3255
+ function* tokenize(iterator) {
2811
3256
  let ind = -1;
2812
3257
  let lin = 1;
2813
3258
  let col = 0;
2814
- const tokens = [];
2815
- const src = options.src;
2816
- const stack = [];
2817
- const ast = {
2818
- typ: "StyleSheet",
2819
- chi: []
2820
- };
2821
3259
  const position = {
2822
3260
  ind: Math.max(ind, 0),
2823
3261
  lin: lin,
@@ -2825,899 +3263,965 @@
2825
3263
  };
2826
3264
  let value;
2827
3265
  let buffer = '';
2828
- let total = iterator.length;
2829
- let bytesIn = total;
2830
- let map = new Map;
2831
- let context = ast;
2832
- if (options.sourcemap) {
2833
- ast.loc = {
2834
- sta: {
2835
- ind: ind,
2836
- lin: lin,
2837
- col: col
2838
- },
2839
- src: ''
2840
- };
2841
- }
2842
- function getType(val) {
2843
- if (val === '') {
2844
- throw new Error('empty string?');
2845
- }
2846
- if (val == ':') {
2847
- return { typ: 'Colon' };
2848
- }
2849
- if (val == ')') {
2850
- return { typ: 'End-parens' };
2851
- }
2852
- if (val == '(') {
2853
- return { typ: 'Start-parens' };
2854
- }
2855
- if (val == '=') {
2856
- return { typ: 'Delim', val };
2857
- }
2858
- if (val == ';') {
2859
- return { typ: 'Semi-colon' };
2860
- }
2861
- if (val == ',') {
2862
- return { typ: 'Comma' };
2863
- }
2864
- if (val == '<') {
2865
- return { typ: 'Lt' };
3266
+ function consumeWhiteSpace() {
3267
+ let count = 0;
3268
+ while (isWhiteSpace(iterator.charAt(count + ind + 1).charCodeAt(0))) {
3269
+ count++;
2866
3270
  }
2867
- if (val == '>') {
2868
- return { typ: 'Gt' };
3271
+ next(count);
3272
+ return count;
3273
+ }
3274
+ function pushToken(token, hint) {
3275
+ const result = { token, hint, position: { ...position }, bytesIn: ind };
3276
+ position.ind = ind;
3277
+ position.lin = lin;
3278
+ position.col = col == 0 ? 1 : col;
3279
+ return result;
3280
+ }
3281
+ function* consumeString(quoteStr) {
3282
+ const quote = quoteStr;
3283
+ let value;
3284
+ let hasNewLine = false;
3285
+ if (buffer.length > 0) {
3286
+ yield pushToken(buffer);
3287
+ buffer = '';
2869
3288
  }
2870
- if (isPseudo(val)) {
2871
- return val.endsWith('(') ? {
2872
- typ: 'Pseudo-class-func',
2873
- val: val.slice(0, -1),
2874
- chi: []
3289
+ buffer += quoteStr;
3290
+ while (value = peek()) {
3291
+ if (ind >= iterator.length) {
3292
+ yield pushToken(buffer, hasNewLine ? 'Bad-string' : 'Unclosed-string');
3293
+ break;
2875
3294
  }
2876
- : {
2877
- typ: 'Pseudo-class',
2878
- val
2879
- };
2880
- }
2881
- if (isAtKeyword(val)) {
2882
- return {
2883
- typ: 'At-rule',
2884
- val: val.slice(1)
2885
- };
2886
- }
2887
- if (isFunction(val)) {
2888
- val = val.slice(0, -1);
2889
- return {
2890
- typ: val == 'url' ? 'UrlFunc' : 'Func',
2891
- val,
2892
- chi: []
2893
- };
2894
- }
2895
- if (isNumber(val)) {
2896
- return {
2897
- typ: 'Number',
2898
- val
2899
- };
2900
- }
2901
- if (isDimension(val)) {
2902
- return parseDimension(val);
2903
- }
2904
- if (isPercentage(val)) {
2905
- return {
2906
- typ: 'Perc',
2907
- val: val.slice(0, -1)
2908
- };
2909
- }
2910
- if (val == 'currentColor') {
2911
- return {
2912
- typ: 'Color',
2913
- val,
2914
- kin: 'lit'
2915
- };
2916
- }
2917
- if (isIdent(val)) {
2918
- return {
2919
- typ: 'Iden',
2920
- val
2921
- };
2922
- }
2923
- if (val.charAt(0) == '#' && isHash(val)) {
2924
- return {
2925
- typ: 'Hash',
2926
- val
2927
- };
2928
- }
2929
- if ('"\''.includes(val.charAt(0))) {
2930
- return {
2931
- typ: 'Unclosed-string',
2932
- val
2933
- };
2934
- }
2935
- return {
2936
- typ: 'Literal',
2937
- val
2938
- };
2939
- }
2940
- // consume and throw away
2941
- function consume(open, close) {
2942
- let count = 1;
2943
- let chr;
2944
- while (true) {
2945
- chr = next();
2946
- if (chr == '\\') {
2947
- if (peek() === '') {
3295
+ if (value == '\\') {
3296
+ const sequence = peek(6);
3297
+ let escapeSequence = '';
3298
+ let codepoint;
3299
+ let i;
3300
+ for (i = 1; i < sequence.length; i++) {
3301
+ codepoint = sequence.charCodeAt(i);
3302
+ if (codepoint == 0x20 ||
3303
+ (codepoint >= 0x61 && codepoint <= 0x66) ||
3304
+ (codepoint >= 0x41 && codepoint <= 0x46) ||
3305
+ (codepoint >= 0x30 && codepoint <= 0x39)) {
3306
+ escapeSequence += sequence[i];
3307
+ if (codepoint == 0x20) {
3308
+ break;
3309
+ }
3310
+ continue;
3311
+ }
2948
3312
  break;
2949
3313
  }
2950
- continue;
2951
- }
2952
- else if (chr == '/' && peek() == '*') {
2953
- next();
2954
- while (true) {
2955
- chr = next();
2956
- if (chr === '') {
2957
- break;
3314
+ // not hex or new line
3315
+ // @ts-ignore
3316
+ if (i == 1 && !isNewLine(codepoint)) {
3317
+ buffer += sequence[i];
3318
+ next(2);
3319
+ continue;
3320
+ }
3321
+ if (escapeSequence.trimEnd().length > 0) {
3322
+ const codepoint = Number(`0x${escapeSequence.trimEnd()}`);
3323
+ if (codepoint == 0 ||
3324
+ // leading surrogate
3325
+ (0xD800 <= codepoint && codepoint <= 0xDBFF) ||
3326
+ // trailing surrogate
3327
+ (0xDC00 <= codepoint && codepoint <= 0xDFFF)) {
3328
+ buffer += String.fromCodePoint(0xFFFD);
2958
3329
  }
2959
- if (chr == '*' && peek() == '/') {
2960
- next();
2961
- break;
3330
+ else {
3331
+ buffer += String.fromCodePoint(codepoint);
2962
3332
  }
3333
+ next(escapeSequence.length + 1);
3334
+ continue;
2963
3335
  }
3336
+ // buffer += value;
3337
+ if (ind >= iterator.length) {
3338
+ // drop '\\' at the end
3339
+ yield pushToken(buffer);
3340
+ break;
3341
+ }
3342
+ buffer += next(2);
3343
+ continue;
2964
3344
  }
2965
- else if (chr == close) {
2966
- count--;
2967
- }
2968
- else if (chr == open) {
2969
- count++;
2970
- }
2971
- if (chr === '' || count == 0) {
3345
+ if (value == quote) {
3346
+ buffer += value;
3347
+ yield pushToken(buffer, hasNewLine ? 'Bad-string' : 'String');
3348
+ next();
3349
+ // i += value.length;
3350
+ buffer = '';
2972
3351
  break;
2973
3352
  }
2974
- }
2975
- }
2976
- async function parseNode(tokens) {
2977
- let i;
2978
- let loc;
2979
- for (i = 0; i < tokens.length; i++) {
2980
- if (tokens[i].typ == 'Comment') {
2981
- // @ts-ignore
2982
- context.chi.push(tokens[i]);
2983
- const position = map.get(tokens[i]);
2984
- loc = {
2985
- sta: position,
2986
- src
2987
- };
2988
- if (options.sourcemap) {
2989
- tokens[i].loc = loc;
2990
- }
3353
+ if (isNewLine(value.charCodeAt(0))) {
3354
+ hasNewLine = true;
2991
3355
  }
2992
- else if (tokens[i].typ != 'Whitespace') {
3356
+ if (hasNewLine && value == ';') {
3357
+ yield pushToken(buffer, 'Bad-string');
3358
+ buffer = '';
2993
3359
  break;
2994
3360
  }
3361
+ buffer += value;
3362
+ // i += value.length;
3363
+ next();
2995
3364
  }
2996
- tokens = tokens.slice(i);
2997
- if (tokens.length == 0) {
2998
- return null;
2999
- }
3000
- let delim = tokens.at(-1);
3001
- if (delim.typ == 'Semi-colon' || delim.typ == 'Block-start' || delim.typ == 'Block-end') {
3002
- tokens.pop();
3365
+ }
3366
+ function peek(count = 1) {
3367
+ if (count == 1) {
3368
+ return iterator.charAt(ind + 1);
3003
3369
  }
3004
- else {
3005
- delim = { typ: 'Semi-colon' };
3370
+ return iterator.slice(ind + 1, ind + count + 1);
3371
+ }
3372
+ function prev(count = 1) {
3373
+ if (count == 1) {
3374
+ return ind == 0 ? '' : iterator.charAt(ind - 1);
3006
3375
  }
3007
- // @ts-ignore
3008
- while (['Whitespace', 'Bad-string', 'Bad-comment'].includes(tokens.at(-1)?.typ)) {
3009
- tokens.pop();
3376
+ return iterator.slice(ind - 1 - count, ind - 1);
3377
+ }
3378
+ function next(count = 1) {
3379
+ let char = '';
3380
+ while (count-- > 0 && ind < iterator.length) {
3381
+ const codepoint = iterator.charCodeAt(++ind);
3382
+ if (isNaN(codepoint)) {
3383
+ return char;
3384
+ }
3385
+ char += iterator.charAt(ind);
3386
+ if (isNewLine(codepoint)) {
3387
+ lin++;
3388
+ col = 0;
3389
+ }
3390
+ else {
3391
+ col++;
3392
+ }
3010
3393
  }
3011
- if (tokens.length == 0) {
3012
- return null;
3394
+ return char;
3395
+ }
3396
+ while (value = next()) {
3397
+ if (ind >= iterator.length) {
3398
+ if (buffer.length > 0) {
3399
+ yield pushToken(buffer);
3400
+ buffer = '';
3401
+ }
3402
+ break;
3013
3403
  }
3014
- if (tokens[0]?.typ == 'At-rule') {
3015
- const atRule = tokens.shift();
3016
- const position = map.get(atRule);
3017
- if (atRule.val == 'charset' && position.ind > 0) {
3018
- errors.push({ action: 'drop', message: 'invalid @charset', location: { src, ...position } });
3019
- return null;
3404
+ if (isWhiteSpace(value.charCodeAt(0))) {
3405
+ if (buffer.length > 0) {
3406
+ yield pushToken(buffer);
3407
+ buffer = '';
3020
3408
  }
3021
- // @ts-ignore
3022
- while (['Whitespace'].includes(tokens[0]?.typ)) {
3023
- tokens.shift();
3409
+ while (value = next()) {
3410
+ if (ind >= iterator.length) {
3411
+ break;
3412
+ }
3413
+ if (!isWhiteSpace(value.charCodeAt(0))) {
3414
+ break;
3415
+ }
3024
3416
  }
3025
- if (atRule.val == 'import') {
3026
- // only @charset and @layer are accepted before @import
3027
- if (context.chi.length > 0) {
3028
- let i = context.chi.length;
3029
- while (i--) {
3030
- const type = context.chi[i].typ;
3031
- if (type == 'Comment') {
3417
+ yield pushToken('', 'Whitespace');
3418
+ buffer = '';
3419
+ if (ind >= iterator.length) {
3420
+ break;
3421
+ }
3422
+ }
3423
+ switch (value) {
3424
+ case '/':
3425
+ if (buffer.length > 0) {
3426
+ yield pushToken(buffer);
3427
+ buffer = '';
3428
+ if (peek() != '*') {
3429
+ yield pushToken(value);
3430
+ break;
3431
+ }
3432
+ }
3433
+ buffer += value;
3434
+ if (peek() == '*') {
3435
+ buffer += '*';
3436
+ // i++;
3437
+ next();
3438
+ while (value = next()) {
3439
+ if (ind >= iterator.length) {
3440
+ yield pushToken(buffer, 'Bad-comment');
3441
+ break;
3442
+ }
3443
+ if (value == '\\') {
3444
+ buffer += value;
3445
+ value = next();
3446
+ if (ind >= iterator.length) {
3447
+ yield pushToken(buffer, 'Bad-comment');
3448
+ break;
3449
+ }
3450
+ buffer += value;
3032
3451
  continue;
3033
3452
  }
3034
- if (type != 'AtRule') {
3035
- errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
3036
- return null;
3453
+ if (value == '*') {
3454
+ buffer += value;
3455
+ value = next();
3456
+ if (ind >= iterator.length) {
3457
+ yield pushToken(buffer, 'Bad-comment');
3458
+ break;
3459
+ }
3460
+ buffer += value;
3461
+ if (value == '/') {
3462
+ yield pushToken(buffer, 'Comment');
3463
+ buffer = '';
3464
+ break;
3465
+ }
3037
3466
  }
3038
- const name = context.chi[i].nam;
3039
- if (name != 'charset' && name != 'import' && name != 'layer') {
3040
- errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
3041
- return null;
3467
+ else {
3468
+ buffer += value;
3042
3469
  }
3043
- break;
3044
3470
  }
3045
3471
  }
3046
- // @ts-ignore
3047
- if (tokens[0]?.typ != 'String' && tokens[0]?.typ != 'UrlFunc') {
3048
- errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
3049
- return null;
3050
- }
3051
- // @ts-ignore
3052
- if (tokens[0].typ == 'UrlFunc' && tokens[1]?.typ != 'Url-token' && tokens[1]?.typ != 'String') {
3053
- errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
3054
- return null;
3472
+ break;
3473
+ case '<':
3474
+ if (buffer.length > 0) {
3475
+ yield pushToken(buffer);
3476
+ buffer = '';
3055
3477
  }
3056
- }
3057
- if (atRule.val == 'import') {
3058
- // @ts-ignore
3059
- if (tokens[0].typ == 'UrlFunc' && tokens[1].typ == 'Url-token') {
3060
- tokens.shift();
3061
- // @ts-ignore
3062
- tokens[0].typ = 'String';
3063
- // @ts-ignore
3064
- tokens[0].val = `"${tokens[0].val}"`;
3478
+ buffer += value;
3479
+ value = next();
3480
+ if (ind >= iterator.length) {
3481
+ break;
3065
3482
  }
3066
- // @ts-ignore
3067
- if (tokens[0].typ == 'String') {
3068
- if (options.resolveImport) {
3069
- const url = tokens[0].val.slice(1, -1);
3070
- try {
3071
- // @ts-ignore
3072
- const root = await options.load(url, options.src).then((src) => {
3073
- return parse$1(src, Object.assign({}, options, {
3074
- compress: false,
3075
- // @ts-ignore
3076
- src: options.resolve(url, options.src).absolute
3077
- }));
3078
- });
3079
- bytesIn += root.bytesIn;
3080
- if (root.ast.chi.length > 0) {
3081
- context.chi.push(...root.ast.chi);
3082
- }
3083
- if (root.errors.length > 0) {
3084
- errors.push(...root.errors);
3085
- }
3086
- return null;
3483
+ if (peek(3) == '!--') {
3484
+ while (value = next()) {
3485
+ if (ind >= iterator.length) {
3486
+ break;
3087
3487
  }
3088
- catch (error) {
3089
- console.error(error);
3488
+ buffer += value;
3489
+ if (value == '>' && prev(2) == '--') {
3490
+ yield pushToken(buffer, 'CDOCOMM');
3491
+ buffer = '';
3492
+ break;
3090
3493
  }
3091
3494
  }
3092
3495
  }
3093
- }
3094
- // https://www.w3.org/TR/css-nesting-1/#conditionals
3095
- // allowed nesting at-rules
3096
- // there must be a top level rule in the stack
3097
- const raw = tokens.reduce((acc, curr, index, array) => {
3098
- acc.push(renderToken(curr, { removeComments: true }));
3099
- return acc;
3100
- }, []);
3101
- const node = {
3102
- typ: 'AtRule',
3103
- nam: renderToken(atRule, { removeComments: true }),
3104
- val: raw.join('')
3105
- };
3106
- Object.defineProperty(node, 'raw', { enumerable: false, writable: false, value: raw });
3107
- if (delim.typ == 'Block-start') {
3108
- node.chi = [];
3109
- }
3110
- loc = {
3111
- sta: position,
3112
- src
3113
- };
3114
- if (options.sourcemap) {
3115
- node.loc = loc;
3116
- }
3117
- // @ts-ignore
3118
- context.chi.push(node);
3119
- return delim.typ == 'Block-start' ? node : null;
3120
- }
3121
- else {
3122
- // rule
3123
- if (delim.typ == 'Block-start') {
3124
- const position = map.get(tokens[0]);
3125
- if (context.typ == 'Rule') {
3126
- if (tokens[0]?.typ == 'Iden') {
3127
- errors.push({ action: 'drop', message: 'invalid nesting rule', location: { src, ...position } });
3128
- return null;
3129
- }
3496
+ if (ind >= iterator.length) {
3497
+ yield pushToken(buffer, 'BADCDO');
3498
+ buffer = '';
3130
3499
  }
3131
- const uniq = new Map;
3132
- parseTokens(tokens, { compress: options.compress }).reduce((acc, curr) => {
3133
- let t = renderToken(curr, { compress: true });
3134
- if (t == ',') {
3135
- acc.push([]);
3136
- }
3137
- else {
3138
- acc[acc.length - 1].push(t);
3139
- }
3140
- return acc;
3141
- }, [[]]).reduce((acc, curr) => {
3142
- acc.set(curr.join(''), curr);
3143
- return acc;
3144
- }, uniq);
3145
- const node = {
3146
- typ: 'Rule',
3147
- // @ts-ignore
3148
- sel: [...uniq.keys()].join(','),
3149
- chi: []
3150
- };
3151
- let raw = [...uniq.values()];
3152
- Object.defineProperty(node, 'raw', { enumerable: false, writable: true, value: raw });
3153
- loc = {
3154
- sta: position,
3155
- src
3156
- };
3157
- if (options.sourcemap) {
3158
- node.loc = loc;
3500
+ break;
3501
+ case '\\':
3502
+ value = next();
3503
+ // EOF
3504
+ if (ind + 1 >= iterator.length) {
3505
+ // end of stream ignore \\
3506
+ yield pushToken(buffer);
3507
+ buffer = '';
3508
+ break;
3159
3509
  }
3160
- // @ts-ignore
3161
- context.chi.push(node);
3162
- return node;
3163
- }
3164
- else {
3165
- // declaration
3166
- // @ts-ignore
3167
- let name = null;
3168
- // @ts-ignore
3169
- let value = null;
3170
- for (let i = 0; i < tokens.length; i++) {
3171
- if (tokens[i].typ == 'Comment') {
3172
- continue;
3173
- }
3174
- if (tokens[i].typ == 'Colon') {
3175
- name = tokens.slice(0, i);
3176
- value = parseTokens(tokens.slice(i + 1), {
3177
- parseColor: true,
3178
- src: options.src,
3179
- resolveUrls: options.resolveUrls,
3180
- resolve: options.resolve,
3181
- cwd: options.cwd
3182
- });
3183
- }
3510
+ buffer += value;
3511
+ break;
3512
+ case '"':
3513
+ case "'":
3514
+ yield* consumeString(value);
3515
+ break;
3516
+ case '~':
3517
+ case '|':
3518
+ if (buffer.length > 0) {
3519
+ yield pushToken(buffer);
3520
+ buffer = '';
3184
3521
  }
3185
- if (name == null) {
3186
- name = tokens;
3522
+ buffer += value;
3523
+ value = next();
3524
+ if (ind >= iterator.length) {
3525
+ yield pushToken(buffer);
3526
+ buffer = '';
3527
+ break;
3187
3528
  }
3188
- const position = map.get(name[0]);
3189
- if (name.length > 0) {
3190
- for (let i = 1; i < name.length; i++) {
3191
- if (name[i].typ != 'Whitespace' && name[i].typ != 'Comment') {
3192
- errors.push({
3193
- action: 'drop',
3194
- message: 'invalid declaration',
3195
- location: { src, ...position }
3196
- });
3197
- return null;
3198
- }
3199
- }
3529
+ if (value == '=') {
3530
+ buffer += value;
3531
+ yield pushToken(buffer, buffer[0] == '~' ? 'Includes' : 'Dash-matches');
3532
+ buffer = '';
3533
+ break;
3200
3534
  }
3201
- if (value == null) {
3202
- errors.push({ action: 'drop', message: 'invalid declaration', location: { src, ...position } });
3203
- return null;
3535
+ yield pushToken(buffer);
3536
+ while (isWhiteSpace(value.charCodeAt(0))) {
3537
+ value = next();
3204
3538
  }
3205
- if (value.length == 0) {
3206
- errors.push({ action: 'drop', message: 'invalid declaration', location: { src, ...position } });
3207
- return null;
3539
+ buffer = value;
3540
+ break;
3541
+ case '>':
3542
+ if (buffer !== '') {
3543
+ yield pushToken(buffer);
3544
+ buffer = '';
3208
3545
  }
3209
- const node = {
3210
- typ: 'Declaration',
3211
- // @ts-ignore
3212
- nam: renderToken(name.shift(), { removeComments: true }),
3213
- // @ts-ignore
3214
- val: value
3215
- };
3216
- while (node.val[0]?.typ == 'Whitespace') {
3217
- node.val.shift();
3546
+ yield pushToken('', 'Gt');
3547
+ consumeWhiteSpace();
3548
+ break;
3549
+ case '.':
3550
+ const codepoint = peek().charCodeAt(0);
3551
+ if (!isDigit(codepoint) && buffer !== '') {
3552
+ yield pushToken(buffer);
3553
+ buffer = value;
3554
+ break;
3218
3555
  }
3219
- if (node.val.length == 0) {
3220
- errors.push({ action: 'drop', message: 'invalid declaration', location: { src, ...position } });
3221
- return null;
3556
+ buffer += value;
3557
+ break;
3558
+ case '+':
3559
+ case ':':
3560
+ case ',':
3561
+ case '=':
3562
+ if (buffer.length > 0) {
3563
+ yield pushToken(buffer);
3564
+ buffer = '';
3565
+ }
3566
+ if (value == ':' && ':' == peek()) {
3567
+ buffer += value + next();
3568
+ break;
3569
+ }
3570
+ yield pushToken(value);
3571
+ buffer = '';
3572
+ if (value == '+' && isWhiteSpace(peek().charCodeAt(0))) {
3573
+ yield pushToken(next());
3574
+ }
3575
+ while (isWhiteSpace(peek().charCodeAt(0))) {
3576
+ next();
3222
3577
  }
3223
- // @ts-ignore
3224
- context.chi.push(node);
3225
- return null;
3226
- }
3227
- }
3228
- }
3229
- function peek(count = 1) {
3230
- if (count == 1) {
3231
- return iterator.charAt(ind + 1);
3232
- }
3233
- return iterator.slice(ind + 1, ind + count + 1);
3234
- }
3235
- function prev(count = 1) {
3236
- if (count == 1) {
3237
- return ind == 0 ? '' : iterator.charAt(ind - 1);
3238
- }
3239
- return iterator.slice(ind - 1 - count, ind - 1);
3240
- }
3241
- function next(count = 1) {
3242
- let char = '';
3243
- while (count-- > 0 && ind < total) {
3244
- const codepoint = iterator.charCodeAt(++ind);
3245
- if (isNaN(codepoint)) {
3246
- return char;
3247
- }
3248
- char += iterator.charAt(ind);
3249
- if (isNewLine(codepoint)) {
3250
- lin++;
3251
- col = 0;
3252
- }
3253
- else {
3254
- col++;
3255
- }
3256
- }
3257
- return char;
3258
- }
3259
- function pushToken(token) {
3260
- tokens.push(token);
3261
- map.set(token, { ...position });
3262
- position.ind = ind;
3263
- position.lin = lin;
3264
- position.col = col == 0 ? 1 : col;
3265
- // }
3266
- }
3267
- function consumeWhiteSpace() {
3268
- let count = 0;
3269
- while (isWhiteSpace(iterator.charAt(count + ind + 1).charCodeAt(0))) {
3270
- count++;
3271
- }
3272
- next(count);
3273
- return count;
3274
- }
3275
- function consumeString(quoteStr) {
3276
- const quote = quoteStr;
3277
- let value;
3278
- let hasNewLine = false;
3279
- if (buffer.length > 0) {
3280
- pushToken(getType(buffer));
3281
- buffer = '';
3282
- }
3283
- buffer += quoteStr;
3284
- while (ind < total) {
3285
- value = peek();
3286
- if (ind >= total) {
3287
- pushToken({ typ: hasNewLine ? 'Bad-string' : 'Unclosed-string', val: buffer });
3288
3578
  break;
3289
- }
3290
- if (value == '\\') {
3291
- const sequence = peek(6);
3292
- let escapeSequence = '';
3293
- let codepoint;
3294
- let i;
3295
- for (i = 1; i < sequence.length; i++) {
3296
- codepoint = sequence.charCodeAt(i);
3297
- if (codepoint == 0x20 ||
3298
- (codepoint >= 0x61 && codepoint <= 0x66) ||
3299
- (codepoint >= 0x41 && codepoint <= 0x46) ||
3300
- (codepoint >= 0x30 && codepoint <= 0x39)) {
3301
- escapeSequence += sequence[i];
3302
- if (codepoint == 0x20) {
3303
- break;
3304
- }
3305
- continue;
3306
- }
3579
+ case ')':
3580
+ if (buffer.length > 0) {
3581
+ yield pushToken(buffer);
3582
+ buffer = '';
3583
+ }
3584
+ yield pushToken('', 'End-parens');
3585
+ break;
3586
+ case '(':
3587
+ if (buffer.length == 0) {
3588
+ yield pushToken('', 'Start-parens');
3307
3589
  break;
3308
3590
  }
3309
- // not hex or new line
3591
+ buffer += value;
3310
3592
  // @ts-ignore
3311
- if (i == 1 && !isNewLine(codepoint)) {
3312
- buffer += sequence[i];
3313
- next(2);
3314
- continue;
3315
- }
3316
- if (escapeSequence.trimEnd().length > 0) {
3317
- const codepoint = Number(`0x${escapeSequence.trimEnd()}`);
3318
- if (codepoint == 0 ||
3319
- // leading surrogate
3320
- (0xD800 <= codepoint && codepoint <= 0xDBFF) ||
3321
- // trailing surrogate
3322
- (0xDC00 <= codepoint && codepoint <= 0xDFFF)) {
3323
- buffer += String.fromCodePoint(0xFFFD);
3593
+ if (buffer == 'url(') {
3594
+ yield pushToken(buffer);
3595
+ buffer = '';
3596
+ // consume either string or url token
3597
+ let whitespace = '';
3598
+ value = peek();
3599
+ while (isWhiteSpace(value.charCodeAt(0))) {
3600
+ whitespace += value;
3601
+ }
3602
+ if (whitespace.length > 0) {
3603
+ next(whitespace.length);
3604
+ }
3605
+ value = peek();
3606
+ if (value == '"' || value == "'") {
3607
+ yield* consumeString(next());
3608
+ break;
3324
3609
  }
3325
3610
  else {
3326
- buffer += String.fromCodePoint(codepoint);
3611
+ buffer = '';
3612
+ do {
3613
+ let cp = value.charCodeAt(0);
3614
+ // EOF -
3615
+ if (cp == null) {
3616
+ yield pushToken('', 'Bad-url-token');
3617
+ break;
3618
+ }
3619
+ // ')'
3620
+ if (cp == 0x29 || cp == null) {
3621
+ if (buffer.length == 0) {
3622
+ yield pushToken(buffer, 'Bad-url-token');
3623
+ }
3624
+ else {
3625
+ yield pushToken(buffer, 'Url-token');
3626
+ }
3627
+ if (cp != null) {
3628
+ yield pushToken(next());
3629
+ }
3630
+ break;
3631
+ }
3632
+ if (isWhiteSpace(cp)) {
3633
+ whitespace = next();
3634
+ while (true) {
3635
+ value = peek();
3636
+ cp = value.charCodeAt(0);
3637
+ if (isWhiteSpace(cp)) {
3638
+ whitespace += value;
3639
+ continue;
3640
+ }
3641
+ break;
3642
+ }
3643
+ if (cp == null || cp == 0x29) {
3644
+ continue;
3645
+ }
3646
+ // bad url token
3647
+ buffer += next(whitespace.length);
3648
+ do {
3649
+ value = peek();
3650
+ cp = value.charCodeAt(0);
3651
+ if (cp == null || cp == 0x29) {
3652
+ break;
3653
+ }
3654
+ buffer += next();
3655
+ } while (true);
3656
+ yield pushToken(buffer, 'Bad-url-token');
3657
+ continue;
3658
+ }
3659
+ buffer += next();
3660
+ value = peek();
3661
+ } while (true);
3662
+ buffer = '';
3327
3663
  }
3328
- next(escapeSequence.length + 1);
3329
- continue;
3664
+ break;
3330
3665
  }
3331
- // buffer += value;
3332
- if (ind >= total) {
3333
- // drop '\\' at the end
3334
- pushToken(getType(buffer));
3666
+ yield pushToken(buffer);
3667
+ buffer = '';
3668
+ break;
3669
+ case '[':
3670
+ case ']':
3671
+ case '{':
3672
+ case '}':
3673
+ case ';':
3674
+ if (buffer.length > 0) {
3675
+ yield pushToken(buffer);
3676
+ buffer = '';
3677
+ }
3678
+ yield pushToken(value);
3679
+ break;
3680
+ case '!':
3681
+ if (buffer.length > 0) {
3682
+ yield pushToken(buffer);
3683
+ buffer = '';
3684
+ }
3685
+ const important = peek(9);
3686
+ if (important == 'important') {
3687
+ yield pushToken('', 'Important');
3688
+ next(9);
3689
+ buffer = '';
3335
3690
  break;
3336
3691
  }
3337
- buffer += next(2);
3338
- continue;
3339
- }
3340
- if (value == quote) {
3692
+ buffer = '!';
3693
+ break;
3694
+ default:
3341
3695
  buffer += value;
3342
- pushToken({ typ: hasNewLine ? 'Bad-string' : 'String', val: buffer });
3343
- next();
3344
- // i += value.length;
3345
- buffer = '';
3346
3696
  break;
3697
+ }
3698
+ }
3699
+ if (buffer.length > 0) {
3700
+ yield pushToken(buffer);
3701
+ }
3702
+ }
3703
+
3704
+ const urlTokenMatcher = /^(["']?)[a-zA-Z0-9_/.-][a-zA-Z0-9_/:.#?-]+(\1)$/;
3705
+ const funcLike = ['Start-parens', 'Func', 'UrlFunc', 'Pseudo-class-func'];
3706
+ /**
3707
+ *
3708
+ * @param iterator
3709
+ * @param opt
3710
+ */
3711
+ async function parse$1(iterator, opt = {}) {
3712
+ const errors = [];
3713
+ const options = {
3714
+ src: '',
3715
+ sourcemap: false,
3716
+ minify: true,
3717
+ nestingRules: false,
3718
+ resolveImport: false,
3719
+ resolveUrls: false,
3720
+ removeEmpty: true,
3721
+ ...opt
3722
+ };
3723
+ if (options.resolveImport) {
3724
+ options.resolveUrls = true;
3725
+ }
3726
+ const src = options.src;
3727
+ const stack = [];
3728
+ const ast = {
3729
+ typ: "StyleSheet",
3730
+ chi: []
3731
+ };
3732
+ let tokens = [];
3733
+ let map = new Map;
3734
+ let bytesIn = 0;
3735
+ let context = ast;
3736
+ if (options.sourcemap) {
3737
+ ast.loc = {
3738
+ sta: {
3739
+ ind: 0,
3740
+ lin: 1,
3741
+ col: 1
3742
+ },
3743
+ src: ''
3744
+ };
3745
+ }
3746
+ async function parseNode(results) {
3747
+ let tokens = results.map(mapToken);
3748
+ let i;
3749
+ let loc;
3750
+ for (i = 0; i < tokens.length; i++) {
3751
+ if (tokens[i].typ == 'Comment') {
3752
+ // @ts-ignore
3753
+ context.chi.push(tokens[i]);
3754
+ const position = map.get(tokens[i]);
3755
+ loc = {
3756
+ sta: position,
3757
+ src
3758
+ };
3759
+ if (options.sourcemap) {
3760
+ tokens[i].loc = loc;
3761
+ }
3347
3762
  }
3348
- if (isNewLine(value.charCodeAt(0))) {
3349
- hasNewLine = true;
3350
- }
3351
- if (hasNewLine && value == ';') {
3352
- pushToken({ typ: 'Bad-string', val: buffer });
3353
- buffer = '';
3763
+ else if (tokens[i].typ != 'Whitespace') {
3354
3764
  break;
3355
3765
  }
3356
- buffer += value;
3357
- // i += value.length;
3358
- next();
3359
3766
  }
3360
- }
3361
- while (ind < total) {
3362
- value = next();
3363
- if (ind >= total) {
3364
- if (buffer.length > 0) {
3365
- pushToken(getType(buffer));
3366
- buffer = '';
3367
- }
3368
- break;
3767
+ tokens = tokens.slice(i);
3768
+ if (tokens.length == 0) {
3769
+ return null;
3369
3770
  }
3370
- if (isWhiteSpace(value.charCodeAt(0))) {
3371
- if (buffer.length > 0) {
3372
- pushToken(getType(buffer));
3373
- buffer = '';
3374
- }
3375
- while (ind < total) {
3376
- value = next();
3377
- if (ind >= total) {
3378
- break;
3379
- }
3380
- if (!isWhiteSpace(value.charCodeAt(0))) {
3381
- break;
3382
- }
3771
+ let delim = tokens.at(-1);
3772
+ if (delim.typ == 'Semi-colon' || delim.typ == 'Block-start' || delim.typ == 'Block-end') {
3773
+ tokens.pop();
3774
+ }
3775
+ else {
3776
+ delim = { typ: 'Semi-colon' };
3777
+ }
3778
+ // @ts-ignore
3779
+ while (['Whitespace', 'Bad-string', 'Bad-comment'].includes(tokens.at(-1)?.typ)) {
3780
+ tokens.pop();
3781
+ }
3782
+ if (tokens.length == 0) {
3783
+ return null;
3784
+ }
3785
+ if (tokens[0]?.typ == 'At-rule') {
3786
+ const atRule = tokens.shift();
3787
+ const position = map.get(atRule);
3788
+ if (atRule.val == 'charset' && position.ind > 0) {
3789
+ errors.push({ action: 'drop', message: 'invalid @charset', location: { src, ...position } });
3790
+ return null;
3383
3791
  }
3384
- pushToken({ typ: 'Whitespace' });
3385
- buffer = '';
3386
- if (ind >= total) {
3387
- break;
3792
+ // @ts-ignore
3793
+ while (['Whitespace'].includes(tokens[0]?.typ)) {
3794
+ tokens.shift();
3388
3795
  }
3389
- }
3390
- switch (value) {
3391
- case '/':
3392
- if (buffer.length > 0 && tokens.at(-1)?.typ == 'Whitespace') {
3393
- pushToken(getType(buffer));
3394
- buffer = '';
3395
- if (peek() != '*') {
3396
- pushToken(getType(value));
3397
- break;
3398
- }
3399
- }
3400
- buffer += value;
3401
- if (peek() == '*') {
3402
- buffer += '*';
3403
- // i++;
3404
- next();
3405
- while (ind < total) {
3406
- value = next();
3407
- if (ind >= total) {
3408
- pushToken({
3409
- typ: 'Bad-comment', val: buffer
3410
- });
3411
- break;
3412
- }
3413
- if (value == '\\') {
3414
- buffer += value;
3415
- value = next();
3416
- if (ind >= total) {
3417
- pushToken({
3418
- typ: 'Bad-comment',
3419
- val: buffer
3420
- });
3421
- break;
3422
- }
3423
- buffer += value;
3796
+ if (atRule.val == 'import') {
3797
+ // only @charset and @layer are accepted before @import
3798
+ if (context.chi.length > 0) {
3799
+ let i = context.chi.length;
3800
+ while (i--) {
3801
+ const type = context.chi[i].typ;
3802
+ if (type == 'Comment') {
3424
3803
  continue;
3425
3804
  }
3426
- if (value == '*') {
3427
- buffer += value;
3428
- value = next();
3429
- if (ind >= total) {
3430
- pushToken({
3431
- typ: 'Bad-comment', val: buffer
3432
- });
3433
- break;
3434
- }
3435
- buffer += value;
3436
- if (value == '/') {
3437
- pushToken({ typ: 'Comment', val: buffer });
3438
- buffer = '';
3439
- break;
3440
- }
3805
+ if (type != 'AtRule') {
3806
+ errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
3807
+ return null;
3441
3808
  }
3442
- else {
3443
- buffer += value;
3809
+ const name = context.chi[i].nam;
3810
+ if (name != 'charset' && name != 'import' && name != 'layer') {
3811
+ errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
3812
+ return null;
3444
3813
  }
3814
+ break;
3445
3815
  }
3446
3816
  }
3447
- break;
3448
- case '<':
3449
- if (buffer.length > 0) {
3450
- pushToken(getType(buffer));
3451
- buffer = '';
3817
+ // @ts-ignore
3818
+ if (tokens[0]?.typ != 'String' && tokens[0]?.typ != 'UrlFunc') {
3819
+ errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
3820
+ return null;
3452
3821
  }
3453
- buffer += value;
3454
- value = next();
3455
- if (ind >= total) {
3456
- break;
3822
+ // @ts-ignore
3823
+ if (tokens[0].typ == 'UrlFunc' && tokens[1]?.typ != 'Url-token' && tokens[1]?.typ != 'String') {
3824
+ errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
3825
+ return null;
3457
3826
  }
3458
- if (peek(3) == '!--') {
3459
- while (ind < total) {
3460
- value = next();
3461
- if (ind >= total) {
3462
- break;
3463
- }
3464
- buffer += value;
3465
- if (value == '>' && prev(2) == '--') {
3466
- pushToken({
3467
- typ: 'CDOCOMM',
3468
- val: buffer
3827
+ }
3828
+ if (atRule.val == 'import') {
3829
+ // @ts-ignore
3830
+ if (tokens[0].typ == 'UrlFunc' && tokens[1].typ == 'Url-token') {
3831
+ tokens.shift();
3832
+ // @ts-ignore
3833
+ tokens[0].typ = 'String';
3834
+ // @ts-ignore
3835
+ tokens[0].val = `"${tokens[0].val}"`;
3836
+ }
3837
+ // @ts-ignore
3838
+ if (tokens[0].typ == 'String') {
3839
+ if (options.resolveImport) {
3840
+ const url = tokens[0].val.slice(1, -1);
3841
+ try {
3842
+ // @ts-ignore
3843
+ const root = await options.load(url, options.src).then((src) => {
3844
+ return parse$1(src, Object.assign({}, options, {
3845
+ minify: false,
3846
+ // @ts-ignore
3847
+ src: options.resolve(url, options.src).absolute
3848
+ }));
3469
3849
  });
3470
- buffer = '';
3471
- break;
3850
+ bytesIn += root.bytesIn;
3851
+ if (root.ast.chi.length > 0) {
3852
+ context.chi.push(...root.ast.chi);
3853
+ }
3854
+ if (root.errors.length > 0) {
3855
+ errors.push(...root.errors);
3856
+ }
3857
+ return null;
3858
+ }
3859
+ catch (error) {
3860
+ console.error(error);
3472
3861
  }
3473
3862
  }
3474
3863
  }
3475
- if (ind >= total) {
3476
- pushToken({ typ: 'BADCDO', val: buffer });
3477
- buffer = '';
3478
- }
3479
- break;
3480
- case '\\':
3481
- value = next();
3482
- // EOF
3483
- if (ind + 1 >= total) {
3484
- // end of stream ignore \\
3485
- pushToken(getType(buffer));
3486
- buffer = '';
3487
- break;
3488
- }
3489
- buffer += value;
3490
- break;
3491
- case '"':
3492
- case "'":
3493
- consumeString(value);
3494
- break;
3495
- case '~':
3496
- case '|':
3497
- if (buffer.length > 0) {
3498
- pushToken(getType(buffer));
3499
- buffer = '';
3500
- }
3501
- buffer += value;
3502
- value = next();
3503
- if (ind >= total) {
3504
- pushToken(getType(buffer));
3505
- buffer = '';
3506
- break;
3507
- }
3508
- if (value == '=') {
3509
- buffer += value;
3510
- pushToken({
3511
- typ: buffer[0] == '~' ? 'Includes' : 'Dash-matches',
3512
- val: buffer
3513
- });
3514
- buffer = '';
3515
- break;
3516
- }
3517
- pushToken(getType(buffer));
3518
- buffer = value;
3519
- break;
3520
- case '>':
3521
- if (tokens[tokens.length - 1]?.typ == 'Whitespace') {
3522
- tokens.pop();
3523
- }
3524
- if (buffer !== '') {
3525
- pushToken(getType(buffer));
3526
- buffer = '';
3527
- }
3528
- pushToken({ typ: 'Gt' });
3529
- consumeWhiteSpace();
3530
- break;
3531
- case ':':
3532
- case ',':
3533
- case '=':
3534
- if (buffer.length > 0) {
3535
- pushToken(getType(buffer));
3536
- buffer = '';
3537
- }
3538
- if (value == ':' && ':' == peek()) {
3539
- buffer += value + next();
3540
- break;
3541
- }
3542
- pushToken(getType(value));
3543
- buffer = '';
3544
- while (isWhiteSpace(peek().charCodeAt(0))) {
3545
- next();
3864
+ }
3865
+ // https://www.w3.org/TR/css-nesting-1/#conditionals
3866
+ // allowed nesting at-rules
3867
+ // there must be a top level rule in the stack
3868
+ const raw = tokens.reduce((acc, curr) => {
3869
+ acc.push(renderToken(curr, { removeComments: true }));
3870
+ return acc;
3871
+ }, []);
3872
+ const node = {
3873
+ typ: 'AtRule',
3874
+ nam: renderToken(atRule, { removeComments: true }),
3875
+ val: raw.join('')
3876
+ };
3877
+ Object.defineProperty(node, 'raw', { enumerable: false, writable: false, value: raw });
3878
+ if (delim.typ == 'Block-start') {
3879
+ node.chi = [];
3880
+ }
3881
+ loc = {
3882
+ sta: position,
3883
+ src
3884
+ };
3885
+ if (options.sourcemap) {
3886
+ node.loc = loc;
3887
+ }
3888
+ // @ts-ignore
3889
+ context.chi.push(node);
3890
+ return delim.typ == 'Block-start' ? node : null;
3891
+ }
3892
+ else {
3893
+ // rule
3894
+ if (delim.typ == 'Block-start') {
3895
+ const position = map.get(tokens[0]);
3896
+ // if (context.typ == 'Rule') {
3897
+ //
3898
+ // if (tokens[0]?.typ == 'Iden') {
3899
+ // errors.push({action: 'drop', message: 'invalid nesting rule', location: {src, ...position}});
3900
+ // return null;
3901
+ // }
3902
+ // }
3903
+ const uniq = new Map;
3904
+ parseTokens(tokens, { minify: options.minify }).reduce((acc, curr, index, array) => {
3905
+ if (curr.typ == 'Whitespace') {
3906
+ if (array[index - 1]?.typ == 'Gt' ||
3907
+ array[index + 1]?.typ == 'Gt' ||
3908
+ combinators.includes(array[index - 1]?.val) ||
3909
+ combinators.includes(array[index + 1]?.val)) {
3910
+ return acc;
3911
+ }
3912
+ }
3913
+ let t = renderToken(curr, { minify: true });
3914
+ if (t == ',') {
3915
+ acc.push([]);
3916
+ }
3917
+ else {
3918
+ acc[acc.length - 1].push(t);
3919
+ }
3920
+ return acc;
3921
+ }, [[]]).reduce((acc, curr) => {
3922
+ acc.set(curr.join(''), curr);
3923
+ return acc;
3924
+ }, uniq);
3925
+ const node = {
3926
+ typ: 'Rule',
3927
+ // @ts-ignore
3928
+ sel: [...uniq.keys()].join(','),
3929
+ chi: []
3930
+ };
3931
+ let raw = [...uniq.values()];
3932
+ Object.defineProperty(node, 'raw', { enumerable: false, writable: true, value: raw });
3933
+ loc = {
3934
+ sta: position,
3935
+ src
3936
+ };
3937
+ if (options.sourcemap) {
3938
+ node.loc = loc;
3546
3939
  }
3547
- break;
3548
- case ')':
3549
- if (buffer.length > 0) {
3550
- pushToken(getType(buffer));
3551
- buffer = '';
3940
+ // @ts-ignore
3941
+ context.chi.push(node);
3942
+ return node;
3943
+ }
3944
+ else {
3945
+ // declaration
3946
+ // @ts-ignore
3947
+ let name = null;
3948
+ // @ts-ignore
3949
+ let value = null;
3950
+ for (let i = 0; i < tokens.length; i++) {
3951
+ if (tokens[i].typ == 'Comment') {
3952
+ continue;
3953
+ }
3954
+ if (tokens[i].typ == 'Colon') {
3955
+ name = tokens.slice(0, i);
3956
+ value = parseTokens(tokens.slice(i + 1), {
3957
+ parseColor: true,
3958
+ src: options.src,
3959
+ resolveUrls: options.resolveUrls,
3960
+ resolve: options.resolve,
3961
+ cwd: options.cwd
3962
+ });
3963
+ }
3552
3964
  }
3553
- pushToken({ typ: 'End-parens' });
3554
- break;
3555
- case '(':
3556
- if (buffer.length == 0) {
3557
- pushToken({ typ: 'Start-parens' });
3965
+ if (name == null) {
3966
+ name = tokens;
3558
3967
  }
3559
- else {
3560
- buffer += value;
3561
- pushToken(getType(buffer));
3562
- buffer = '';
3563
- const token = tokens[tokens.length - 1];
3564
- if (token.typ == 'UrlFunc') {
3565
- // consume either string or url token
3566
- let whitespace = '';
3567
- value = peek();
3568
- while (isWhiteSpace(value.charCodeAt(0))) {
3569
- whitespace += value;
3570
- }
3571
- if (whitespace.length > 0) {
3572
- next(whitespace.length);
3573
- }
3574
- value = peek();
3575
- if (value == '"' || value == "'") {
3576
- consumeString(next());
3577
- let token = tokens[tokens.length - 1];
3578
- if (['String', 'Literal'].includes(token.typ) && urlTokenMatcher.test(token.val)) {
3579
- if (token.val.slice(1, 6) != 'data:') {
3580
- if (token.typ == 'String') {
3581
- token.val = token.val.slice(1, -1);
3582
- }
3583
- // @ts-ignore
3584
- token.typ = 'Url-token';
3585
- }
3586
- }
3587
- break;
3588
- }
3589
- else {
3590
- buffer = '';
3591
- do {
3592
- let cp = value.charCodeAt(0);
3593
- // EOF -
3594
- if (cp == null) {
3595
- pushToken({ typ: 'Bad-url-token', val: buffer });
3596
- break;
3597
- }
3598
- // ')'
3599
- if (cp == 0x29 || cp == null) {
3600
- if (buffer.length == 0) {
3601
- pushToken({ typ: 'Bad-url-token', val: '' });
3602
- }
3603
- else {
3604
- pushToken({ typ: 'Url-token', val: buffer });
3605
- }
3606
- if (cp != null) {
3607
- pushToken(getType(next()));
3608
- }
3609
- break;
3610
- }
3611
- if (isWhiteSpace(cp)) {
3612
- whitespace = next();
3613
- while (true) {
3614
- value = peek();
3615
- cp = value.charCodeAt(0);
3616
- if (isWhiteSpace(cp)) {
3617
- whitespace += value;
3618
- continue;
3619
- }
3620
- break;
3621
- }
3622
- if (cp == null || cp == 0x29) {
3623
- continue;
3624
- }
3625
- // bad url token
3626
- buffer += next(whitespace.length);
3627
- do {
3628
- value = peek();
3629
- cp = value.charCodeAt(0);
3630
- if (cp == null || cp == 0x29) {
3631
- break;
3632
- }
3633
- buffer += next();
3634
- } while (true);
3635
- pushToken({ typ: 'Bad-url-token', val: buffer });
3636
- continue;
3637
- }
3638
- buffer += next();
3639
- value = peek();
3640
- } while (true);
3641
- buffer = '';
3968
+ const position = map.get(name[0]);
3969
+ if (name.length > 0) {
3970
+ for (let i = 1; i < name.length; i++) {
3971
+ if (name[i].typ != 'Whitespace' && name[i].typ != 'Comment') {
3972
+ errors.push({
3973
+ action: 'drop',
3974
+ message: 'invalid declaration',
3975
+ location: { src, ...position }
3976
+ });
3977
+ return null;
3642
3978
  }
3643
3979
  }
3644
3980
  }
3645
- break;
3646
- case '[':
3647
- case ']':
3648
- case '{':
3649
- case '}':
3650
- case ';':
3651
- if (buffer.length > 0) {
3652
- pushToken(getType(buffer));
3653
- buffer = '';
3981
+ if (value == null) {
3982
+ errors.push({
3983
+ action: 'drop',
3984
+ message: 'invalid declaration',
3985
+ location: { src, ...position }
3986
+ });
3987
+ return null;
3654
3988
  }
3655
- pushToken(getBlockType(value));
3656
- let node = null;
3657
- if (value == '{' || value == ';') {
3658
- node = await parseNode(tokens);
3659
- if (node != null) {
3660
- stack.push(node);
3661
- // @ts-ignore
3662
- context = node;
3663
- }
3664
- else if (value == '{') {
3665
- // node == null
3666
- // consume and throw away until the closing '}' or EOF
3667
- consume('{', '}');
3668
- }
3669
- tokens.length = 0;
3670
- map.clear();
3989
+ if (value.length == 0) {
3990
+ errors.push({
3991
+ action: 'drop',
3992
+ message: 'invalid declaration',
3993
+ location: { src, ...position }
3994
+ });
3995
+ return null;
3671
3996
  }
3672
- else if (value == '}') {
3673
- await parseNode(tokens);
3674
- const previousNode = stack.pop();
3997
+ const node = {
3998
+ typ: 'Declaration',
3675
3999
  // @ts-ignore
3676
- context = stack[stack.length - 1] || ast;
4000
+ nam: renderToken(name.shift(), { removeComments: true }),
3677
4001
  // @ts-ignore
3678
- if (options.removeEmpty && previousNode != null && previousNode.chi.length == 0 && context.chi[context.chi.length - 1] == previousNode) {
3679
- context.chi.pop();
3680
- }
3681
- tokens.length = 0;
3682
- map.clear();
3683
- buffer = '';
3684
- }
3685
- break;
3686
- case '!':
3687
- if (buffer.length > 0) {
3688
- pushToken(getType(buffer));
3689
- buffer = '';
4002
+ val: value
4003
+ };
4004
+ while (node.val[0]?.typ == 'Whitespace') {
4005
+ node.val.shift();
3690
4006
  }
3691
- const important = peek(9);
3692
- if (important == 'important') {
3693
- if (tokens[tokens.length - 1]?.typ == 'Whitespace') {
3694
- tokens.pop();
3695
- }
3696
- pushToken({ typ: 'Important' });
3697
- next(9);
3698
- buffer = '';
3699
- break;
4007
+ if (node.val.length == 0) {
4008
+ errors.push({
4009
+ action: 'drop',
4010
+ message: 'invalid declaration',
4011
+ location: { src, ...position }
4012
+ });
4013
+ return null;
3700
4014
  }
3701
- buffer = '!';
3702
- break;
3703
- default:
3704
- buffer += value;
3705
- break;
4015
+ // @ts-ignore
4016
+ context.chi.push(node);
4017
+ return null;
4018
+ }
3706
4019
  }
3707
4020
  }
3708
- if (buffer.length > 0) {
3709
- pushToken(getType(buffer));
4021
+ function mapToken(token) {
4022
+ const node = getTokenType(token.token, token.hint);
4023
+ map.set(node, token.position);
4024
+ return node;
4025
+ }
4026
+ const iter = tokenize(iterator);
4027
+ let item;
4028
+ while (true) {
4029
+ item = iter.next().value;
4030
+ if (item == null) {
4031
+ break;
4032
+ }
4033
+ tokens.push(item);
4034
+ bytesIn = item.bytesIn;
4035
+ if (item.token == ';' || item.token == '{') {
4036
+ let node = await parseNode(tokens);
4037
+ if (node != null) {
4038
+ stack.push(node);
4039
+ // @ts-ignore
4040
+ context = node;
4041
+ }
4042
+ else if (item.token == '{') {
4043
+ // node == null
4044
+ // consume and throw away until the closing '}' or EOF
4045
+ let inBlock = 1;
4046
+ do {
4047
+ item = iter.next().value;
4048
+ if (item == null) {
4049
+ break;
4050
+ }
4051
+ if (item.token == '{') {
4052
+ inBlock++;
4053
+ }
4054
+ else if (item.token == '}') {
4055
+ inBlock--;
4056
+ }
4057
+ } while (inBlock != 0);
4058
+ }
4059
+ tokens = [];
4060
+ map = new Map;
4061
+ }
4062
+ else if (item.token == '}') {
4063
+ await parseNode(tokens);
4064
+ const previousNode = stack.pop();
4065
+ // @ts-ignore
4066
+ context = stack[stack.length - 1] || ast;
4067
+ // @ts-ignore
4068
+ if (options.removeEmpty && previousNode != null && previousNode.chi.length == 0 && context.chi[context.chi.length - 1] == previousNode) {
4069
+ context.chi.pop();
4070
+ }
4071
+ tokens = [];
4072
+ map = new Map;
4073
+ }
3710
4074
  }
3711
4075
  if (tokens.length > 0) {
3712
4076
  await parseNode(tokens);
3713
4077
  }
3714
- if (options.compress) {
4078
+ if (options.minify) {
3715
4079
  if (ast.chi.length > 0) {
3716
- deduplicate(ast, options, true);
4080
+ minify(ast, options, true);
3717
4081
  }
3718
4082
  }
3719
4083
  return { ast, errors, bytesIn };
3720
4084
  }
4085
+ function parseString(src, options = { location: false }) {
4086
+ return [...tokenize(src)].map(t => {
4087
+ const token = getTokenType(t.token, t.hint);
4088
+ if (options.location) {
4089
+ Object.assign(token, { loc: t.position });
4090
+ }
4091
+ return token;
4092
+ });
4093
+ }
4094
+ function getTokenType(val, hint) {
4095
+ if (val === '' && hint == null) {
4096
+ throw new Error('empty string?');
4097
+ }
4098
+ if (hint != null) {
4099
+ return ([
4100
+ 'Whitespace', 'Semi-colon', 'Colon', 'Block-start',
4101
+ 'Block-start', 'Attr-start', 'Attr-end', 'Start-parens', 'End-parens',
4102
+ 'Comma', 'Gt', 'Lt'
4103
+ ].includes(hint) ? { typ: hint } : { typ: hint, val });
4104
+ }
4105
+ if (val == ' ') {
4106
+ return { typ: 'Whitespace' };
4107
+ }
4108
+ if (val == ';') {
4109
+ return { typ: 'Semi-colon' };
4110
+ }
4111
+ if (val == '{') {
4112
+ return { typ: 'Block-start' };
4113
+ }
4114
+ if (val == '}') {
4115
+ return { typ: 'Block-end' };
4116
+ }
4117
+ if (val == '[') {
4118
+ return { typ: 'Attr-start' };
4119
+ }
4120
+ if (val == ']') {
4121
+ return { typ: 'Attr-end' };
4122
+ }
4123
+ if (val == ':') {
4124
+ return { typ: 'Colon' };
4125
+ }
4126
+ if (val == ')') {
4127
+ return { typ: 'End-parens' };
4128
+ }
4129
+ if (val == '(') {
4130
+ return { typ: 'Start-parens' };
4131
+ }
4132
+ if (val == '=') {
4133
+ return { typ: 'Delim', val };
4134
+ }
4135
+ if (val == ';') {
4136
+ return { typ: 'Semi-colon' };
4137
+ }
4138
+ if (val == ',') {
4139
+ return { typ: 'Comma' };
4140
+ }
4141
+ if (val == '<') {
4142
+ return { typ: 'Lt' };
4143
+ }
4144
+ if (val == '>') {
4145
+ return { typ: 'Gt' };
4146
+ }
4147
+ if (isPseudo(val)) {
4148
+ return val.endsWith('(') ? {
4149
+ typ: 'Pseudo-class-func',
4150
+ val: val.slice(0, -1),
4151
+ chi: []
4152
+ }
4153
+ : {
4154
+ typ: 'Pseudo-class',
4155
+ val
4156
+ };
4157
+ }
4158
+ if (isAtKeyword(val)) {
4159
+ return {
4160
+ typ: 'At-rule',
4161
+ val: val.slice(1)
4162
+ };
4163
+ }
4164
+ if (isFunction(val)) {
4165
+ val = val.slice(0, -1);
4166
+ return {
4167
+ typ: val == 'url' ? 'UrlFunc' : 'Func',
4168
+ val,
4169
+ chi: []
4170
+ };
4171
+ }
4172
+ if (isNumber(val)) {
4173
+ return {
4174
+ typ: 'Number',
4175
+ val
4176
+ };
4177
+ }
4178
+ if (isDimension(val)) {
4179
+ return parseDimension(val);
4180
+ }
4181
+ if (isPercentage(val)) {
4182
+ return {
4183
+ typ: 'Perc',
4184
+ val: val.slice(0, -1)
4185
+ };
4186
+ }
4187
+ const v = val.toLowerCase();
4188
+ if (v == 'currentcolor' || val == 'transparent' || v in COLORS_NAMES) {
4189
+ return {
4190
+ typ: 'Color',
4191
+ val,
4192
+ kin: 'lit'
4193
+ };
4194
+ }
4195
+ if (isIdent(val)) {
4196
+ return {
4197
+ typ: 'Iden',
4198
+ val
4199
+ };
4200
+ }
4201
+ if (val.charAt(0) == '#' && isHexColor(val)) {
4202
+ return {
4203
+ typ: 'Color',
4204
+ val,
4205
+ kin: 'hex'
4206
+ };
4207
+ }
4208
+ if (val.charAt(0) == '#' && isHash(val)) {
4209
+ return {
4210
+ typ: 'Hash',
4211
+ val
4212
+ };
4213
+ }
4214
+ if ('"\''.includes(val.charAt(0))) {
4215
+ return {
4216
+ typ: 'Unclosed-string',
4217
+ val
4218
+ };
4219
+ }
4220
+ return {
4221
+ typ: 'Literal',
4222
+ val
4223
+ };
4224
+ }
3721
4225
  function parseTokens(tokens, options = {}) {
3722
4226
  for (let i = 0; i < tokens.length; i++) {
3723
4227
  const t = tokens[i];
@@ -3725,6 +4229,7 @@
3725
4229
  i + 1 == tokens.length ||
3726
4230
  ['Comma'].includes(tokens[i + 1].typ) ||
3727
4231
  (i > 0 &&
4232
+ tokens[i + 1]?.typ != 'Literal' &&
3728
4233
  funcLike.includes(tokens[i - 1].typ) &&
3729
4234
  !['var', 'calc'].includes(tokens[i - 1].val))))) {
3730
4235
  tokens.splice(i--, 1);
@@ -3771,7 +4276,7 @@
3771
4276
  if (t.chi.length > 1) {
3772
4277
  /*(<AttrToken>t).chi =*/
3773
4278
  // @ts-ignore
3774
- parseTokens(t.chi, options);
4279
+ parseTokens(t.chi, t.typ);
3775
4280
  }
3776
4281
  // @ts-ignore
3777
4282
  t.chi.forEach(val => {
@@ -3883,8 +4388,8 @@
3883
4388
  // @ts-ignore
3884
4389
  if (t.chi.length > 0) {
3885
4390
  // @ts-ignore
3886
- parseTokens(t.chi, options);
3887
- if (t.typ == 'Pseudo-class-func' && t.val == ':is' && options.compress) {
4391
+ parseTokens(t.chi, t.typ);
4392
+ if (t.typ == 'Pseudo-class-func' && t.val == ':is' && options.minify) {
3888
4393
  //
3889
4394
  const count = t.chi.filter(t => t.typ != 'Comment').length;
3890
4395
  if (count == 1 ||
@@ -3922,40 +4427,9 @@
3922
4427
  }
3923
4428
  return tokens;
3924
4429
  }
3925
- function getBlockType(chr) {
3926
- if (chr == ';') {
3927
- return { typ: 'Semi-colon' };
3928
- }
3929
- if (chr == '{') {
3930
- return { typ: 'Block-start' };
3931
- }
3932
- if (chr == '}') {
3933
- return { typ: 'Block-end' };
3934
- }
3935
- if (chr == '[') {
3936
- return { typ: 'Attr-start' };
3937
- }
3938
- if (chr == ']') {
3939
- return { typ: 'Attr-end' };
3940
- }
3941
- throw new Error(`unhandled token: '${chr}'`);
3942
- }
3943
-
3944
- function* walk(node) {
3945
- // @ts-ignore
3946
- yield* doWalk(node, null, null);
3947
- }
3948
- function* doWalk(node, parent, root) {
3949
- yield { node, parent, root };
3950
- if ('chi' in node) {
3951
- for (const child of node.chi) {
3952
- yield* doWalk(child, node, (root == null ? node : root));
3953
- }
3954
- }
3955
- }
3956
4430
 
3957
4431
  async function transform$1(css, options = {}) {
3958
- options = { compress: true, removeEmpty: true, ...options };
4432
+ options = { minify: true, removeEmpty: true, ...options };
3959
4433
  const startTime = performance.now();
3960
4434
  const parseResult = await parse$1(css, options);
3961
4435
  const renderTime = performance.now();
@@ -4103,18 +4577,44 @@
4103
4577
  }));
4104
4578
  }
4105
4579
 
4106
- exports.deduplicate = deduplicate;
4107
- exports.deduplicateRule = deduplicateRule;
4580
+ exports.combinators = combinators;
4108
4581
  exports.dirname = dirname;
4582
+ exports.getConfig = getConfig;
4109
4583
  exports.hasDeclaration = hasDeclaration;
4584
+ exports.isAngle = isAngle;
4585
+ exports.isAtKeyword = isAtKeyword;
4586
+ exports.isDigit = isDigit;
4587
+ exports.isDimension = isDimension;
4588
+ exports.isFrequency = isFrequency;
4589
+ exports.isFunction = isFunction;
4590
+ exports.isHash = isHash;
4591
+ exports.isHexColor = isHexColor;
4592
+ exports.isHexDigit = isHexDigit;
4593
+ exports.isIdent = isIdent;
4594
+ exports.isIdentCodepoint = isIdentCodepoint;
4595
+ exports.isIdentStart = isIdentStart;
4596
+ exports.isLength = isLength;
4597
+ exports.isNewLine = isNewLine;
4598
+ exports.isNumber = isNumber;
4599
+ exports.isPercentage = isPercentage;
4600
+ exports.isPseudo = isPseudo;
4601
+ exports.isResolution = isResolution;
4602
+ exports.isTime = isTime;
4603
+ exports.isWhiteSpace = isWhiteSpace;
4110
4604
  exports.load = load;
4111
4605
  exports.matchUrl = matchUrl;
4606
+ exports.minify = minify;
4607
+ exports.minifyRule = minifyRule;
4112
4608
  exports.parse = parse;
4609
+ exports.parseDimension = parseDimension;
4610
+ exports.parseString = parseString;
4113
4611
  exports.reduceSelector = reduceSelector;
4114
4612
  exports.render = render;
4115
4613
  exports.renderToken = renderToken;
4116
4614
  exports.resolve = resolve;
4615
+ exports.tokenize = tokenize;
4117
4616
  exports.transform = transform;
4617
+ exports.urlTokenMatcher = urlTokenMatcher;
4118
4618
  exports.walk = walk;
4119
4619
 
4120
4620
  }));