@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.
- package/.editorconfig +484 -0
- package/README.md +5 -3
- package/dist/index-umd-web.js +3272 -1898
- package/dist/index.cjs +3272 -1898
- package/dist/index.d.ts +48 -11
- package/dist/lib/ast/expand.js +14 -2
- package/dist/lib/ast/math/expression.js +1 -1
- package/dist/lib/ast/minify.js +30 -16
- package/dist/lib/ast/types.js +1 -0
- package/dist/lib/ast/walk.js +12 -0
- package/dist/lib/parser/declaration/map.js +59 -52
- package/dist/lib/parser/declaration/set.js +0 -12
- package/dist/lib/parser/parse.js +140 -101
- package/dist/lib/parser/tokenize.js +15 -3
- package/dist/lib/renderer/color/hex.js +1 -1
- package/dist/lib/renderer/color/hsl.js +1 -1
- package/dist/lib/renderer/color/hwb.js +2 -2
- package/dist/lib/renderer/color/lab.js +1 -1
- package/dist/lib/renderer/color/lch.js +1 -1
- package/dist/lib/renderer/color/oklab.js +2 -2
- package/dist/lib/renderer/color/oklch.js +1 -1
- package/dist/lib/renderer/color/p3.js +1 -1
- package/dist/lib/renderer/color/prophotoRgb.js +1 -1
- package/dist/lib/renderer/color/prophotorgb.js +1 -1
- package/dist/lib/renderer/color/rgb.js +1 -1
- package/dist/lib/renderer/color/srgb.js +2 -2
- package/dist/lib/renderer/color/utils/constants.js +1 -1
- package/dist/lib/renderer/color/xyz.js +1 -1
- package/dist/lib/renderer/render.js +34 -6
- package/dist/lib/syntax/syntax.js +336 -1
- package/dist/lib/validation/at-rules/container.js +353 -0
- package/dist/lib/validation/at-rules/counter-style.js +2 -2
- package/dist/lib/validation/at-rules/custom-media.js +52 -0
- package/dist/lib/validation/at-rules/else.js +5 -0
- package/dist/lib/validation/at-rules/font-feature-values.js +3 -0
- package/dist/lib/validation/at-rules/import.js +3 -0
- package/dist/lib/validation/at-rules/layer.js +3 -0
- package/dist/lib/validation/at-rules/media.js +117 -29
- package/dist/lib/validation/at-rules/supports.js +11 -11
- package/dist/lib/validation/at-rules/when.js +178 -0
- package/dist/lib/validation/atrule.js +25 -10
- package/dist/lib/validation/config.js +20 -15
- package/dist/lib/validation/config.json.js +162 -42
- package/dist/lib/validation/declaration.js +39 -9
- package/dist/lib/validation/parser/parse.js +91 -13
- package/dist/lib/validation/parser/types.js +1 -0
- package/dist/lib/validation/selector.js +6 -3
- package/dist/lib/validation/syntax.js +50 -4
- package/dist/lib/validation/syntaxes/complex-selector-list.js +16 -12
- package/dist/lib/validation/syntaxes/complex-selector.js +17 -247
- package/dist/lib/validation/syntaxes/compound-selector.js +226 -0
- package/dist/lib/validation/syntaxes/image.js +29 -0
- package/dist/lib/validation/syntaxes/keyframe-block-list.js +1 -1
- package/dist/lib/validation/syntaxes/keyframe-selector.js +1 -1
- package/dist/lib/validation/syntaxes/relative-selector-list.js +43 -13
- package/dist/lib/validation/utils/list.js +2 -2
- package/dist/node/index.js +1 -1
- package/dist/web/index.js +1 -1
- 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
|
|
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.
|
|
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 };
|
|
@@ -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.
|
|
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: '
|
|
33
|
+
error: '',
|
|
21
34
|
tokens: []
|
|
22
35
|
};
|
|
23
36
|
}
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
tokens.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
141
|
+
if (mediaFeatureType.typ != tokens[0].typ) {
|
|
102
142
|
// @ts-ignore
|
|
103
|
-
|
|
143
|
+
result = {
|
|
104
144
|
valid: ValidationLevel.Drop,
|
|
105
145
|
matches: [],
|
|
106
146
|
node: tokens[0] ?? atRule,
|
|
107
147
|
syntax: '@media',
|
|
108
|
-
error: '
|
|
148
|
+
error: 'mixing and/or not allowed at the same level',
|
|
109
149
|
tokens: []
|
|
110
150
|
};
|
|
151
|
+
break;
|
|
111
152
|
}
|
|
112
|
-
tokens.shift();
|
|
113
|
-
|
|
153
|
+
match.push({ typ: EnumToken.WhitespaceTokenType }, tokens.shift());
|
|
154
|
+
consumeWhitespace(tokens);
|
|
155
|
+
if (tokens.length == 0) {
|
|
114
156
|
// @ts-ignore
|
|
115
|
-
|
|
157
|
+
result = {
|
|
116
158
|
valid: ValidationLevel.Drop,
|
|
117
159
|
matches: [],
|
|
118
160
|
node: tokens[0] ?? atRule,
|
|
119
161
|
syntax: '@media',
|
|
120
|
-
error: 'expected
|
|
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
|
|
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: '@
|
|
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: '@
|
|
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: '@
|
|
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: '@
|
|
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: '@
|
|
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: '@
|
|
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: '@
|
|
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: '@
|
|
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: '@
|
|
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: '@
|
|
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 };
|