cssstyle 4.4.0 → 4.6.0

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.
@@ -11,6 +11,7 @@ const generatedProperties = require("./generated/properties");
11
11
  const { hasVarFunc, parseKeyword, parseShorthand, prepareValue, splitValue } = require("./parsers");
12
12
  const { dashedToCamelCase } = require("./utils/camelize");
13
13
  const { getPropertyDescriptor } = require("./utils/propertyDescriptors");
14
+ const { asciiLowercase } = require("./utils/strings");
14
15
 
15
16
  /**
16
17
  * @see https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface
@@ -275,7 +276,7 @@ class CSSStyleDeclaration {
275
276
  this._setProperty(property, value);
276
277
  return;
277
278
  }
278
- property = property.toLowerCase();
279
+ property = asciiLowercase(property);
279
280
  if (!allProperties.has(property) && !allExtraProperties.has(property)) {
280
281
  return;
281
282
  }
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // autogenerated - 2025-06-08
2
+ // autogenerated - 2025-06-25
3
3
  // https://www.w3.org/Style/CSS/all-properties.en.html
4
4
 
5
5
  module.exports = new Set([
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
- // autogenerated - 2025-06-08
2
+ // autogenerated - 2025-06-25
3
3
  // https://www.w3.org/Style/CSS/all-properties.en.html
4
4
 
5
5
  var external_dependency_parsers_0 = require("../parsers.js");
6
+ var external_dependency_strings_1 = require("../utils/strings.js");
6
7
  var backgroundImage_export_parse, backgroundImage_export_isValid, backgroundImage_export_definition;
7
8
  backgroundImage_export_parse = function parse(v) {
8
9
  return external_dependency_parsers_0.parseImage(v);
@@ -18,7 +19,7 @@ backgroundImage_export_definition = {
18
19
  v = external_dependency_parsers_0.prepareValue(v, this._global);
19
20
  if (external_dependency_parsers_0.hasVarFunc(v)) {
20
21
  this._setProperty("background", "");
21
- this._setProperty("backgound-image", v);
22
+ this._setProperty("background-image", v);
22
23
  } else {
23
24
  this._setProperty("background-image", backgroundImage_export_parse(v));
24
25
  }
@@ -65,7 +66,7 @@ backgroundPosition_export_definition = {
65
66
  v = external_dependency_parsers_0.prepareValue(v, this._global);
66
67
  if (external_dependency_parsers_0.hasVarFunc(v)) {
67
68
  this._setProperty("background", "");
68
- this._setProperty("backgound-position", v);
69
+ this._setProperty("background-position", v);
69
70
  } else {
70
71
  this._setProperty("background-position", backgroundPosition_export_parse(v));
71
72
  }
@@ -92,7 +93,7 @@ backgroundRepeat_export_definition = {
92
93
  v = external_dependency_parsers_0.prepareValue(v, this._global);
93
94
  if (external_dependency_parsers_0.hasVarFunc(v)) {
94
95
  this._setProperty("background", "");
95
- this._setProperty("backgound-repeat", v);
96
+ this._setProperty("background-repeat", v);
96
97
  } else {
97
98
  this._setProperty("background-repeat", backgroundRepeat_export_parse(v));
98
99
  }
@@ -119,7 +120,7 @@ backgroundAttachment_export_definition = {
119
120
  v = external_dependency_parsers_0.prepareValue(v, this._global);
120
121
  if (external_dependency_parsers_0.hasVarFunc(v)) {
121
122
  this._setProperty("background", "");
122
- this._setProperty("backgound-attachemnt", v);
123
+ this._setProperty("background-attachment", v);
123
124
  } else {
124
125
  this._setProperty("background-attachment", backgroundAttachment_export_parse(v));
125
126
  }
@@ -149,7 +150,7 @@ backgroundColor_export_definition = {
149
150
  v = external_dependency_parsers_0.prepareValue(v, this._global);
150
151
  if (external_dependency_parsers_0.hasVarFunc(v)) {
151
152
  this._setProperty("background", "");
152
- this._setProperty("backgound-color", v);
153
+ this._setProperty("background-color", v);
153
154
  } else {
154
155
  this._setProperty("background-color", backgroundColor_export_parse(v));
155
156
  }
@@ -189,7 +190,12 @@ const background_local_var_shorthandFor = new Map([["background-image", {
189
190
  background_export_definition = {
190
191
  set(v) {
191
192
  v = external_dependency_parsers_0.prepareValue(v, this._global);
192
- if (v.toLowerCase() === "none" || external_dependency_parsers_0.hasVarFunc(v)) {
193
+ if (/^none$/i.test(v)) {
194
+ for (const [key] of background_local_var_shorthandFor) {
195
+ this._setProperty(key, "");
196
+ }
197
+ this._setProperty("background", external_dependency_strings_1.asciiLowercase(v));
198
+ } else if (external_dependency_parsers_0.hasVarFunc(v)) {
193
199
  for (const [key] of background_local_var_shorthandFor) {
194
200
  this._setProperty(key, "");
195
201
  }
@@ -258,7 +264,7 @@ borderStyle_export_isValid = function isValid(v) {
258
264
  borderStyle_export_definition = {
259
265
  set(v) {
260
266
  v = external_dependency_parsers_0.prepareValue(v, this._global);
261
- if (v.toLowerCase() === "none") {
267
+ if (/^none$/i.test(v)) {
262
268
  v = "";
263
269
  }
264
270
  if (external_dependency_parsers_0.hasVarFunc(v)) {
@@ -323,7 +329,7 @@ const border_local_var_shorthandFor = new Map([["border-width", {
323
329
  border_export_definition = {
324
330
  set(v) {
325
331
  v = external_dependency_parsers_0.prepareValue(v, this._global);
326
- if (v.toLowerCase() === "none") {
332
+ if (/^none$/i.test(v)) {
327
333
  v = "";
328
334
  }
329
335
  if (external_dependency_parsers_0.hasVarFunc(v)) {
@@ -1014,7 +1020,7 @@ clip_export_parse = function parse(v) {
1014
1020
  return val;
1015
1021
  }
1016
1022
  // parse legacy <shape>
1017
- v = v.toLowerCase();
1023
+ v = external_dependency_strings_1.asciiLowercase(v);
1018
1024
  const matches = v.match(/^rect\(\s*(.*)\s*\)$/);
1019
1025
  if (!matches) {
1020
1026
  return;
@@ -1427,7 +1433,8 @@ fontFamily_export_parse = function parse(v) {
1427
1433
  if (v === "") {
1428
1434
  return v;
1429
1435
  }
1430
- const keywords = ["serif", "sans-serif", "system-ui", "cursive", "fantasy", "monospace"];
1436
+ const keywords = ["serif", "sans-serif", "cursive", "fantasy", "monospace", "system-ui", "math", "ui-serif", "ui-sans-serif", "ui-monospace", "ui-rounded"];
1437
+ const genericValues = ["fangsong", "kai", "khmer-mul", "nastaliq"];
1431
1438
  const val = external_dependency_parsers_0.splitValue(v, {
1432
1439
  delimiter: ","
1433
1440
  });
@@ -1446,16 +1453,27 @@ fontFamily_export_parse = function parse(v) {
1446
1453
  valid = true;
1447
1454
  continue;
1448
1455
  }
1449
- // This implementation does not strictly follow the specification. The spec
1450
- // does not require the first letter of the font-family to be capitalized.
1451
- // Also, unquoted font-family names are not restricted to ASCII only.
1456
+ const obj = external_dependency_parsers_0.parseFunction(i);
1457
+ if (obj) {
1458
+ const {
1459
+ name,
1460
+ value
1461
+ } = obj;
1462
+ if (name === "generic" && genericValues.includes(value)) {
1463
+ font.push(`${name}(${value})`);
1464
+ valid = true;
1465
+ continue;
1466
+ }
1467
+ }
1468
+ // This implementation does not strictly follow the specification.
1469
+ // The spec does not require the first letter of the font-family to be
1470
+ // capitalized, and unquoted font-family names are not restricted to ASCII.
1452
1471
  // However, in the real world, the first letter of the ASCII font-family
1453
- // names are always capitalized, and unquoted font-family names do not
1454
- // contain spaces, e.g. `Times`, and AFAIK, non-ASCII font-family names are
1455
- // always quoted even without spaces, e.g. `"メイリオ"`.
1456
- // Therefore, it is unlikely that this implementation will cause problems.
1472
+ // names are capitalized, and unquoted font-family names do not contain
1473
+ // spaces, e.g. `Times`. And non-ASCII font-family names are quoted even
1474
+ // without spaces, e.g. `"メイリオ"`.
1457
1475
  // @see https://drafts.csswg.org/css-fonts/#font-family-prop
1458
- if (/^\s*(?:[A-Z][A-Za-z\d\s-]+)\s*$/.test(i)) {
1476
+ if (i !== "undefined" && /^(?:[A-Z][A-Za-z\d-]+(?:\s+[A-Z][A-Za-z\d-]+)*|-?[a-z][a-z-]+)$/.test(i)) {
1459
1477
  font.push(i.trim());
1460
1478
  valid = true;
1461
1479
  continue;
@@ -1523,50 +1541,131 @@ font_export_parse = function parse(v) {
1523
1541
  const [fontBlock, ...families] = external_dependency_parsers_0.splitValue(v, {
1524
1542
  delimiter: ","
1525
1543
  });
1526
- let blockA, blockB;
1527
- if (fontBlock.includes("/")) {
1528
- [blockA, blockB] = external_dependency_parsers_0.splitValue(fontBlock, {
1529
- delimiter: "/"
1530
- });
1531
- } else {
1532
- blockA = fontBlock.trim();
1533
- }
1534
- const obj = external_dependency_parsers_0.parseShorthand(blockA, font_local_var_shorthandFor, true);
1535
- if (!obj) {
1536
- return;
1537
- }
1538
- const font = {};
1544
+ const [fontBlockA, fontBlockB] = external_dependency_parsers_0.splitValue(fontBlock, {
1545
+ delimiter: "/"
1546
+ });
1547
+ const font = {
1548
+ "font-style": "normal",
1549
+ "font-variant": "normal",
1550
+ "font-weight": "normal"
1551
+ };
1539
1552
  const fontFamilies = new Set();
1540
- for (const [property, value] of Object.entries(obj)) {
1541
- if (property === "font-family") {
1542
- if (!blockB) {
1543
- fontFamilies.add(value);
1544
- }
1545
- } else {
1546
- font[property] = value;
1553
+ if (fontBlockB) {
1554
+ const [lineB, ...familiesB] = fontBlockB.trim().split(" ");
1555
+ if (!lineB || !{
1556
+ parse: lineHeight_export_parse,
1557
+ isValid: lineHeight_export_isValid,
1558
+ definition: lineHeight_export_definition
1559
+ }.isValid(lineB) || !familiesB.length) {
1560
+ return;
1547
1561
  }
1548
- }
1549
- // blockB, if matched, includes line-height and first font-family
1550
- if (blockB) {
1551
- const [lineheight, family] = external_dependency_parsers_0.splitValue(blockB);
1552
- if ({
1562
+ const lineHeightB = {
1553
1563
  parse: lineHeight_export_parse,
1554
1564
  isValid: lineHeight_export_isValid,
1555
1565
  definition: lineHeight_export_definition
1556
- }.isValid(lineheight)) {
1557
- font["line-height"] = {
1558
- parse: lineHeight_export_parse,
1559
- isValid: lineHeight_export_isValid,
1560
- definition: lineHeight_export_definition
1561
- }.parse(lineheight);
1566
+ }.parse(lineB);
1567
+ const familyB = familiesB.join(" ");
1568
+ if ({
1569
+ parse: fontFamily_export_parse,
1570
+ isValid: fontFamily_export_isValid,
1571
+ definition: fontFamily_export_definition
1572
+ }.isValid(familyB)) {
1573
+ fontFamilies.add({
1574
+ parse: fontFamily_export_parse,
1575
+ isValid: fontFamily_export_isValid,
1576
+ definition: fontFamily_export_definition
1577
+ }.parse(familyB));
1562
1578
  } else {
1563
1579
  return;
1564
1580
  }
1565
- if ({
1581
+ const parts = external_dependency_parsers_0.splitValue(fontBlockA.trim());
1582
+ const properties = ["font-style", "font-variant", "font-weight", "font-size"];
1583
+ for (const part of parts) {
1584
+ if (part === "normal") {
1585
+ continue;
1586
+ } else {
1587
+ for (const property of properties) {
1588
+ switch (property) {
1589
+ case "font-style":
1590
+ case "font-variant":
1591
+ case "font-weight":
1592
+ case "font-size":
1593
+ {
1594
+ const value = font_local_var_shorthandFor.get(property);
1595
+ if (value.isValid(part)) {
1596
+ font[property] = value.parse(part);
1597
+ }
1598
+ break;
1599
+ }
1600
+ default:
1601
+ }
1602
+ }
1603
+ }
1604
+ }
1605
+ if (Object.hasOwn(font, "font-size")) {
1606
+ font["line-height"] = lineHeightB;
1607
+ } else {
1608
+ return;
1609
+ }
1610
+ } else {
1611
+ // FIXME: Switch to toReversed() when we can drop Node.js 18 support.
1612
+ const revParts = [...external_dependency_parsers_0.splitValue(fontBlockA.trim())].reverse();
1613
+ const revFontFamily = [];
1614
+ const properties = ["font-style", "font-variant", "font-weight", "line-height"];
1615
+ font["font-style"] = "normal";
1616
+ font["font-variant"] = "normal";
1617
+ font["font-weight"] = "normal";
1618
+ font["line-height"] = "normal";
1619
+ let fontSizeA;
1620
+ for (const part of revParts) {
1621
+ if (fontSizeA) {
1622
+ if (part === "normal") {
1623
+ continue;
1624
+ } else {
1625
+ for (const property of properties) {
1626
+ switch (property) {
1627
+ case "font-style":
1628
+ case "font-variant":
1629
+ case "font-weight":
1630
+ case "line-height":
1631
+ {
1632
+ const value = font_local_var_shorthandFor.get(property);
1633
+ if (value.isValid(part)) {
1634
+ font[property] = value.parse(part);
1635
+ }
1636
+ break;
1637
+ }
1638
+ default:
1639
+ }
1640
+ }
1641
+ }
1642
+ } else if ({
1643
+ parse: fontSize_export_parse,
1644
+ isValid: fontSize_export_isValid,
1645
+ definition: fontSize_export_definition
1646
+ }.isValid(part)) {
1647
+ fontSizeA = {
1648
+ parse: fontSize_export_parse,
1649
+ isValid: fontSize_export_isValid,
1650
+ definition: fontSize_export_definition
1651
+ }.parse(part);
1652
+ } else if ({
1653
+ parse: fontFamily_export_parse,
1654
+ isValid: fontFamily_export_isValid,
1655
+ definition: fontFamily_export_definition
1656
+ }.isValid(part)) {
1657
+ revFontFamily.push(part);
1658
+ } else {
1659
+ return;
1660
+ }
1661
+ }
1662
+ const family = revFontFamily.reverse().join(" ");
1663
+ if (fontSizeA && {
1566
1664
  parse: fontFamily_export_parse,
1567
1665
  isValid: fontFamily_export_isValid,
1568
1666
  definition: fontFamily_export_definition
1569
1667
  }.isValid(family)) {
1668
+ font["font-size"] = fontSizeA;
1570
1669
  fontFamilies.add({
1571
1670
  parse: fontFamily_export_parse,
1572
1671
  isValid: fontFamily_export_isValid,
@@ -1597,7 +1696,7 @@ font_export_parse = function parse(v) {
1597
1696
  font_export_definition = {
1598
1697
  set(v) {
1599
1698
  v = external_dependency_parsers_0.prepareValue(v, this._global);
1600
- if (external_dependency_parsers_0.hasVarFunc(v)) {
1699
+ if (v === "" || external_dependency_parsers_0.hasVarFunc(v)) {
1601
1700
  for (const [key] of font_local_var_shorthandFor) {
1602
1701
  this._setProperty(key, "");
1603
1702
  }
@@ -1612,7 +1711,7 @@ font_export_definition = {
1612
1711
  const val = obj[key];
1613
1712
  if (typeof val === "string") {
1614
1713
  this._setProperty(key, val);
1615
- if (val && !str.has(val)) {
1714
+ if (val && val !== "normal" && !str.has(val)) {
1616
1715
  if (key === "line-height") {
1617
1716
  str.add(`/ ${val}`);
1618
1717
  } else {
@@ -1635,7 +1734,7 @@ font_export_definition = {
1635
1734
  if (external_dependency_parsers_0.hasVarFunc(v)) {
1636
1735
  return "";
1637
1736
  }
1638
- if (v && !str.has(v)) {
1737
+ if (v && v !== "normal" && !str.has(v)) {
1639
1738
  if (key === "line-height") {
1640
1739
  str.add(`/ ${v}`);
1641
1740
  } else {
@@ -1727,7 +1826,7 @@ lightingColor_export_definition = {
1727
1826
  var margin_export_parse, margin_export_isValid, margin_export_definition;
1728
1827
  const margin_local_var_positions = ["top", "right", "bottom", "left"];
1729
1828
  margin_export_parse = function parse(v) {
1730
- const val = external_dependency_parsers_0.parseMeasurement(v, true);
1829
+ const val = external_dependency_parsers_0.parseMeasurement(v);
1731
1830
  if (val) {
1732
1831
  return val;
1733
1832
  }
@@ -1764,7 +1863,7 @@ margin_export_definition = {
1764
1863
  };
1765
1864
  var marginBottom_export_parse, marginBottom_export_isValid, marginBottom_export_definition;
1766
1865
  marginBottom_export_parse = function parse(v) {
1767
- const val = external_dependency_parsers_0.parseMeasurement(v, true);
1866
+ const val = external_dependency_parsers_0.parseMeasurement(v);
1768
1867
  if (val) {
1769
1868
  return val;
1770
1869
  }
@@ -1794,7 +1893,7 @@ marginBottom_export_definition = {
1794
1893
  };
1795
1894
  var marginLeft_export_parse, marginLeft_export_isValid, marginLeft_export_definition;
1796
1895
  marginLeft_export_parse = function parse(v) {
1797
- const val = external_dependency_parsers_0.parseMeasurement(v, true);
1896
+ const val = external_dependency_parsers_0.parseMeasurement(v);
1798
1897
  if (val) {
1799
1898
  return val;
1800
1899
  }
@@ -1824,7 +1923,7 @@ marginLeft_export_definition = {
1824
1923
  };
1825
1924
  var marginRight_export_parse, marginRight_export_isValid, marginRight_export_definition;
1826
1925
  marginRight_export_parse = function parse(v) {
1827
- const val = external_dependency_parsers_0.parseMeasurement(v, true);
1926
+ const val = external_dependency_parsers_0.parseMeasurement(v);
1828
1927
  if (val) {
1829
1928
  return val;
1830
1929
  }
@@ -1854,7 +1953,7 @@ marginRight_export_definition = {
1854
1953
  };
1855
1954
  var marginTop_export_parse, marginTop_export_isValid, marginTop_export_definition;
1856
1955
  marginTop_export_parse = function parse(v) {
1857
- const val = external_dependency_parsers_0.parseMeasurement(v, true);
1956
+ const val = external_dependency_parsers_0.parseMeasurement(v);
1858
1957
  if (val) {
1859
1958
  return val;
1860
1959
  }
package/lib/parsers.js CHANGED
@@ -5,6 +5,7 @@
5
5
  "use strict";
6
6
 
7
7
  const { resolve: resolveColor, utils } = require("@asamuzakjp/css-color");
8
+ const { asciiLowercase } = require("./utils/strings");
8
9
 
9
10
  const { cssCalc, isColor, isGradient, splitValue } = utils;
10
11
 
@@ -24,44 +25,48 @@ const NUM_TYPE = Object.freeze({
24
25
  });
25
26
 
26
27
  // System colors
28
+ // @see https://drafts.csswg.org/css-color/#css-system-colors
29
+ // @see https://drafts.csswg.org/css-color/#deprecated-system-colors
27
30
  const SYS_COLOR = Object.freeze([
28
31
  "accentcolor",
29
32
  "accentcolortext",
33
+ "activeborder",
34
+ "activecaption",
30
35
  "activetext",
36
+ "appworkspace",
37
+ "background",
31
38
  "buttonborder",
32
39
  "buttonface",
40
+ "buttonhighlight",
41
+ "buttonshadow",
33
42
  "buttontext",
34
43
  "canvas",
35
44
  "canvastext",
45
+ "captiontext",
36
46
  "field",
37
47
  "fieldtext",
38
48
  "graytext",
39
49
  "highlight",
40
50
  "highlighttext",
41
- "linktext",
42
- "mark",
43
- "marktext",
44
- "visitedtext",
45
- "activeborder",
46
- "activecaption",
47
- "appworkspace",
48
- "background",
49
- "buttonhighlight",
50
- "buttonshadow",
51
- "captiontext",
52
51
  "inactiveborder",
53
52
  "inactivecaption",
54
53
  "inactivecaptiontext",
55
54
  "infobackground",
56
55
  "infotext",
56
+ "linktext",
57
+ "mark",
58
+ "marktext",
57
59
  "menu",
58
60
  "menutext",
59
61
  "scrollbar",
62
+ "selecteditem",
63
+ "selecteditemtext",
60
64
  "threeddarkshadow",
61
65
  "threedface",
62
66
  "threedhighlight",
63
67
  "threedlightshadow",
64
68
  "threedshadow",
69
+ "visitedtext",
65
70
  "window",
66
71
  "windowframe",
67
72
  "windowtext"
@@ -78,6 +83,7 @@ const varRegEx = /^var\(/;
78
83
  const varContainedRegEx = /(?<=[*/\s(])var\(/;
79
84
  const calcRegEx =
80
85
  /^(?:a?(?:cos|sin|tan)|abs|atan2|calc|clamp|exp|hypot|log|max|min|mod|pow|rem|round|sign|sqrt)\(/;
86
+ const functionRegEx = /^([a-z][a-z\d]*(?:-[a-z\d]+)*)\(/i;
81
87
 
82
88
  const getNumericType = function getNumericType(val) {
83
89
  if (varRegEx.test(val)) {
@@ -184,7 +190,7 @@ exports.parseLength = function parseLength(val, restrictToPositive = false) {
184
190
  if (restrictToPositive && num < 0) {
185
191
  return;
186
192
  }
187
- return `${num}${unit.toLowerCase()}`;
193
+ return `${num}${asciiLowercase(unit)}`;
188
194
  }
189
195
  default:
190
196
  if (varContainedRegEx.test(val)) {
@@ -216,7 +222,7 @@ exports.parsePercent = function parsePercent(val, restrictToPositive = false) {
216
222
  if (restrictToPositive && num < 0) {
217
223
  return;
218
224
  }
219
- return `${num}${unit.toLowerCase()}`;
225
+ return `${num}${asciiLowercase(unit)}`;
220
226
  }
221
227
  default:
222
228
  if (varContainedRegEx.test(val)) {
@@ -250,7 +256,7 @@ exports.parseMeasurement = function parseMeasurement(val, restrictToPositive = f
250
256
  if (restrictToPositive && num < 0) {
251
257
  return;
252
258
  }
253
- return `${num}${unit.toLowerCase()}`;
259
+ return `${num}${asciiLowercase(unit)}`;
254
260
  }
255
261
  default:
256
262
  if (varContainedRegEx.test(val)) {
@@ -279,7 +285,7 @@ exports.parseAngle = function parseAngle(val, normalizeDeg = false) {
279
285
  case NUM_TYPE.ANGLE: {
280
286
  let [, numVal, unit] = unitRegEx.exec(val);
281
287
  numVal = parseFloat(numVal);
282
- unit = unit.toLowerCase();
288
+ unit = asciiLowercase(unit);
283
289
  if (unit === "deg") {
284
290
  if (normalizeDeg && numVal < 0) {
285
291
  while (numVal < 0) {
@@ -391,7 +397,7 @@ exports.parseKeyword = function parseKeyword(val, validKeywords = []) {
391
397
  if (varRegEx.test(val)) {
392
398
  return val;
393
399
  }
394
- val = val.toString().toLowerCase();
400
+ val = asciiLowercase(val.toString());
395
401
  if (validKeywords.includes(val) || GLOBAL_VALUE.includes(val)) {
396
402
  return val;
397
403
  }
@@ -404,8 +410,11 @@ exports.parseColor = function parseColor(val) {
404
410
  if (varRegEx.test(val)) {
405
411
  return val;
406
412
  }
407
- if (/^[a-z]+$/i.test(val) && SYS_COLOR.includes(val.toLowerCase())) {
408
- return val;
413
+ if (/^[a-z]+$/i.test(val)) {
414
+ const v = asciiLowercase(val);
415
+ if (SYS_COLOR.includes(v)) {
416
+ return v;
417
+ }
409
418
  }
410
419
  const res = resolveColor(val, {
411
420
  format: "specifiedValue"
@@ -452,6 +461,32 @@ exports.parseImage = function parseImage(val) {
452
461
  }
453
462
  };
454
463
 
464
+ exports.parseFunction = function parseFunction(val) {
465
+ if (val === "") {
466
+ return {
467
+ name: null,
468
+ value: ""
469
+ };
470
+ }
471
+ if (functionRegEx.test(val) && val.endsWith(")")) {
472
+ if (varRegEx.test(val) || varContainedRegEx.test(val)) {
473
+ return {
474
+ name: "var",
475
+ value: val
476
+ };
477
+ }
478
+ const [, name] = functionRegEx.exec(val);
479
+ const value = val
480
+ .replace(new RegExp(`^${name}\\(`), "")
481
+ .replace(/\)$/, "")
482
+ .trim();
483
+ return {
484
+ name,
485
+ value
486
+ };
487
+ }
488
+ };
489
+
455
490
  exports.parseShorthand = function parseShorthand(val, shorthandFor, preserve = false) {
456
491
  const obj = {};
457
492
  if (val === "" || exports.hasVarFunc(val)) {
@@ -491,7 +526,7 @@ exports.parseShorthand = function parseShorthand(val, shorthandFor, preserve = f
491
526
 
492
527
  // Returns `false` for global values, e.g. "inherit".
493
528
  exports.isValidColor = function isValidColor(val) {
494
- if (SYS_COLOR.includes(val.toLowerCase())) {
529
+ if (SYS_COLOR.includes(asciiLowercase(val))) {
495
530
  return true;
496
531
  }
497
532
  return isColor(val);
@@ -4,6 +4,7 @@
4
4
  // * also fix longhands
5
5
 
6
6
  const parsers = require("../parsers");
7
+ const strings = require("../utils/strings");
7
8
  const backgroundImage = require("./backgroundImage");
8
9
  const backgroundPosition = require("./backgroundPosition");
9
10
  const backgroundRepeat = require("./backgroundRepeat");
@@ -21,7 +22,12 @@ const shorthandFor = new Map([
21
22
  module.exports.definition = {
22
23
  set(v) {
23
24
  v = parsers.prepareValue(v, this._global);
24
- if (v.toLowerCase() === "none" || parsers.hasVarFunc(v)) {
25
+ if (/^none$/i.test(v)) {
26
+ for (const [key] of shorthandFor) {
27
+ this._setProperty(key, "");
28
+ }
29
+ this._setProperty("background", strings.asciiLowercase(v));
30
+ } else if (parsers.hasVarFunc(v)) {
25
31
  for (const [key] of shorthandFor) {
26
32
  this._setProperty(key, "");
27
33
  }
@@ -19,7 +19,7 @@ module.exports.definition = {
19
19
  v = parsers.prepareValue(v, this._global);
20
20
  if (parsers.hasVarFunc(v)) {
21
21
  this._setProperty("background", "");
22
- this._setProperty("backgound-attachemnt", v);
22
+ this._setProperty("background-attachment", v);
23
23
  } else {
24
24
  this._setProperty("background-attachment", module.exports.parse(v));
25
25
  }
@@ -22,7 +22,7 @@ module.exports.definition = {
22
22
  v = parsers.prepareValue(v, this._global);
23
23
  if (parsers.hasVarFunc(v)) {
24
24
  this._setProperty("background", "");
25
- this._setProperty("backgound-color", v);
25
+ this._setProperty("background-color", v);
26
26
  } else {
27
27
  this._setProperty("background-color", module.exports.parse(v));
28
28
  }
@@ -18,7 +18,7 @@ module.exports.definition = {
18
18
  v = parsers.prepareValue(v, this._global);
19
19
  if (parsers.hasVarFunc(v)) {
20
20
  this._setProperty("background", "");
21
- this._setProperty("backgound-image", v);
21
+ this._setProperty("background-image", v);
22
22
  } else {
23
23
  this._setProperty("background-image", module.exports.parse(v));
24
24
  }
@@ -39,7 +39,7 @@ module.exports.definition = {
39
39
  v = parsers.prepareValue(v, this._global);
40
40
  if (parsers.hasVarFunc(v)) {
41
41
  this._setProperty("background", "");
42
- this._setProperty("backgound-position", v);
42
+ this._setProperty("background-position", v);
43
43
  } else {
44
44
  this._setProperty("background-position", module.exports.parse(v));
45
45
  }
@@ -19,7 +19,7 @@ module.exports.definition = {
19
19
  v = parsers.prepareValue(v, this._global);
20
20
  if (parsers.hasVarFunc(v)) {
21
21
  this._setProperty("background", "");
22
- this._setProperty("backgound-repeat", v);
22
+ this._setProperty("background-repeat", v);
23
23
  } else {
24
24
  this._setProperty("background-repeat", module.exports.parse(v));
25
25
  }
@@ -14,7 +14,7 @@ const shorthandFor = new Map([
14
14
  module.exports.definition = {
15
15
  set(v) {
16
16
  v = parsers.prepareValue(v, this._global);
17
- if (v.toLowerCase() === "none") {
17
+ if (/^none$/i.test(v)) {
18
18
  v = "";
19
19
  }
20
20
  if (parsers.hasVarFunc(v)) {
@@ -28,7 +28,7 @@ module.exports.isValid = function isValid(v) {
28
28
  module.exports.definition = {
29
29
  set(v) {
30
30
  v = parsers.prepareValue(v, this._global);
31
- if (v.toLowerCase() === "none") {
31
+ if (/^none$/i.test(v)) {
32
32
  v = "";
33
33
  }
34
34
  if (parsers.hasVarFunc(v)) {
@@ -3,6 +3,7 @@
3
3
  // @see https://drafts.fxtf.org/css-masking/#clip-property
4
4
 
5
5
  const parsers = require("../parsers");
6
+ const strings = require("../utils/strings");
6
7
 
7
8
  module.exports.parse = function parse(v) {
8
9
  if (v === "") {
@@ -13,7 +14,7 @@ module.exports.parse = function parse(v) {
13
14
  return val;
14
15
  }
15
16
  // parse legacy <shape>
16
- v = v.toLowerCase();
17
+ v = strings.asciiLowercase(v);
17
18
  const matches = v.match(/^rect\(\s*(.*)\s*\)$/);
18
19
  if (!matches) {
19
20
  return;
@@ -26,38 +26,97 @@ module.exports.parse = function parse(v) {
26
26
  const [fontBlock, ...families] = parsers.splitValue(v, {
27
27
  delimiter: ","
28
28
  });
29
- let blockA, blockB;
30
- if (fontBlock.includes("/")) {
31
- [blockA, blockB] = parsers.splitValue(fontBlock, {
32
- delimiter: "/"
33
- });
34
- } else {
35
- blockA = fontBlock.trim();
36
- }
37
- const obj = parsers.parseShorthand(blockA, shorthandFor, true);
38
- if (!obj) {
39
- return;
40
- }
41
- const font = {};
29
+ const [fontBlockA, fontBlockB] = parsers.splitValue(fontBlock, {
30
+ delimiter: "/"
31
+ });
32
+ const font = {
33
+ "font-style": "normal",
34
+ "font-variant": "normal",
35
+ "font-weight": "normal"
36
+ };
42
37
  const fontFamilies = new Set();
43
- for (const [property, value] of Object.entries(obj)) {
44
- if (property === "font-family") {
45
- if (!blockB) {
46
- fontFamilies.add(value);
47
- }
38
+ if (fontBlockB) {
39
+ const [lineB, ...familiesB] = fontBlockB.trim().split(" ");
40
+ if (!lineB || !lineHeight.isValid(lineB) || !familiesB.length) {
41
+ return;
42
+ }
43
+ const lineHeightB = lineHeight.parse(lineB);
44
+ const familyB = familiesB.join(" ");
45
+ if (fontFamily.isValid(familyB)) {
46
+ fontFamilies.add(fontFamily.parse(familyB));
48
47
  } else {
49
- font[property] = value;
48
+ return;
50
49
  }
51
- }
52
- // blockB, if matched, includes line-height and first font-family
53
- if (blockB) {
54
- const [lineheight, family] = parsers.splitValue(blockB);
55
- if (lineHeight.isValid(lineheight)) {
56
- font["line-height"] = lineHeight.parse(lineheight);
50
+ const parts = parsers.splitValue(fontBlockA.trim());
51
+ const properties = ["font-style", "font-variant", "font-weight", "font-size"];
52
+ for (const part of parts) {
53
+ if (part === "normal") {
54
+ continue;
55
+ } else {
56
+ for (const property of properties) {
57
+ switch (property) {
58
+ case "font-style":
59
+ case "font-variant":
60
+ case "font-weight":
61
+ case "font-size": {
62
+ const value = shorthandFor.get(property);
63
+ if (value.isValid(part)) {
64
+ font[property] = value.parse(part);
65
+ }
66
+ break;
67
+ }
68
+ default:
69
+ }
70
+ }
71
+ }
72
+ }
73
+ if (Object.hasOwn(font, "font-size")) {
74
+ font["line-height"] = lineHeightB;
57
75
  } else {
58
76
  return;
59
77
  }
60
- if (fontFamily.isValid(family)) {
78
+ } else {
79
+ // FIXME: Switch to toReversed() when we can drop Node.js 18 support.
80
+ const revParts = [...parsers.splitValue(fontBlockA.trim())].reverse();
81
+ const revFontFamily = [];
82
+ const properties = ["font-style", "font-variant", "font-weight", "line-height"];
83
+ font["font-style"] = "normal";
84
+ font["font-variant"] = "normal";
85
+ font["font-weight"] = "normal";
86
+ font["line-height"] = "normal";
87
+ let fontSizeA;
88
+ for (const part of revParts) {
89
+ if (fontSizeA) {
90
+ if (part === "normal") {
91
+ continue;
92
+ } else {
93
+ for (const property of properties) {
94
+ switch (property) {
95
+ case "font-style":
96
+ case "font-variant":
97
+ case "font-weight":
98
+ case "line-height": {
99
+ const value = shorthandFor.get(property);
100
+ if (value.isValid(part)) {
101
+ font[property] = value.parse(part);
102
+ }
103
+ break;
104
+ }
105
+ default:
106
+ }
107
+ }
108
+ }
109
+ } else if (fontSize.isValid(part)) {
110
+ fontSizeA = fontSize.parse(part);
111
+ } else if (fontFamily.isValid(part)) {
112
+ revFontFamily.push(part);
113
+ } else {
114
+ return;
115
+ }
116
+ }
117
+ const family = revFontFamily.reverse().join(" ");
118
+ if (fontSizeA && fontFamily.isValid(family)) {
119
+ font["font-size"] = fontSizeA;
61
120
  fontFamilies.add(fontFamily.parse(family));
62
121
  } else {
63
122
  return;
@@ -77,7 +136,7 @@ module.exports.parse = function parse(v) {
77
136
  module.exports.definition = {
78
137
  set(v) {
79
138
  v = parsers.prepareValue(v, this._global);
80
- if (parsers.hasVarFunc(v)) {
139
+ if (v === "" || parsers.hasVarFunc(v)) {
81
140
  for (const [key] of shorthandFor) {
82
141
  this._setProperty(key, "");
83
142
  }
@@ -92,7 +151,7 @@ module.exports.definition = {
92
151
  const val = obj[key];
93
152
  if (typeof val === "string") {
94
153
  this._setProperty(key, val);
95
- if (val && !str.has(val)) {
154
+ if (val && val !== "normal" && !str.has(val)) {
96
155
  if (key === "line-height") {
97
156
  str.add(`/ ${val}`);
98
157
  } else {
@@ -115,7 +174,7 @@ module.exports.definition = {
115
174
  if (parsers.hasVarFunc(v)) {
116
175
  return "";
117
176
  }
118
- if (v && !str.has(v)) {
177
+ if (v && v !== "normal" && !str.has(v)) {
119
178
  if (key === "line-height") {
120
179
  str.add(`/ ${v}`);
121
180
  } else {
@@ -6,7 +6,20 @@ module.exports.parse = function parse(v) {
6
6
  if (v === "") {
7
7
  return v;
8
8
  }
9
- const keywords = ["serif", "sans-serif", "system-ui", "cursive", "fantasy", "monospace"];
9
+ const keywords = [
10
+ "serif",
11
+ "sans-serif",
12
+ "cursive",
13
+ "fantasy",
14
+ "monospace",
15
+ "system-ui",
16
+ "math",
17
+ "ui-serif",
18
+ "ui-sans-serif",
19
+ "ui-monospace",
20
+ "ui-rounded"
21
+ ];
22
+ const genericValues = ["fangsong", "kai", "khmer-mul", "nastaliq"];
10
23
  const val = parsers.splitValue(v, {
11
24
  delimiter: ","
12
25
  });
@@ -25,16 +38,27 @@ module.exports.parse = function parse(v) {
25
38
  valid = true;
26
39
  continue;
27
40
  }
28
- // This implementation does not strictly follow the specification. The spec
29
- // does not require the first letter of the font-family to be capitalized.
30
- // Also, unquoted font-family names are not restricted to ASCII only.
41
+ const obj = parsers.parseFunction(i);
42
+ if (obj) {
43
+ const { name, value } = obj;
44
+ if (name === "generic" && genericValues.includes(value)) {
45
+ font.push(`${name}(${value})`);
46
+ valid = true;
47
+ continue;
48
+ }
49
+ }
50
+ // This implementation does not strictly follow the specification.
51
+ // The spec does not require the first letter of the font-family to be
52
+ // capitalized, and unquoted font-family names are not restricted to ASCII.
31
53
  // However, in the real world, the first letter of the ASCII font-family
32
- // names are always capitalized, and unquoted font-family names do not
33
- // contain spaces, e.g. `Times`, and AFAIK, non-ASCII font-family names are
34
- // always quoted even without spaces, e.g. `"メイリオ"`.
35
- // Therefore, it is unlikely that this implementation will cause problems.
54
+ // names are capitalized, and unquoted font-family names do not contain
55
+ // spaces, e.g. `Times`. And non-ASCII font-family names are quoted even
56
+ // without spaces, e.g. `"メイリオ"`.
36
57
  // @see https://drafts.csswg.org/css-fonts/#font-family-prop
37
- if (/^\s*(?:[A-Z][A-Za-z\d\s-]+)\s*$/.test(i)) {
58
+ if (
59
+ i !== "undefined" &&
60
+ /^(?:[A-Z][A-Za-z\d-]+(?:\s+[A-Z][A-Za-z\d-]+)*|-?[a-z][a-z-]+)$/.test(i)
61
+ ) {
38
62
  font.push(i.trim());
39
63
  valid = true;
40
64
  continue;
@@ -5,7 +5,7 @@ const parsers = require("../parsers");
5
5
  const positions = ["top", "right", "bottom", "left"];
6
6
 
7
7
  module.exports.parse = function parse(v) {
8
- const val = parsers.parseMeasurement(v, true);
8
+ const val = parsers.parseMeasurement(v);
9
9
  if (val) {
10
10
  return val;
11
11
  }
@@ -3,7 +3,7 @@
3
3
  const parsers = require("../parsers");
4
4
 
5
5
  module.exports.parse = function parse(v) {
6
- const val = parsers.parseMeasurement(v, true);
6
+ const val = parsers.parseMeasurement(v);
7
7
  if (val) {
8
8
  return val;
9
9
  }
@@ -3,7 +3,7 @@
3
3
  const parsers = require("../parsers");
4
4
 
5
5
  module.exports.parse = function parse(v) {
6
- const val = parsers.parseMeasurement(v, true);
6
+ const val = parsers.parseMeasurement(v);
7
7
  if (val) {
8
8
  return val;
9
9
  }
@@ -3,7 +3,7 @@
3
3
  const parsers = require("../parsers");
4
4
 
5
5
  module.exports.parse = function parse(v) {
6
- const val = parsers.parseMeasurement(v, true);
6
+ const val = parsers.parseMeasurement(v);
7
7
  if (val) {
8
8
  return val;
9
9
  }
@@ -3,7 +3,7 @@
3
3
  const parsers = require("../parsers");
4
4
 
5
5
  module.exports.parse = function parse(v) {
6
- const val = parsers.parseMeasurement(v, true);
6
+ const val = parsers.parseMeasurement(v);
7
7
  if (val) {
8
8
  return val;
9
9
  }
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
 
3
+ const { asciiLowercase } = require("./strings");
4
+
3
5
  // Utility to translate from `border-width` to `borderWidth`.
4
6
  // NOTE: For values prefixed with webkit, e.g. `-webkit-foo`, we need to provide
5
7
  // both `webkitFoo` and `WebkitFoo`. Here we only return `webkitFoo`.
@@ -27,7 +29,7 @@ exports.camelCaseToDashed = function (camelCase) {
27
29
  if (camelCase.startsWith("--")) {
28
30
  return camelCase;
29
31
  }
30
- const dashed = camelCase.replace(/(?<=[a-z])[A-Z]/g, "-$&").toLowerCase();
32
+ const dashed = asciiLowercase(camelCase.replace(/(?<=[a-z])[A-Z]/g, "-$&"));
31
33
  if (/^webkit-/.test(dashed)) {
32
34
  return `-${dashed}`;
33
35
  }
@@ -0,0 +1,167 @@
1
+ // Forked from https://github.com/jsdom/jsdom/blob/main/lib/jsdom/living/helpers/strings.js
2
+
3
+ "use strict";
4
+
5
+ // https://infra.spec.whatwg.org/#ascii-whitespace
6
+ const asciiWhitespaceRe = /^[\t\n\f\r ]$/;
7
+ exports.asciiWhitespaceRe = asciiWhitespaceRe;
8
+
9
+ // https://infra.spec.whatwg.org/#ascii-lowercase
10
+ exports.asciiLowercase = (s) => {
11
+ const len = s.length;
12
+ const out = new Array(len);
13
+ for (let i = 0; i < len; i++) {
14
+ const code = s.charCodeAt(i);
15
+ // If the character is between 'A' (65) and 'Z' (90), convert using bitwise OR with 32
16
+ out[i] = code >= 65 && code <= 90 ? String.fromCharCode(code | 32) : s[i];
17
+ }
18
+ return out.join("");
19
+ };
20
+
21
+ // https://infra.spec.whatwg.org/#ascii-uppercase
22
+ exports.asciiUppercase = (s) => {
23
+ const len = s.length;
24
+ const out = new Array(len);
25
+ for (let i = 0; i < len; i++) {
26
+ const code = s.charCodeAt(i);
27
+ // If the character is between 'a' (97) and 'z' (122), convert using bitwise AND with ~32
28
+ out[i] = code >= 97 && code <= 122 ? String.fromCharCode(code & ~32) : s[i];
29
+ }
30
+ return out.join("");
31
+ };
32
+
33
+ // https://infra.spec.whatwg.org/#strip-newlines
34
+ exports.stripNewlines = (s) => {
35
+ return s.replace(/[\n\r]+/g, "");
36
+ };
37
+
38
+ // https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace
39
+ exports.stripLeadingAndTrailingASCIIWhitespace = (s) => {
40
+ return s.replace(/^[ \t\n\f\r]+/, "").replace(/[ \t\n\f\r]+$/, "");
41
+ };
42
+
43
+ // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
44
+ exports.stripAndCollapseASCIIWhitespace = (s) => {
45
+ return s
46
+ .replace(/[ \t\n\f\r]+/g, " ")
47
+ .replace(/^[ \t\n\f\r]+/, "")
48
+ .replace(/[ \t\n\f\r]+$/, "");
49
+ };
50
+
51
+ // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-simple-colour
52
+ exports.isValidSimpleColor = (s) => {
53
+ return /^#[a-fA-F\d]{6}$/.test(s);
54
+ };
55
+
56
+ // https://infra.spec.whatwg.org/#ascii-case-insensitive
57
+ exports.asciiCaseInsensitiveMatch = (a, b) => {
58
+ if (a.length !== b.length) {
59
+ return false;
60
+ }
61
+
62
+ for (let i = 0; i < a.length; ++i) {
63
+ if ((a.charCodeAt(i) | 32) !== (b.charCodeAt(i) | 32)) {
64
+ return false;
65
+ }
66
+ }
67
+
68
+ return true;
69
+ };
70
+
71
+ // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-integers
72
+ // Error is represented as null.
73
+ const parseInteger = (exports.parseInteger = (input) => {
74
+ // The implementation here is slightly different from the spec's. We want to use parseInt(), but parseInt() trims
75
+ // Unicode whitespace in addition to just ASCII ones, so we make sure that the trimmed prefix contains only ASCII
76
+ // whitespace ourselves.
77
+ const numWhitespace = input.length - input.trimStart().length;
78
+ if (/[^\t\n\f\r ]/.test(input.slice(0, numWhitespace))) {
79
+ return null;
80
+ }
81
+ // We don't allow hexadecimal numbers here.
82
+ // eslint-disable-next-line radix
83
+ const value = parseInt(input, 10);
84
+ if (Number.isNaN(value)) {
85
+ return null;
86
+ }
87
+ // parseInt() returns -0 for "-0". Normalize that here.
88
+ return value === 0 ? 0 : value;
89
+ });
90
+
91
+ // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-non-negative-integers
92
+ // Error is represented as null.
93
+ exports.parseNonNegativeInteger = (input) => {
94
+ const value = parseInteger(input);
95
+ if (value === null) {
96
+ return null;
97
+ }
98
+ if (value < 0) {
99
+ return null;
100
+ }
101
+ return value;
102
+ };
103
+
104
+ // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-floating-point-number
105
+ const floatingPointNumRe = /^-?(?:\d+|\d*\.\d+)(?:[eE][-+]?\d+)?$/;
106
+ exports.isValidFloatingPointNumber = (str) => floatingPointNumRe.test(str);
107
+
108
+ // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#rules-for-parsing-floating-point-number-values
109
+ // Error is represented as null.
110
+ exports.parseFloatingPointNumber = (str) => {
111
+ // The implementation here is slightly different from the spec's. We need to use parseFloat() in order to retain
112
+ // accuracy, but parseFloat() trims Unicode whitespace in addition to just ASCII ones, so we make sure that the
113
+ // trimmed prefix contains only ASCII whitespace ourselves.
114
+ const numWhitespace = str.length - str.trimStart().length;
115
+ if (/[^\t\n\f\r ]/.test(str.slice(0, numWhitespace))) {
116
+ return null;
117
+ }
118
+ const parsed = parseFloat(str);
119
+ return isFinite(parsed) ? parsed : null;
120
+ };
121
+
122
+ // https://infra.spec.whatwg.org/#split-on-ascii-whitespace
123
+ exports.splitOnASCIIWhitespace = (str) => {
124
+ let position = 0;
125
+ const tokens = [];
126
+ while (position < str.length && asciiWhitespaceRe.test(str[position])) {
127
+ position++;
128
+ }
129
+ if (position === str.length) {
130
+ return tokens;
131
+ }
132
+ while (position < str.length) {
133
+ const start = position;
134
+ while (position < str.length && !asciiWhitespaceRe.test(str[position])) {
135
+ position++;
136
+ }
137
+ tokens.push(str.slice(start, position));
138
+ while (position < str.length && asciiWhitespaceRe.test(str[position])) {
139
+ position++;
140
+ }
141
+ }
142
+ return tokens;
143
+ };
144
+
145
+ // https://infra.spec.whatwg.org/#split-on-commas
146
+ exports.splitOnCommas = (str) => {
147
+ let position = 0;
148
+ const tokens = [];
149
+ while (position < str.length) {
150
+ let start = position;
151
+ while (position < str.length && str[position] !== ",") {
152
+ position++;
153
+ }
154
+ let end = position;
155
+ while (start < str.length && asciiWhitespaceRe.test(str[start])) {
156
+ start++;
157
+ }
158
+ while (end > start && asciiWhitespaceRe.test(str[end - 1])) {
159
+ end--;
160
+ }
161
+ tokens.push(str.slice(start, end));
162
+ if (position < str.length) {
163
+ position++;
164
+ }
165
+ }
166
+ return tokens;
167
+ };
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "CSSStyleDeclaration",
7
7
  "StyleSheet"
8
8
  ],
9
- "version": "4.4.0",
9
+ "version": "4.6.0",
10
10
  "homepage": "https://github.com/jsdom/cssstyle",
11
11
  "maintainers": [
12
12
  {