@tbela99/css-parser 0.9.0 → 1.0.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 (70) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +22 -12
  3. package/dist/index-umd-web.js +2678 -2838
  4. package/dist/index.cjs +2468 -2628
  5. package/dist/index.d.ts +71 -26
  6. package/dist/lib/ast/expand.js +15 -2
  7. package/dist/lib/ast/features/calc.js +7 -10
  8. package/dist/lib/ast/features/index.js +1 -0
  9. package/dist/lib/ast/features/inlinecssvariables.js +0 -5
  10. package/dist/lib/ast/features/prefix.js +2 -7
  11. package/dist/lib/ast/features/shorthand.js +6 -9
  12. package/dist/lib/ast/features/transform.js +60 -0
  13. package/dist/lib/ast/math/expression.js +14 -10
  14. package/dist/lib/ast/math/math.js +14 -2
  15. package/dist/lib/ast/minify.js +47 -6
  16. package/dist/lib/ast/transform/compute.js +336 -0
  17. package/dist/lib/ast/transform/convert.js +33 -0
  18. package/dist/lib/ast/transform/matrix.js +111 -0
  19. package/dist/lib/ast/transform/minify.js +296 -0
  20. package/dist/lib/ast/transform/perspective.js +10 -0
  21. package/dist/lib/ast/transform/rotate.js +40 -0
  22. package/dist/lib/ast/transform/scale.js +32 -0
  23. package/dist/lib/ast/transform/skew.js +23 -0
  24. package/dist/lib/ast/transform/translate.js +32 -0
  25. package/dist/lib/ast/transform/utils.js +198 -0
  26. package/dist/lib/ast/types.js +2 -0
  27. package/dist/lib/ast/walk.js +23 -17
  28. package/dist/lib/parser/parse.js +174 -127
  29. package/dist/lib/parser/utils/declaration.js +1 -1
  30. package/dist/lib/renderer/color/{colormix.js → color-mix.js} +6 -0
  31. package/dist/lib/renderer/color/color.js +96 -20
  32. package/dist/lib/renderer/color/hex.js +17 -7
  33. package/dist/lib/renderer/color/hsl.js +7 -2
  34. package/dist/lib/renderer/color/lab.js +10 -1
  35. package/dist/lib/renderer/color/lch.js +8 -0
  36. package/dist/lib/renderer/color/oklab.js +8 -0
  37. package/dist/lib/renderer/color/oklch.js +8 -0
  38. package/dist/lib/renderer/color/prophotorgb.js +2 -2
  39. package/dist/lib/renderer/color/relativecolor.js +10 -21
  40. package/dist/lib/renderer/color/rgb.js +10 -7
  41. package/dist/lib/renderer/color/srgb.js +30 -6
  42. package/dist/lib/renderer/color/utils/components.js +13 -2
  43. package/dist/lib/renderer/color/xyz.js +2 -18
  44. package/dist/lib/renderer/color/xyzd50.js +20 -2
  45. package/dist/lib/renderer/render.js +70 -32
  46. package/dist/lib/renderer/sourcemap/sourcemap.js +1 -1
  47. package/dist/lib/syntax/syntax.js +75 -56
  48. package/dist/lib/validation/at-rules/container.js +6 -6
  49. package/dist/lib/validation/at-rules/document.js +40 -60
  50. package/dist/lib/validation/at-rules/import.js +61 -59
  51. package/dist/lib/validation/at-rules/keyframes.js +1 -1
  52. package/dist/lib/validation/at-rules/media.js +1 -1
  53. package/dist/lib/validation/at-rules/supports.js +40 -9
  54. package/dist/lib/validation/atrule.js +0 -4
  55. package/dist/lib/validation/config.json.js +83 -35
  56. package/dist/lib/validation/parser/parse.js +1 -95
  57. package/dist/lib/validation/parser/types.js +1 -2
  58. package/dist/lib/validation/selector.js +5 -2
  59. package/dist/lib/validation/syntaxes/compound-selector.js +2 -2
  60. package/dist/lib/validation/syntaxes/keyframe-block-list.js +2 -2
  61. package/dist/lib/validation/syntaxes/keyframe-selector.js +11 -90
  62. package/dist/lib/validation/syntaxes/layer-name.js +5 -16
  63. package/dist/lib/validation/syntaxes/relative-selector.js +15 -14
  64. package/dist/lib/validation/utils/list.js +18 -1
  65. package/dist/node/load.js +1 -1
  66. package/package.json +13 -12
  67. package/dist/lib/renderer/color/prophotoRgb.js +0 -56
  68. package/dist/lib/validation/declaration.js +0 -102
  69. package/dist/lib/validation/syntax.js +0 -1475
  70. package/dist/lib/validation/syntaxes/image.js +0 -29
@@ -1,5 +1,12 @@
1
1
  import { EnumToken } from './types.js';
2
2
 
3
+ var WalkerOptionEnum;
4
+ (function (WalkerOptionEnum) {
5
+ WalkerOptionEnum[WalkerOptionEnum["Ignore"] = 0] = "Ignore";
6
+ WalkerOptionEnum[WalkerOptionEnum["Stop"] = 1] = "Stop";
7
+ WalkerOptionEnum[WalkerOptionEnum["Children"] = 2] = "Children";
8
+ WalkerOptionEnum[WalkerOptionEnum["IgnoreChildren"] = 3] = "IgnoreChildren";
9
+ })(WalkerOptionEnum || (WalkerOptionEnum = {}));
3
10
  var WalkerValueEvent;
4
11
  (function (WalkerValueEvent) {
5
12
  WalkerValueEvent[WalkerValueEvent["Enter"] = 0] = "Enter";
@@ -18,10 +25,10 @@ function* walk(node, filter) {
18
25
  let option = null;
19
26
  if (filter != null) {
20
27
  option = filter(node);
21
- if (option === 'ignore') {
28
+ if (option === WalkerOptionEnum.Ignore) {
22
29
  continue;
23
30
  }
24
- if (option === 'stop') {
31
+ if (option === WalkerOptionEnum.Stop) {
25
32
  break;
26
33
  }
27
34
  }
@@ -30,7 +37,7 @@ function* walk(node, filter) {
30
37
  // @ts-ignore
31
38
  yield { node, parent: map.get(node), root };
32
39
  }
33
- if (option !== 'ignore-children' && 'chi' in node) {
40
+ if (option !== WalkerOptionEnum.IgnoreChildren && 'chi' in node) {
34
41
  parents.unshift(...node.chi);
35
42
  for (const child of node.chi.slice()) {
36
43
  map.set(child, node);
@@ -62,19 +69,20 @@ function* walkValues(values, root = null, filter, reverse) {
62
69
  event: WalkerValueEvent.Enter
63
70
  };
64
71
  }
72
+ const eventType = filter.event ?? WalkerValueEvent.Enter;
65
73
  while (stack.length > 0) {
66
74
  let value = reverse ? stack.pop() : stack.shift();
67
75
  let option = null;
68
- if (filter.fn != null && filter.event == WalkerValueEvent.Enter) {
76
+ if (filter.fn != null && eventType == WalkerValueEvent.Enter) {
69
77
  const isValid = filter.type == null || value.typ == filter.type ||
70
78
  (Array.isArray(filter.type) && filter.type.includes(value.typ)) ||
71
79
  (typeof filter.type == 'function' && filter.type(value));
72
80
  if (isValid) {
73
- option = filter.fn(value, map.get(value) ?? root, WalkerValueEvent.Enter);
74
- if (option === 'ignore') {
81
+ option = filter.fn(value, map.get(value) ?? root);
82
+ if (option === WalkerOptionEnum.Ignore) {
75
83
  continue;
76
84
  }
77
- if (option === 'stop') {
85
+ if (option === WalkerOptionEnum.Stop) {
78
86
  break;
79
87
  }
80
88
  // @ts-ignore
@@ -83,8 +91,7 @@ function* walkValues(values, root = null, filter, reverse) {
83
91
  }
84
92
  }
85
93
  }
86
- // @ts-ignore
87
- if (filter.event == WalkerValueEvent.Enter && option !== 'children') {
94
+ if (eventType == WalkerValueEvent.Enter && option !== WalkerOptionEnum.Children) {
88
95
  yield {
89
96
  value,
90
97
  parent: map.get(value) ?? root,
@@ -94,7 +101,7 @@ function* walkValues(values, root = null, filter, reverse) {
94
101
  root: root ?? null
95
102
  };
96
103
  }
97
- if (option !== 'ignore-children' && 'chi' in value) {
104
+ if (option !== WalkerOptionEnum.IgnoreChildren && 'chi' in value) {
98
105
  const sliced = value.chi.slice();
99
106
  for (const child of sliced) {
100
107
  map.set(child, value);
@@ -107,24 +114,23 @@ function* walkValues(values, root = null, filter, reverse) {
107
114
  }
108
115
  }
109
116
  else if (value.typ == EnumToken.BinaryExpressionTokenType) {
110
- map.set(value.l, map.get(value) ?? root);
111
- map.set(value.r, map.get(value) ?? root);
117
+ map.set(value.l, value);
118
+ map.set(value.r, value);
112
119
  stack.unshift(value.l, value.r);
113
120
  }
114
- if (filter.event == WalkerValueEvent.Leave && filter.fn != null) {
121
+ if (eventType == WalkerValueEvent.Leave && filter.fn != null) {
115
122
  const isValid = filter.type == null || value.typ == filter.type ||
116
123
  (Array.isArray(filter.type) && filter.type.includes(value.typ)) ||
117
124
  (typeof filter.type == 'function' && filter.type(value));
118
125
  if (isValid) {
119
- option = filter.fn(value, map.get(value), WalkerValueEvent.Leave);
126
+ option = filter.fn(value, map.get(value));
120
127
  // @ts-ignore
121
128
  if (option != null && 'typ' in option) {
122
129
  map.set(option, map.get(value) ?? root);
123
130
  }
124
131
  }
125
132
  }
126
- // @ts-ignore
127
- if (filter.event == WalkerValueEvent.Leave && option !== 'children') {
133
+ if (eventType == WalkerValueEvent.Leave && option !== WalkerOptionEnum.Children) {
128
134
  yield {
129
135
  value,
130
136
  parent: map.get(value) ?? root,
@@ -138,4 +144,4 @@ function* walkValues(values, root = null, filter, reverse) {
138
144
  }
139
145
  }
140
146
 
141
- export { WalkerValueEvent, walk, walkValues };
147
+ export { WalkerOptionEnum, WalkerValueEvent, walk, walkValues };
@@ -1,8 +1,8 @@
1
- import { webkitPseudoAliasMap, isIdentStart, isIdent, mathFuncs, isColor, isHexColor, isPseudo, isAtKeyword, isFunction, isNumber, isPercentage, isFlex, isDimension, parseDimension, isHash, mediaTypes } from '../syntax/syntax.js';
1
+ import { webkitPseudoAliasMap, isIdentStart, isIdent, mathFuncs, isColor, isHexColor, isPseudo, pseudoElements, isAtKeyword, isFunction, isNumber, isPercentage, isFlex, isDimension, parseDimension, isHash, mediaTypes } from '../syntax/syntax.js';
2
2
  import './utils/config.js';
3
3
  import { EnumToken, funcLike, ValidationLevel } from '../ast/types.js';
4
4
  import { minify, definedPropertySettings, combinators } from '../ast/minify.js';
5
- import { walkValues, walk } from '../ast/walk.js';
5
+ import { walkValues, walk, WalkerOptionEnum } from '../ast/walk.js';
6
6
  import { expand } from '../ast/expand.js';
7
7
  import { parseDeclarationNode } from './utils/declaration.js';
8
8
  import { renderToken } from '../renderer/render.js';
@@ -14,6 +14,8 @@ import '../validation/parser/parse.js';
14
14
  import { validateSelector } from '../validation/selector.js';
15
15
  import { validateAtRule } from '../validation/atrule.js';
16
16
  import '../validation/syntaxes/complex-selector.js';
17
+ import { validateKeyframeSelector } from '../validation/syntaxes/keyframe-selector.js';
18
+ import { validateAtRuleKeyframes } from '../validation/at-rules/keyframes.js';
17
19
 
18
20
  const urlTokenMatcher = /^(["']?)[a-zA-Z0-9_/.-][a-zA-Z0-9_/:.#?-]+(\1)$/;
19
21
  const trimWhiteSpace = [EnumToken.CommentTokenType, EnumToken.GtTokenType, EnumToken.GteTokenType, EnumToken.LtTokenType, EnumToken.LteTokenType, EnumToken.ColumnCombinatorTokenType];
@@ -46,13 +48,15 @@ async function doParse(iterator, options = {}) {
46
48
  src: '',
47
49
  sourcemap: false,
48
50
  minify: true,
51
+ pass: 1,
49
52
  parseColor: true,
50
- nestingRules: false,
53
+ nestingRules: true,
51
54
  resolveImport: false,
52
55
  resolveUrls: false,
53
56
  removeCharset: true,
54
57
  removeEmpty: true,
55
58
  removeDuplicateDeclarations: true,
59
+ computeTransform: true,
56
60
  computeShorthand: true,
57
61
  computeCalcExpression: true,
58
62
  inlineCssVariables: false,
@@ -121,11 +125,13 @@ async function doParse(iterator, options = {}) {
121
125
  }
122
126
  else if (item.token == '{') {
123
127
  let inBlock = 1;
128
+ tokens = [item];
124
129
  do {
125
130
  item = iter.next().value;
126
131
  if (item == null) {
127
132
  break;
128
133
  }
134
+ tokens.push(item);
129
135
  if (item.token == '{') {
130
136
  inBlock++;
131
137
  }
@@ -133,6 +139,13 @@ async function doParse(iterator, options = {}) {
133
139
  inBlock--;
134
140
  }
135
141
  } while (inBlock != 0);
142
+ if (tokens.length > 0) {
143
+ errors.push({
144
+ action: 'drop',
145
+ message: 'invalid block',
146
+ rawTokens: tokens.slice()
147
+ });
148
+ }
136
149
  }
137
150
  tokens = [];
138
151
  map = new Map;
@@ -165,7 +178,8 @@ async function doParse(iterator, options = {}) {
165
178
  await parseNode(tokens, context, stats, options, errors, src, map, rawTokens);
166
179
  rawTokens.length = 0;
167
180
  if (context != null && context.typ == EnumToken.InvalidRuleTokenType) {
168
- const index = context.chi.findIndex(node => node == context);
181
+ // @ts-ignore
182
+ const index = context.chi.findIndex((node) => node == context);
169
183
  if (index > -1) {
170
184
  context.chi.splice(index, 1);
171
185
  }
@@ -225,7 +239,10 @@ async function doParse(iterator, options = {}) {
225
239
  }
226
240
  if (options.minify) {
227
241
  if (ast.chi.length > 0) {
228
- minify(ast, options, true, errors, false);
242
+ let passes = options.pass ?? 1;
243
+ while (passes--) {
244
+ minify(ast, options, true, errors, false);
245
+ }
229
246
  }
230
247
  }
231
248
  const endTime = performance.now();
@@ -326,8 +343,13 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
326
343
  continue;
327
344
  }
328
345
  if (type != EnumToken.AtRuleNodeType) {
329
- errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
330
- return null;
346
+ // @ts-ignore
347
+ if (!(type == EnumToken.InvalidAtRuleTokenType &&
348
+ // @ts-ignore
349
+ ['charset', 'layer', 'import'].includes(context.chi[i].nam))) {
350
+ errors.push({ action: 'drop', message: 'invalid @import', location: { src, ...position } });
351
+ return null;
352
+ }
331
353
  }
332
354
  // @ts-ignore
333
355
  const name = context.chi[i].nam;
@@ -362,12 +384,20 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
362
384
  if (tokens[0].typ == EnumToken.UrlFunctionTokenType) {
363
385
  if (tokens[1].typ == EnumToken.UrlTokenTokenType || tokens[1].typ == EnumToken.StringTokenType) {
364
386
  tokens.shift();
365
- if (tokens[1].typ == EnumToken.UrlTokenTokenType) {
387
+ if (tokens[0]?.typ == EnumToken.UrlTokenTokenType) {
366
388
  // @ts-ignore
367
389
  tokens[0].typ = EnumToken.StringTokenType;
368
390
  // @ts-ignore
369
391
  tokens[0].val = `"${tokens[0].val}"`;
370
392
  }
393
+ // @ts-ignore
394
+ while (tokens[1]?.typ == EnumToken.WhitespaceTokenType || tokens[1]?.typ == EnumToken.CommentTokenType) {
395
+ tokens.splice(1, 1);
396
+ }
397
+ // @ts-ignore
398
+ if (tokens[1]?.typ == EnumToken.EndParensTokenType) {
399
+ tokens.splice(1, 1);
400
+ }
371
401
  }
372
402
  }
373
403
  // @ts-ignore
@@ -448,14 +478,16 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
448
478
  acc.push(renderToken(curr, { removeComments: true }));
449
479
  return acc;
450
480
  }, []);
481
+ const nam = renderToken(atRule, { removeComments: true });
482
+ // @ts-ignore
451
483
  const node = {
452
- typ: EnumToken.AtRuleNodeType,
453
- nam: renderToken(atRule, { removeComments: true }),
454
- tokens: t,
484
+ typ: /^(-[a-z]+-)?keyframes$/.test(nam) ? EnumToken.KeyframeAtRuleNodeType : EnumToken.AtRuleNodeType,
485
+ nam,
486
+ // tokens: t,
455
487
  val: raw.join('')
456
488
  };
457
489
  Object.defineProperties(node, {
458
- tokens: { ...definedPropertySettings, enumerable: true, value: tokens.slice() },
490
+ tokens: { ...definedPropertySettings, enumerable: false, value: tokens.slice() },
459
491
  raw: { ...definedPropertySettings, value: raw }
460
492
  });
461
493
  if (delim.typ == EnumToken.BlockStartTokenType) {
@@ -481,7 +513,7 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
481
513
  isValid = false;
482
514
  }
483
515
  }
484
- const valid = isValid ? validateAtRule(node, options, context) : {
516
+ const valid = isValid ? (node.typ == EnumToken.KeyframeAtRuleNodeType ? validateAtRuleKeyframes(node) : validateAtRule(node, options, context)) : {
485
517
  valid: ValidationLevel.Drop,
486
518
  node,
487
519
  syntax: '@' + node.nam,
@@ -497,7 +529,10 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
497
529
  node.typ = EnumToken.InvalidAtRuleTokenType;
498
530
  }
499
531
  else {
500
- node.val = node.tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false, removeComments: true }), '');
532
+ node.val = node.tokens.reduce((acc, curr) => acc + renderToken(curr, {
533
+ minify: false,
534
+ removeComments: true
535
+ }), '');
501
536
  }
502
537
  }
503
538
  // @ts-ignore
@@ -510,77 +545,84 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
510
545
  if (delim.typ == EnumToken.BlockStartTokenType) {
511
546
  const position = map.get(tokens[0]);
512
547
  const uniq = new Map;
513
- parseTokens(tokens, { minify: true }).reduce((acc, curr, index, array) => {
514
- if (curr.typ == EnumToken.CommentTokenType) {
515
- return acc;
516
- }
517
- if (curr.typ == EnumToken.WhitespaceTokenType) {
518
- if (trimWhiteSpace.includes(array[index - 1]?.typ) ||
519
- trimWhiteSpace.includes(array[index + 1]?.typ) ||
520
- combinators.includes(array[index - 1]?.val) ||
521
- combinators.includes(array[index + 1]?.val)) {
522
- return acc;
523
- }
524
- }
525
- let t = renderToken(curr, { minify: false });
526
- if (t == ',') {
527
- acc.push([]);
528
- // uniqTokens.push([]);
529
- }
530
- else {
531
- acc[acc.length - 1].push(t);
532
- // uniqTokens[uniqTokens.length - 1].push(curr);
533
- }
534
- return acc;
535
- }, [[]]).reduce((acc, curr) => {
536
- let i = 0;
537
- for (; i < curr.length; i++) {
538
- if (i + 1 < curr.length && curr[i] == '*') {
539
- if (curr[i] == '*') {
540
- let index = curr[i + 1] == ' ' ? 2 : 1;
541
- if (!['>', '~', '+'].includes(curr[index])) {
542
- curr.splice(i, index);
543
- }
544
- }
545
- }
546
- }
547
- acc.set(curr.join(''), curr);
548
- return acc;
549
- }, uniq);
550
- const ruleType = context.typ == EnumToken.AtRuleNodeType && context.nam == 'keyframes' ? EnumToken.KeyFrameRuleNodeType : EnumToken.RuleNodeType;
548
+ parseTokens(tokens, { minify: true });
549
+ const ruleType = context.typ == EnumToken.KeyframeAtRuleNodeType ? EnumToken.KeyFrameRuleNodeType : EnumToken.RuleNodeType;
551
550
  if (ruleType == EnumToken.RuleNodeType) {
552
551
  parseSelector(tokens);
553
- if (options.validation) {
554
- // @ts-ignore
555
- const valid = validateSelector(tokens, options, context);
556
- if (valid.valid != ValidationLevel.Valid) {
557
- const node = {
558
- typ: EnumToken.InvalidRuleTokenType,
559
- // @ts-ignore
560
- sel: tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false }), ''),
561
- chi: []
562
- };
563
- errors.push({
564
- action: 'drop',
565
- message: valid.error + ' - "' + tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false }), '') + '"',
566
- // @ts-ignore
567
- location: { src, ...(map.get(valid.node) ?? position) }
568
- });
552
+ }
553
+ if (options.validation) {
554
+ // @ts-ignore
555
+ const valid = ruleType == EnumToken.KeyFrameRuleNodeType ? validateKeyframeSelector(tokens) : validateSelector(tokens, options, context);
556
+ if (valid.valid != ValidationLevel.Valid) {
557
+ const node = {
558
+ typ: EnumToken.InvalidRuleTokenType,
559
+ sel: tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false }), ''),
560
+ chi: []
561
+ };
562
+ errors.push({
563
+ action: 'drop',
564
+ message: valid.error + ' - "' + tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false }), '') + '"',
569
565
  // @ts-ignore
570
- context.chi.push(node);
571
- Object.defineProperty(node, 'parent', { ...definedPropertySettings, value: context });
572
- return node;
573
- }
566
+ location: { src, ...(map.get(valid.node) ?? position) }
567
+ });
568
+ // @ts-ignore
569
+ context.chi.push(node);
570
+ Object.defineProperty(node, 'parent', { ...definedPropertySettings, value: context });
571
+ return node;
574
572
  }
575
573
  }
576
574
  const node = {
577
575
  typ: ruleType,
578
- sel: [...uniq.keys()].join(','),
576
+ sel: [...tokens.reduce((acc, curr, index, array) => {
577
+ if (curr.typ == EnumToken.CommentTokenType) {
578
+ return acc;
579
+ }
580
+ if (curr.typ == EnumToken.WhitespaceTokenType) {
581
+ if (trimWhiteSpace.includes(array[index - 1]?.typ) ||
582
+ trimWhiteSpace.includes(array[index + 1]?.typ) ||
583
+ combinators.includes(array[index - 1]?.val) ||
584
+ combinators.includes(array[index + 1]?.val)) {
585
+ return acc;
586
+ }
587
+ }
588
+ if (ruleType == EnumToken.KeyFrameRuleNodeType) {
589
+ if (curr.typ == EnumToken.IdenTokenType && curr.val == 'from') {
590
+ Object.assign(curr, { typ: EnumToken.PercentageTokenType, val: '0' });
591
+ }
592
+ else if (curr.typ == EnumToken.PercentageTokenType && curr.val == '100') {
593
+ Object.assign(curr, { typ: EnumToken.IdenTokenType, val: 'to' });
594
+ }
595
+ }
596
+ let t = renderToken(curr, { minify: false });
597
+ if (t == ',') {
598
+ acc.push([]);
599
+ // uniqTokens.push([]);
600
+ }
601
+ else {
602
+ acc[acc.length - 1].push(t);
603
+ // uniqTokens[uniqTokens.length - 1].push(curr);
604
+ }
605
+ return acc;
606
+ }, [[]]).reduce((acc, curr) => {
607
+ let i = 0;
608
+ for (; i < curr.length; i++) {
609
+ if (i + 1 < curr.length && curr[i] == '*') {
610
+ if (curr[i] == '*') {
611
+ let index = curr[i + 1] == ' ' ? 2 : 1;
612
+ if (!['>', '~', '+'].includes(curr[index])) {
613
+ curr.splice(i, index);
614
+ }
615
+ }
616
+ }
617
+ }
618
+ acc.set(curr.join(''), curr);
619
+ return acc;
620
+ }, uniq).keys()].join(','),
579
621
  chi: []
580
622
  };
581
623
  Object.defineProperty(node, 'tokens', {
582
624
  ...definedPropertySettings,
583
- enumerable: true,
625
+ enumerable: false,
584
626
  value: tokens.slice()
585
627
  });
586
628
  let raw = [...uniq.values()];
@@ -660,7 +702,8 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
660
702
  }
661
703
  }
662
704
  }
663
- if (value == null || value.length == 0) {
705
+ const nam = renderToken(name.shift(), { removeComments: true });
706
+ if (value == null || (!nam.startsWith('--') && value.length == 0)) {
664
707
  errors.push({
665
708
  action: 'drop',
666
709
  message: 'doParse: invalid declaration',
@@ -668,33 +711,30 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
668
711
  });
669
712
  return null;
670
713
  }
714
+ for (const { value: token } of walkValues(value, null, {
715
+ fn: (node) => node.typ == EnumToken.FunctionTokenType && node.val == 'calc' ? WalkerOptionEnum.IgnoreChildren : null,
716
+ type: EnumToken.FunctionTokenType
717
+ })) {
718
+ if (token.typ == EnumToken.FunctionTokenType && token.val == 'calc') {
719
+ for (const { value: node, parent } of walkValues(token.chi, token)) {
720
+ // fix expressions starting with '/' or '*' such as '/4' in (1 + 1)/4
721
+ if (node.typ == EnumToken.LiteralTokenType && node.val.length > 0) {
722
+ if (node.val[0] == '/' || node.val[0] == '*') {
723
+ parent.chi.splice(parent.chi.indexOf(node), 1, { typ: node.val[0] == '/' ? EnumToken.Div : EnumToken.Mul }, ...parseString(node.val.slice(1)));
724
+ }
725
+ }
726
+ }
727
+ }
728
+ }
671
729
  const node = {
672
730
  typ: EnumToken.DeclarationNodeType,
673
731
  // @ts-ignore
674
- nam: renderToken(name.shift(), { removeComments: true }),
732
+ nam,
675
733
  // @ts-ignore
676
734
  val: value
677
735
  };
678
736
  const result = parseDeclarationNode(node, errors, src, position);
679
737
  if (result != null) {
680
- // if (options.validation) {
681
- //
682
- // const valid: ValidationResult = validateDeclaration(result, options, context);
683
- //
684
- // console.error({valid});
685
- //
686
- // if (valid.valid == ValidationLevel.Drop) {
687
- //
688
- // errors.push({
689
- // action: 'drop',
690
- // message: valid.error + ' - "' + tokens.reduce((acc, curr) => acc + renderToken(curr, {minify: false}), '') + '"',
691
- // // @ts-ignore
692
- // location: {src, ...(map.get(valid.node) ?? position)}
693
- // });
694
- //
695
- // return null;
696
- // }
697
- // }
698
738
  // @ts-ignore
699
739
  context.chi.push(result);
700
740
  Object.defineProperty(result, 'parent', { ...definedPropertySettings, value: context });
@@ -777,8 +817,6 @@ function parseAtRulePrelude(tokens, atRule) {
777
817
  }
778
818
  }
779
819
  if (value.typ == EnumToken.ParensTokenType || (value.typ == EnumToken.FunctionTokenType && ['media', 'supports', 'style', 'scroll-state'].includes(value.val))) {
780
- // @todo parse range and declarations
781
- // parseDeclaration(parent.chi);
782
820
  let i;
783
821
  let nameIndex = -1;
784
822
  let valueIndex = -1;
@@ -1053,10 +1091,16 @@ function getTokenType(val, hint) {
1053
1091
  val: val.slice(0, -1),
1054
1092
  chi: []
1055
1093
  }
1056
- : {
1057
- typ: EnumToken.PseudoClassTokenType,
1094
+ : (
1095
+ // https://www.w3.org/TR/selectors-4/#single-colon-pseudos
1096
+ val.startsWith('::') || pseudoElements.includes(val) ? {
1097
+ typ: EnumToken.PseudoElementTokenType,
1058
1098
  val
1059
- };
1099
+ } :
1100
+ {
1101
+ typ: EnumToken.PseudoClassTokenType,
1102
+ val
1103
+ });
1060
1104
  }
1061
1105
  if (isAtKeyword(val)) {
1062
1106
  return {
@@ -1233,25 +1277,25 @@ function parseTokens(tokens, options = {}) {
1233
1277
  break;
1234
1278
  }
1235
1279
  }
1236
- Object.assign(t, {
1280
+ const attr = Object.assign(t, {
1237
1281
  typ: inAttr == 0 ? EnumToken.AttrTokenType : EnumToken.InvalidAttrTokenType,
1238
1282
  chi: tokens.splice(i + 1, k - i)
1239
1283
  });
1240
1284
  // @ts-ignore
1241
- if (t.chi.at(-1).typ == EnumToken.AttrEndTokenType) {
1285
+ if (attr.chi.at(-1).typ == EnumToken.AttrEndTokenType) {
1242
1286
  // @ts-ignore
1243
- t.chi.pop();
1287
+ attr.chi.pop();
1244
1288
  }
1245
1289
  // @ts-ignore
1246
- if (t.chi.length > 1) {
1290
+ if (attr.chi.length > 1) {
1247
1291
  /*(<AttrToken>t).chi =*/
1248
1292
  // @ts-ignore
1249
- parseTokens(t.chi, t.typ);
1293
+ parseTokens(attr.chi, t.typ);
1250
1294
  }
1251
- let m = t.chi.length;
1295
+ let m = attr.chi.length;
1252
1296
  let val;
1253
- for (m = 0; m < t.chi.length; m++) {
1254
- val = t.chi[m];
1297
+ for (m = 0; m < attr.chi.length; m++) {
1298
+ val = attr.chi[m];
1255
1299
  if (val.typ == EnumToken.StringTokenType) {
1256
1300
  const slice = val.val.slice(1, -1);
1257
1301
  if ((slice.charAt(0) != '-' || (slice.charAt(0) == '-' && isIdentStart(slice.charCodeAt(1)))) && isIdent(slice)) {
@@ -1261,55 +1305,55 @@ function parseTokens(tokens, options = {}) {
1261
1305
  else if (val.typ == EnumToken.LiteralTokenType && val.val == '|') {
1262
1306
  let upper = m;
1263
1307
  let lower = m;
1264
- while (++upper < t.chi.length) {
1265
- if (t.chi[upper].typ == EnumToken.CommentTokenType) {
1308
+ while (++upper < attr.chi.length) {
1309
+ if (attr.chi[upper].typ == EnumToken.CommentTokenType) {
1266
1310
  continue;
1267
1311
  }
1268
1312
  break;
1269
1313
  }
1270
1314
  while (lower-- > 0) {
1271
- if (t.chi[lower].typ == EnumToken.CommentTokenType) {
1315
+ if (attr.chi[lower].typ == EnumToken.CommentTokenType) {
1272
1316
  continue;
1273
1317
  }
1274
1318
  break;
1275
1319
  }
1276
1320
  // @ts-ignore
1277
- t.chi[m] = {
1321
+ attr.chi[m] = {
1278
1322
  typ: EnumToken.NameSpaceAttributeTokenType,
1279
- l: t.chi[lower],
1280
- r: t.chi[upper]
1323
+ l: attr.chi[lower],
1324
+ r: attr.chi[upper]
1281
1325
  };
1282
- t.chi.splice(upper, 1);
1326
+ attr.chi.splice(upper, 1);
1283
1327
  if (lower >= 0) {
1284
- t.chi.splice(lower, 1);
1328
+ attr.chi.splice(lower, 1);
1285
1329
  m--;
1286
1330
  }
1287
1331
  }
1288
1332
  else if ([
1289
1333
  EnumToken.DashMatchTokenType, EnumToken.StartMatchTokenType, EnumToken.ContainMatchTokenType, EnumToken.EndMatchTokenType, EnumToken.IncludeMatchTokenType, EnumToken.DelimTokenType
1290
- ].includes(t.chi[m].typ)) {
1334
+ ].includes(attr.chi[m].typ)) {
1291
1335
  let upper = m;
1292
1336
  let lower = m;
1293
- while (++upper < t.chi.length) {
1294
- if (t.chi[upper].typ == EnumToken.CommentTokenType) {
1337
+ while (++upper < attr.chi.length) {
1338
+ if (attr.chi[upper].typ == EnumToken.CommentTokenType) {
1295
1339
  continue;
1296
1340
  }
1297
1341
  break;
1298
1342
  }
1299
1343
  while (lower-- > 0) {
1300
- if (t.chi[lower].typ == EnumToken.CommentTokenType) {
1344
+ if (attr.chi[lower].typ == EnumToken.CommentTokenType) {
1301
1345
  continue;
1302
1346
  }
1303
1347
  break;
1304
1348
  }
1305
- val = t.chi[lower];
1349
+ val = attr.chi[lower];
1306
1350
  if (val.typ == EnumToken.StringTokenType) {
1307
1351
  const slice = val.val.slice(1, -1);
1308
1352
  if ((slice.charAt(0) != '-' || (slice.charAt(0) == '-' && isIdentStart(slice.charCodeAt(1)))) && isIdent(slice)) {
1309
1353
  Object.assign(val, { typ: EnumToken.IdenTokenType, val: slice });
1310
1354
  }
1311
1355
  }
1312
- val = t.chi[upper];
1356
+ val = attr.chi[upper];
1313
1357
  if (val.typ == EnumToken.StringTokenType) {
1314
1358
  const slice = val.val.slice(1, -1);
1315
1359
  if ((slice.charAt(0) != '-' || (slice.charAt(0) == '-' && isIdentStart(slice.charCodeAt(1)))) && isIdent(slice)) {
@@ -1336,7 +1380,7 @@ function parseTokens(tokens, options = {}) {
1336
1380
  upper++;
1337
1381
  }
1338
1382
  if (upper < t.chi.length &&
1339
- t.chi[upper].typ == EnumToken.Iden &&
1383
+ t.chi[upper].typ == EnumToken.IdenTokenType &&
1340
1384
  ['i', 's'].includes(t.chi[upper].val.toLowerCase())) {
1341
1385
  t.chi[m].attr = t.chi[upper].val;
1342
1386
  t.chi.splice(upper, 1);
@@ -1429,7 +1473,9 @@ function parseTokens(tokens, options = {}) {
1429
1473
  t.typ = EnumToken.ColorTokenType;
1430
1474
  // @ts-ignore
1431
1475
  t.kin = t.val;
1476
+ // @ts-ignore
1432
1477
  if (t.chi[0].typ == EnumToken.IdenTokenType) {
1478
+ // @ts-ignore
1433
1479
  if (t.chi[0].val == 'from') {
1434
1480
  // @ts-ignore
1435
1481
  t.cal = 'rel';
@@ -1439,10 +1485,11 @@ function parseTokens(tokens, options = {}) {
1439
1485
  // @ts-ignore
1440
1486
  t.cal = 'mix';
1441
1487
  }
1442
- else if (t.val == 'color') {
1443
- // @ts-ignore
1444
- t.cal = 'col';
1445
- // t.chi = t.chi.filter((t: Token) => [EnumToken.IdenTokenType, EnumToken.NumberTokenType, EnumToken.PercentageTokenType].includes(t.typ));
1488
+ else { // @ts-ignore
1489
+ if (t.val == 'color') {
1490
+ // @ts-ignore
1491
+ t.cal = 'col';
1492
+ }
1446
1493
  }
1447
1494
  }
1448
1495
  const filter = [EnumToken.WhitespaceTokenType, EnumToken.CommentTokenType];
@@ -1476,7 +1523,7 @@ function parseTokens(tokens, options = {}) {
1476
1523
  if (t.chi.length > 0) {
1477
1524
  if (t.typ == EnumToken.PseudoClassFuncTokenType && t.val == ':is' && options.minify) {
1478
1525
  //
1479
- const count = t.chi.filter(t => t.typ != EnumToken.CommentTokenType).length;
1526
+ const count = t.chi.filter((t) => t.typ != EnumToken.CommentTokenType).length;
1480
1527
  if (count == 1 ||
1481
1528
  (i == 0 &&
1482
1529
  (tokens[i + 1]?.typ == EnumToken.CommaTokenType || tokens.length == i + 1)) ||
@@ -11,7 +11,7 @@ function parseDeclarationNode(node, errors, src, position) {
11
11
  while (node.val[0]?.typ == EnumToken.WhitespaceTokenType) {
12
12
  node.val.shift();
13
13
  }
14
- if (node.val.filter((t) => ![EnumToken.WhitespaceTokenType, EnumToken.CommentTokenType].includes(t.typ)).length == 0) {
14
+ if (!node.nam.startsWith('--') && node.val.filter((t) => ![EnumToken.WhitespaceTokenType, EnumToken.CommentTokenType].includes(t.typ)).length == 0) {
15
15
  errors.push({
16
16
  action: 'drop',
17
17
  message: 'doParse: invalid declaration',