@tbela99/css-parser 0.0.1-alpha5 → 0.0.1-rc2

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