@tbela99/css-parser 0.7.1 → 0.9.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.
Files changed (90) hide show
  1. package/.editorconfig +484 -0
  2. package/README.md +140 -84
  3. package/dist/index-umd-web.js +8461 -51655
  4. package/dist/index.cjs +8437 -51636
  5. package/dist/index.d.ts +220 -68
  6. package/dist/lib/ast/expand.js +46 -9
  7. package/dist/lib/ast/features/calc.js +76 -12
  8. package/dist/lib/ast/features/inlinecssvariables.js +6 -1
  9. package/dist/lib/ast/features/prefix.js +17 -9
  10. package/dist/lib/ast/features/shorthand.js +1 -0
  11. package/dist/lib/ast/math/expression.js +299 -11
  12. package/dist/lib/ast/math/math.js +7 -1
  13. package/dist/lib/ast/minify.js +30 -16
  14. package/dist/lib/ast/types.js +59 -49
  15. package/dist/lib/ast/walk.js +92 -18
  16. package/dist/lib/parser/declaration/list.js +1 -0
  17. package/dist/lib/parser/declaration/map.js +60 -52
  18. package/dist/lib/parser/declaration/set.js +1 -12
  19. package/dist/lib/parser/parse.js +371 -119
  20. package/dist/lib/parser/tokenize.js +31 -6
  21. package/dist/lib/parser/utils/declaration.js +2 -2
  22. package/dist/lib/parser/utils/type.js +6 -6
  23. package/dist/lib/renderer/color/a98rgb.js +1 -0
  24. package/dist/lib/renderer/color/color.js +1 -0
  25. package/dist/lib/renderer/color/colormix.js +1 -0
  26. package/dist/lib/renderer/color/hex.js +2 -1
  27. package/dist/lib/renderer/color/hsl.js +2 -1
  28. package/dist/lib/renderer/color/hwb.js +3 -2
  29. package/dist/lib/renderer/color/lab.js +2 -1
  30. package/dist/lib/renderer/color/lch.js +2 -1
  31. package/dist/lib/renderer/color/oklab.js +3 -2
  32. package/dist/lib/renderer/color/oklch.js +2 -1
  33. package/dist/lib/renderer/color/p3.js +2 -1
  34. package/dist/lib/renderer/color/prophotoRgb.js +56 -0
  35. package/dist/lib/renderer/color/prophotorgb.js +1 -1
  36. package/dist/lib/renderer/color/rec2020.js +1 -0
  37. package/dist/lib/renderer/color/relativecolor.js +52 -28
  38. package/dist/lib/renderer/color/rgb.js +2 -1
  39. package/dist/lib/renderer/color/srgb.js +3 -2
  40. package/dist/lib/renderer/color/utils/components.js +1 -0
  41. package/dist/lib/renderer/color/utils/constants.js +2 -1
  42. package/dist/lib/renderer/color/xyz.js +2 -1
  43. package/dist/lib/renderer/color/xyzd50.js +1 -0
  44. package/dist/lib/renderer/render.js +62 -12
  45. package/dist/lib/syntax/syntax.js +362 -4
  46. package/dist/lib/validation/at-rules/container.js +353 -0
  47. package/dist/lib/validation/at-rules/counter-style.js +78 -0
  48. package/dist/lib/validation/at-rules/custom-media.js +52 -0
  49. package/dist/lib/validation/at-rules/document.js +114 -0
  50. package/dist/lib/validation/at-rules/else.js +5 -0
  51. package/dist/lib/validation/at-rules/font-feature-values.js +52 -0
  52. package/dist/lib/validation/at-rules/import.js +199 -0
  53. package/dist/lib/validation/at-rules/keyframes.js +70 -0
  54. package/dist/lib/validation/at-rules/layer.js +30 -0
  55. package/dist/lib/validation/at-rules/media.js +254 -0
  56. package/dist/lib/validation/at-rules/namespace.js +85 -0
  57. package/dist/lib/validation/at-rules/page-margin-box.js +56 -0
  58. package/dist/lib/validation/at-rules/page.js +88 -0
  59. package/dist/lib/validation/at-rules/supports.js +262 -0
  60. package/dist/lib/validation/at-rules/when.js +178 -0
  61. package/dist/lib/validation/atrule.js +187 -0
  62. package/dist/lib/validation/config.js +35 -2
  63. package/dist/lib/validation/config.json.js +1683 -50905
  64. package/dist/lib/validation/declaration.js +102 -0
  65. package/dist/lib/validation/parser/parse.js +1137 -7
  66. package/dist/lib/validation/parser/types.js +28 -12
  67. package/dist/lib/validation/selector.js +26 -444
  68. package/dist/lib/validation/syntax.js +1475 -0
  69. package/dist/lib/validation/syntaxes/complex-selector-list.js +45 -0
  70. package/dist/lib/validation/syntaxes/complex-selector.js +53 -0
  71. package/dist/lib/validation/syntaxes/compound-selector.js +226 -0
  72. package/dist/lib/validation/syntaxes/family-name.js +91 -0
  73. package/dist/lib/validation/syntaxes/image.js +29 -0
  74. package/dist/lib/validation/syntaxes/keyframe-block-list.js +27 -0
  75. package/dist/lib/validation/syntaxes/keyframe-selector.js +137 -0
  76. package/dist/lib/validation/syntaxes/layer-name.js +67 -0
  77. package/dist/lib/validation/syntaxes/relative-selector-list.js +57 -0
  78. package/dist/lib/validation/syntaxes/relative-selector.js +36 -0
  79. package/dist/lib/validation/syntaxes/selector-list.js +5 -0
  80. package/dist/lib/validation/syntaxes/selector.js +5 -0
  81. package/dist/lib/validation/syntaxes/url.js +75 -0
  82. package/dist/lib/validation/utils/list.js +24 -0
  83. package/dist/lib/validation/utils/whitespace.js +22 -0
  84. package/dist/node/index.js +5 -5
  85. package/dist/web/index.js +5 -1
  86. package/dist/web/load.js +1 -0
  87. package/package.json +16 -14
  88. package/dist/lib/ast/utils/minifyfeature.js +0 -9
  89. package/dist/lib/iterable/weakset.js +0 -58
  90. package/dist/lib/parser/utils/syntax.js +0 -450
@@ -1,6 +1,8 @@
1
1
  import { EnumToken } from '../types.js';
2
- import { walkValues } from '../walk.js';
2
+ import { walkValues, WalkerValueEvent } from '../walk.js';
3
3
  import { evaluate } from '../math/expression.js';
4
+ import { renderToken } from '../../renderer/render.js';
5
+ import { mathFuncs } from '../../syntax/syntax.js';
4
6
 
5
7
  class ComputeCalcExpressionFeature {
6
8
  static get ordering() {
@@ -27,30 +29,92 @@ class ComputeCalcExpressionFeature {
27
29
  continue;
28
30
  }
29
31
  const set = new Set;
30
- for (const { value, parent } of walkValues(node.val)) {
31
- if (value != null && value.typ == EnumToken.FunctionTokenType && value.val == 'calc') {
32
- if (!set.has(parent)) {
32
+ for (const { value, parent } of walkValues(node.val, node, {
33
+ event: WalkerValueEvent.Enter,
34
+ fn(node, parent, event) {
35
+ if (parent != null &&
36
+ parent.typ == EnumToken.DeclarationNodeType &&
37
+ parent.val.length == 1 &&
38
+ node.typ == EnumToken.FunctionTokenType &&
39
+ mathFuncs.includes(node.val) &&
40
+ node.chi.length == 1 &&
41
+ node.chi[0].typ == EnumToken.IdenTokenType) {
42
+ return 'ignore';
43
+ }
44
+ if ((node.typ == EnumToken.FunctionTokenType && node.val == 'var') || (!mathFuncs.includes(parent.val) && [EnumToken.ColorTokenType, EnumToken.DeclarationNodeType, EnumToken.RuleNodeType, EnumToken.AtRuleNodeType, EnumToken.StyleSheetNodeType].includes(parent?.typ))) {
45
+ return null;
46
+ }
47
+ // @ts-ignore
48
+ const slice = (node.typ == EnumToken.FunctionTokenType ? node.chi : (node.typ == EnumToken.DeclarationNodeType ? node.val : node.chi))?.slice();
49
+ if (slice != null && node.typ == EnumToken.FunctionTokenType && mathFuncs.includes(node.val)) {
50
+ // @ts-ignore
51
+ const cp = (node.typ == EnumToken.FunctionTokenType && mathFuncs.includes(node.val) && node.val != 'calc' ? [node] : (node.typ == EnumToken.DeclarationNodeType ? node.val : node.chi)).slice();
52
+ const values = evaluate(cp);
53
+ const key = 'chi' in node ? 'chi' : 'val';
54
+ const str1 = renderToken({ ...node, [key]: slice });
55
+ const str2 = renderToken(node); // values.reduce((acc: string, curr: Token): string => acc + renderToken(curr), '');
56
+ if (str1.length <= str2.length) {
57
+ // @ts-ignore
58
+ node[key] = slice;
59
+ }
60
+ else {
61
+ // @ts-ignore
62
+ node[key] = values;
63
+ }
64
+ return 'ignore';
65
+ }
66
+ return null;
67
+ }
68
+ })) {
69
+ if (value != null && value.typ == EnumToken.FunctionTokenType && mathFuncs.includes(value.val)) {
70
+ if (!set.has(value)) {
33
71
  set.add(value);
34
- value.chi = evaluate(value.chi);
35
- if (value.chi.length == 1 && value.chi[0].typ != EnumToken.BinaryExpressionTokenType) {
36
- if (parent != null) {
72
+ if (parent != null) {
73
+ // @ts-ignore
74
+ const cp = value.typ == EnumToken.FunctionTokenType && mathFuncs.includes(value.val) && value.val != 'calc' ? [value] : (value.typ == EnumToken.DeclarationNodeType ? value.val : value.chi);
75
+ const values = evaluate(cp);
76
+ // @ts-ignore
77
+ const children = parent.typ == EnumToken.DeclarationNodeType ? parent.val : parent.chi;
78
+ if (values.length == 1 && values[0].typ != EnumToken.BinaryExpressionTokenType) {
37
79
  if (parent.typ == EnumToken.BinaryExpressionTokenType) {
38
80
  if (parent.l == value) {
39
- parent.l = value.chi[0];
81
+ parent.l = values[0];
40
82
  }
41
83
  else {
42
- parent.r = value.chi[0];
84
+ parent.r = values[0];
43
85
  }
44
86
  }
45
87
  else {
46
- for (let i = 0; i < parent.chi.length; i++) {
47
- if (parent.chi[i] == value) {
48
- parent.chi.splice(i, 1, value.chi[0]);
88
+ for (let i = 0; i < children.length; i++) {
89
+ if (children[i] == value) {
90
+ // @ts-ignore
91
+ children.splice(i, 1, !(parent.typ == EnumToken.FunctionTokenType && parent.val == 'calc') && typeof values[0].val != 'string' ? {
92
+ typ: EnumToken.FunctionTokenType,
93
+ val: 'calc',
94
+ chi: values
95
+ } : values[0]);
49
96
  break;
50
97
  }
51
98
  }
52
99
  }
53
100
  }
101
+ else {
102
+ for (let i = 0; i < children.length; i++) {
103
+ if (children[i] == value) {
104
+ if (parent.typ == EnumToken.FunctionTokenType && parent.val == 'calc') {
105
+ children.splice(i, 1, ...values);
106
+ }
107
+ else {
108
+ children.splice(i, 1, {
109
+ typ: EnumToken.FunctionTokenType,
110
+ val: 'calc',
111
+ chi: values
112
+ });
113
+ }
114
+ break;
115
+ }
116
+ }
117
+ }
54
118
  }
55
119
  }
56
120
  }
@@ -1,5 +1,6 @@
1
1
  import { EnumToken } from '../types.js';
2
2
  import { walkValues } from '../walk.js';
3
+ import { renderToken } from '../../renderer/render.js';
3
4
 
4
5
  function replace(node, variableScope) {
5
6
  for (const { value, parent: parentValue } of walkValues(node.val)) {
@@ -108,7 +109,11 @@ class InlineCssVariablesFeature {
108
109
  i = parent.chi?.length ?? 0;
109
110
  while (i--) {
110
111
  if (parent.chi[i].typ == EnumToken.DeclarationNodeType && parent.chi[i].nam == info.node.nam) {
111
- parent.chi.splice(i, 1);
112
+ // @ts-ignore
113
+ parent.chi.splice(i++, 1, {
114
+ typ: EnumToken.CommentTokenType,
115
+ val: `/* ${info.node.nam}: ${info.node.val.reduce((acc, curr) => acc + renderToken(curr), '')} */`
116
+ });
112
117
  }
113
118
  }
114
119
  if (parent.chi?.length == 0 && 'parent' in parent) {
@@ -1,10 +1,16 @@
1
1
  import { EnumToken } from '../types.js';
2
- import { getConfig } from '../../validation/config.js';
3
- import { walkValues } from '../walk.js';
2
+ import { getSyntaxConfig } from '../../validation/config.js';
4
3
  import { ValidationTokenEnum } from '../../validation/parser/types.js';
5
4
  import '../../validation/parser/parse.js';
5
+ import '../minify.js';
6
+ import { walkValues } from '../walk.js';
7
+ import '../../parser/parse.js';
8
+ import '../../renderer/color/utils/constants.js';
9
+ import '../../renderer/sourcemap/lib/encode.js';
10
+ import '../../parser/utils/config.js';
11
+ import '../../validation/syntaxes/complex-selector.js';
6
12
 
7
- const config = getConfig();
13
+ const config = getSyntaxConfig();
8
14
  class ComputePrefixFeature {
9
15
  static get ordering() {
10
16
  return 2;
@@ -30,7 +36,7 @@ class ComputePrefixFeature {
30
36
  const node = ast.chi[k];
31
37
  if (node.typ == EnumToken.DeclarationNodeType) {
32
38
  if (node.nam.charAt(0) == '-') {
33
- const match = node.nam.match(/^-(.*?)-(.*)$/);
39
+ const match = node.nam.match(/^-([^-]+)-(.+)$/);
34
40
  if (match != null) {
35
41
  const nam = match[2];
36
42
  if (nam.toLowerCase() in config.declarations) {
@@ -67,7 +73,6 @@ function matchToken(token, matches) {
67
73
  case ValidationTokenEnum.Comma:
68
74
  break;
69
75
  case ValidationTokenEnum.Keyword:
70
- console.error(matches[i], token);
71
76
  if (token.typ == EnumToken.IdenTokenType && token.val == matches[i].val) {
72
77
  return token;
73
78
  }
@@ -90,6 +95,13 @@ function matchToken(token, matches) {
90
95
  }
91
96
  break;
92
97
  case ValidationTokenEnum.PipeToken:
98
+ for (let j = 0; j < matches[i].chi.length; j++) {
99
+ result = matchToken(token, matches[i].chi[j]);
100
+ if (result != null) {
101
+ return result;
102
+ }
103
+ }
104
+ break;
93
105
  case ValidationTokenEnum.ColumnToken:
94
106
  case ValidationTokenEnum.AmpersandToken:
95
107
  result = matchToken(token, matches[i].l);
@@ -106,10 +118,6 @@ function matchToken(token, matches) {
106
118
  return result;
107
119
  }
108
120
  break;
109
- // default:
110
- //
111
- // console.error(token, matches[i]);
112
- // throw new Error('bar bar');
113
121
  }
114
122
  }
115
123
  return null;
@@ -1,6 +1,7 @@
1
1
  import { PropertyList } from '../../parser/declaration/list.js';
2
2
  import { EnumToken } from '../types.js';
3
3
  import '../minify.js';
4
+ import '../walk.js';
4
5
  import '../../parser/parse.js';
5
6
  import '../../renderer/color/utils/constants.js';
6
7
  import '../../renderer/sourcemap/lib/encode.js';
@@ -1,6 +1,7 @@
1
1
  import { EnumToken } from '../types.js';
2
- import { compute } from './math.js';
2
+ import { rem, compute } from './math.js';
3
3
  import { reduceNumber } from '../../renderer/render.js';
4
+ import { mathFuncs } from '../../syntax/syntax.js';
4
5
 
5
6
  /**
6
7
  * evaluate an array of tokens
@@ -8,15 +9,45 @@ import { reduceNumber } from '../../renderer/render.js';
8
9
  */
9
10
  function evaluate(tokens) {
10
11
  let nodes;
12
+ if (tokens.length == 1 && tokens[0].typ == EnumToken.FunctionTokenType && tokens[0].val != 'calc' && mathFuncs.includes(tokens[0].val)) {
13
+ const chi = tokens[0].chi.reduce((acc, t) => {
14
+ if (acc.length == 0 || t.typ == EnumToken.CommaTokenType) {
15
+ acc.push([]);
16
+ }
17
+ if ([EnumToken.WhitespaceTokenType, EnumToken.CommaTokenType, EnumToken.CommaTokenType].includes(t.typ)) {
18
+ return acc;
19
+ }
20
+ acc.at(-1).push(t);
21
+ return acc;
22
+ }, []);
23
+ for (let i = 0; i < chi.length; i++) {
24
+ chi[i] = evaluate(chi[i]);
25
+ }
26
+ tokens[0].chi = chi.reduce((acc, t) => {
27
+ if (acc.length > 0) {
28
+ acc.push({ typ: EnumToken.CommaTokenType });
29
+ }
30
+ acc.push(...t);
31
+ return acc;
32
+ });
33
+ return evaluateFunc(tokens[0]);
34
+ }
11
35
  try {
12
36
  nodes = inlineExpression(evaluateExpression(buildExpression(tokens)));
13
37
  }
14
38
  catch (e) {
15
- // console.error({tokens});
16
- // console.error(e);
17
39
  return tokens;
18
40
  }
19
41
  if (nodes.length <= 1) {
42
+ // @ts-ignore
43
+ if (nodes.length == 1 && nodes[0].typ == EnumToken.IdenTokenType && typeof Math[nodes[0].val.toUpperCase()] == 'number') {
44
+ return [{
45
+ ...nodes[0],
46
+ // @ts-ignore
47
+ val: '' + Math[nodes[0].val.toUpperCase()],
48
+ typ: EnumToken.NumberTokenType
49
+ }];
50
+ }
20
51
  return nodes;
21
52
  }
22
53
  const map = new Map;
@@ -71,9 +102,33 @@ function doEvaluate(l, r, op) {
71
102
  l,
72
103
  r
73
104
  };
74
- if (!isScalarToken(l) || !isScalarToken(r)) {
105
+ if (!isScalarToken(l) || !isScalarToken(r) || (l.typ == r.typ && 'unit' in l && 'unit' in r && l.unit != r.unit)) {
75
106
  return defaultReturn;
76
107
  }
108
+ if (l.typ == EnumToken.FunctionTokenType) {
109
+ const val = evaluateFunc(l);
110
+ if (val.length == 1) {
111
+ l = val[0];
112
+ }
113
+ else {
114
+ return defaultReturn;
115
+ }
116
+ }
117
+ if (r.typ == EnumToken.FunctionTokenType) {
118
+ const val = evaluateFunc(r);
119
+ if (val.length == 1) {
120
+ r = val[0];
121
+ }
122
+ else {
123
+ return defaultReturn;
124
+ }
125
+ }
126
+ if (l.typ == EnumToken.FunctionTokenType) {
127
+ const val = evaluateFunc(l);
128
+ if (val.length == 1) {
129
+ l = val[0];
130
+ }
131
+ }
77
132
  if ((op == EnumToken.Add || op == EnumToken.Sub)) {
78
133
  // @ts-ignore
79
134
  if (l.typ != r.typ) {
@@ -85,11 +140,13 @@ function doEvaluate(l, r, op) {
85
140
  ![EnumToken.NumberTokenType, EnumToken.PercentageTokenType].includes(r.typ)) {
86
141
  return defaultReturn;
87
142
  }
88
- const typ = l.typ == EnumToken.NumberTokenType ? r.typ : (r.typ == EnumToken.NumberTokenType ? l.typ : (l.typ == EnumToken.PercentageTokenType ? r.typ : l.typ));
89
- // @ts-ignore
90
- let v1 = typeof l.val == 'string' ? +l.val : l.val;
143
+ let typ = l.typ == EnumToken.NumberTokenType ? r.typ : (r.typ == EnumToken.NumberTokenType ? l.typ : (l.typ == EnumToken.PercentageTokenType ? r.typ : l.typ));
91
144
  // @ts-ignore
92
- let v2 = typeof r.val == 'string' ? +r.val : r.val;
145
+ let v1 = getValue(l);
146
+ let v2 = getValue(r);
147
+ if (v1 == null || v2 == null) {
148
+ return defaultReturn;
149
+ }
93
150
  if (op == EnumToken.Mul) {
94
151
  if (l.typ != EnumToken.NumberTokenType && r.typ != EnumToken.NumberTokenType) {
95
152
  if (typeof v1 == 'number' && l.typ == EnumToken.PercentageTokenType) {
@@ -110,11 +167,238 @@ function doEvaluate(l, r, op) {
110
167
  }
111
168
  // @ts-ignore
112
169
  const val = compute(v1, v2, op);
113
- return {
170
+ // typ = typeof val == 'number' ? EnumToken.NumberTokenType : EnumToken.FractionTokenType;
171
+ const token = {
114
172
  ...(l.typ == EnumToken.NumberTokenType ? r : l),
115
173
  typ,
116
174
  val: typeof val == 'number' ? reduceNumber(val) : val
117
175
  };
176
+ if (token.typ == EnumToken.IdenTokenType) {
177
+ // @ts-ignore
178
+ token.typ = EnumToken.NumberTokenType;
179
+ }
180
+ return token;
181
+ }
182
+ function getValue(t) {
183
+ let v1;
184
+ if (t.typ == EnumToken.FunctionTokenType) {
185
+ v1 = evaluateFunc(t);
186
+ if (v1.length != 1 || v1[0].typ == EnumToken.BinaryExpressionTokenType) {
187
+ return null;
188
+ }
189
+ t = v1[0];
190
+ }
191
+ if (t.typ == EnumToken.IdenTokenType) {
192
+ // @ts-ignore
193
+ return Math[t.val.toUpperCase()];
194
+ }
195
+ if (t.val.typ == EnumToken.FractionTokenType) {
196
+ // @ts-ignore
197
+ return t.val.l.val / t.val.r.val;
198
+ }
199
+ // @ts-ignore
200
+ return t.typ == EnumToken.FractionTokenType ? t.l.val / t.r.val : +t.val;
201
+ }
202
+ function evaluateFunc(token) {
203
+ const values = token.chi.slice();
204
+ switch (token.val) {
205
+ case 'abs':
206
+ case 'sin':
207
+ case 'cos':
208
+ case 'tan':
209
+ case 'asin':
210
+ case 'acos':
211
+ case 'atan':
212
+ case 'sign':
213
+ case 'sqrt':
214
+ case 'exp': {
215
+ const value = evaluate(values);
216
+ if (value.length != 1 || (value[0].typ != EnumToken.NumberTokenType && value[0].typ != EnumToken.FractionTokenType) || (value[0].typ == EnumToken.FractionTokenType && (+value[0].r.val == 0 || !Number.isFinite(+value[0].l.val) || !Number.isFinite(+value[0].r.val)))) {
217
+ return value;
218
+ }
219
+ // @ts-ignore
220
+ let val = value[0].typ == EnumToken.NumberTokenType ? +value[0].val : value[0].l.val / value[0].r.val;
221
+ return [{
222
+ typ: EnumToken.NumberTokenType,
223
+ val: '' + Math[token.val](val)
224
+ }];
225
+ }
226
+ case 'hypot': {
227
+ const chi = values.filter(t => ![EnumToken.WhitespaceTokenType, EnumToken.CommentTokenType, EnumToken.CommaTokenType].includes(t.typ));
228
+ let all = [];
229
+ let ref = chi[0];
230
+ let value = 0;
231
+ if (![EnumToken.NumberTokenType, EnumToken.PercentageTokenType].includes(ref.typ) && !('unit' in ref)) {
232
+ return [token];
233
+ }
234
+ for (let i = 0; i < chi.length; i++) {
235
+ // @ts-ignore
236
+ if (chi[i].typ != ref.typ || ('unit' in chi[i] && 'unit' in ref && chi[i].unit != ref.unit)) {
237
+ return [token];
238
+ }
239
+ // @ts-ignore
240
+ const val = getValue(chi[i]);
241
+ if (val == null) {
242
+ return [token];
243
+ }
244
+ all.push(val);
245
+ value += val * val;
246
+ }
247
+ return [
248
+ {
249
+ ...ref,
250
+ val: Math.sqrt(value).toFixed(rem(...all))
251
+ }
252
+ ];
253
+ }
254
+ case 'atan2':
255
+ case 'pow':
256
+ case 'rem':
257
+ case 'mod': {
258
+ const chi = values.filter(t => ![EnumToken.WhitespaceTokenType, EnumToken.CommentTokenType].includes(t.typ));
259
+ if (chi.length != 3 || chi[1].typ != EnumToken.CommaTokenType) {
260
+ return [token];
261
+ }
262
+ if (token.val == 'pow' && (chi[0].typ != EnumToken.NumberTokenType || chi[2].typ != EnumToken.NumberTokenType)) {
263
+ return [token];
264
+ }
265
+ if (['rem', 'mod'].includes(token.val) &&
266
+ (chi[0].typ != chi[2].typ) || ('unit' in chi[0] && 'unit' in chi[2] &&
267
+ chi[0].unit != chi[2].unit)) {
268
+ return [token];
269
+ }
270
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/mod
271
+ const v1 = evaluate([chi[0]]);
272
+ const v2 = evaluate([chi[2]]);
273
+ const types = [EnumToken.PercentageTokenType, EnumToken.DimensionTokenType, EnumToken.AngleTokenType, EnumToken.NumberTokenType, EnumToken.LengthTokenType, EnumToken.TimeTokenType, EnumToken.FrequencyTokenType, EnumToken.ResolutionTokenType];
274
+ if (v1.length != 1 || v2.length != 1 || !types.includes(v1[0].typ) || !types.includes(v2[0].typ) || v1[0].unit != v2[0].unit) {
275
+ return [token];
276
+ }
277
+ // @ts-ignore
278
+ const val1 = getValue(v1[0]);
279
+ // @ts-ignore
280
+ const val2 = getValue(v2[0]);
281
+ if (val1 == null || val2 == null || (v1[0].typ != v2[0].typ && val1 != 0 && val2 != 0)) {
282
+ return [token];
283
+ }
284
+ if (token.val == 'rem') {
285
+ if (val2 == 0) {
286
+ return [token];
287
+ }
288
+ return [
289
+ {
290
+ ...v1[0],
291
+ val: (val1 % val2).toFixed(rem(val1, val2))
292
+ }
293
+ ];
294
+ }
295
+ if (token.val == 'pow') {
296
+ return [
297
+ {
298
+ ...v1[0],
299
+ val: String(Math.pow(val1, val2))
300
+ }
301
+ ];
302
+ }
303
+ if (token.val == 'atan2') {
304
+ return [
305
+ {
306
+ ...{}, ...v1[0],
307
+ val: String(Math.atan2(val1, val2))
308
+ }
309
+ ];
310
+ }
311
+ return [
312
+ {
313
+ ...v1[0],
314
+ val: String(val2 == 0 ? val1 : val1 - (Math.floor(val1 / val2) * val2))
315
+ }
316
+ ];
317
+ }
318
+ case 'clamp':
319
+ token.chi = values;
320
+ return [token];
321
+ case 'log':
322
+ case 'round':
323
+ case 'min':
324
+ case 'max':
325
+ {
326
+ const strategy = token.val == 'round' && values[0]?.typ == EnumToken.IdenTokenType ? values.shift().val : null;
327
+ const valuesMap = new Map;
328
+ for (const curr of values) {
329
+ if (curr.typ == EnumToken.CommaTokenType || curr.typ == EnumToken.WhitespaceTokenType || curr.typ == EnumToken.CommentTokenType) {
330
+ continue;
331
+ }
332
+ const result = evaluate([curr]);
333
+ if (result.length != 1 || result[0].typ == EnumToken.FunctionTokenType) {
334
+ return [token];
335
+ }
336
+ const key = result[0].typ + ('unit' in result[0] ? result[0].unit : '');
337
+ if (!valuesMap.has(key)) {
338
+ valuesMap.set(key, []);
339
+ }
340
+ valuesMap.get(key).push(result[0]);
341
+ }
342
+ if (valuesMap.size == 1) {
343
+ const values = valuesMap.values().next().value;
344
+ if (token.val == 'log') {
345
+ if (values[0].typ != EnumToken.NumberTokenType || values.length > 2) {
346
+ return [token];
347
+ }
348
+ const val1 = getValue(values[0]);
349
+ const val2 = values.length == 2 ? getValue(values[1]) : null;
350
+ if (values.length == 1) {
351
+ return [
352
+ {
353
+ ...values[0],
354
+ val: String(Math.log(val1))
355
+ }
356
+ ];
357
+ }
358
+ return [
359
+ {
360
+ ...values[0],
361
+ val: String(Math.log(val1) / Math.log(val2))
362
+ }
363
+ ];
364
+ }
365
+ if (token.val == 'min' || token.val == 'max') {
366
+ let val = getValue(values[0]);
367
+ let val2 = val;
368
+ let ret = values[0];
369
+ for (const curr of values.slice(1)) {
370
+ val2 = getValue(curr);
371
+ if (val2 < val && token.val == 'min') {
372
+ val = val2;
373
+ ret = curr;
374
+ }
375
+ else if (val2 > val && token.val == 'max') {
376
+ val = val2;
377
+ ret = curr;
378
+ }
379
+ }
380
+ return [ret];
381
+ }
382
+ if (token.val == 'round') {
383
+ let val = getValue(values[0]);
384
+ let val2 = getValue(values[1]);
385
+ if (Number.isNaN(val) || Number.isNaN(val2)) {
386
+ return [token];
387
+ }
388
+ if (strategy == null || strategy == 'down') {
389
+ val = val - (val % val2);
390
+ }
391
+ else {
392
+ val = strategy == 'to-zero' ? Math.trunc(val / val2) * val2 : (strategy == 'nearest' ? Math.round(val / val2) * val2 : Math.ceil(val / val2) * val2);
393
+ }
394
+ // @ts-ignore
395
+ return [{ ...values[0], val: String(val) }];
396
+ }
397
+ }
398
+ }
399
+ return [token];
400
+ }
401
+ return [token];
118
402
  }
119
403
  /**
120
404
  * convert BinaryExpression into an array
@@ -155,7 +439,11 @@ function evaluateExpression(token) {
155
439
  return doEvaluate(token.l, token.r, token.op);
156
440
  }
157
441
  function isScalarToken(token) {
158
- return 'unit' in token || [EnumToken.NumberTokenType, EnumToken.FractionTokenType, EnumToken.PercentageTokenType].includes(token.typ);
442
+ return 'unit' in token ||
443
+ (token.typ == EnumToken.FunctionTokenType && mathFuncs.includes(token.val)) ||
444
+ // @ts-ignore
445
+ (token.typ == EnumToken.IdenTokenType && typeof Math[token.val.toUpperCase()] == 'number') ||
446
+ [EnumToken.NumberTokenType, EnumToken.FractionTokenType, EnumToken.PercentageTokenType].includes(token.typ);
159
447
  }
160
448
  /**
161
449
  *
@@ -225,4 +513,4 @@ function factor(tokens, ops) {
225
513
  return tokens;
226
514
  }
227
515
 
228
- export { evaluate };
516
+ export { evaluate, evaluateFunc };
@@ -87,9 +87,15 @@ function compute(a, b, op) {
87
87
  r: { typ: EnumToken.NumberTokenType, val: reduceNumber(a2[1]) }
88
88
  };
89
89
  }
90
+ function rem(...a) {
91
+ if (a.some((i) => !Number.isInteger(i))) {
92
+ return a.reduce((a, b) => Math.max(a, String(b).split('.')[1]?.length ?? 0), 0);
93
+ }
94
+ return 0;
95
+ }
90
96
  function simplify(a, b) {
91
97
  const g = gcd(a, b);
92
98
  return g > 1 ? [a / g, b / g] : [a, b];
93
99
  }
94
100
 
95
- export { compute, gcd, simplify };
101
+ export { compute, gcd, rem, simplify };
@@ -2,10 +2,10 @@ import { parseString } from '../parser/parse.js';
2
2
  import { EnumToken } from './types.js';
3
3
  import { walkValues } from './walk.js';
4
4
  import { replaceCompound } from './expand.js';
5
- import { isWhiteSpace, isIdent, isFunction, isIdentStart } from '../syntax/syntax.js';
5
+ import { isIdent, isFunction, isWhiteSpace, isIdentStart } from '../syntax/syntax.js';
6
6
  import '../parser/utils/config.js';
7
7
  import { eq } from '../parser/utils/eq.js';
8
- import { renderToken, doRender } from '../renderer/render.js';
8
+ import { doRender, renderToken } from '../renderer/render.js';
9
9
  import * as index from './features/index.js';
10
10
 
11
11
  const combinators = ['+', '>', '~', '||', '|'];
@@ -13,6 +13,15 @@ const definedPropertySettings = { configurable: true, enumerable: false, writabl
13
13
  const notEndingWith = ['(', '['].concat(combinators);
14
14
  // @ts-ignore
15
15
  const features = Object.values(index).sort((a, b) => a.ordering - b.ordering);
16
+ /**
17
+ * minify ast
18
+ * @param ast
19
+ * @param options
20
+ * @param recursive
21
+ * @param errors
22
+ * @param nestingContent
23
+ * @param context
24
+ */
16
25
  function minify(ast, options = {}, recursive = false, errors, nestingContent, context = {}) {
17
26
  if (!('nodes' in context)) {
18
27
  context.nodes = new Set;
@@ -82,7 +91,8 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co
82
91
  continue;
83
92
  }
84
93
  if (node.typ == EnumToken.AtRuleNodeType) {
85
- if (node.nam == 'media' && node.val == 'all') {
94
+ // @ts-ignore
95
+ if (node.nam == 'media' && ['all', '', null].includes(node.val)) {
86
96
  // @ts-ignore
87
97
  ast.chi?.splice(i, 1, ...node.chi);
88
98
  i--;
@@ -397,6 +407,18 @@ function minify(ast, options = {}, recursive = false, errors, nestingContent, co
397
407
  }
398
408
  return ast;
399
409
  }
410
+ function hasDeclaration(node) {
411
+ // @ts-ignore
412
+ for (let i = 0; i < node.chi?.length; i++) {
413
+ // @ts-ignore
414
+ if (node.chi[i].typ == EnumToken.CommentNodeType) {
415
+ continue;
416
+ }
417
+ // @ts-ignore
418
+ return node.chi[i].typ == EnumToken.DeclarationNodeType;
419
+ }
420
+ return true;
421
+ }
400
422
  function reduceSelector(selector) {
401
423
  if (selector.length == 0) {
402
424
  return null;
@@ -497,18 +519,10 @@ function reduceSelector(selector) {
497
519
  reducible: selector.every((selector) => !['>', '+', '~', '&'].includes(selector[0]))
498
520
  };
499
521
  }
500
- function hasDeclaration(node) {
501
- // @ts-ignore
502
- for (let i = 0; i < node.chi?.length; i++) {
503
- // @ts-ignore
504
- if (node.chi[i].typ == EnumToken.CommentNodeType) {
505
- continue;
506
- }
507
- // @ts-ignore
508
- return node.chi[i].typ == EnumToken.DeclarationNodeType;
509
- }
510
- return true;
511
- }
522
+ /**
523
+ * split selector string
524
+ * @param buffer
525
+ */
512
526
  function splitRule(buffer) {
513
527
  const result = [[]];
514
528
  let str = '';
@@ -995,4 +1009,4 @@ function reduceRuleSelector(node) {
995
1009
  }
996
1010
  }
997
1011
 
998
- export { combinators, definedPropertySettings, hasDeclaration, minify, reduceSelector, splitRule };
1012
+ export { combinators, definedPropertySettings, minify, splitRule };