@tbela99/css-parser 0.8.0 → 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 (59) hide show
  1. package/.editorconfig +484 -0
  2. package/README.md +5 -3
  3. package/dist/index-umd-web.js +3272 -1898
  4. package/dist/index.cjs +3272 -1898
  5. package/dist/index.d.ts +48 -11
  6. package/dist/lib/ast/expand.js +14 -2
  7. package/dist/lib/ast/math/expression.js +1 -1
  8. package/dist/lib/ast/minify.js +30 -16
  9. package/dist/lib/ast/types.js +1 -0
  10. package/dist/lib/ast/walk.js +12 -0
  11. package/dist/lib/parser/declaration/map.js +59 -52
  12. package/dist/lib/parser/declaration/set.js +0 -12
  13. package/dist/lib/parser/parse.js +140 -101
  14. package/dist/lib/parser/tokenize.js +15 -3
  15. package/dist/lib/renderer/color/hex.js +1 -1
  16. package/dist/lib/renderer/color/hsl.js +1 -1
  17. package/dist/lib/renderer/color/hwb.js +2 -2
  18. package/dist/lib/renderer/color/lab.js +1 -1
  19. package/dist/lib/renderer/color/lch.js +1 -1
  20. package/dist/lib/renderer/color/oklab.js +2 -2
  21. package/dist/lib/renderer/color/oklch.js +1 -1
  22. package/dist/lib/renderer/color/p3.js +1 -1
  23. package/dist/lib/renderer/color/prophotoRgb.js +1 -1
  24. package/dist/lib/renderer/color/prophotorgb.js +1 -1
  25. package/dist/lib/renderer/color/rgb.js +1 -1
  26. package/dist/lib/renderer/color/srgb.js +2 -2
  27. package/dist/lib/renderer/color/utils/constants.js +1 -1
  28. package/dist/lib/renderer/color/xyz.js +1 -1
  29. package/dist/lib/renderer/render.js +34 -6
  30. package/dist/lib/syntax/syntax.js +336 -1
  31. package/dist/lib/validation/at-rules/container.js +353 -0
  32. package/dist/lib/validation/at-rules/counter-style.js +2 -2
  33. package/dist/lib/validation/at-rules/custom-media.js +52 -0
  34. package/dist/lib/validation/at-rules/else.js +5 -0
  35. package/dist/lib/validation/at-rules/font-feature-values.js +3 -0
  36. package/dist/lib/validation/at-rules/import.js +3 -0
  37. package/dist/lib/validation/at-rules/layer.js +3 -0
  38. package/dist/lib/validation/at-rules/media.js +117 -29
  39. package/dist/lib/validation/at-rules/supports.js +11 -11
  40. package/dist/lib/validation/at-rules/when.js +178 -0
  41. package/dist/lib/validation/atrule.js +25 -10
  42. package/dist/lib/validation/config.js +20 -15
  43. package/dist/lib/validation/config.json.js +162 -42
  44. package/dist/lib/validation/declaration.js +39 -9
  45. package/dist/lib/validation/parser/parse.js +91 -13
  46. package/dist/lib/validation/parser/types.js +1 -0
  47. package/dist/lib/validation/selector.js +6 -3
  48. package/dist/lib/validation/syntax.js +50 -4
  49. package/dist/lib/validation/syntaxes/complex-selector-list.js +16 -12
  50. package/dist/lib/validation/syntaxes/complex-selector.js +17 -247
  51. package/dist/lib/validation/syntaxes/compound-selector.js +226 -0
  52. package/dist/lib/validation/syntaxes/image.js +29 -0
  53. package/dist/lib/validation/syntaxes/keyframe-block-list.js +1 -1
  54. package/dist/lib/validation/syntaxes/keyframe-selector.js +1 -1
  55. package/dist/lib/validation/syntaxes/relative-selector-list.js +43 -13
  56. package/dist/lib/validation/utils/list.js +2 -2
  57. package/dist/node/index.js +1 -1
  58. package/dist/web/index.js +1 -1
  59. package/package.json +7 -7
@@ -15,7 +15,7 @@ function validateAtRuleCounterStyle(atRule, options, root) {
15
15
  matches: [],
16
16
  node: atRule,
17
17
  syntax: '@counter-style',
18
- error: 'expected media query list',
18
+ error: 'expected counter style name',
19
19
  tokens: []
20
20
  };
21
21
  }
@@ -23,7 +23,7 @@ function validateAtRuleCounterStyle(atRule, options, root) {
23
23
  if (tokens.length == 0) {
24
24
  // @ts-ignore
25
25
  return {
26
- valid: ValidationLevel.Valid,
26
+ valid: ValidationLevel.Drop,
27
27
  matches: [],
28
28
  node: atRule,
29
29
  syntax: '@counter-style',
@@ -0,0 +1,52 @@
1
+ import { ValidationLevel, EnumToken } from '../../ast/types.js';
2
+ import '../../ast/minify.js';
3
+ import '../../ast/walk.js';
4
+ import '../../parser/parse.js';
5
+ import '../../renderer/color/utils/constants.js';
6
+ import '../../renderer/sourcemap/lib/encode.js';
7
+ import '../../parser/utils/config.js';
8
+ import { consumeWhitespace } from '../utils/whitespace.js';
9
+ import { validateAtRuleMediaQueryList } from './media.js';
10
+
11
+ function validateAtRuleCustomMedia(atRule, options, root) {
12
+ // media-query-list
13
+ if (!Array.isArray(atRule.tokens) || atRule.tokens.length == 0) {
14
+ // @ts-ignore
15
+ return {
16
+ valid: ValidationLevel.Valid,
17
+ matches: [],
18
+ node: null,
19
+ syntax: null,
20
+ error: '',
21
+ tokens: []
22
+ };
23
+ }
24
+ const queries = atRule.tokens.slice();
25
+ consumeWhitespace(queries);
26
+ if (queries.length == 0 || queries[0].typ != EnumToken.DashedIdenTokenType) {
27
+ return {
28
+ valid: ValidationLevel.Drop,
29
+ matches: [],
30
+ node: atRule,
31
+ syntax: '@custom-media',
32
+ error: 'expecting dashed identifier',
33
+ tokens: []
34
+ };
35
+ }
36
+ queries.shift();
37
+ const result = validateAtRuleMediaQueryList(queries, atRule);
38
+ if (result.valid == ValidationLevel.Drop) {
39
+ atRule.tokens = [];
40
+ return {
41
+ valid: ValidationLevel.Valid,
42
+ matches: [],
43
+ node: atRule,
44
+ syntax: '@custom-media',
45
+ error: '',
46
+ tokens: []
47
+ };
48
+ }
49
+ return result;
50
+ }
51
+
52
+ export { validateAtRuleCustomMedia };
@@ -0,0 +1,5 @@
1
+ import { validateAtRuleWhen } from './when.js';
2
+
3
+ const validateAtRuleElse = validateAtRuleWhen;
4
+
5
+ export { validateAtRuleElse };
@@ -7,6 +7,9 @@ import '../../renderer/sourcemap/lib/encode.js';
7
7
  import '../../parser/utils/config.js';
8
8
  import { validateFamilyName } from '../syntaxes/family-name.js';
9
9
  import '../syntaxes/complex-selector.js';
10
+ import '../parser/types.js';
11
+ import '../parser/parse.js';
12
+ import '../config.js';
10
13
 
11
14
  function validateAtRuleFontFeatureValues(atRule, options, root) {
12
15
  if (!Array.isArray(atRule.tokens) || atRule.tokens.length == 0) {
@@ -10,6 +10,9 @@ import { validateAtRuleMediaQueryList } from './media.js';
10
10
  import { consumeWhitespace } from '../utils/whitespace.js';
11
11
  import { validateLayerName } from '../syntaxes/layer-name.js';
12
12
  import '../syntaxes/complex-selector.js';
13
+ import '../parser/types.js';
14
+ import '../parser/parse.js';
15
+ import '../config.js';
13
16
 
14
17
  function validateAtRuleImport(atRule, options, root) {
15
18
  if (!Array.isArray(atRule.tokens) || atRule.tokens.length == 0) {
@@ -7,6 +7,9 @@ import '../../renderer/sourcemap/lib/encode.js';
7
7
  import '../../parser/utils/config.js';
8
8
  import { validateLayerName } from '../syntaxes/layer-name.js';
9
9
  import '../syntaxes/complex-selector.js';
10
+ import '../parser/types.js';
11
+ import '../parser/parse.js';
12
+ import '../config.js';
10
13
 
11
14
  function validateAtRuleLayer(atRule, options, root) {
12
15
  // media-query-list
@@ -13,15 +13,28 @@ function validateAtRuleMedia(atRule, options, root) {
13
13
  if (!Array.isArray(atRule.tokens) || atRule.tokens.length == 0) {
14
14
  // @ts-ignore
15
15
  return {
16
- valid: ValidationLevel.Drop,
16
+ valid: ValidationLevel.Valid,
17
+ matches: [],
18
+ node: null,
19
+ syntax: null,
20
+ error: '',
21
+ tokens: []
22
+ };
23
+ }
24
+ let result = null;
25
+ const slice = atRule.tokens.slice();
26
+ consumeWhitespace(slice);
27
+ if (slice.length == 0) {
28
+ return {
29
+ valid: ValidationLevel.Valid,
17
30
  matches: [],
18
31
  node: atRule,
19
32
  syntax: '@media',
20
- error: 'expected media query list',
33
+ error: '',
21
34
  tokens: []
22
35
  };
23
36
  }
24
- const result = validateAtRuleMediaQueryList(atRule.tokens, atRule);
37
+ result = validateAtRuleMediaQueryList(atRule.tokens, atRule);
25
38
  if (result.valid == ValidationLevel.Drop) {
26
39
  return result;
27
40
  }
@@ -47,10 +60,20 @@ function validateAtRuleMedia(atRule, options, root) {
47
60
  };
48
61
  }
49
62
  function validateAtRuleMediaQueryList(tokenList, atRule) {
50
- for (const tokens of splitTokenList(tokenList)) {
63
+ const split = splitTokenList(tokenList);
64
+ const matched = [];
65
+ let result = null;
66
+ let previousToken;
67
+ let mediaFeatureType;
68
+ for (let i = 0; i < split.length; i++) {
69
+ const tokens = split[i].slice();
70
+ const match = [];
71
+ result = null;
72
+ mediaFeatureType = null;
73
+ previousToken = null;
51
74
  if (tokens.length == 0) {
52
75
  // @ts-ignore
53
- return {
76
+ result = {
54
77
  valid: ValidationLevel.Drop,
55
78
  matches: [],
56
79
  node: tokens[0] ?? atRule,
@@ -58,26 +81,38 @@ function validateAtRuleMediaQueryList(tokenList, atRule) {
58
81
  error: 'unexpected token',
59
82
  tokens: []
60
83
  };
84
+ continue;
61
85
  }
62
- let previousToken = null;
63
86
  while (tokens.length > 0) {
64
- // media-condition
65
- if (validateMediaCondition(tokens[0])) {
66
- previousToken = tokens[0];
67
- tokens.shift();
68
- }
69
- // media-type
70
- else if (validateMediaFeature(tokens[0])) {
71
- previousToken = tokens[0];
72
- tokens.shift();
87
+ previousToken = tokens[0];
88
+ // media-condition | media-type | custom-media
89
+ if (!(validateMediaCondition(tokens[0], atRule) || validateMediaFeature(tokens[0]) || validateCustomMediaCondition(tokens[0], atRule))) {
90
+ if (tokens[0].typ == EnumToken.ParensTokenType) {
91
+ result = validateAtRuleMediaQueryList(tokens[0].chi, atRule);
92
+ }
93
+ else {
94
+ result = {
95
+ valid: ValidationLevel.Drop,
96
+ matches: [],
97
+ node: tokens[0] ?? atRule,
98
+ syntax: '@media',
99
+ error: 'expecting media feature or media condition',
100
+ tokens: []
101
+ };
102
+ }
103
+ if (result.valid == ValidationLevel.Drop) {
104
+ break;
105
+ }
106
+ result = null;
73
107
  }
108
+ match.push(tokens.shift());
74
109
  if (tokens.length == 0) {
75
110
  break;
76
111
  }
77
112
  if (!consumeWhitespace(tokens)) {
78
113
  if (previousToken?.typ != EnumToken.ParensTokenType) {
79
114
  // @ts-ignore
80
- return {
115
+ result = {
81
116
  valid: ValidationLevel.Drop,
82
117
  matches: [],
83
118
  node: tokens[0] ?? atRule,
@@ -85,11 +120,12 @@ function validateAtRuleMediaQueryList(tokenList, atRule) {
85
120
  error: 'expected media query list',
86
121
  tokens: []
87
122
  };
123
+ break;
88
124
  }
89
125
  }
90
- if (![EnumToken.MediaFeatureOrTokenType, EnumToken.MediaFeatureAndTokenType].includes(tokens[0].typ)) {
126
+ else if (![EnumToken.MediaFeatureOrTokenType, EnumToken.MediaFeatureAndTokenType].includes(tokens[0].typ)) {
91
127
  // @ts-ignore
92
- return {
128
+ result = {
93
129
  valid: ValidationLevel.Drop,
94
130
  matches: [],
95
131
  node: tokens[0] ?? atRule,
@@ -97,31 +133,70 @@ function validateAtRuleMediaQueryList(tokenList, atRule) {
97
133
  error: 'expected and/or',
98
134
  tokens: []
99
135
  };
136
+ break;
137
+ }
138
+ if (mediaFeatureType == null) {
139
+ mediaFeatureType = tokens[0];
100
140
  }
101
- if (tokens.length == 1) {
141
+ if (mediaFeatureType.typ != tokens[0].typ) {
102
142
  // @ts-ignore
103
- return {
143
+ result = {
104
144
  valid: ValidationLevel.Drop,
105
145
  matches: [],
106
146
  node: tokens[0] ?? atRule,
107
147
  syntax: '@media',
108
- error: 'expected media-condition',
148
+ error: 'mixing and/or not allowed at the same level',
109
149
  tokens: []
110
150
  };
151
+ break;
111
152
  }
112
- tokens.shift();
113
- if (!consumeWhitespace(tokens)) {
153
+ match.push({ typ: EnumToken.WhitespaceTokenType }, tokens.shift());
154
+ consumeWhitespace(tokens);
155
+ if (tokens.length == 0) {
114
156
  // @ts-ignore
115
- return {
157
+ result = {
116
158
  valid: ValidationLevel.Drop,
117
159
  matches: [],
118
160
  node: tokens[0] ?? atRule,
119
161
  syntax: '@media',
120
- error: 'expected whitespace',
162
+ error: 'expected media-condition',
121
163
  tokens: []
122
164
  };
165
+ break;
123
166
  }
167
+ match.push({ typ: EnumToken.WhitespaceTokenType });
124
168
  }
169
+ if (result == null && match.length > 0) {
170
+ matched.push(match);
171
+ }
172
+ }
173
+ if (result != null) {
174
+ return result;
175
+ }
176
+ if (matched.length == 0) {
177
+ return {
178
+ valid: ValidationLevel.Drop,
179
+ matches: [],
180
+ node: atRule,
181
+ syntax: '@media',
182
+ error: 'expected media query list',
183
+ tokens: []
184
+ };
185
+ }
186
+ tokenList.length = 0;
187
+ let hasAll = false;
188
+ for (let i = 0; i < matched.length; i++) {
189
+ if (tokenList.length > 0) {
190
+ tokenList.push({ typ: EnumToken.CommaTokenType });
191
+ }
192
+ if (matched[i].length == 1 && matched.length > 1 && matched[i][0].typ == EnumToken.MediaFeatureTokenType && matched[i][0].val == 'all') {
193
+ hasAll = true;
194
+ continue;
195
+ }
196
+ tokenList.push(...matched[i]);
197
+ }
198
+ if (hasAll && tokenList.length == 0) {
199
+ tokenList.push({ typ: EnumToken.MediaFeatureTokenType, val: 'all' });
125
200
  }
126
201
  // @ts-ignore
127
202
  return {
@@ -133,9 +208,9 @@ function validateAtRuleMediaQueryList(tokenList, atRule) {
133
208
  tokens: []
134
209
  };
135
210
  }
136
- function validateMediaCondition(token) {
211
+ function validateCustomMediaCondition(token, atRule) {
137
212
  if (token.typ == EnumToken.MediaFeatureNotTokenType) {
138
- return validateMediaCondition(token.val);
213
+ return validateMediaCondition(token.val, atRule);
139
214
  }
140
215
  if (token.typ != EnumToken.ParensTokenType) {
141
216
  return false;
@@ -144,11 +219,24 @@ function validateMediaCondition(token) {
144
219
  if (chi.length != 1) {
145
220
  return false;
146
221
  }
222
+ return chi[0].typ == EnumToken.DashedIdenTokenType;
223
+ }
224
+ function validateMediaCondition(token, atRule) {
225
+ if (token.typ == EnumToken.MediaFeatureNotTokenType) {
226
+ return validateMediaCondition(token.val, atRule);
227
+ }
228
+ if (token.typ != EnumToken.ParensTokenType && !(['when', 'else'].includes(atRule.nam) && token.typ == EnumToken.FunctionTokenType && ['media', 'supports'].includes(token.val))) {
229
+ return false;
230
+ }
231
+ const chi = token.chi.filter((t) => t.typ != EnumToken.CommentTokenType && t.typ != EnumToken.WhitespaceTokenType);
232
+ if (chi.length != 1) {
233
+ return false;
234
+ }
147
235
  if (chi[0].typ == EnumToken.IdenTokenType) {
148
236
  return true;
149
237
  }
150
238
  if (chi[0].typ == EnumToken.MediaFeatureNotTokenType) {
151
- return validateMediaCondition(chi[0].val);
239
+ return validateMediaCondition(chi[0].val, atRule);
152
240
  }
153
241
  if (chi[0].typ == EnumToken.MediaQueryConditionTokenType) {
154
242
  return chi[0].l.typ == EnumToken.IdenTokenType;
@@ -163,4 +251,4 @@ function validateMediaFeature(token) {
163
251
  return val.typ == EnumToken.MediaFeatureTokenType;
164
252
  }
165
253
 
166
- export { validateAtRuleMedia, validateAtRuleMediaQueryList };
254
+ export { validateAtRuleMedia, validateAtRuleMediaQueryList, validateMediaCondition, validateMediaFeature };
@@ -19,7 +19,7 @@ function validateAtRuleSupports(atRule, options, root) {
19
19
  valid: ValidationLevel.Drop,
20
20
  matches: [],
21
21
  node: atRule,
22
- syntax: '@supports',
22
+ syntax: '@' + atRule.nam,
23
23
  error: 'expected supports query list',
24
24
  tokens: []
25
25
  };
@@ -37,7 +37,7 @@ function validateAtRuleSupports(atRule, options, root) {
37
37
  valid: ValidationLevel.Drop,
38
38
  matches: [],
39
39
  node: atRule,
40
- syntax: '@supports',
40
+ syntax: '@' + atRule.nam,
41
41
  error: 'expected at-rule body',
42
42
  tokens: []
43
43
  };
@@ -47,7 +47,7 @@ function validateAtRuleSupports(atRule, options, root) {
47
47
  valid: ValidationLevel.Valid,
48
48
  matches: [],
49
49
  node: atRule,
50
- syntax: '@supports',
50
+ syntax: '@' + atRule.nam,
51
51
  error: '',
52
52
  tokens: []
53
53
  };
@@ -60,7 +60,7 @@ function validateAtRuleSupportsConditions(atRule, tokenList) {
60
60
  valid: ValidationLevel.Drop,
61
61
  matches: [],
62
62
  node: tokens[0] ?? atRule,
63
- syntax: '@supports',
63
+ syntax: '@' + atRule.nam,
64
64
  error: 'unexpected token',
65
65
  tokens: []
66
66
  };
@@ -94,7 +94,7 @@ function validateAtRuleSupportsConditions(atRule, tokenList) {
94
94
  valid: ValidationLevel.Drop,
95
95
  matches: [],
96
96
  node: tokens[0] ?? previousToken ?? atRule,
97
- syntax: '@supports',
97
+ syntax: '@' + atRule.nam,
98
98
  error: 'expected whitespace',
99
99
  tokens: []
100
100
  };
@@ -106,7 +106,7 @@ function validateAtRuleSupportsConditions(atRule, tokenList) {
106
106
  valid: ValidationLevel.Drop,
107
107
  matches: [],
108
108
  node: tokens[0] ?? atRule,
109
- syntax: '@supports',
109
+ syntax: '@' + atRule.nam,
110
110
  error: 'expected and/or',
111
111
  tokens: []
112
112
  };
@@ -117,7 +117,7 @@ function validateAtRuleSupportsConditions(atRule, tokenList) {
117
117
  valid: ValidationLevel.Drop,
118
118
  matches: [],
119
119
  node: tokens[0] ?? atRule,
120
- syntax: '@supports',
120
+ syntax: '@' + atRule.nam,
121
121
  error: 'expected supports-condition',
122
122
  tokens: []
123
123
  };
@@ -129,7 +129,7 @@ function validateAtRuleSupportsConditions(atRule, tokenList) {
129
129
  valid: ValidationLevel.Drop,
130
130
  matches: [],
131
131
  node: tokens[0] ?? atRule,
132
- syntax: '@supports',
132
+ syntax: '@' + atRule.nam,
133
133
  error: 'expected whitespace',
134
134
  tokens: []
135
135
  };
@@ -142,13 +142,13 @@ function validateSupportCondition(atRule, token) {
142
142
  if (token.typ == EnumToken.MediaFeatureNotTokenType) {
143
143
  return validateSupportCondition(atRule, token.val);
144
144
  }
145
- if (token.typ != EnumToken.ParensTokenType) {
145
+ if (token.typ != EnumToken.ParensTokenType && !(['when', 'else'].includes(atRule.nam) && token.typ == EnumToken.FunctionTokenType && ['supports', 'font-format', 'font-tech'].includes(token.val))) {
146
146
  // @ts-ignore
147
147
  return {
148
148
  valid: ValidationLevel.Drop,
149
149
  matches: [],
150
150
  node: token,
151
- syntax: '@supports',
151
+ syntax: '@' + atRule.nam,
152
152
  error: 'expected supports condition-in-parens',
153
153
  tokens: []
154
154
  };
@@ -163,7 +163,7 @@ function validateSupportCondition(atRule, token) {
163
163
  valid: ValidationLevel.Valid,
164
164
  matches: [],
165
165
  node: null,
166
- syntax: '@supports',
166
+ syntax: '@' + atRule.nam,
167
167
  error: '',
168
168
  tokens: []
169
169
  };
@@ -0,0 +1,178 @@
1
+ import { ValidationLevel, EnumToken } from '../../ast/types.js';
2
+ import '../../ast/minify.js';
3
+ import '../../ast/walk.js';
4
+ import '../../parser/parse.js';
5
+ import '../../renderer/color/utils/constants.js';
6
+ import '../../renderer/sourcemap/lib/encode.js';
7
+ import '../../parser/utils/config.js';
8
+ import { consumeWhitespace } from '../utils/whitespace.js';
9
+ import { splitTokenList } from '../utils/list.js';
10
+ import { validateMediaFeature, validateMediaCondition } from './media.js';
11
+ import { validateSupportCondition } from './supports.js';
12
+
13
+ function validateAtRuleWhen(atRule, options, root) {
14
+ const slice = Array.isArray(atRule.tokens) ? atRule.tokens.slice() : [];
15
+ consumeWhitespace(slice);
16
+ if (slice.length == 0) {
17
+ // @ts-ignore
18
+ return {
19
+ valid: ValidationLevel.Valid,
20
+ matches: [],
21
+ node: atRule,
22
+ syntax: '@when',
23
+ error: '',
24
+ tokens: []
25
+ };
26
+ }
27
+ const result = validateAtRuleWhenQueryList(atRule.tokens, atRule);
28
+ if (result.valid == ValidationLevel.Drop) {
29
+ return result;
30
+ }
31
+ if (!('chi' in atRule)) {
32
+ // @ts-ignore
33
+ return {
34
+ valid: ValidationLevel.Drop,
35
+ matches: [],
36
+ node: atRule,
37
+ syntax: '@when',
38
+ error: 'expected at-rule body',
39
+ tokens: []
40
+ };
41
+ }
42
+ return {
43
+ valid: ValidationLevel.Valid,
44
+ matches: [],
45
+ node: atRule,
46
+ syntax: '@when',
47
+ error: '',
48
+ tokens: result.tokens
49
+ };
50
+ }
51
+ // media() = media( [ <mf-plain> | <mf-boolean> | <mf-range> ] )
52
+ // supports() = supports( <declaration> )
53
+ function validateAtRuleWhenQueryList(tokenList, atRule) {
54
+ const matched = [];
55
+ let result = null;
56
+ for (const split of splitTokenList(tokenList)) {
57
+ const match = [];
58
+ result = null;
59
+ consumeWhitespace(split);
60
+ if (split.length == 0) {
61
+ continue;
62
+ }
63
+ while (split.length > 0) {
64
+ if (split[0].typ != EnumToken.FunctionTokenType || !['media', 'supports', 'font-tech', 'font-format'].includes(split[0].val)) {
65
+ result = {
66
+ valid: ValidationLevel.Drop,
67
+ matches: [],
68
+ node: split[0] ?? atRule,
69
+ syntax: '@when',
70
+ error: 'unexpected token',
71
+ tokens: []
72
+ };
73
+ break;
74
+ }
75
+ const chi = split[0].chi.slice();
76
+ consumeWhitespace(chi);
77
+ if (split[0].val == 'media') {
78
+ // result = valida
79
+ if (chi.length != 1 || !(validateMediaFeature(chi[0]) || validateMediaCondition(split[0], atRule))) {
80
+ result = {
81
+ valid: ValidationLevel.Drop,
82
+ matches: [],
83
+ node: split[0] ?? atRule,
84
+ syntax: 'media( [ <mf-plain> | <mf-boolean> | <mf-range> ] )',
85
+ error: 'unexpected token',
86
+ tokens: []
87
+ };
88
+ break;
89
+ }
90
+ }
91
+ else if (['supports', 'font-tech', 'font-format'].includes(split[0].val)) {
92
+ // result = valida
93
+ if (!validateSupportCondition(atRule, split[0])) {
94
+ result = {
95
+ valid: ValidationLevel.Drop,
96
+ matches: [],
97
+ node: split[0] ?? atRule,
98
+ syntax: 'media( [ <mf-plain> | <mf-boolean> | <mf-range> ] )',
99
+ error: 'unexpected token',
100
+ tokens: []
101
+ };
102
+ break;
103
+ }
104
+ }
105
+ if (match.length > 0) {
106
+ match.push({ typ: EnumToken.WhitespaceTokenType });
107
+ }
108
+ match.push(split.shift());
109
+ consumeWhitespace(split);
110
+ if (split.length == 0) {
111
+ break;
112
+ }
113
+ if (![EnumToken.MediaFeatureAndTokenType, EnumToken.MediaFeatureOrTokenType].includes(split[0].typ)) {
114
+ result = {
115
+ valid: ValidationLevel.Drop,
116
+ matches: [],
117
+ node: split[0] ?? atRule,
118
+ syntax: '@when',
119
+ error: 'expecting and/or media-condition',
120
+ tokens: []
121
+ };
122
+ break;
123
+ }
124
+ if (match.length > 0) {
125
+ match.push({ typ: EnumToken.WhitespaceTokenType });
126
+ }
127
+ match.push(split.shift());
128
+ consumeWhitespace(split);
129
+ if (split.length == 0) {
130
+ result = {
131
+ valid: ValidationLevel.Drop,
132
+ matches: [],
133
+ node: split[0] ?? atRule,
134
+ syntax: '@when',
135
+ error: 'expecting media-condition',
136
+ tokens: []
137
+ };
138
+ break;
139
+ }
140
+ }
141
+ if (result == null && match.length > 0) {
142
+ matched.push(match);
143
+ }
144
+ }
145
+ if (result != null) {
146
+ return result;
147
+ }
148
+ if (matched.length == 0) {
149
+ return {
150
+ valid: ValidationLevel.Drop,
151
+ matches: [],
152
+ // @ts-ignore
153
+ node: result?.node ?? atRule,
154
+ syntax: '@when',
155
+ error: 'invalid at-rule body',
156
+ tokens: []
157
+ };
158
+ }
159
+ tokenList.length = 0;
160
+ for (const match of matched) {
161
+ if (tokenList.length > 0) {
162
+ tokenList.push({
163
+ typ: EnumToken.CommaTokenType
164
+ });
165
+ }
166
+ tokenList.push(...match);
167
+ }
168
+ return {
169
+ valid: ValidationLevel.Valid,
170
+ matches: [],
171
+ node: atRule,
172
+ syntax: '@when',
173
+ error: '',
174
+ tokens: tokenList
175
+ };
176
+ }
177
+
178
+ export { validateAtRuleWhen, validateAtRuleWhenQueryList };