@tbela99/css-parser 0.9.1 → 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 (58) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +15 -8
  3. package/dist/index-umd-web.js +1815 -498
  4. package/dist/index.cjs +1816 -499
  5. package/dist/index.d.ts +46 -14
  6. package/dist/lib/ast/features/calc.js +7 -10
  7. package/dist/lib/ast/features/index.js +1 -0
  8. package/dist/lib/ast/features/inlinecssvariables.js +0 -5
  9. package/dist/lib/ast/features/prefix.js +2 -7
  10. package/dist/lib/ast/features/shorthand.js +6 -9
  11. package/dist/lib/ast/features/transform.js +60 -0
  12. package/dist/lib/ast/math/expression.js +14 -10
  13. package/dist/lib/ast/math/math.js +14 -2
  14. package/dist/lib/ast/minify.js +46 -5
  15. package/dist/lib/ast/transform/compute.js +336 -0
  16. package/dist/lib/ast/transform/convert.js +33 -0
  17. package/dist/lib/ast/transform/matrix.js +111 -0
  18. package/dist/lib/ast/transform/minify.js +296 -0
  19. package/dist/lib/ast/transform/perspective.js +10 -0
  20. package/dist/lib/ast/transform/rotate.js +40 -0
  21. package/dist/lib/ast/transform/scale.js +32 -0
  22. package/dist/lib/ast/transform/skew.js +23 -0
  23. package/dist/lib/ast/transform/translate.js +32 -0
  24. package/dist/lib/ast/transform/utils.js +198 -0
  25. package/dist/lib/ast/types.js +1 -0
  26. package/dist/lib/ast/walk.js +23 -17
  27. package/dist/lib/parser/parse.js +109 -88
  28. package/dist/lib/parser/utils/declaration.js +1 -1
  29. package/dist/lib/renderer/color/{colormix.js → color-mix.js} +6 -0
  30. package/dist/lib/renderer/color/color.js +94 -18
  31. package/dist/lib/renderer/color/hex.js +17 -7
  32. package/dist/lib/renderer/color/hsl.js +7 -2
  33. package/dist/lib/renderer/color/lab.js +8 -0
  34. package/dist/lib/renderer/color/lch.js +8 -0
  35. package/dist/lib/renderer/color/oklab.js +8 -0
  36. package/dist/lib/renderer/color/oklch.js +8 -0
  37. package/dist/lib/renderer/color/relativecolor.js +10 -21
  38. package/dist/lib/renderer/color/rgb.js +10 -7
  39. package/dist/lib/renderer/color/srgb.js +30 -6
  40. package/dist/lib/renderer/color/utils/components.js +13 -2
  41. package/dist/lib/renderer/render.js +67 -30
  42. package/dist/lib/syntax/syntax.js +74 -56
  43. package/dist/lib/validation/at-rules/container.js +6 -6
  44. package/dist/lib/validation/at-rules/document.js +1 -1
  45. package/dist/lib/validation/at-rules/keyframes.js +1 -1
  46. package/dist/lib/validation/at-rules/media.js +0 -1
  47. package/dist/lib/validation/atrule.js +0 -4
  48. package/dist/lib/validation/selector.js +5 -2
  49. package/dist/lib/validation/syntaxes/keyframe-block-list.js +2 -2
  50. package/dist/lib/validation/syntaxes/keyframe-selector.js +11 -90
  51. package/dist/lib/validation/syntaxes/relative-selector.js +15 -14
  52. package/dist/lib/validation/utils/list.js +18 -1
  53. package/dist/node/load.js +1 -1
  54. package/package.json +12 -12
  55. package/dist/lib/renderer/color/prophotoRgb.js +0 -56
  56. package/dist/lib/validation/declaration.js +0 -94
  57. package/dist/lib/validation/syntax.js +0 -1509
  58. 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 };
@@ -2,7 +2,7 @@ import { webkitPseudoAliasMap, isIdentStart, isIdent, mathFuncs, isColor, isHexC
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];
@@ -48,12 +50,13 @@ async function doParse(iterator, options = {}) {
48
50
  minify: true,
49
51
  pass: 1,
50
52
  parseColor: true,
51
- nestingRules: false,
53
+ nestingRules: true,
52
54
  resolveImport: false,
53
55
  resolveUrls: false,
54
56
  removeCharset: true,
55
57
  removeEmpty: true,
56
58
  removeDuplicateDeclarations: true,
59
+ computeTransform: true,
57
60
  computeShorthand: true,
58
61
  computeCalcExpression: true,
59
62
  inlineCssVariables: false,
@@ -122,11 +125,13 @@ async function doParse(iterator, options = {}) {
122
125
  }
123
126
  else if (item.token == '{') {
124
127
  let inBlock = 1;
128
+ tokens = [item];
125
129
  do {
126
130
  item = iter.next().value;
127
131
  if (item == null) {
128
132
  break;
129
133
  }
134
+ tokens.push(item);
130
135
  if (item.token == '{') {
131
136
  inBlock++;
132
137
  }
@@ -134,6 +139,13 @@ async function doParse(iterator, options = {}) {
134
139
  inBlock--;
135
140
  }
136
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
+ }
137
149
  }
138
150
  tokens = [];
139
151
  map = new Map;
@@ -166,6 +178,7 @@ async function doParse(iterator, options = {}) {
166
178
  await parseNode(tokens, context, stats, options, errors, src, map, rawTokens);
167
179
  rawTokens.length = 0;
168
180
  if (context != null && context.typ == EnumToken.InvalidRuleTokenType) {
181
+ // @ts-ignore
169
182
  const index = context.chi.findIndex((node) => node == context);
170
183
  if (index > -1) {
171
184
  context.chi.splice(index, 1);
@@ -465,9 +478,11 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
465
478
  acc.push(renderToken(curr, { removeComments: true }));
466
479
  return acc;
467
480
  }, []);
481
+ const nam = renderToken(atRule, { removeComments: true });
482
+ // @ts-ignore
468
483
  const node = {
469
- typ: EnumToken.AtRuleNodeType,
470
- nam: renderToken(atRule, { removeComments: true }),
484
+ typ: /^(-[a-z]+-)?keyframes$/.test(nam) ? EnumToken.KeyframeAtRuleNodeType : EnumToken.AtRuleNodeType,
485
+ nam,
471
486
  // tokens: t,
472
487
  val: raw.join('')
473
488
  };
@@ -498,7 +513,7 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
498
513
  isValid = false;
499
514
  }
500
515
  }
501
- const valid = isValid ? validateAtRule(node, options, context) : {
516
+ const valid = isValid ? (node.typ == EnumToken.KeyframeAtRuleNodeType ? validateAtRuleKeyframes(node) : validateAtRule(node, options, context)) : {
502
517
  valid: ValidationLevel.Drop,
503
518
  node,
504
519
  syntax: '@' + node.nam,
@@ -514,7 +529,10 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
514
529
  node.typ = EnumToken.InvalidAtRuleTokenType;
515
530
  }
516
531
  else {
517
- 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
+ }), '');
518
536
  }
519
537
  }
520
538
  // @ts-ignore
@@ -527,72 +545,79 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
527
545
  if (delim.typ == EnumToken.BlockStartTokenType) {
528
546
  const position = map.get(tokens[0]);
529
547
  const uniq = new Map;
530
- parseTokens(tokens, { minify: true }).reduce((acc, curr, index, array) => {
531
- if (curr.typ == EnumToken.CommentTokenType) {
532
- return acc;
533
- }
534
- if (curr.typ == EnumToken.WhitespaceTokenType) {
535
- if (trimWhiteSpace.includes(array[index - 1]?.typ) ||
536
- trimWhiteSpace.includes(array[index + 1]?.typ) ||
537
- combinators.includes(array[index - 1]?.val) ||
538
- combinators.includes(array[index + 1]?.val)) {
539
- return acc;
540
- }
541
- }
542
- let t = renderToken(curr, { minify: false });
543
- if (t == ',') {
544
- acc.push([]);
545
- // uniqTokens.push([]);
546
- }
547
- else {
548
- acc[acc.length - 1].push(t);
549
- // uniqTokens[uniqTokens.length - 1].push(curr);
550
- }
551
- return acc;
552
- }, [[]]).reduce((acc, curr) => {
553
- let i = 0;
554
- for (; i < curr.length; i++) {
555
- if (i + 1 < curr.length && curr[i] == '*') {
556
- if (curr[i] == '*') {
557
- let index = curr[i + 1] == ' ' ? 2 : 1;
558
- if (!['>', '~', '+'].includes(curr[index])) {
559
- curr.splice(i, index);
560
- }
561
- }
562
- }
563
- }
564
- acc.set(curr.join(''), curr);
565
- return acc;
566
- }, uniq);
567
- 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;
568
550
  if (ruleType == EnumToken.RuleNodeType) {
569
551
  parseSelector(tokens);
570
- if (options.validation) {
571
- // @ts-ignore
572
- const valid = validateSelector(tokens, options, context);
573
- if (valid.valid != ValidationLevel.Valid) {
574
- const node = {
575
- typ: EnumToken.InvalidRuleTokenType,
576
- // @ts-ignore
577
- sel: tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false }), ''),
578
- chi: []
579
- };
580
- errors.push({
581
- action: 'drop',
582
- message: valid.error + ' - "' + tokens.reduce((acc, curr) => acc + renderToken(curr, { minify: false }), '') + '"',
583
- // @ts-ignore
584
- location: { src, ...(map.get(valid.node) ?? position) }
585
- });
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 }), '') + '"',
586
565
  // @ts-ignore
587
- context.chi.push(node);
588
- Object.defineProperty(node, 'parent', { ...definedPropertySettings, value: context });
589
- return node;
590
- }
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;
591
572
  }
592
573
  }
593
574
  const node = {
594
575
  typ: ruleType,
595
- 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(','),
596
621
  chi: []
597
622
  };
598
623
  Object.defineProperty(node, 'tokens', {
@@ -677,7 +702,8 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
677
702
  }
678
703
  }
679
704
  }
680
- if (value == null || value.length == 0) {
705
+ const nam = renderToken(name.shift(), { removeComments: true });
706
+ if (value == null || (!nam.startsWith('--') && value.length == 0)) {
681
707
  errors.push({
682
708
  action: 'drop',
683
709
  message: 'doParse: invalid declaration',
@@ -685,33 +711,30 @@ async function parseNode(results, context, stats, options, errors, src, map, raw
685
711
  });
686
712
  return null;
687
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
+ }
688
729
  const node = {
689
730
  typ: EnumToken.DeclarationNodeType,
690
731
  // @ts-ignore
691
- nam: renderToken(name.shift(), { removeComments: true }),
732
+ nam,
692
733
  // @ts-ignore
693
734
  val: value
694
735
  };
695
736
  const result = parseDeclarationNode(node, errors, src, position);
696
737
  if (result != null) {
697
- // if (options.validation) {
698
- //
699
- // const valid: ValidationResult = validateDeclaration(result, options, context);
700
- //
701
- // // console.error({valid});
702
- //
703
- // if (valid.valid == ValidationLevel.Drop) {
704
- //
705
- // errors.push({
706
- // action: 'drop',
707
- // message: valid.error + ' - "' + tokens.reduce((acc, curr) => acc + renderToken(curr, {minify: false}), '') + '"',
708
- // // @ts-ignore
709
- // location: {src, ...(map.get(valid.node) ?? position)}
710
- // });
711
- //
712
- // return null;
713
- // }
714
- // }
715
738
  // @ts-ignore
716
739
  context.chi.push(result);
717
740
  Object.defineProperty(result, 'parent', { ...definedPropertySettings, value: context });
@@ -794,8 +817,6 @@ function parseAtRulePrelude(tokens, atRule) {
794
817
  }
795
818
  }
796
819
  if (value.typ == EnumToken.ParensTokenType || (value.typ == EnumToken.FunctionTokenType && ['media', 'supports', 'style', 'scroll-state'].includes(value.val))) {
797
- // @todo parse range and declarations
798
- // parseDeclaration(parent.chi);
799
820
  let i;
800
821
  let nameIndex = -1;
801
822
  let valueIndex = -1;
@@ -1359,7 +1380,7 @@ function parseTokens(tokens, options = {}) {
1359
1380
  upper++;
1360
1381
  }
1361
1382
  if (upper < t.chi.length &&
1362
- t.chi[upper].typ == EnumToken.Iden &&
1383
+ t.chi[upper].typ == EnumToken.IdenTokenType &&
1363
1384
  ['i', 's'].includes(t.chi[upper].val.toLowerCase())) {
1364
1385
  t.chi[m].attr = t.chi[upper].val;
1365
1386
  t.chi.splice(upper, 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',
@@ -57,6 +57,9 @@ function interpolateHue(interpolationMethod, h1, h2) {
57
57
  return [h1, h2];
58
58
  }
59
59
  function colorMix(colorSpace, hueInterpolationMethod, color1, percentage1, color2, percentage2) {
60
+ if (color1.val == 'currentcolor' || color2.val == 'currentcolor') {
61
+ return null;
62
+ }
60
63
  if (hueInterpolationMethod != null && isRectangularOrthogonalColorspace(colorSpace)) {
61
64
  return null;
62
65
  }
@@ -110,6 +113,9 @@ function colorMix(colorSpace, hueInterpolationMethod, color1, percentage1, color
110
113
  }
111
114
  const components1 = getComponents(color1);
112
115
  const components2 = getComponents(color2);
116
+ if (components1 == null || components2 == null) {
117
+ return null;
118
+ }
113
119
  if ((components1[3] != null && components1[3].typ == EnumToken.IdenTokenType && components1[3].val == 'none') && values2.length == 4) {
114
120
  values1[3] = values2[3];
115
121
  }
@@ -8,7 +8,7 @@ import { lch2hwb, lab2hwb, oklch2hwb, oklab2hwb, hsl2hwb, rgb2hwb } from './hwb.
8
8
  import { srgb2lab, oklch2lab, oklab2lab, lch2lab, hwb2lab, hsl2lab, rgb2lab, hex2lab } from './lab.js';
9
9
  import { srgb2lch, oklch2lch, oklab2lch, lab2lch, hwb2lch, hsl2lch, rgb2lch, hex2lch } from './lch.js';
10
10
  import { srgb2oklab, oklch2oklab, lch2oklab, lab2oklab, hwb2oklab, hsl2oklab, rgb2oklab, hex2oklab } from './oklab.js';
11
- import { lch2oklch, oklab2oklch, lab2oklch, hwb2oklch, hsl2oklch, rgb2oklch, hex2oklch, srgb2oklch } from './oklch.js';
11
+ import { lch2oklch, oklab2oklch, lab2oklch, hwb2oklch, hsl2oklch, rgb2oklch, hex2oklch } from './oklch.js';
12
12
  import { colorFuncColorSpace } from './utils/constants.js';
13
13
  import { getComponents } from './utils/components.js';
14
14
  import { xyz2srgb, lsrgb2srgbvalues, srgb2lsrgbvalues, lch2srgb, oklab2srgb, lab2srgb, hwb2srgb, hsl2srgb, rgb2srgb, hex2srgb } from './srgb.js';
@@ -33,10 +33,15 @@ function convert(token, to) {
33
33
  }
34
34
  let values = [];
35
35
  if (to == 'hsl') {
36
+ let t;
36
37
  switch (token.kin) {
37
38
  case 'rgb':
38
39
  case 'rgba':
39
- values.push(...rgb2hsl(token));
40
+ t = rgb2hsl(token);
41
+ if (t == null) {
42
+ return null;
43
+ }
44
+ values.push(...t);
40
45
  break;
41
46
  case 'hex':
42
47
  case 'lit':
@@ -46,10 +51,18 @@ function convert(token, to) {
46
51
  values.push(...hwb2hsl(token));
47
52
  break;
48
53
  case 'oklab':
49
- values.push(...oklab2hsl(token));
54
+ t = oklab2hsl(token);
55
+ if (t == null) {
56
+ return null;
57
+ }
58
+ values.push(...t);
50
59
  break;
51
60
  case 'oklch':
52
- values.push(...oklch2hsl(token));
61
+ t = oklch2hsl(token);
62
+ if (t == null) {
63
+ return null;
64
+ }
65
+ values.push(...t);
53
66
  break;
54
67
  case 'lab':
55
68
  values.push(...lab2hsl(token));
@@ -98,28 +111,53 @@ function convert(token, to) {
98
111
  }
99
112
  }
100
113
  else if (to == 'rgb') {
114
+ let t;
101
115
  switch (token.kin) {
102
116
  case 'hex':
103
117
  case 'lit':
104
118
  values.push(...hex2rgb(token));
105
119
  break;
106
120
  case 'hsl':
107
- values.push(...hsl2rgb(token));
121
+ t = hsl2rgb(token);
122
+ if (t == null) {
123
+ return null;
124
+ }
125
+ values.push(...t);
108
126
  break;
109
127
  case 'hwb':
110
- values.push(...hwb2rgb(token));
128
+ t = hwb2rgb(token);
129
+ if (t == null) {
130
+ return null;
131
+ }
132
+ values.push(...t);
111
133
  break;
112
134
  case 'oklab':
113
- values.push(...oklab2rgb(token));
135
+ t = oklab2rgb(token);
136
+ if (t == null) {
137
+ return null;
138
+ }
139
+ values.push(...t);
114
140
  break;
115
141
  case 'oklch':
116
- values.push(...oklch2rgb(token));
142
+ t = oklch2rgb(token);
143
+ if (t == null) {
144
+ return null;
145
+ }
146
+ values.push(...t);
117
147
  break;
118
148
  case 'lab':
119
- values.push(...lab2rgb(token));
149
+ t = lab2rgb(token);
150
+ if (t == null) {
151
+ return null;
152
+ }
153
+ values.push(...t);
120
154
  break;
121
155
  case 'lch':
122
- values.push(...lch2rgb(token));
156
+ t = lch2rgb(token);
157
+ if (t == null) {
158
+ return null;
159
+ }
160
+ values.push(...t);
123
161
  break;
124
162
  case 'color':
125
163
  // @ts-ignore
@@ -305,6 +343,7 @@ function convert(token, to) {
305
343
  }
306
344
  }
307
345
  else if (colorFuncColorSpace.includes(to)) {
346
+ let t;
308
347
  switch (token.kin) {
309
348
  case 'hex':
310
349
  case 'lit':
@@ -312,30 +351,60 @@ function convert(token, to) {
312
351
  break;
313
352
  case 'rgb':
314
353
  case 'rgba':
315
- values.push(...rgb2srgb(token));
354
+ t = rgb2srgb(token);
355
+ if (t == null) {
356
+ return null;
357
+ }
358
+ values.push(...t);
316
359
  break;
317
360
  case 'hsl':
318
361
  case 'hsla':
319
- values.push(...hsl2srgb(token));
362
+ t = hsl2srgb(token);
363
+ if (t == null) {
364
+ return null;
365
+ }
366
+ values.push(...t);
320
367
  break;
321
368
  case 'hwb':
322
- values.push(...hwb2srgb(token));
369
+ t = hwb2srgb(token);
370
+ if (t == null) {
371
+ return null;
372
+ }
373
+ values.push(...t);
323
374
  break;
324
375
  case 'lab':
325
- values.push(...lab2srgb(token));
376
+ t = lab2srgb(token);
377
+ if (t == null) {
378
+ return null;
379
+ }
380
+ values.push(...t);
326
381
  break;
327
382
  case 'oklab':
328
- values.push(...oklab2srgb(token));
383
+ t = oklab2srgb(token);
384
+ if (t == null) {
385
+ return null;
386
+ }
387
+ values.push(...t);
329
388
  break;
330
389
  case 'lch':
331
- values.push(...lch2srgb(token));
390
+ t = lch2srgb(token);
391
+ if (t == null) {
392
+ return null;
393
+ }
394
+ values.push(...t);
332
395
  break;
333
396
  case 'oklch':
334
- // @ts-ignore
335
- values.push(...srgb2oklch(...color2srgbvalues(token)));
397
+ t = color2srgbvalues(token);
398
+ if (t == null) {
399
+ return null;
400
+ }
401
+ values.push(...t);
336
402
  break;
337
403
  case 'color':
338
404
  const val = color2srgbvalues(token);
405
+ if (val == null) {
406
+ return null;
407
+ }
339
408
  switch (to) {
340
409
  case 'srgb':
341
410
  values.push(...val);
@@ -389,6 +458,9 @@ function minmax(value, min, max) {
389
458
  }
390
459
  function color2srgbvalues(token) {
391
460
  const components = getComponents(token);
461
+ if (components == null) {
462
+ return null;
463
+ }
392
464
  const colorSpace = components.shift();
393
465
  let values = components.map((val) => getNumber(val));
394
466
  switch (colorSpace.val) {
@@ -531,6 +603,10 @@ function getNumber(token) {
531
603
  // @ts-ignore
532
604
  return token.typ == EnumToken.PercentageTokenType ? token.val / 100 : +token.val;
533
605
  }
606
+ /**
607
+ * convert angle to turn
608
+ * @param token
609
+ */
534
610
  function getAngle(token) {
535
611
  if (token.typ == EnumToken.IdenTokenType) {
536
612
  if (token.val == 'none') {