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