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

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