@tbela99/css-parser 0.8.0 → 0.9.1

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 (64) hide show
  1. package/.editorconfig +484 -0
  2. package/README.md +13 -8
  3. package/dist/index-umd-web.js +2451 -2554
  4. package/dist/index.cjs +2605 -2708
  5. package/dist/index.d.ts +73 -23
  6. package/dist/lib/ast/expand.js +29 -4
  7. package/dist/lib/ast/math/expression.js +1 -1
  8. package/dist/lib/ast/minify.js +31 -17
  9. package/dist/lib/ast/types.js +2 -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 +204 -139
  14. package/dist/lib/parser/tokenize.js +15 -3
  15. package/dist/lib/renderer/color/color.js +2 -2
  16. package/dist/lib/renderer/color/hex.js +1 -1
  17. package/dist/lib/renderer/color/hsl.js +1 -1
  18. package/dist/lib/renderer/color/hwb.js +2 -2
  19. package/dist/lib/renderer/color/lab.js +3 -2
  20. package/dist/lib/renderer/color/lch.js +1 -1
  21. package/dist/lib/renderer/color/oklab.js +2 -2
  22. package/dist/lib/renderer/color/oklch.js +1 -1
  23. package/dist/lib/renderer/color/p3.js +1 -1
  24. package/dist/lib/renderer/color/prophotoRgb.js +2 -2
  25. package/dist/lib/renderer/color/prophotorgb.js +2 -2
  26. package/dist/lib/renderer/color/rgb.js +1 -1
  27. package/dist/lib/renderer/color/srgb.js +2 -2
  28. package/dist/lib/renderer/color/utils/constants.js +1 -1
  29. package/dist/lib/renderer/color/xyz.js +2 -18
  30. package/dist/lib/renderer/color/xyzd50.js +20 -2
  31. package/dist/lib/renderer/render.js +37 -8
  32. package/dist/lib/renderer/sourcemap/sourcemap.js +1 -1
  33. package/dist/lib/syntax/syntax.js +337 -1
  34. package/dist/lib/validation/at-rules/container.js +353 -0
  35. package/dist/lib/validation/at-rules/counter-style.js +2 -2
  36. package/dist/lib/validation/at-rules/custom-media.js +52 -0
  37. package/dist/lib/validation/at-rules/document.js +40 -60
  38. package/dist/lib/validation/at-rules/else.js +5 -0
  39. package/dist/lib/validation/at-rules/font-feature-values.js +3 -0
  40. package/dist/lib/validation/at-rules/import.js +64 -59
  41. package/dist/lib/validation/at-rules/layer.js +3 -0
  42. package/dist/lib/validation/at-rules/media.js +118 -29
  43. package/dist/lib/validation/at-rules/supports.js +51 -20
  44. package/dist/lib/validation/at-rules/when.js +178 -0
  45. package/dist/lib/validation/atrule.js +25 -10
  46. package/dist/lib/validation/config.js +20 -15
  47. package/dist/lib/validation/config.json.js +242 -74
  48. package/dist/lib/validation/declaration.js +32 -10
  49. package/dist/lib/validation/parser/parse.js +87 -103
  50. package/dist/lib/validation/parser/types.js +2 -2
  51. package/dist/lib/validation/selector.js +6 -3
  52. package/dist/lib/validation/syntax.js +86 -6
  53. package/dist/lib/validation/syntaxes/complex-selector-list.js +16 -12
  54. package/dist/lib/validation/syntaxes/complex-selector.js +17 -247
  55. package/dist/lib/validation/syntaxes/compound-selector.js +226 -0
  56. package/dist/lib/validation/syntaxes/image.js +29 -0
  57. package/dist/lib/validation/syntaxes/keyframe-block-list.js +1 -1
  58. package/dist/lib/validation/syntaxes/keyframe-selector.js +1 -1
  59. package/dist/lib/validation/syntaxes/layer-name.js +5 -16
  60. package/dist/lib/validation/syntaxes/relative-selector-list.js +43 -13
  61. package/dist/lib/validation/utils/list.js +2 -2
  62. package/dist/node/index.js +1 -1
  63. package/dist/web/index.js +1 -1
  64. package/package.json +10 -9
@@ -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 };
@@ -6,6 +6,7 @@ import '../../renderer/color/utils/constants.js';
6
6
  import '../../renderer/sourcemap/lib/encode.js';
7
7
  import '../../parser/utils/config.js';
8
8
  import { consumeWhitespace } from '../utils/whitespace.js';
9
+ import { splitTokenList } from '../utils/list.js';
9
10
  import { validateURL } from '../syntaxes/url.js';
10
11
 
11
12
  function validateAtRuleDocument(atRule, options, root) {
@@ -34,71 +35,50 @@ function validateAtRuleDocument(atRule, options, root) {
34
35
  tokens
35
36
  };
36
37
  }
37
- if (tokens[0].typ == EnumToken.CommaTokenType) {
38
+ for (const t of splitTokenList(tokens)) {
39
+ if (t.length != 1) {
40
+ return {
41
+ valid: ValidationLevel.Drop,
42
+ matches: [],
43
+ node: t[0] ?? atRule,
44
+ syntax: '@document',
45
+ error: 'unexpected token',
46
+ tokens
47
+ };
48
+ }
38
49
  // @ts-ignore
39
- return {
40
- valid: ValidationLevel.Drop,
41
- matches: [],
42
- node: tokens[0],
43
- syntax: '@document',
44
- error: 'unexpected token',
45
- tokens
46
- };
47
- }
48
- while (tokens.length > 0) {
49
- if (tokens[0].typ == EnumToken.CommentTokenType) {
50
- tokens.shift();
51
- consumeWhitespace(tokens);
50
+ if ((t[0].typ != EnumToken.FunctionTokenType && t[0].typ != EnumToken.UrlFunctionTokenType) || !['url', 'url-prefix', 'domain', 'media-document', 'regexp'].some((f) => f.localeCompare(t[0].val, undefined, { sensitivity: 'base' }) == 0)) {
51
+ return {
52
+ valid: ValidationLevel.Drop,
53
+ matches: [],
54
+ node: t[0] ?? atRule,
55
+ syntax: '@document',
56
+ error: 'expecting any of url-prefix(), domain(), media-document(), regexp() but found ' + t[0].val,
57
+ tokens
58
+ };
52
59
  }
53
- result = validateURL(tokens[0]);
54
- if (result.valid == ValidationLevel.Valid) {
55
- tokens.shift();
56
- consumeWhitespace(tokens);
60
+ if (t[0].typ == EnumToken.UrlFunctionTokenType) {
61
+ result = validateURL(t[0]);
62
+ if (result.valid == ValidationLevel.Drop) {
63
+ return result;
64
+ }
57
65
  continue;
58
66
  }
59
- if (tokens[0].typ == EnumToken.FunctionTokenType) {
60
- if (!['url-prefix', 'domain', 'media-document', 'regexp'].some((t) => t.localeCompare(tokens[0].val, undefined, { sensitivity: 'base' }) == 0)) {
61
- // @ts-ignore
62
- return {
63
- valid: ValidationLevel.Drop,
64
- matches: [],
65
- node: tokens[0],
66
- syntax: '@document',
67
- error: 'unexpected token',
68
- tokens
69
- };
70
- }
71
- const children = tokens[0].chi.slice();
72
- consumeWhitespace(children);
73
- if (children.length == 0) {
74
- // @ts-ignore
75
- return {
76
- valid: ValidationLevel.Drop,
77
- matches: [],
78
- node: tokens[0],
79
- syntax: '@document',
80
- error: 'expecting string argument',
81
- tokens
82
- };
83
- }
84
- if (children[0].typ == EnumToken.StringTokenType) {
85
- children.shift();
86
- consumeWhitespace(children);
87
- }
88
- if (children.length > 0) {
89
- // @ts-ignore
90
- return {
91
- valid: ValidationLevel.Drop,
92
- matches: [],
93
- node: children[0],
94
- syntax: '@document',
95
- error: 'unexpected token',
96
- tokens
97
- };
98
- }
99
- tokens.shift();
100
- consumeWhitespace(tokens);
67
+ const children = t[0].chi.slice();
68
+ consumeWhitespace(children);
69
+ if (children.length != 1 || (children[0].typ != EnumToken.StringTokenType && children[0].typ != EnumToken.UrlTokenTokenType)) {
70
+ // @ts-ignore
71
+ return {
72
+ valid: ValidationLevel.Drop,
73
+ matches: [],
74
+ node: tokens[0],
75
+ syntax: '@document',
76
+ error: 'expecting string argument',
77
+ tokens
78
+ };
101
79
  }
80
+ tokens.shift();
81
+ consumeWhitespace(tokens);
102
82
  }
103
83
  // @ts-ignore
104
84
  return {
@@ -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) {
@@ -5,11 +5,14 @@ import '../../parser/parse.js';
5
5
  import '../../renderer/color/utils/constants.js';
6
6
  import '../../renderer/sourcemap/lib/encode.js';
7
7
  import '../../parser/utils/config.js';
8
- import { validateAtRuleSupports } from './supports.js';
9
8
  import { validateAtRuleMediaQueryList } from './media.js';
10
9
  import { consumeWhitespace } from '../utils/whitespace.js';
11
10
  import { validateLayerName } from '../syntaxes/layer-name.js';
12
11
  import '../syntaxes/complex-selector.js';
12
+ import '../parser/types.js';
13
+ import '../parser/parse.js';
14
+ import '../config.js';
15
+ import { validateAtRuleSupportsConditions } from './supports.js';
13
16
 
14
17
  function validateAtRuleImport(atRule, options, root) {
15
18
  if (!Array.isArray(atRule.tokens) || atRule.tokens.length == 0) {
@@ -79,6 +82,9 @@ function validateAtRuleImport(atRule, options, root) {
79
82
  };
80
83
  }
81
84
  }
85
+ tokens.shift();
86
+ // @ts-ignore
87
+ consumeWhitespace(tokens);
82
88
  }
83
89
  else {
84
90
  // @ts-ignore
@@ -114,71 +120,70 @@ function validateAtRuleImport(atRule, options, root) {
114
120
  // @ts-ignore
115
121
  else if (tokens[0].typ == EnumToken.FunctionTokenType) {
116
122
  // @ts-ignore
117
- if ('layer'.localeCompare(tokens[0].val, undefined, { sensitivity: 'base' }) != 0) {
118
- // @ts-ignore
119
- return {
120
- valid: ValidationLevel.Drop,
121
- matches: [],
122
- node: tokens[0],
123
- syntax: '@' + atRule.nam,
124
- error: 'expecting layer()',
125
- tokens
126
- };
127
- }
128
- // @ts-ignore
129
- const result = validateLayerName(tokens[0].chi);
130
- if (result.valid == ValidationLevel.Drop) {
131
- return result;
132
- }
133
- tokens.shift();
134
- // @ts-ignore
135
- if (!consumeWhitespace(tokens)) {
136
- // @ts-ignore
137
- return {
138
- valid: ValidationLevel.Drop,
139
- matches: [],
140
- node: tokens[0],
141
- syntax: '@' + atRule.nam,
142
- error: 'expecting whitespace',
143
- tokens
144
- };
145
- }
146
- }
147
- }
148
- if (tokens.length > 0) {
149
- // @ts-ignore
150
- if (tokens[0].typ == EnumToken.AtRuleTokenType) {
151
- if (tokens[0].nam != 'supports') {
123
+ if ('layer'.localeCompare(tokens[0].val, undefined, { sensitivity: 'base' }) == 0) {
124
+ const result = validateLayerName(tokens[0].chi);
125
+ if (result.valid == ValidationLevel.Drop) {
126
+ return result;
127
+ }
128
+ tokens.shift();
152
129
  // @ts-ignore
153
- return {
154
- valid: ValidationLevel.Drop,
155
- matches: [],
156
- node: tokens[0],
157
- syntax: '@' + atRule.nam,
158
- error: 'expecting @supports or media query list',
159
- tokens
160
- };
130
+ consumeWhitespace(tokens);
161
131
  }
162
132
  // @ts-ignore
163
- const result = validateAtRuleSupports(tokens[0]);
164
- if (result.valid == ValidationLevel.Drop) {
165
- return result;
166
- }
167
- tokens.shift();
168
- // @ts-ignore
169
- if (!consumeWhitespace(tokens)) {
133
+ if ('supports'.localeCompare(tokens[0]?.val, undefined, { sensitivity: 'base' }) == 0) {
134
+ const result = validateAtRuleSupportsConditions(atRule, tokens[0].chi);
135
+ if (result.valid == ValidationLevel.Drop) {
136
+ return result;
137
+ }
138
+ tokens.shift();
170
139
  // @ts-ignore
171
- return {
172
- valid: ValidationLevel.Drop,
173
- matches: [],
174
- node: tokens[0],
175
- syntax: '@' + atRule.nam,
176
- error: 'expecting whitespace',
177
- tokens
178
- };
140
+ consumeWhitespace(tokens);
179
141
  }
180
142
  }
181
143
  }
144
+ // if (tokens.length > 0) {
145
+ //
146
+ // // @ts-ignore
147
+ // if (tokens[0].typ == EnumToken.AtRuleTokenType) {
148
+ //
149
+ // if ((tokens[0] as AstAtRule).nam != 'supports') {
150
+ //
151
+ // // @ts-ignore
152
+ // return {
153
+ // valid: ValidationLevel.Drop,
154
+ // matches: [],
155
+ // node: tokens[0],
156
+ // syntax: '@' + atRule.nam,
157
+ // error: 'expecting @supports or media query list',
158
+ // tokens
159
+ // }
160
+ // }
161
+ //
162
+ // // @ts-ignore
163
+ // const result: ValidationSyntaxResult = validateAtRuleSupports(tokens[0] as AstAtRule, options, atRule);
164
+ //
165
+ // if (result.valid == ValidationLevel.Drop) {
166
+ //
167
+ // return result;
168
+ // }
169
+ //
170
+ // tokens.shift();
171
+ //
172
+ // // @ts-ignore
173
+ // if (!consumeWhitespace(tokens)) {
174
+ //
175
+ // // @ts-ignore
176
+ // return {
177
+ // valid: ValidationLevel.Drop,
178
+ // matches: [],
179
+ // node: tokens[0],
180
+ // syntax: '@' + atRule.nam,
181
+ // error: 'expecting whitespace',
182
+ // tokens
183
+ // }
184
+ // }
185
+ // }
186
+ // }
182
187
  if (tokens.length > 0) {
183
188
  return validateAtRuleMediaQueryList(tokens, atRule);
184
189
  }
@@ -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,15 +219,29 @@ 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', 'import'].includes(atRule.nam) && token.typ == EnumToken.FunctionTokenType && ['media', 'supports', 'selector'].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;
155
243
  }
244
+ console.error(chi[0].parent);
156
245
  return false;
157
246
  }
158
247
  function validateMediaFeature(token) {
@@ -163,4 +252,4 @@ function validateMediaFeature(token) {
163
252
  return val.typ == EnumToken.MediaFeatureTokenType;
164
253
  }
165
254
 
166
- export { validateAtRuleMedia, validateAtRuleMediaQueryList };
255
+ export { validateAtRuleMedia, validateAtRuleMediaQueryList, validateMediaCondition, validateMediaFeature };