@tbela99/css-parser 0.9.1 → 1.1.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 (99) hide show
  1. package/CHANGELOG.md +265 -0
  2. package/LICENSE.md +1 -1
  3. package/README.md +29 -17
  4. package/dist/index-umd-web.js +7461 -4360
  5. package/dist/index.cjs +8608 -5507
  6. package/dist/index.d.ts +203 -61
  7. package/dist/lib/ast/expand.js +2 -1
  8. package/dist/lib/ast/features/calc.js +19 -11
  9. package/dist/lib/ast/features/index.js +1 -0
  10. package/dist/lib/ast/features/inlinecssvariables.js +47 -29
  11. package/dist/lib/ast/features/prefix.js +117 -91
  12. package/dist/lib/ast/features/shorthand.js +34 -14
  13. package/dist/lib/ast/features/transform.js +67 -0
  14. package/dist/lib/ast/features/type.js +7 -0
  15. package/dist/lib/ast/math/expression.js +20 -10
  16. package/dist/lib/ast/math/math.js +20 -2
  17. package/dist/lib/ast/minify.js +209 -80
  18. package/dist/lib/ast/transform/compute.js +337 -0
  19. package/dist/lib/ast/transform/convert.js +33 -0
  20. package/dist/lib/ast/transform/matrix.js +112 -0
  21. package/dist/lib/ast/transform/minify.js +296 -0
  22. package/dist/lib/ast/transform/perspective.js +10 -0
  23. package/dist/lib/ast/transform/rotate.js +40 -0
  24. package/dist/lib/ast/transform/scale.js +32 -0
  25. package/dist/lib/ast/transform/skew.js +23 -0
  26. package/dist/lib/ast/transform/translate.js +32 -0
  27. package/dist/lib/ast/transform/utils.js +198 -0
  28. package/dist/lib/ast/types.js +18 -15
  29. package/dist/lib/ast/walk.js +54 -22
  30. package/dist/lib/fs/resolve.js +10 -0
  31. package/dist/lib/parser/declaration/list.js +48 -45
  32. package/dist/lib/parser/declaration/map.js +1 -0
  33. package/dist/lib/parser/declaration/set.js +2 -1
  34. package/dist/lib/parser/parse.js +449 -340
  35. package/dist/lib/parser/tokenize.js +147 -72
  36. package/dist/lib/parser/utils/declaration.js +5 -4
  37. package/dist/lib/parser/utils/type.js +2 -1
  38. package/dist/lib/renderer/color/a98rgb.js +2 -1
  39. package/dist/lib/renderer/color/{colormix.js → color-mix.js} +16 -7
  40. package/dist/lib/renderer/color/color.js +264 -170
  41. package/dist/lib/renderer/color/hex.js +19 -8
  42. package/dist/lib/renderer/color/hsl.js +9 -3
  43. package/dist/lib/renderer/color/hwb.js +2 -1
  44. package/dist/lib/renderer/color/lab.js +10 -1
  45. package/dist/lib/renderer/color/lch.js +10 -1
  46. package/dist/lib/renderer/color/oklab.js +10 -1
  47. package/dist/lib/renderer/color/oklch.js +10 -1
  48. package/dist/lib/renderer/color/p3.js +2 -1
  49. package/dist/lib/renderer/color/rec2020.js +2 -1
  50. package/dist/lib/renderer/color/relativecolor.js +27 -32
  51. package/dist/lib/renderer/color/rgb.js +14 -10
  52. package/dist/lib/renderer/color/srgb.js +48 -23
  53. package/dist/lib/renderer/color/utils/components.js +18 -6
  54. package/dist/lib/renderer/color/utils/constants.js +47 -3
  55. package/dist/lib/renderer/color/xyz.js +2 -1
  56. package/dist/lib/renderer/color/xyzd50.js +2 -1
  57. package/dist/lib/renderer/render.js +108 -43
  58. package/dist/lib/syntax/syntax.js +267 -136
  59. package/dist/lib/validation/at-rules/container.js +81 -103
  60. package/dist/lib/validation/at-rules/counter-style.js +9 -8
  61. package/dist/lib/validation/at-rules/custom-media.js +13 -15
  62. package/dist/lib/validation/at-rules/document.js +22 -27
  63. package/dist/lib/validation/at-rules/font-feature-values.js +8 -8
  64. package/dist/lib/validation/at-rules/import.js +30 -81
  65. package/dist/lib/validation/at-rules/keyframes.js +19 -23
  66. package/dist/lib/validation/at-rules/layer.js +5 -5
  67. package/dist/lib/validation/at-rules/media.js +42 -53
  68. package/dist/lib/validation/at-rules/namespace.js +19 -23
  69. package/dist/lib/validation/at-rules/page-margin-box.js +15 -18
  70. package/dist/lib/validation/at-rules/page.js +8 -7
  71. package/dist/lib/validation/at-rules/supports.js +73 -82
  72. package/dist/lib/validation/at-rules/when.js +32 -36
  73. package/dist/lib/validation/atrule.js +15 -18
  74. package/dist/lib/validation/config.js +24 -1
  75. package/dist/lib/validation/config.json.js +563 -63
  76. package/dist/lib/validation/parser/parse.js +196 -185
  77. package/dist/lib/validation/parser/types.js +1 -1
  78. package/dist/lib/validation/selector.js +8 -5
  79. package/dist/lib/validation/syntax.js +724 -1405
  80. package/dist/lib/validation/syntaxes/complex-selector-list.js +10 -11
  81. package/dist/lib/validation/syntaxes/complex-selector.js +10 -11
  82. package/dist/lib/validation/syntaxes/compound-selector.js +40 -50
  83. package/dist/lib/validation/syntaxes/family-name.js +9 -8
  84. package/dist/lib/validation/syntaxes/keyframe-block-list.js +6 -5
  85. package/dist/lib/validation/syntaxes/keyframe-selector.js +23 -105
  86. package/dist/lib/validation/syntaxes/layer-name.js +6 -5
  87. package/dist/lib/validation/syntaxes/relative-selector-list.js +7 -6
  88. package/dist/lib/validation/syntaxes/relative-selector.js +17 -15
  89. package/dist/lib/validation/syntaxes/url.js +18 -22
  90. package/dist/lib/validation/utils/list.js +20 -2
  91. package/dist/lib/validation/utils/whitespace.js +2 -1
  92. package/dist/node/index.js +4 -2
  93. package/dist/node/load.js +6 -1
  94. package/dist/web/index.js +4 -2
  95. package/dist/web/load.js +5 -0
  96. package/package.json +16 -15
  97. package/dist/lib/renderer/color/prophotoRgb.js +0 -56
  98. package/dist/lib/validation/declaration.js +0 -94
  99. package/dist/lib/validation/syntaxes/image.js +0 -29
@@ -1,1509 +1,828 @@
1
- import { ValidationTokenEnum, specialValues } from './parser/types.js';
1
+ import { ValidationTokenEnum } from './parser/types.js';
2
2
  import { renderSyntax } from './parser/parse.js';
3
- import { ValidationLevel, EnumToken, funcLike } from '../ast/types.js';
3
+ import { EnumToken, SyntaxValidationResult } from '../ast/types.js';
4
4
  import '../ast/minify.js';
5
5
  import '../ast/walk.js';
6
6
  import '../parser/parse.js';
7
- import { isLength } from '../syntax/syntax.js';
7
+ import '../parser/tokenize.js';
8
8
  import '../parser/utils/config.js';
9
+ import { wildCardFuncs, isIdentColor, mathFuncs } from '../syntax/syntax.js';
9
10
  import { renderToken } from '../renderer/render.js';
10
- import { getSyntaxConfig, getParsedSyntax } from './config.js';
11
- import { validateSelector } from './selector.js';
11
+ import { funcLike, ColorKind, colorsFunc } from '../renderer/color/utils/constants.js';
12
+ import { getSyntaxConfig, getParsedSyntax, getSyntax } from './config.js';
12
13
  import './syntaxes/complex-selector.js';
13
- import { validateImage } from './syntaxes/image.js';
14
14
 
15
15
  const config = getSyntaxConfig();
16
- function consumeToken(tokens) {
17
- tokens.shift();
18
- }
19
- function consumeSyntax(syntaxes) {
20
- syntaxes.shift();
21
- }
22
- function splice(tokens, matches) {
23
- if (matches.length == 0) {
24
- return tokens;
25
- }
26
- // @ts-ignore
27
- const index = tokens.indexOf(matches.at(-1));
28
- if (index > -1) {
29
- tokens.splice(0, index + 1);
30
- }
31
- return tokens;
32
- }
33
- function validateSyntax(syntaxes, tokens, root, options, context = { level: 0 }) {
34
- console.error(JSON.stringify({
35
- syntax: syntaxes?.reduce?.((acc, curr) => acc + renderSyntax(curr), ''),
36
- // syntaxes,
37
- tokens: tokens.reduce((acc, curr) => acc + renderToken(curr), ''),
38
- s: new Error('bar').stack
39
- }, null, 1));
40
- if (syntaxes == null) {
41
- // @ts-ignore
42
- return {
43
- valid: ValidationLevel.Drop,
44
- matches: [],
45
- node: tokens[0] ?? null,
46
- syntax: null,
47
- error: 'no matching syntaxes found',
48
- tokens
49
- };
50
- }
51
- let token = null;
52
- let syntax;
53
- let result = null;
54
- let validSyntax = false;
55
- let matched = false;
56
- const matches = [];
57
- tokens = tokens.slice();
58
- syntaxes = syntaxes.slice();
59
- tokens = tokens.slice();
60
- if (context.cache == null) {
61
- context.cache = new WeakMap;
62
- }
63
- if (context.tokens == null) {
64
- context.tokens = tokens.slice();
16
+ // @ts-ignore
17
+ const allValues = getSyntaxConfig()["declarations" /* ValidationSyntaxGroupEnum.Declarations */].all.syntax.trim().split(/[\s|]+/g);
18
+ function createContext(input) {
19
+ const values = input.slice();
20
+ const result = values.slice();
21
+ if (result.at(-1)?.typ == EnumToken.WhitespaceTokenType) {
22
+ result.pop();
65
23
  }
66
- context = { ...context };
67
- main: while (tokens.length > 0) {
68
- if (syntaxes.length == 0) {
69
- break;
70
- }
71
- token = tokens[0];
72
- syntax = syntaxes[0];
73
- // @ts-ignore
74
- context.position = context.tokens.indexOf(token);
75
- const cached = context.cache.get(token)?.get(syntax.text) ?? null;
76
- if (cached != null) {
77
- if (cached.error.length > 0) {
78
- return { ...cached, tokens, node: cached.valid == ValidationLevel.Valid ? null : token };
79
- }
80
- syntaxes.shift();
81
- tokens.shift();
82
- continue;
83
- }
84
- if (token.typ == EnumToken.DescendantCombinatorTokenType) {
85
- tokens.shift();
86
- if (syntax.typ == ValidationTokenEnum.Whitespace) {
87
- syntaxes.shift();
88
- }
89
- continue;
90
- }
91
- else if (syntax.typ == ValidationTokenEnum.Whitespace) {
92
- syntaxes.shift();
93
- if (token.typ == EnumToken.WhitespaceTokenType) {
94
- tokens.shift();
95
- }
96
- continue;
97
- }
98
- else if (syntax.typ == ValidationTokenEnum.Block && EnumToken.AtRuleTokenType == token.typ && ('chi' in token)) {
99
- syntaxes.shift();
100
- tokens.shift();
24
+ return {
25
+ index: -1,
26
+ peek() {
27
+ let index = this.index + 1;
28
+ if (index >= result.length) {
29
+ return null;
30
+ }
31
+ if (result[index]?.typ == EnumToken.WhitespaceTokenType) {
32
+ index++;
33
+ }
34
+ return result[index] ?? null;
35
+ },
36
+ update(context) {
101
37
  // @ts-ignore
102
- matches.push(token);
103
- continue;
38
+ const newIndex = result.indexOf(context.current());
39
+ if (newIndex != -1) {
40
+ // console.error({newIndex, v: result[newIndex]});
41
+ // console.error(new Error('update'))
42
+ this.index = newIndex;
43
+ }
44
+ },
45
+ done() {
46
+ return this.index + 1 >= result.length;
47
+ },
48
+ current() {
49
+ return result[this.index] ?? null;
50
+ },
51
+ next() {
52
+ let index = this.index + 1;
53
+ if (index >= result.length) {
54
+ return null;
55
+ }
56
+ if (result[index]?.typ == EnumToken.WhitespaceTokenType) {
57
+ index++;
58
+ }
59
+ this.index = index;
60
+ return result[this.index] ?? null;
61
+ },
62
+ tokens() {
63
+ return result;
64
+ },
65
+ slice() {
66
+ return result.slice(this.index + 1);
67
+ },
68
+ clone() {
69
+ const context = createContext(input.slice());
70
+ context.index = this.index;
71
+ return context;
104
72
  }
105
- if (syntax.isOptional) {
106
- if (!context.cache.has(token)) {
107
- context.cache.set(token, new Map);
108
- }
109
- if (context.cache.get(token).has(syntax.text)) {
110
- result = context.cache.get(token).get(syntax.text);
111
- return { ...result, tokens, node: result.valid == ValidationLevel.Valid ? null : token };
112
- }
113
- // @ts-ignore
114
- const { isOptional, ...c } = syntax;
115
- // @ts-ignore
116
- let result2;
117
- // @ts-ignore
118
- result2 = validateSyntax([c], tokens, root, options, context);
119
- if (result2.valid == ValidationLevel.Valid && result2.matches.length > 0) {
120
- tokens = result2.tokens;
121
- // splice(tokens, result2.matches);
122
- // tokens = result2.tokens;
123
- // @ts-ignore
124
- matches.push(...result2.matches);
125
- matched = true;
126
- result = result2;
127
- }
128
- else {
129
- syntaxes.shift();
130
- continue;
131
- }
132
- syntaxes.shift();
133
- if (syntaxes.length == 0) {
134
- // @ts-ignore
135
- return {
136
- valid: ValidationLevel.Valid,
137
- matches: result2.matches,
138
- node: result2.node,
139
- syntax: result2.syntax,
140
- error: result2.error,
141
- tokens
142
- };
73
+ };
74
+ }
75
+ function evaluateSyntax(node, options, parent) {
76
+ let ast;
77
+ let result;
78
+ switch (node.typ) {
79
+ case EnumToken.DeclarationNodeType:
80
+ if (node.nam.startsWith('--')) {
81
+ break;
143
82
  }
144
- continue;
145
- }
146
- if (syntax.isList) {
147
- let index = -1;
148
- // @ts-ignore
149
- let { isList, ...c } = syntax;
150
- // const c: ValidationToken = {...syntaxes, isList: false} as ValidationToken;
151
- let result2 = null;
152
- validSyntax = false;
153
- do {
154
- for (let i = index + 1; i < tokens.length; i++) {
155
- if (tokens[i].typ == EnumToken.CommaTokenType) {
156
- index = i;
83
+ ast = getParsedSyntax("declarations" /* ValidationSyntaxGroupEnum.Declarations */, node.nam);
84
+ if (ast != null) {
85
+ let token = null;
86
+ const values = node.val.slice();
87
+ while (values.length > 0) {
88
+ token = values.at(-1);
89
+ if (token.typ == EnumToken.WhitespaceTokenType || token.typ == EnumToken.CommentTokenType) {
90
+ values.pop();
91
+ }
92
+ else {
93
+ if (token.typ == EnumToken.ImportantTokenType) {
94
+ values.pop();
95
+ if (values.at(-1)?.typ == EnumToken.WhitespaceTokenType) {
96
+ values.pop();
97
+ }
98
+ }
157
99
  break;
158
100
  }
159
101
  }
160
- if (tokens[index + 1]?.typ == EnumToken.CommaTokenType) {
161
- return {
162
- valid: ValidationLevel.Drop,
163
- matches,
164
- node: tokens[0],
165
- syntax,
166
- error: 'unexpected token',
167
- tokens
168
- };
169
- }
170
- if (index == -1) {
171
- index = tokens.length;
172
- }
173
- if (index == 0) {
174
- break;
175
- }
176
- // @ts-ignore
177
- result2 = validateSyntax([c], tokens.slice(0, index), root, options, context);
178
- matched = result2.valid == ValidationLevel.Valid && result2.matches.length > 0;
179
- if (matched) {
180
- const l = tokens.length;
181
- validSyntax = true;
182
- // @ts-ignore
183
- // matches.push(...result2.matches);
184
- // splice(tokens, result2.matches);
185
- if (tokens.length == 1 && tokens[0].typ == EnumToken.CommaTokenType) {
102
+ result = doEvaluateSyntax(ast, createContext(values), { ...options, visited: new WeakMap() });
103
+ if (result.valid == SyntaxValidationResult.Valid && !result.context.done()) {
104
+ let token = null;
105
+ while ((token = result.context.next()) != null) {
106
+ if (token.typ == EnumToken.WhitespaceTokenType || token.typ == EnumToken.CommentTokenType) {
107
+ continue;
108
+ }
186
109
  return {
187
- valid: ValidationLevel.Drop,
188
- matches,
189
- node: tokens[0],
190
- syntax,
191
- error: 'unexpected token',
192
- tokens
110
+ ...result,
111
+ valid: SyntaxValidationResult.Drop,
112
+ node: token,
113
+ syntax: getSyntax("declarations" /* ValidationSyntaxGroupEnum.Declarations */, node.nam),
114
+ error: `unexpected token: '${renderToken(token)}'`,
193
115
  };
194
116
  }
195
- tokens = tokens.slice(index);
196
- result = result2;
197
- // @ts-ignore
198
- matches.push(...result2.matches);
199
- if (result.tokens.length > 0) {
200
- if (index == -1) {
201
- tokens = result.tokens;
202
- }
203
- else {
204
- tokens = tokens.slice(index - result.tokens.length);
205
- }
206
- }
207
- else if (index > 0) {
208
- tokens = tokens.slice(index);
209
- }
210
- index = -1;
211
- if (l == tokens.length) {
212
- break;
213
- }
214
- }
215
- else {
216
- break;
217
117
  }
218
- } while (tokens.length > 0);
219
- // if (level == 0) {
220
- // }
221
- if (!matched) {
222
118
  return {
223
- valid: ValidationLevel.Drop,
224
- // @ts-ignore
225
- matches: [...new Set(matches)],
226
- node: token,
227
- syntax,
228
- error: 'unexpected token',
229
- tokens
119
+ ...result,
120
+ syntax: getSyntax("declarations" /* ValidationSyntaxGroupEnum.Declarations */, node.nam)
230
121
  };
231
122
  }
232
- syntaxes.shift();
233
- continue;
123
+ break;
124
+ case EnumToken.RuleNodeType:
125
+ case EnumToken.AtRuleNodeType:
126
+ case EnumToken.KeyframeAtRuleNodeType:
127
+ case EnumToken.KeyFrameRuleNodeType:
128
+ // default:
129
+ //
130
+ // throw new Error(`Not implemented: ${node.typ}`);
131
+ }
132
+ return {
133
+ valid: SyntaxValidationResult.Valid,
134
+ node,
135
+ syntax: null,
136
+ error: ''
137
+ };
138
+ }
139
+ function clearVisited(token, syntax, key, options) {
140
+ options.visited.get(token)?.get?.(key)?.delete(syntax);
141
+ }
142
+ function isVisited(token, syntax, key, options) {
143
+ if (options.visited.get(token)?.get?.(key)?.has(syntax)) {
144
+ return true;
145
+ }
146
+ if (!options.visited.has(token)) {
147
+ options.visited.set(token, new Map());
148
+ }
149
+ if (!options.visited.get(token).has(key)) {
150
+ options.visited.get(token).set(key, new Set());
151
+ }
152
+ options.visited.get(token).get(key).add(syntax);
153
+ return false;
154
+ }
155
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Values_and_Units/Value_definition_syntax
156
+ function doEvaluateSyntax(syntaxes, context, options) {
157
+ let syntax;
158
+ let i = 0;
159
+ let result;
160
+ let token = null;
161
+ for (; i < syntaxes.length; i++) {
162
+ syntax = syntaxes[i];
163
+ if (context.done()) {
164
+ if (syntax.typ == ValidationTokenEnum.Whitespace || syntax.isOptional || syntax.isRepeatable) {
165
+ continue;
166
+ }
167
+ break;
234
168
  }
235
- if (syntax.isRepeatable) {
236
- // @ts-ignore
237
- let { isRepeatable, ...c } = syntax;
238
- let result2 = null;
239
- validSyntax = false;
240
- let l = tokens.length;
241
- let tok = null;
242
- do {
243
- // @ts-ignore
244
- result2 = validateSyntax([c], tokens, root, options, context);
245
- if (result2.matches.length == 0 && result2.error.length > 0) {
246
- syntaxes.shift();
247
- break main;
248
- }
249
- if (result2.valid == ValidationLevel.Valid) {
250
- tokens = result2.tokens;
251
- // @ts-ignore
252
- matches.push(...result2.matches);
253
- result = result2;
254
- if (l == tokens.length) {
255
- if (tok == tokens[0]) {
256
- break;
257
- }
258
- if (result2.matches.length == 0 && tokens.length > 0) {
259
- tokens = result2.tokens;
260
- tok = tokens[0];
261
- continue;
262
- }
263
- break;
264
- }
265
- if (matches.length == 0) {
266
- tokens = result2.tokens;
267
- }
268
- l = tokens.length;
269
- continue;
270
- }
271
- break;
272
- } while (result2.valid == ValidationLevel.Valid && tokens.length > 0);
273
- // if (lastResult != null) {
274
- //
275
- // splice(tokens, lastResult.matches);
276
- // // tokens = lastResult.tokens;
277
- // }
278
- syntaxes.shift();
169
+ token = context.peek();
170
+ if (syntax.typ == ValidationTokenEnum.Whitespace) {
171
+ if (context.peek()?.typ == EnumToken.WhitespaceTokenType) {
172
+ context.next();
173
+ }
279
174
  continue;
280
175
  }
281
- // at least one match
282
- if (syntax.isRepeatableGroup) {
283
- validSyntax = false;
284
- let count = 0;
285
- let l = tokens.length;
286
- let result2 = null;
287
- do {
288
- // @ts-ignore
289
- const { isRepeatableGroup, ...c } = syntax;
290
- // @ts-ignore
291
- result2 = validateSyntax([c], tokens, root, options, context);
292
- if (result2.valid == ValidationLevel.Drop || result2.matches.length == 0) {
293
- if (count > 0) {
294
- syntaxes.shift();
295
- // if (result2.matches.length == 0) {
296
- tokens = result2.tokens;
297
- // break main;
298
- if (syntaxes.length == 0) {
299
- return result2;
300
- }
301
- break main;
302
- }
303
- return result2;
304
- }
305
- if (result2.valid == ValidationLevel.Valid && result2.matches.length > 0) {
306
- count++;
307
- // lastResult = result;
308
- validSyntax = true;
309
- tokens = result2.tokens;
310
- // splice(tokens, result2.matches);
311
- // tokens = result2.tokens;
312
- // @ts-ignore
313
- matches.push(...result2.matches);
314
- result = result2;
315
- if (l == tokens.length) {
316
- break;
317
- }
318
- l = tokens.length;
319
- }
320
- else {
321
- break;
322
- }
323
- } while (tokens.length > 0 && result.valid == ValidationLevel.Valid);
324
- // if (lastResult != null) {
325
- //
326
- // splice(tokens, lastResult.matches);
327
- // // tokens = lastResult.tokens;
328
- // }
329
- // at least one match is expected
330
- if (!validSyntax /* || result.matches.length == 0 */) {
331
- // @ts-ignore
176
+ else if (options.isList !== false && syntax.isList) {
177
+ result = matchList(syntax, context, options);
178
+ }
179
+ else if (options.isRepeatable !== false && syntax.isRepeatable) {
180
+ result = matchRepeatable(syntax, context, options);
181
+ }
182
+ else if (options.occurence !== false && syntax.occurence != null) {
183
+ result = matchOccurence(syntax, context, options);
184
+ }
185
+ else if (options.atLeastOnce !== false && syntax.atLeastOnce) {
186
+ result = matchAtLeastOnce(syntax, context, options);
187
+ }
188
+ else {
189
+ if (isVisited(token, syntax, 'doEvaluateSyntax', options)) {
332
190
  return {
333
- valid: ValidationLevel.Drop,
191
+ valid: SyntaxValidationResult.Drop,
334
192
  node: token,
335
- tokens,
336
193
  syntax,
337
- error: 'unexpected token',
338
- matches: []
194
+ error: `cyclic dependency: ${renderSyntax(syntax)}`,
195
+ context
339
196
  };
340
197
  }
341
- syntaxes.shift();
342
- continue;
343
- }
344
- if (syntax.atLeastOnce) {
345
- const { atLeastOnce, ...c } = syntax;
346
- result = validateSyntax([c], tokens, root, options, context);
347
- if (result.valid == ValidationLevel.Drop) {
348
- return result;
349
- }
350
- splice(tokens, result.matches);
351
- // tokens = result.tokens;
352
- // @ts-ignore
353
- matches.push(...result.matches);
354
- let l = tokens.length;
355
- let r = validateSyntax([c], tokens, root, options, context);
356
- while (r.valid == ValidationLevel.Valid) {
357
- splice(tokens, r.matches);
358
- // tokens = r.tokens;
359
- r = validateSyntax([c], tokens, root, options, context);
360
- if (l == tokens.length) {
361
- break;
362
- }
363
- if (r.valid == ValidationLevel.Valid && r.matches.length > 0) {
364
- // @ts-ignore
365
- matches.push(...result.matches);
366
- }
367
- l = tokens.length;
198
+ result = match(syntax, context, options);
199
+ if (result.valid == SyntaxValidationResult.Valid) {
200
+ clearVisited(token, syntax, 'doEvaluateSyntax', options);
368
201
  }
369
- syntaxes.shift();
370
- continue;
371
202
  }
372
- // @ts-ignore
373
- if (syntax.occurence != null) {
374
- // @ts-ignore
375
- const { occurence, ...c } = syntax;
376
- // && syntaxes.occurence.max != null
377
- // consume all tokens
378
- let match = 1;
379
- // @ts-ignore
380
- result = validateSyntax([c], tokens, root, options, context);
381
- if (result.valid == ValidationLevel.Drop) {
382
- return result;
383
- }
384
- if (result.matches.length == 0) {
385
- syntaxes.shift();
203
+ if (result.valid == SyntaxValidationResult.Drop) {
204
+ if (syntax.isOptional) {
386
205
  continue;
387
206
  }
388
- // splice(tokens, result.matches);
389
- // tokens = result.tokens;
390
- // @ts-ignore
391
- matches.push(...result.matches);
392
- matched = true;
393
- tokens = result.tokens;
394
- while (occurence.max == null || match < occurence.max) {
395
- // trim whitespace
396
- if (tokens[0]?.typ == EnumToken.WhitespaceTokenType) {
397
- tokens.shift();
398
- }
399
- // @ts-ignore
400
- let r = validateSyntax([c], tokens, root, options, context);
401
- if (r.valid != ValidationLevel.Valid || r.matches.length == 0) {
402
- break;
403
- }
404
- result = r;
405
- // splice(tokens, r.matches);
406
- // tokens = r.tokens;
407
- // @ts-ignore
408
- matches.push(...result.matches);
409
- match++;
410
- tokens = r.tokens;
411
- result = r;
412
- if (tokens.length == 0 || (occurence.max != null && match >= occurence.max)) {
413
- break;
414
- }
415
- // @ts-ignore
416
- // r = validateSyntax([c], tokens, root, options, context);
417
- }
418
- syntaxes.shift();
207
+ return result;
208
+ }
209
+ context.update(result.context);
210
+ }
211
+ return {
212
+ valid: SyntaxValidationResult.Valid,
213
+ node: null,
214
+ syntax: syntaxes[i - 1],
215
+ error: '',
216
+ context
217
+ };
218
+ }
219
+ function matchAtLeastOnce(syntax, context, options) {
220
+ let success = false;
221
+ let result;
222
+ while (!context.done()) {
223
+ result = match(syntax, context.clone(), { ...options, atLeastOnce: false });
224
+ if (result.valid == SyntaxValidationResult.Valid) {
225
+ success = true;
226
+ context.update(result.context);
419
227
  continue;
420
228
  }
421
- // @ts-ignore
422
- if (syntax.typ == ValidationTokenEnum.Whitespace) {
423
- if (token.typ == EnumToken.WhitespaceTokenType) {
424
- tokens.shift();
425
- }
426
- syntaxes.shift();
229
+ break;
230
+ }
231
+ return {
232
+ valid: success ? SyntaxValidationResult.Valid : SyntaxValidationResult.Drop,
233
+ node: context.current(),
234
+ syntax,
235
+ error: success ? '' : `could not match atLeastOnce: ${renderSyntax(syntax)}`,
236
+ context
237
+ };
238
+ }
239
+ function matchRepeatable(syntax, context, options) {
240
+ let result;
241
+ while (!context.done()) {
242
+ result = match(syntax, context.clone(), { ...options, isRepeatable: false });
243
+ if (result.valid == SyntaxValidationResult.Valid) {
244
+ context.update(result.context);
427
245
  continue;
428
246
  }
429
- // @ts-ignore
430
- if (token.val != null && specialValues.includes(token.val)) {
431
- matched = true;
432
- result = {
433
- valid: ValidationLevel.Valid,
434
- matches: [token],
435
- node: null,
247
+ break;
248
+ }
249
+ return {
250
+ valid: SyntaxValidationResult.Valid,
251
+ node: null,
252
+ syntax,
253
+ error: '',
254
+ context
255
+ };
256
+ }
257
+ function matchList(syntax, context, options) {
258
+ let success = false;
259
+ let result;
260
+ let count = 0;
261
+ let con = context.clone();
262
+ let tokens = [];
263
+ while (!con.done()) {
264
+ while (!con.done() && con.peek()?.typ != EnumToken.CommaTokenType) {
265
+ tokens.push(con.next());
266
+ }
267
+ if (tokens.length == 0) {
268
+ return {
269
+ valid: SyntaxValidationResult.Drop,
270
+ node: context.peek(),
436
271
  syntax,
437
- error: '',
438
- tokens
272
+ error: `could not match list: ${renderSyntax(syntax)}`,
273
+ context
439
274
  };
440
- // @ts-ignore
441
- matches.push(...result.matches);
442
275
  }
443
- else {
444
- result = doValidateSyntax(syntax, token, tokens, root, options, context);
445
- matched = result.valid == ValidationLevel.Valid && result.matches.length > 0;
446
- if (matched) {
447
- // splice(tokens, result.matches);
448
- tokens = result.tokens;
449
- // @ts-ignore
450
- matches.push(...result.matches);
276
+ result = doEvaluateSyntax([syntax], createContext(tokens), {
277
+ ...options,
278
+ isList: false,
279
+ occurence: false
280
+ });
281
+ if (result.valid == SyntaxValidationResult.Valid) {
282
+ context = con.clone();
283
+ count++;
284
+ // pop comma
285
+ if (con.done() || con.peek()?.typ != EnumToken.CommaTokenType) {
286
+ break;
451
287
  }
288
+ con.next();
289
+ tokens.length = 0;
452
290
  }
453
- if (result.valid == ValidationLevel.Drop) {
454
- // @ts-ignore
455
- return { ...result, matches, tokens, node: result.valid == ValidationLevel.Valid ? null : token };
456
- }
457
- consumeSyntax(syntaxes);
458
- if (tokens.length == 0) {
459
- return result;
291
+ else {
292
+ break;
460
293
  }
461
294
  }
462
- if (result?.valid == ValidationLevel.Valid) {
463
- // splice(tokens, result.matches);
464
- tokens = result.tokens;
465
- // @ts-ignore
466
- matches.push(...result.matches);
467
- }
468
- if ( /* result == null && */tokens.length == 0 && syntaxes.length > 0) {
469
- validSyntax = isOptionalSyntax(syntaxes);
470
- }
471
- if (result == null) {
472
- result = {
473
- valid: validSyntax ? ValidationLevel.Valid : ValidationLevel.Drop,
474
- matches,
475
- node: validSyntax ? null : tokens[0] ?? null,
476
- // @ts-ignore
477
- syntax,
478
- error: validSyntax ? '' : 'unexpected token',
479
- tokens
480
- };
481
- }
482
- if (token != null) {
483
- if (!context.cache.has(token)) {
484
- context.cache.set(token, new Map);
295
+ success = count > 0;
296
+ if (count && syntax.occurence != null) {
297
+ success = count >= syntax.occurence.min;
298
+ if (success && syntax.occurence.max != null) {
299
+ success = count <= syntax.occurence.max;
485
300
  }
486
- context.cache.get(token).set(syntax.text, result);
487
- }
488
- if (result != null) {
489
- // @ts-ignore
490
- return { ...result, matches: [...(new Set(matches))] };
491
301
  }
492
- return result;
493
- }
494
- function isOptionalSyntax(syntaxes) {
495
- return syntaxes.length > 0 && syntaxes.every(t => t.typ == ValidationTokenEnum.Whitespace || t.isOptional || t.isRepeatable || (t.typ == ValidationTokenEnum.PropertyType && isOptionalSyntax(getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, t.val) ?? getParsedSyntax("declarations" /* ValidationSyntaxGroupEnum.Declarations */, t.val) ?? [])));
302
+ return {
303
+ valid: success ? SyntaxValidationResult.Valid : SyntaxValidationResult.Drop,
304
+ node: context.current(),
305
+ syntax,
306
+ error: '',
307
+ context
308
+ };
496
309
  }
497
- function doValidateSyntax(syntax, token, tokens, root, options, context) {
498
- let valid = false;
310
+ function matchOccurence(syntax, context, options) {
311
+ let counter = 0;
499
312
  let result;
500
- let children;
501
- let queue;
502
- let matches;
503
- let child;
504
- let astNodes = new Set;
505
- if (token.typ == EnumToken.NestingSelectorTokenType && syntax.typ == 2) {
506
- valid = root != null && 'relative-selector' == syntax.val;
507
- return {
508
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
509
- matches: valid ? [token] : [],
510
- node: valid ? null : token,
511
- syntax,
512
- error: valid ? '' : 'unexpected token',
513
- tokens
514
- };
313
+ do {
314
+ result = match(syntax, context.clone(), { ...options, occurence: false });
315
+ if (result.valid == SyntaxValidationResult.Drop) {
316
+ break;
317
+ }
318
+ counter++;
319
+ context.update(result.context);
320
+ } while (result.valid == SyntaxValidationResult.Valid && !context.done());
321
+ let sucesss = counter >= syntax.occurence.min;
322
+ if (sucesss && syntax.occurence.max != null) {
323
+ if (Number.isFinite(syntax.occurence.max)) {
324
+ sucesss = sucesss && counter <= syntax.occurence.max;
325
+ }
515
326
  }
327
+ return {
328
+ valid: sucesss ? SyntaxValidationResult.Valid : SyntaxValidationResult.Drop,
329
+ node: context.current(),
330
+ syntax,
331
+ error: sucesss ? '' : `expected ${renderSyntax(syntax)} ${syntax.occurence.min} to ${syntax.occurence.max} occurences, got ${counter}`,
332
+ context
333
+ };
334
+ }
335
+ function match(syntax, context, options) {
336
+ let success = false;
337
+ let result;
338
+ let token = context.peek();
516
339
  switch (syntax.typ) {
517
- case ValidationTokenEnum.Comma:
518
- valid = token.typ === EnumToken.CommaTokenType;
519
- // @ts-ignore
520
- result = {
521
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
522
- matches: valid ? [token] : [],
523
- node: valid ? null : token,
340
+ case ValidationTokenEnum.PipeToken:
341
+ return someOf(syntax.chi, context, options);
342
+ case ValidationTokenEnum.Bracket:
343
+ return doEvaluateSyntax(syntax.chi, context, options);
344
+ case ValidationTokenEnum.AmpersandToken:
345
+ return allOf(flatten(syntax), context, options);
346
+ case ValidationTokenEnum.ColumnToken: {
347
+ let result = anyOf(flatten(syntax), context, options);
348
+ if (result.valid == SyntaxValidationResult.Valid) {
349
+ return result;
350
+ }
351
+ return {
352
+ valid: SyntaxValidationResult.Drop,
353
+ node: context.next(),
524
354
  syntax,
525
- error: valid ? '' : 'unexpected token',
526
- tokens
355
+ error: `expected '${ValidationTokenEnum[syntax.typ].toLowerCase()}', got '${context.done() ? null : renderToken(context.peek())}'`,
356
+ context
527
357
  };
528
- break;
529
- case ValidationTokenEnum.AtRule:
530
- if (token.typ != EnumToken.AtRuleNodeType) {
531
- // @ts-ignore
532
- return {
533
- valid: ValidationLevel.Drop,
534
- matches: [],
535
- node: token,
536
- syntax,
537
- error: 'expecting at-rule',
538
- tokens
539
- };
540
- }
541
- if (token.nam != syntax.val) {
542
- // @ts-ignore
543
- return {
544
- valid: ValidationLevel.Drop,
545
- matches: [],
546
- node: token,
547
- syntax,
548
- error: `expecting '@${syntax.val}' but found '@${token.nam}'`,
549
- tokens
550
- };
551
- }
552
- if (root == null) {
553
- return {
554
- valid: ValidationLevel.Valid,
555
- matches: [token],
556
- node: null,
557
- syntax,
558
- error: '',
559
- tokens
560
- };
561
- }
562
- if (root.typ != EnumToken.AtRuleNodeType) {
563
- // @ts-ignore
564
- return {
565
- valid: ValidationLevel.Drop,
566
- matches: [],
567
- node: token,
568
- syntax,
569
- error: 'not allowed here',
570
- tokens
571
- };
572
- }
573
- if (!('chi' in token)) {
574
- // @ts-ignore
575
- return {
576
- valid: ValidationLevel.Drop,
577
- matches: [],
578
- node: token,
579
- syntax,
580
- error: '@at-rule must have children',
581
- tokens
582
- };
583
- }
584
- // @ts-ignore
585
- result = {
586
- valid: ValidationLevel.Valid,
587
- matches: [token],
358
+ }
359
+ }
360
+ if (token.typ == EnumToken.WhitespaceTokenType) {
361
+ context.next();
362
+ // @ts-ignore
363
+ if (syntax?.typ == ValidationTokenEnum.Whitespace) {
364
+ return {
365
+ valid: SyntaxValidationResult.Valid,
588
366
  node: null,
589
367
  syntax,
590
368
  error: '',
591
- tokens
369
+ context
592
370
  };
593
- break;
594
- case ValidationTokenEnum.AtRuleDefinition:
595
- if (token.typ != EnumToken.AtRuleNodeType) {
596
- // @ts-ignore
597
- return {
598
- valid: ValidationLevel.Drop,
599
- matches: [],
600
- node: token,
601
- syntax,
602
- error: 'expecting at-rule',
603
- tokens
604
- };
605
- }
606
- if ('chi' in syntax && !('chi' in token)) {
607
- // @ts-ignore
371
+ }
372
+ }
373
+ if (syntax.typ != ValidationTokenEnum.PropertyType && (token?.typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(token.val))) {
374
+ const result = doEvaluateSyntax(getParsedSyntax("functions" /* ValidationSyntaxGroupEnum.Functions */, token.val)?.[0]?.chi ?? [], createContext(token.chi), {
375
+ ...options,
376
+ isRepeatable: null,
377
+ isList: null,
378
+ occurence: null,
379
+ atLeastOnce: null
380
+ });
381
+ if (result.valid == SyntaxValidationResult.Valid) {
382
+ context.next();
383
+ }
384
+ return { ...result, context };
385
+ }
386
+ switch (syntax.typ) {
387
+ case ValidationTokenEnum.Keyword:
388
+ success = (token.typ == EnumToken.IdenTokenType || token.typ == EnumToken.DashedIdenTokenType) &&
389
+ (token.val == syntax.val ||
390
+ syntax.val.localeCompare(token.val, undefined, { sensitivity: 'base' }) == 0 ||
391
+ // config.declarations.all
392
+ allValues.includes(token.val.toLowerCase()));
393
+ if (success) {
394
+ context.next();
608
395
  return {
609
- valid: ValidationLevel.Drop,
610
- matches: [],
396
+ valid: success ? SyntaxValidationResult.Valid : SyntaxValidationResult.Drop,
611
397
  node: token,
612
398
  syntax,
613
- error: '@at-rule must have children',
614
- tokens
399
+ error: success ? '' : `expected keyword: '${syntax.val}', got ${renderToken(token)}`,
400
+ context
615
401
  };
616
402
  }
617
- if ('chi' in token && !('chi' in token)) {
618
- // @ts-ignore
403
+ break;
404
+ case ValidationTokenEnum.PropertyType:
405
+ return matchPropertyType(syntax, context, options);
406
+ case ValidationTokenEnum.ValidationFunctionDefinition:
407
+ token = context.peek();
408
+ if (token.typ == EnumToken.ParensTokenType || !funcLike.concat(EnumToken.ColorTokenType) || (!('chi' in token))) {
619
409
  return {
620
- valid: ValidationLevel.Drop,
621
- matches: [],
622
- node: token,
410
+ valid: SyntaxValidationResult.Drop,
411
+ node: context.next(),
623
412
  syntax,
624
- error: 'children not allowed here',
625
- tokens
413
+ error: `expected function or color token, got ${renderToken(token)}`,
414
+ context
626
415
  };
627
416
  }
628
- const s = getParsedSyntax("atRules" /* ValidationSyntaxGroupEnum.AtRules */, '@' + token.nam);
629
- if ('prelude' in syntax) {
630
- if (!('tokens' in token)) {
631
- // @ts-ignore
632
- return {
633
- valid: ValidationLevel.Drop,
634
- matches: [],
635
- node: token,
636
- syntax,
637
- error: 'expected at-rule prelude',
638
- tokens
639
- };
640
- }
641
- result = validateSyntax(s[0].prelude, token.tokens, root, options, {
642
- ...context,
643
- tokens: null,
644
- level: context.level + 1
417
+ {
418
+ result = doEvaluateSyntax((getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, syntax.val + '()')?.[0]).chi, createContext(token.chi), {
419
+ ...options,
420
+ isRepeatable: null,
421
+ isList: null,
422
+ occurence: null,
423
+ atLeastOnce: null
645
424
  });
646
- if (result.valid == ValidationLevel.Drop) {
425
+ if (result.valid == SyntaxValidationResult.Valid) {
426
+ context.next();
427
+ result.context = context;
647
428
  return result;
648
429
  }
649
430
  }
650
- const hasBody = 'chi' in s[0];
651
- if ('chi' in token) {
652
- if (!hasBody) {
653
- // @ts-ignore
654
- return {
655
- valid: ValidationLevel.Drop,
656
- matches: [],
657
- node: token,
658
- syntax,
659
- error: 'unexpected at-rule body',
660
- tokens
661
- };
662
- }
663
- }
664
- else if (hasBody) {
665
- // @ts-ignore
666
- return {
667
- valid: ValidationLevel.Drop,
668
- matches: [],
669
- node: token,
670
- syntax,
671
- error: 'expecting at-rule body',
672
- tokens
673
- };
674
- }
675
431
  break;
676
432
  case ValidationTokenEnum.DeclarationType:
677
- // @ts-ignore
678
- result = validateSyntax(getParsedSyntax("declarations" /* ValidationSyntaxGroupEnum.Declarations */, syntax.val), [token], root, options, context);
679
- break;
680
- case ValidationTokenEnum.Keyword:
681
- valid = (token.typ == EnumToken.IdenTokenType && token.val.localeCompare(syntax.val, 'en', { sensitivity: 'base' }) == 0) ||
682
- (token.typ == EnumToken.ColorTokenType && token.kin == 'lit' && syntax.val.localeCompare(token.val, 'en', { sensitivity: 'base' }) == 0);
683
- result = {
684
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
685
- matches: valid ? [token] : [],
686
- node: valid ? null : token,
687
- syntax,
688
- error: valid ? '' : 'unexpected token',
689
- tokens
690
- };
691
- break;
692
- case ValidationTokenEnum.SemiColon:
693
- valid = root == null || [EnumToken.RuleNodeType, EnumToken.AtRuleNodeType, EnumToken.StyleSheetNodeType].includes(root.typ);
694
- result = {
695
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
696
- matches: valid ? [token] : [],
697
- node: valid ? null : token,
698
- syntax,
699
- error: valid ? '' : 'unexpected token',
700
- tokens
701
- };
702
- break;
703
- case ValidationTokenEnum.Separator:
704
- valid = token.typ == EnumToken.LiteralTokenType && token.val != '/';
705
- result = {
706
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
707
- matches: valid ? [token] : [],
708
- node: valid ? null : token,
709
- syntax,
710
- error: valid ? '' : 'unexpected token',
711
- tokens
712
- };
713
- break;
714
- case ValidationTokenEnum.PropertyType:
715
- //
716
- if ('image' == syntax.val) {
717
- valid = token.typ == EnumToken.UrlFunctionTokenType || token.typ == EnumToken.ImageFunctionTokenType;
718
- if (!valid) {
719
- return {
720
- valid: ValidationLevel.Drop,
721
- matches: [],
722
- node: token,
723
- syntax,
724
- error: 'unexpected <image>',
725
- tokens
726
- };
727
- }
728
- result = validateImage(token);
729
- }
730
- else if (['media-feature', 'mf-plain'].includes(syntax.val)) {
731
- valid = token.typ == EnumToken.DeclarationNodeType;
732
- result = {
733
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
734
- matches: valid ? [token] : [],
735
- node: valid ? null : token,
736
- syntax,
737
- error: valid ? '' : 'unexpected token',
738
- tokens
739
- };
740
- }
741
- else if (syntax.val == 'pseudo-page') {
742
- valid = token.typ == EnumToken.PseudoClassTokenType && [':left', ':right', ':first', ':blank'].includes(token.val);
743
- result = {
744
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
745
- matches: valid ? [token] : [],
746
- node: valid ? null : token,
747
- syntax,
748
- error: valid ? '' : 'unexpected token',
749
- tokens
750
- };
751
- }
752
- else if (syntax.val == 'page-body') {
753
- if (token.typ == EnumToken.DeclarationNodeType) {
754
- valid = true;
755
- // @ts-ignore
756
- result = {
757
- valid: ValidationLevel.Valid,
758
- matches: [token],
759
- node: null,
760
- syntax,
761
- error: '',
762
- tokens
763
- };
764
- while (tokens.length > 0 && [EnumToken.DeclarationNodeType].includes(tokens[0].typ)) {
765
- // @ts-ignore
766
- result.matches.push(tokens.shift());
767
- }
768
- }
769
- else if (token.typ == EnumToken.AtRuleNodeType) {
770
- result = validateSyntax(getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, 'page-margin-box-type'), [token], root, options, context);
771
- }
772
- }
773
- else if (syntax.val == 'group-rule-body') {
774
- valid = [EnumToken.AtRuleNodeType, EnumToken.RuleNodeType].includes(token.typ);
775
- if (!valid && token.typ == EnumToken.DeclarationNodeType && root?.typ == EnumToken.AtRuleNodeType && root.nam == 'media') {
776
- // allowed only if nested rule
777
- let parent = root;
778
- while (parent != null) {
779
- if (parent.typ == EnumToken.RuleNodeType) {
780
- valid = true;
781
- break;
782
- }
783
- // @ts-ignore
784
- parent = parent.parent;
785
- }
786
- }
787
- // @ts-ignore
788
- result = {
789
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
790
- matches: [],
791
- node: valid ? null : token,
792
- syntax,
793
- error: valid ? '' : 'token is not allowed as a child',
794
- tokens
795
- };
796
- if (!valid) {
797
- return result;
798
- }
799
- }
800
- //
801
- else if ('type-selector' == syntax.val) {
802
- valid = (token.typ == EnumToken.UniversalSelectorTokenType) ||
803
- token.typ == EnumToken.IdenTokenType || (token.typ == EnumToken.NameSpaceAttributeTokenType &&
804
- (token.l == null || token.l.typ == EnumToken.IdenTokenType ||
805
- (token.l.typ == EnumToken.LiteralTokenType && token.l.val == '*')) &&
806
- token.r.typ == EnumToken.IdenTokenType);
807
- result = {
808
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
809
- matches: valid ? [token] : [],
810
- node: token,
811
- syntax,
812
- error: valid ? '' : 'unexpected token',
813
- tokens
814
- };
815
- }
816
- else if ('wq-name' == syntax.val) {
817
- valid = token.typ == EnumToken.IdenTokenType || (token.typ == EnumToken.NameSpaceAttributeTokenType &&
818
- (token.l == null || token.l.typ == EnumToken.IdenTokenType || (token.l.typ == EnumToken.LiteralTokenType && token.l.val == '*')) &&
819
- token.r.typ == EnumToken.IdenTokenType);
820
- result = {
821
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
822
- matches: valid ? [token] : [],
823
- node: token,
824
- syntax,
825
- error: valid ? '' : 'unexpected token',
826
- tokens
827
- };
828
- }
829
- else if (EnumToken.UniversalSelectorTokenType == token.typ && 'subclass-selector' == syntax.val) {
830
- valid = true;
831
- result = {
832
- valid: ValidationLevel.Valid,
833
- matches: [token],
834
- node: null,
835
- syntax,
836
- error: '',
837
- tokens
838
- };
839
- }
840
- else if ('attribute-selector' == syntax.val) {
841
- valid = token.typ == EnumToken.AttrTokenType && token.chi.length > 0;
842
- if (valid) {
843
- const children = token.chi.filter(t => t.typ != EnumToken.WhitespaceTokenType && t.typ != EnumToken.CommaTokenType);
844
- valid = children.length == 1 && [
845
- EnumToken.IdenTokenType,
846
- EnumToken.NameSpaceAttributeTokenType,
847
- EnumToken.MatchExpressionTokenType
848
- ].includes(children[0].typ);
849
- if (valid && children[0].typ == EnumToken.MatchExpressionTokenType) {
850
- const t = children[0];
851
- valid = [
852
- EnumToken.IdenTokenType,
853
- EnumToken.NameSpaceAttributeTokenType
854
- ].includes(t.l.typ) &&
855
- (t.op == null || ([
856
- EnumToken.DelimTokenType, EnumToken.DashMatchTokenType,
857
- EnumToken.StartMatchTokenType, EnumToken.ContainMatchTokenType,
858
- EnumToken.EndMatchTokenType, EnumToken.IncludeMatchTokenType
859
- ].includes(t.op.typ) &&
860
- t.r != null &&
861
- [
862
- EnumToken.StringTokenType,
863
- EnumToken.IdenTokenType
864
- ].includes(t.r.typ)));
865
- if (valid && t.attr != null) {
866
- const s = getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, 'attr-modifier')[0];
867
- valid = s.chi.some((l) => l.some((r) => r.val == t.attr));
868
- }
869
- }
870
- }
871
- result = {
872
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
873
- matches: valid ? [token] : [],
874
- node: valid ? null : token,
875
- syntax,
876
- error: valid ? '' : 'unexpected token',
877
- tokens
878
- };
879
- if (!valid) {
880
- return result;
881
- }
882
- }
883
- else if ('combinator' == syntax.val) {
884
- valid = [
885
- EnumToken.DescendantCombinatorTokenType,
886
- EnumToken.SubsequentSiblingCombinatorTokenType,
887
- EnumToken.NextSiblingCombinatorTokenType,
888
- EnumToken.ChildCombinatorTokenType,
889
- EnumToken.ColumnCombinatorTokenType
890
- ].includes(token.typ);
891
- if (valid) {
892
- // @ts-ignore
893
- const position = context.tokens.indexOf(token);
894
- if (root == null) {
895
- valid = position > 0 && context.tokens[position - 1]?.typ != EnumToken.CommaTokenType;
896
- }
897
- }
898
- result = {
899
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
900
- matches: valid ? [token] : [],
901
- node: valid ? null : token,
902
- syntax,
903
- error: valid ? '' : 'unexpected token',
904
- tokens
905
- };
906
- if (!valid) {
907
- return result;
908
- }
909
- }
910
- else if ('ident-token' == syntax.val) {
911
- valid = token.typ == EnumToken.IdenTokenType;
912
- result = {
913
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
914
- matches: valid ? [token] : [],
915
- node: valid ? null : token,
916
- syntax,
917
- error: valid ? '' : 'unexpected token',
918
- tokens
919
- };
920
- }
921
- else if ('hex-color' == syntax.val) {
922
- valid = token.typ == EnumToken.ColorTokenType && token.kin == 'hex';
923
- result = {
924
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
925
- matches: valid ? [token] : [],
926
- node: valid ? null : token,
927
- syntax,
928
- error: valid ? '' : 'unexpected token',
929
- tokens
930
- };
931
- }
932
- else if ('resolution' == syntax.val) {
933
- valid = token.typ == EnumToken.ResolutionTokenType;
934
- result = {
935
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
936
- matches: valid ? [token] : [],
937
- node: valid ? null : token,
938
- syntax,
939
- error: valid ? '' : 'unexpected token',
940
- tokens
941
- };
942
- }
943
- else if ('angle' == syntax.val) {
944
- valid = token.typ == EnumToken.AngleTokenType || (token.typ == EnumToken.NumberTokenType && token.val == '0');
945
- result = {
946
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
947
- matches: valid ? [token] : [],
948
- node: valid ? null : token,
949
- syntax,
950
- error: valid ? '' : 'unexpected token',
951
- tokens
952
- };
953
- }
954
- else if ('time' == syntax.val) {
955
- valid = token.typ == EnumToken.TimingFunctionTokenType;
956
- result = {
957
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
958
- matches: valid ? [token] : [],
959
- node: valid ? null : token,
960
- syntax,
961
- error: valid ? '' : 'unexpected token',
962
- tokens
963
- };
964
- }
965
- else if ('ident' == syntax.val) {
966
- valid = token.typ == EnumToken.IdenTokenType;
967
- result = {
968
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
969
- matches: valid ? [token] : [],
970
- node: valid ? null : token,
971
- syntax,
972
- error: valid ? '' : 'unexpected token',
973
- tokens
974
- };
975
- }
976
- else if (['id-selector', 'hash-token'].includes(syntax.val)) {
977
- valid = token.typ == EnumToken.HashTokenType;
978
- result = {
979
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
980
- matches: valid ? [token] : [],
981
- node: valid ? null : token,
982
- syntax,
983
- error: valid ? '' : 'unexpected token',
984
- tokens
985
- };
986
- }
987
- else if (['integer', 'number'].includes(syntax.val)) {
988
- // valid = token.typ == EnumToken.NumberTokenType;
989
- valid = token.typ == EnumToken.NumberTokenType && ('integer' != syntax.val || Number.isInteger(+token.val));
990
- if (valid && 'range' in syntax) {
991
- const value = Number(token.val);
992
- const range = syntax.range;
993
- valid = value >= range[0] && (range[1] == null || value <= range[1]);
994
- }
995
- result = {
996
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
997
- matches: valid ? [token] : [],
998
- node: valid ? null : token,
999
- syntax,
1000
- error: valid ? '' : 'unexpected token',
1001
- tokens
1002
- };
1003
- }
1004
- else if ('length' == syntax.val) {
1005
- valid = isLength(token) || (token.typ == EnumToken.NumberTokenType && token.val == '0');
1006
- // @ts-ignore
1007
- result = {
1008
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1009
- matches: valid ? [token] : [],
1010
- node: valid ? null : token,
1011
- syntax,
1012
- error: valid ? '' : 'unexpected token',
1013
- tokens
1014
- };
1015
- }
1016
- else if ('percentage' == syntax.val) {
1017
- valid = token.typ == EnumToken.PercentageTokenType || (token.typ == EnumToken.NumberTokenType && token.val == '0');
1018
- result = {
1019
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1020
- matches: valid ? [token] : [],
1021
- node: valid ? null : token,
1022
- syntax,
1023
- error: valid ? '' : 'unexpected token',
1024
- tokens
1025
- };
1026
- }
1027
- else if ('dashed-ident' == syntax.val) {
1028
- valid = token.typ == EnumToken.DashedIdenTokenType;
1029
- result = {
1030
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1031
- matches: valid ? [token] : [],
1032
- node: valid ? null : token,
1033
- syntax,
1034
- error: valid ? '' : 'unexpected token',
1035
- tokens
1036
- };
1037
- }
1038
- else if ('custom-ident' == syntax.val) {
1039
- valid = token.typ == EnumToken.DashedIdenTokenType || token.typ == EnumToken.IdenTokenType;
1040
- result = {
1041
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1042
- matches: valid ? [token] : [],
1043
- node: valid ? null : token,
1044
- syntax,
1045
- error: valid ? '' : 'unexpected token',
1046
- tokens
1047
- };
1048
- }
1049
- else if ('custom-property-name' == syntax.val) {
1050
- valid = token.typ == EnumToken.DashedIdenTokenType;
1051
- result = {
1052
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1053
- matches: valid ? [token] : [],
1054
- node: valid ? null : token,
1055
- syntax,
1056
- error: valid ? '' : 'unexpected token',
1057
- tokens
1058
- };
433
+ return doEvaluateSyntax(getParsedSyntax("declarations" /* ValidationSyntaxGroupEnum.Declarations */, syntax.val), context, {
434
+ ...options,
435
+ isRepeatable: null,
436
+ isList: null,
437
+ occurence: null,
438
+ atLeastOnce: null
439
+ });
440
+ case ValidationTokenEnum.Parens:
441
+ token = context.peek();
442
+ if (token.typ != EnumToken.ParensTokenType) {
443
+ break;
1059
444
  }
1060
- else if ('string' == syntax.val) {
1061
- valid = token.typ == EnumToken.StringTokenType;
1062
- result = {
1063
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1064
- matches: valid ? [token] : [],
1065
- node: valid ? null : token,
1066
- syntax,
1067
- error: valid ? '' : 'unexpected token',
1068
- tokens
1069
- };
445
+ success = doEvaluateSyntax(syntax.chi, createContext(token.chi), {
446
+ ...options,
447
+ isRepeatable: null,
448
+ isList: null,
449
+ occurence: null,
450
+ atLeastOnce: null
451
+ }).valid == SyntaxValidationResult.Valid;
452
+ break;
453
+ case ValidationTokenEnum.Comma:
454
+ success = context.peek()?.typ == EnumToken.CommaTokenType;
455
+ if (success) {
456
+ context.next();
1070
457
  }
1071
- else if ('declaration-value' == syntax.val) {
1072
- valid = token.typ != EnumToken.LiteralTokenType;
1073
- result = {
1074
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1075
- matches: valid ? [token] : [],
1076
- node: valid ? null : token,
1077
- syntax,
1078
- error: valid ? '' : 'unexpected token',
1079
- tokens
1080
- };
458
+ break;
459
+ case ValidationTokenEnum.Number:
460
+ success = context.peek()?.typ == EnumToken.NumberTokenType;
461
+ if (success) {
462
+ context.next();
1081
463
  }
1082
- else if ('url' == syntax.val) {
1083
- valid = token.typ == EnumToken.UrlFunctionTokenType || token.typ == EnumToken.StringTokenType;
1084
- result = {
1085
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1086
- matches: valid ? [token] : [],
1087
- node: valid ? null : token,
1088
- syntax,
1089
- error: valid ? '' : 'unexpected token',
1090
- tokens
1091
- };
464
+ break;
465
+ case ValidationTokenEnum.Whitespace:
466
+ success = context.peek()?.typ == EnumToken.WhitespaceTokenType;
467
+ if (success) {
468
+ context.next();
1092
469
  }
1093
- else if ('declaration' == syntax.val) {
1094
- valid = token.typ == EnumToken.DeclarationNodeType && (token.nam.startsWith(('--')) || token.nam in config.declarations || token.nam in config.syntaxes);
1095
- if (!valid) {
1096
- // @ts-ignore
1097
- result = {
1098
- valid: ValidationLevel.Drop,
1099
- matches: [],
1100
- node: token,
1101
- syntax,
1102
- error: 'unexpected token',
1103
- tokens
1104
- };
1105
- }
1106
- else if (token.nam.startsWith(('--'))) {
1107
- result = {
1108
- valid: ValidationLevel.Valid,
1109
- matches: [token],
1110
- node: null,
1111
- syntax,
1112
- error: '',
1113
- tokens
1114
- };
1115
- }
1116
- else {
1117
- result = validateSyntax(getParsedSyntax("declarations" /* ValidationSyntaxGroupEnum.Declarations */, token.nam) ?? getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, token.nam), token.val, token, options, {
1118
- ...context,
1119
- tokens: null,
1120
- level: 0
1121
- });
1122
- if (result.valid == ValidationLevel.Valid && result.error.length == 0) {
1123
- tokens = result.tokens;
1124
- }
470
+ break;
471
+ case ValidationTokenEnum.Separator:
472
+ {
473
+ const token = context.peek();
474
+ success = token?.typ == EnumToken.LiteralTokenType && token.val == '/';
475
+ if (success) {
476
+ context.next();
1125
477
  }
1126
478
  }
1127
- else if ('class-selector' == syntax.val) {
1128
- valid = EnumToken.ClassSelectorTokenType == token.typ || EnumToken.UniversalSelectorTokenType == token.typ;
1129
- result = {
1130
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1131
- matches: valid ? [token] : [],
1132
- node: token,
1133
- syntax,
1134
- error: valid ? '' : 'unexpected token',
1135
- tokens
1136
- };
479
+ break;
480
+ default:
481
+ token = context.peek();
482
+ if (!wildCardFuncs.includes(syntax.val) && syntax.val != token.val) {
483
+ break;
1137
484
  }
1138
- // else if ('complex-selector' == (syntaxes as ValidationPropertyToken).val) {
1139
- //
1140
- // result = validateSyntax(getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, (syntaxes as ValidationPropertyToken).val) as ValidationToken[], tokens, root as AstNode, options, context);
1141
- //
1142
- // }
1143
- else if (['pseudo-element-selector', 'pseudo-class-selector'].includes(syntax.val)) {
1144
- valid = false;
1145
- if (token.typ == EnumToken.PseudoClassTokenType) {
1146
- let val = token.val;
1147
- if (val == ':before' || val == ':after') {
1148
- val = ':' + val;
1149
- }
1150
- valid = val in config.selectors;
1151
- if (!valid && val.match(/^:?:-/) != null) {
1152
- const match = token.val.match(/^(:?:)(-[^-]+-)(.*)$/);
1153
- if (match != null) {
1154
- valid = true;
1155
- }
1156
- }
1157
- result = {
1158
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1159
- matches: valid ? [token] : [],
1160
- node: valid ? null : token,
1161
- syntax,
1162
- error: valid ? '' : 'invalid pseudo class',
1163
- tokens
1164
- };
485
+ if (token.typ != EnumToken.ParensTokenType && funcLike.includes(token.typ)) {
486
+ success = doEvaluateSyntax(syntax.chi, createContext(token.chi), {
487
+ ...options,
488
+ isRepeatable: null,
489
+ isList: null,
490
+ occurence: null,
491
+ atLeastOnce: null
492
+ }).valid == SyntaxValidationResult.Valid;
493
+ if (success) {
494
+ context.next();
1165
495
  }
1166
- else if (token.typ == EnumToken.PseudoClassFuncTokenType) {
1167
- let key = token.val in config.selectors ? token.val : token.val + '()';
1168
- valid = key in config.selectors;
1169
- if (!valid && token.val.match(/^:?:-/)) {
1170
- const match = token.val.match(/^(:?:)(-[^-]+-)(.*)$/);
1171
- if (match != null) {
1172
- key = match[1] + match[3] in config.selectors ? match[1] + match[3] : match[1] + match[3] + '()';
1173
- valid = key in config.selectors;
1174
- }
1175
- }
1176
- const s = getParsedSyntax("selectors" /* ValidationSyntaxGroupEnum.Selectors */, key);
1177
- if (s != null) {
1178
- const r = s[0];
1179
- if (r.typ != ValidationTokenEnum.PseudoClassFunctionToken) {
1180
- valid = false;
1181
- }
1182
- else {
1183
- result = validateSyntax(s[0].chi, token.chi, root, options, {
1184
- ...context,
1185
- tokens: null,
1186
- level: context.level + 1
1187
- });
1188
- break;
1189
- }
1190
- }
1191
- }
1192
- result = {
1193
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1194
- matches: valid ? [token] : [],
1195
- node: token,
1196
- syntax,
1197
- error: valid ? '' : 'unexpected token',
1198
- tokens
1199
- };
496
+ break;
1200
497
  }
1201
- // <relative-selector-list>
1202
- // <complex-selector-list>
1203
- else if ('relative-selector' == syntax.val) {
1204
- if (tokens.length == 1 && token.typ == EnumToken.NestingSelectorTokenType) {
1205
- return {
1206
- valid: ValidationLevel.Valid,
1207
- matches: [token],
1208
- node: token,
1209
- syntax,
1210
- error: '',
1211
- tokens
1212
- };
498
+ if (syntax.typ == ValidationTokenEnum.Function) {
499
+ success = funcLike.includes(token.typ) && doEvaluateSyntax(getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, syntax.val + '()')?.[0]?.chi, createContext(token.chi), {
500
+ ...options,
501
+ isRepeatable: null,
502
+ isList: null,
503
+ occurence: null,
504
+ atLeastOnce: null
505
+ }).valid == SyntaxValidationResult.Valid;
506
+ if (success) {
507
+ context.next();
1213
508
  }
1214
- result = validateSyntax(getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, syntax.val), token.typ == EnumToken.NestingSelectorTokenType ? tokens.slice(1) : tokens, root, options, context);
509
+ break;
1215
510
  }
1216
- // <relative-selector-list>
1217
- // <complex-selector-list>
1218
- else if (['forgiving-selector-list', 'forgiving-relative-selector-list'].includes(syntax.val)) {
511
+ // throw new Error(`Not implemented: ${ValidationTokenEnum[syntax.typ] ?? syntax.typ} : ${renderSyntax(syntax)} : ${renderToken(context.peek() as Token)} : ${JSON.stringify(syntax, null, 1)} | ${JSON.stringify(context.peek(), null, 1)}`);
512
+ }
513
+ if (!success && token.typ == EnumToken.IdenTokenType && allValues.includes(token.val.toLowerCase())) {
514
+ success = true;
515
+ context.next();
516
+ }
517
+ return {
518
+ valid: success ? SyntaxValidationResult.Valid : SyntaxValidationResult.Drop,
519
+ node: context.peek(),
520
+ syntax,
521
+ error: success ? '' : `expected '${ValidationTokenEnum[syntax.typ].toLowerCase()}', got '${renderToken(context.peek())}'`,
522
+ context
523
+ };
524
+ }
525
+ function matchPropertyType(syntax, context, options) {
526
+ if (![
527
+ 'length-percentage', 'flex', 'calc-sum', 'color', 'color-base', 'system-color', 'deprecated-system-color',
528
+ 'pseudo-class-selector', 'pseudo-element-selector'
529
+ ].includes(syntax.val)) {
530
+ if (syntax.val in config["syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */]) {
531
+ return doEvaluateSyntax(getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, syntax.val), context, {
532
+ ...options,
533
+ isRepeatable: null,
534
+ isList: null,
535
+ occurence: null,
536
+ atLeastOnce: null
537
+ });
538
+ }
539
+ }
540
+ let success = true;
541
+ let token = context.peek();
542
+ if ((token?.typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(token.val))) {
543
+ const result = doEvaluateSyntax(getParsedSyntax("functions" /* ValidationSyntaxGroupEnum.Functions */, token.val)?.[0]?.chi ?? [], createContext(token.chi), {
544
+ ...options,
545
+ isRepeatable: null,
546
+ isList: null,
547
+ occurence: null,
548
+ atLeastOnce: null
549
+ });
550
+ if (result.valid == SyntaxValidationResult.Valid) {
551
+ context.next();
552
+ }
553
+ return { ...result, context };
554
+ }
555
+ switch (syntax.val) {
556
+ case 'calc-sum':
557
+ success = (token.typ == EnumToken.FunctionTokenType && mathFuncs.includes(token.val)) ||
1219
558
  // @ts-ignore
1220
- result = { tokens: tokens.slice(), ...validateSelector(tokens, options, root) };
1221
- }
1222
- // https://github.com/mdn/data/pull/186#issuecomment-369604537
1223
- else if (syntax.val.endsWith('-token')) {
1224
- const val = syntax.val;
1225
- valid = true;
1226
- switch (val) {
1227
- case 'function-token':
1228
- valid = token.typ != EnumToken.ParensTokenType && funcLike.includes(token.typ);
1229
- break;
1230
- case 'ident-token':
1231
- valid = token.typ == EnumToken.DashedIdenTokenType || token.typ == EnumToken.IdenTokenType;
1232
- break;
1233
- case 'hash-token':
1234
- valid = token.typ == EnumToken.HashTokenType;
1235
- break;
1236
- case 'string-token':
1237
- valid = token.typ == EnumToken.StringTokenType;
1238
- break;
1239
- default:
1240
- console.error(new Error(`unhandled syntax: '<${val}>'`));
1241
- break;
1242
- }
1243
- result = {
1244
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1245
- matches: valid ? [token] : [],
1246
- node: valid ? null : token,
1247
- syntax,
1248
- error: valid ? '' : 'unexpected token',
1249
- tokens
1250
- };
1251
- }
1252
- else if ('wq-name' == syntax.val) {
1253
- valid = token.typ == EnumToken.IdenTokenType || (token.typ == EnumToken.NameSpaceAttributeTokenType &&
1254
- (token.l == null || token.l.typ == EnumToken.IdenTokenType || (token.l.typ == EnumToken.LiteralTokenType && token.l.val == '*')) &&
1255
- token.r.typ == EnumToken.IdenTokenType);
1256
- result = {
1257
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1258
- matches: valid ? [token] : [],
1259
- node: token,
1260
- syntax,
1261
- error: valid ? '' : 'unexpected token',
1262
- tokens
1263
- };
1264
- }
1265
- else {
1266
- const val = syntax.val;
1267
- // https://github.com/mdn/data/pull/186#issuecomment-369604537
1268
- if (val == 'any-value') {
1269
- return {
1270
- valid: ValidationLevel.Valid,
1271
- matches: [token],
1272
- node: null,
1273
- syntax,
1274
- error: '',
1275
- tokens
1276
- };
1277
- }
1278
- else if (val in config.declarations || val in config.syntaxes) {
1279
- result = validateSyntax(getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, val) ?? getParsedSyntax("declarations" /* ValidationSyntaxGroupEnum.Declarations */, val), tokens, root, options, context);
1280
- }
1281
- else {
1282
- // @ts-ignore
1283
- result = {
1284
- valid: ValidationLevel.Drop,
1285
- matches: [],
1286
- node: token,
1287
- syntax,
1288
- error: 'unexpected token',
1289
- tokens
1290
- };
1291
- }
1292
- }
559
+ (token.typ == EnumToken.IdenTokenType && typeof Math[token.val.toUpperCase()] == 'number') ||
560
+ [EnumToken.BinaryExpressionTokenType, EnumToken.NumberTokenType, EnumToken.PercentageTokenType, EnumToken.DimensionTokenType, EnumToken.LengthTokenType, EnumToken.AngleTokenType, EnumToken.TimeTokenType, EnumToken.ResolutionTokenType, EnumToken.FrequencyTokenType].includes(token.typ);
1293
561
  break;
1294
- case ValidationTokenEnum.Parens:
1295
- case ValidationTokenEnum.Function:
1296
- if (syntax.typ == ValidationTokenEnum.Parens) {
1297
- valid = token.typ == EnumToken.ParensTokenType;
1298
- }
1299
- else {
1300
- valid = 'chi' in token && 'val' in token &&
1301
- token.val.localeCompare(syntax.val, 'en', { sensitivity: 'base' }) == 0;
562
+ case 'declaration-value':
563
+ while (!context.done()) {
564
+ context.next();
1302
565
  }
1303
- result = !valid ?
1304
- // @ts-ignore
1305
- {
1306
- valid: ValidationLevel.Drop,
1307
- matches: [],
1308
- node: token,
1309
- syntax,
1310
- error: 'unexpected token',
1311
- tokens
1312
- } : validateSyntax(syntax.chi, token.chi, root, options, {
1313
- ...context,
1314
- tokens: null,
1315
- level: context.level + 1
1316
- });
566
+ success = true;
1317
567
  break;
1318
- case ValidationTokenEnum.ValidationFunctionDefinition:
1319
- valid = 'val' in token && 'chi' in token;
1320
- result = {
1321
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1322
- matches: valid ? [token] : [],
1323
- node: valid ? null : token,
1324
- syntax,
1325
- error: '',
1326
- tokens
1327
- };
1328
- if (result.valid == ValidationLevel.Valid) {
1329
- valid = token.val.localeCompare(syntax.val, 'en', { sensitivity: 'base' }) == 0;
1330
- result = {
1331
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1332
- matches: valid ? [token] : [],
1333
- node: valid ? null : token,
1334
- error: '',
1335
- syntax,
1336
- tokens
1337
- };
1338
- if (result.valid == ValidationLevel.Valid) {
1339
- const s = getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, syntax.val + '()'); // config[ValidationSyntaxGroupEnum.Syntaxes][(syntaxes as ValidationFunctionDefinitionToken).val + '()'] as ValidationSyntaxNode;
1340
- result = validateSyntax(s, tokens, root, options, context);
1341
- }
1342
- }
568
+ case 'url-token':
569
+ success = token.typ == EnumToken.UrlTokenTokenType || token.typ == EnumToken.StringTokenType || (token.typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(token.val));
1343
570
  break;
1344
- case ValidationTokenEnum.Bracket:
1345
- result = validateSyntax(syntax.chi, tokens, root, options, context);
571
+ case 'ident':
572
+ case 'ident-token':
573
+ case 'custom-ident':
574
+ success = token.typ == EnumToken.IdenTokenType || token.typ == EnumToken.DashedIdenTokenType || isIdentColor(token);
1346
575
  break;
1347
- case ValidationTokenEnum.PipeToken:
1348
- for (const lines of syntax.chi) {
1349
- result = validateSyntax(lines, tokens, root, options, context);
1350
- if (result.valid == ValidationLevel.Valid) {
1351
- break;
1352
- }
1353
- }
576
+ case 'dashed-ident':
577
+ case 'custom-property-name':
578
+ success = token.typ == EnumToken.DashedIdenTokenType;
1354
579
  break;
1355
- case ValidationTokenEnum.AmpersandToken:
1356
- children = [...syntax.l.slice(), ...syntax.r.slice()];
1357
- matches = [];
1358
- queue = [];
1359
- let m = [];
1360
- for (let j = 0; j < children.length; j++) {
1361
- const res = validateSyntax([children[j]], tokens, root, options, context);
1362
- // @ts-ignore
1363
- if (res.valid == ValidationLevel.Valid) {
1364
- m.push(...res.matches);
1365
- matches.push(...children.splice(j, 1));
1366
- j = 0;
1367
- // @ts-ignore
1368
- astNodes.delete(token);
1369
- consumeToken(tokens);
1370
- token = tokens[0];
1371
- if (token == null) {
1372
- break;
1373
- }
1374
- // @ts-ignore
1375
- astNodes.add(token);
1376
- }
580
+ case 'system-color':
581
+ success = (token.typ == EnumToken.ColorTokenType && token.kin == ColorKind.SYS) || (token.typ == EnumToken.IdenTokenType && token.val.localeCompare('currentcolor', 'en', { sensitivity: 'base' }) == 0) || (token.typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(token.val));
582
+ break;
583
+ case 'deprecated-system-color':
584
+ success = (token.typ == EnumToken.ColorTokenType && token.kin == ColorKind.DPSYS) || (token.typ == EnumToken.IdenTokenType && token.val.localeCompare('currentcolor', 'en', { sensitivity: 'base' }) == 0) || (token.typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(token.val));
585
+ break;
586
+ case 'color':
587
+ case 'color-base':
588
+ success = token.typ == EnumToken.ColorTokenType || (token.typ == EnumToken.IdenTokenType && token.val.localeCompare('currentcolor', 'en', { sensitivity: 'base' }) == 0) || (token.typ == EnumToken.IdenTokenType && token.val.localeCompare('transparent', 'en', { sensitivity: 'base' }) == 0) || (token.typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(token.val));
589
+ if (!success && token.typ == EnumToken.FunctionTokenType && colorsFunc.includes(token.val)) {
590
+ success = doEvaluateSyntax(getParsedSyntax("functions" /* ValidationSyntaxGroupEnum.Functions */, token.val)?.[0]?.chi, createContext(token.chi), {
591
+ ...options,
592
+ isRepeatable: null,
593
+ isList: null,
594
+ occurence: null,
595
+ atLeastOnce: null
596
+ }).valid == SyntaxValidationResult.Valid;
1377
597
  }
1378
- if (astNodes.size > 0) {
1379
- // @ts-ignore
1380
- tokens.unshift(...astNodes);
1381
- astNodes = new Set();
598
+ break;
599
+ case 'hex-color':
600
+ success = (token.typ == EnumToken.ColorTokenType && token.kin == ColorKind.HEX) || (token.typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(token.val));
601
+ break;
602
+ case 'integer':
603
+ success = (token.typ == EnumToken.NumberTokenType && Number.isInteger(+(token.val))) || (token.typ == EnumToken.FunctionTokenType && token.val == 'calc');
604
+ if ('range' in syntax) {
605
+ success = success && +token.val >= +syntax.range[0] && +token.val <= +syntax.range[1];
1382
606
  }
1383
- valid = matches.length > 0;
1384
- result = {
1385
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1386
- matches: m,
1387
- node: valid ? null : token,
1388
- syntax,
1389
- error: valid ? '' : 'expecting token',
1390
- tokens
1391
- };
1392
607
  break;
1393
- case ValidationTokenEnum.ColumnArrayToken:
1394
- {
1395
- matches = [];
1396
- queue = [];
1397
- const children = syntax.chi;
1398
- let child;
1399
- while (child = children.shift()) {
1400
- result = validateSyntax([child], tokens, root, options, context);
1401
- if (result.valid == ValidationLevel.Valid) {
1402
- matches.push(child);
1403
- consumeToken(tokens);
1404
- token = tokens[0];
1405
- if (queue.length > 0) {
1406
- children.unshift(...queue);
1407
- queue = [];
1408
- }
1409
- if (token == null) {
1410
- break;
1411
- }
1412
- }
1413
- else {
1414
- queue.push(child);
1415
- }
1416
- }
1417
- valid = matches.length > 0;
1418
- result = {
1419
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1420
- matches: valid ? [token] : [],
1421
- node: valid ? null : token,
1422
- syntax,
1423
- error: valid ? '' : 'expecting token',
1424
- tokens
1425
- };
608
+ case 'dimension':
609
+ success = [
610
+ EnumToken.DimensionTokenType,
611
+ EnumToken.LengthTokenType,
612
+ EnumToken.AngleTokenType,
613
+ EnumToken.TimeTokenType,
614
+ EnumToken.ResolutionTokenType,
615
+ EnumToken.FrequencyTokenType
616
+ ].includes(token.typ) || (token.typ == EnumToken.FunctionTokenType && token.val == 'calc');
617
+ break;
618
+ case 'flex':
619
+ success = token.typ == EnumToken.FlexTokenType || (token.typ == EnumToken.FunctionTokenType && token.val == 'calc');
620
+ break;
621
+ case 'number':
622
+ case 'number-token':
623
+ success = token.typ == EnumToken.NumberTokenType;
624
+ if ('range' in syntax) {
625
+ success = success && +token.val >= +syntax.range[0] && +token.val <= +syntax.range[1];
1426
626
  }
1427
627
  break;
1428
- case ValidationTokenEnum.ColumnToken:
1429
- children = [...syntax.l.slice(), ...syntax.r.slice()];
1430
- matches = [];
1431
- queue = [];
1432
- while ((child = children.shift())) {
1433
- const res = validateSyntax([child], tokens, root, options, context);
1434
- if (res.valid == ValidationLevel.Valid) {
1435
- matches.push(child);
1436
- consumeToken(tokens);
1437
- token = tokens[0];
1438
- if (queue.length > 0) {
1439
- children.unshift(...queue);
1440
- queue = [];
1441
- }
1442
- if (token == null) {
1443
- break;
1444
- }
1445
- }
1446
- else {
1447
- queue.push(child);
628
+ case 'angle':
629
+ success = token.typ == EnumToken.AngleTokenType || (token.typ == EnumToken.NumberTokenType && token.val == '0') || (token.typ == EnumToken.FunctionTokenType && token.val == 'calc');
630
+ break;
631
+ case 'length':
632
+ success = token.typ == EnumToken.LengthTokenType || (token.typ == EnumToken.NumberTokenType && token.val == '0') || (token.typ == EnumToken.FunctionTokenType && token.val == 'calc');
633
+ break;
634
+ case 'percentage':
635
+ success = token.typ == EnumToken.PercentageTokenType || (token.typ == EnumToken.NumberTokenType && token.val == '0') || (token.typ == EnumToken.FunctionTokenType && token.val == 'calc');
636
+ break;
637
+ case 'length-percentage':
638
+ success = token.typ == EnumToken.LengthTokenType || token.typ == EnumToken.PercentageTokenType || (token.typ == EnumToken.NumberTokenType && token.val == '0') || (token.typ == EnumToken.FunctionTokenType && token.val == 'calc');
639
+ break;
640
+ case 'resolution':
641
+ success = token.typ == EnumToken.ResolutionTokenType || token.typ == EnumToken.PercentageTokenType || (token.typ == EnumToken.NumberTokenType && token.val == '0') || (token.typ == EnumToken.FunctionTokenType && token.val == 'calc');
642
+ break;
643
+ case 'hash-token':
644
+ success = token.typ == EnumToken.HashTokenType;
645
+ break;
646
+ case 'string':
647
+ success = token.typ == EnumToken.StringTokenType || token.typ == EnumToken.IdenTokenType || (token.typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(token.val));
648
+ break;
649
+ case 'time':
650
+ success = token.typ == EnumToken.TimeTokenType || (token.typ == EnumToken.FunctionTokenType && token.val == 'calc');
651
+ break;
652
+ case 'zero':
653
+ success = token.val == '0' || (token.typ == EnumToken.FunctionTokenType && token.val == 'calc');
654
+ break;
655
+ case 'pseudo-element-selector':
656
+ success = token.typ == EnumToken.PseudoElementTokenType;
657
+ break;
658
+ case 'pseudo-class-selector':
659
+ success = token.typ == EnumToken.PseudoClassTokenType || token.typ == EnumToken.PseudoClassFuncTokenType;
660
+ if (success) {
661
+ success = token.val + (token.typ == EnumToken.PseudoClassTokenType ? '' : '()') in config["selectors" /* ValidationSyntaxGroupEnum.Selectors */];
662
+ if (success && token.typ == EnumToken.PseudoClassFuncTokenType) {
663
+ success = doEvaluateSyntax(getParsedSyntax("selectors" /* ValidationSyntaxGroupEnum.Selectors */, token.val + '()')?.[0]?.chi ?? [], createContext(token.chi), {
664
+ ...options,
665
+ isRepeatable: null,
666
+ isList: null,
667
+ occurence: null,
668
+ atLeastOnce: null
669
+ }).valid == SyntaxValidationResult.Valid;
1448
670
  }
1449
671
  }
1450
- valid = matches.length > 0;
1451
- result = {
1452
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1453
- matches: valid ? [token] : [],
1454
- node: valid ? null : token,
1455
- syntax,
1456
- error: valid ? '' : 'expecting token',
1457
- tokens
1458
- };
1459
672
  break;
1460
- case ValidationTokenEnum.StringToken:
1461
- valid = token.typ == EnumToken.StringTokenType && syntax.val.slice(1, -1) == token.val.slice(1, -1);
1462
- return {
1463
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1464
- matches: valid ? [token] : [],
1465
- node: valid ? null : token,
1466
- syntax,
1467
- error: valid ? '' : 'expecting token',
1468
- tokens
1469
- };
1470
- case ValidationTokenEnum.PseudoClassFunctionToken:
1471
- valid = token.typ == EnumToken.PseudoClassFuncTokenType;
1472
- if (valid) {
1473
- let key = token.val in config.selectors ? token.val : token.val + '()';
1474
- const s = getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, key);
1475
- valid = s != null && validateSyntax(s, token.chi, root, options, {
1476
- ...context,
1477
- tokens: null,
1478
- level: context.level + 1
1479
- }).valid == ValidationLevel.Valid;
673
+ // default:
674
+ //
675
+ // throw new Error(`Not implemented: ${ValidationTokenEnum[syntax.typ] ?? syntax.typ} : ${renderSyntax(syntax)}\n${JSON.stringify(syntax, null, 1)}`);
676
+ }
677
+ if (!success &&
678
+ token.typ == EnumToken.FunctionTokenType &&
679
+ ['length-percentage', 'length', 'number', 'number-token', 'angle', 'percentage', 'dimension'].includes(syntax.val)) {
680
+ if (!success) {
681
+ success = mathFuncs.includes(token.val.toLowerCase()) &&
682
+ // (token as FunctionToken).val + '()' in config[ValidationSyntaxGroupEnum.Syntaxes] &&
683
+ doEvaluateSyntax(getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, token.val + '()')?.[0]?.chi ?? [], createContext(token.chi), {
684
+ ...options,
685
+ isRepeatable: null,
686
+ isList: null,
687
+ occurence: null,
688
+ atLeastOnce: null
689
+ }).valid == SyntaxValidationResult.Valid;
690
+ }
691
+ }
692
+ if (!success && token.typ == EnumToken.IdenTokenType) {
693
+ success = allValues.includes(token.val.toLowerCase());
694
+ }
695
+ if (success) {
696
+ context.next();
697
+ }
698
+ return {
699
+ valid: success ? SyntaxValidationResult.Valid : SyntaxValidationResult.Drop,
700
+ node: token,
701
+ syntax,
702
+ error: success ? '' : `expected '${syntax.val}', got ${renderToken(token)}`,
703
+ context
704
+ };
705
+ }
706
+ function someOf(syntaxes, context, options) {
707
+ let result;
708
+ let i;
709
+ let success = false;
710
+ const matched = [];
711
+ for (i = 0; i < syntaxes.length; i++) {
712
+ if (context.peek()?.typ == EnumToken.WhitespaceTokenType) {
713
+ context.next();
714
+ }
715
+ result = doEvaluateSyntax(syntaxes[i], context.clone(), options);
716
+ if (result.valid == SyntaxValidationResult.Valid) {
717
+ success = true;
718
+ if (result.context.done()) {
719
+ return result;
1480
720
  }
1481
- result = {
1482
- valid: valid ? ValidationLevel.Valid : ValidationLevel.Drop,
1483
- matches: valid ? [token] : [],
1484
- node: valid ? null : token,
1485
- syntax,
1486
- error: valid ? '' : 'invalid token',
1487
- tokens
1488
- };
1489
- break;
1490
- case ValidationTokenEnum.DeclarationDefinitionToken:
1491
- if (token.typ != EnumToken.DeclarationNodeType || token.nam != syntax.nam) {
1492
- return {
1493
- valid: ValidationLevel.Drop,
1494
- matches: [],
1495
- node: token,
1496
- syntax,
1497
- error: '',
1498
- tokens
1499
- };
721
+ matched.push(result);
722
+ }
723
+ }
724
+ if (matched.length > 0) {
725
+ // pick the best match
726
+ matched.sort((a, b) => a.context.done() ? -1 : b.context.done() ? 1 : b.context.index - a.context.index);
727
+ }
728
+ return matched[0] ?? {
729
+ valid: SyntaxValidationResult.Drop,
730
+ node: context.current(),
731
+ syntax: null,
732
+ error: success ? '' : `could not match someOf: ${syntaxes.reduce((acc, curr) => acc + (acc.length > 0 ? ' | ' : '') + curr.reduce((acc, curr) => acc + renderSyntax(curr), ''), '')}`,
733
+ context
734
+ };
735
+ }
736
+ function anyOf(syntaxes, context, options) {
737
+ let result;
738
+ let i;
739
+ let success = false;
740
+ for (i = 0; i < syntaxes.length; i++) {
741
+ result = doEvaluateSyntax(syntaxes[i], context.clone(), options);
742
+ if (result.valid == SyntaxValidationResult.Valid) {
743
+ success = true;
744
+ context.update(result.context);
745
+ if (result.context.done()) {
746
+ return result;
1500
747
  }
1501
- return validateSyntax([syntax.val], token.val, root, options, context);
1502
- default:
1503
- throw new Error('not implemented: ' + JSON.stringify({ syntax, token, tokens }, null, 1));
748
+ syntaxes.splice(i, 1);
749
+ i = -1;
750
+ }
751
+ }
752
+ return {
753
+ valid: success ? SyntaxValidationResult.Valid : SyntaxValidationResult.Drop,
754
+ node: context.current(),
755
+ syntax: null,
756
+ error: success ? '' : `could not match anyOf: ${syntaxes.reduce((acc, curr) => acc + '[' + curr.reduce((acc, curr) => acc + renderSyntax(curr), '') + ']', '')}`,
757
+ context
758
+ };
759
+ }
760
+ function allOf(syntax, context, options) {
761
+ let result;
762
+ let i;
763
+ // sort tokens -> wildCard -> last
764
+ // 1px var(...) 2px => 1px 2px var(...)
765
+ const slice = context.slice();
766
+ const vars = [];
767
+ const tokens = [];
768
+ for (i = 0; i < slice.length; i++) {
769
+ if (slice[i].typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(slice[i].val.toLowerCase())) {
770
+ vars.push(slice[i]);
771
+ slice.splice(i, 1);
772
+ if (slice[i]?.typ == EnumToken.WhitespaceTokenType) {
773
+ vars.push(slice[i]);
774
+ slice.splice(i, 1);
775
+ if (i > 0) {
776
+ i--;
777
+ }
778
+ }
779
+ if (i > 0) {
780
+ i--;
781
+ }
782
+ continue;
783
+ }
784
+ if (slice[i].typ == EnumToken.CommaTokenType || (slice[i].typ == EnumToken.LiteralTokenType && slice[i].val == '/')) {
785
+ tokens.push(...vars);
786
+ vars.length = 0;
787
+ }
788
+ tokens.push(slice[i]);
789
+ }
790
+ if (vars.length > 0) {
791
+ tokens.push(...vars);
792
+ }
793
+ const con = createContext(tokens);
794
+ for (i = 0; i < syntax.length; i++) {
795
+ result = doEvaluateSyntax(syntax[i], con.clone(), options);
796
+ if (result.valid == SyntaxValidationResult.Valid) {
797
+ con.update(result.context);
798
+ syntax.splice(i, 1);
799
+ i = -1;
800
+ }
801
+ }
802
+ const success = syntax.length == 0;
803
+ return {
804
+ valid: success ? SyntaxValidationResult.Valid : SyntaxValidationResult.Drop,
805
+ node: context.current(),
806
+ syntax: syntax?.[0]?.[0] ?? null,
807
+ error: `could not match allOf: ${syntax.reduce((acc, curr) => acc + '[' + curr.reduce((acc, curr) => acc + renderSyntax(curr), '') + ']', '')}`,
808
+ context: success ? con : context
809
+ };
810
+ }
811
+ function flatten(syntax) {
812
+ const stack = [syntax.l, syntax.r];
813
+ const data = [];
814
+ let s;
815
+ let i = 0;
816
+ for (; i < stack.length; i++) {
817
+ if (stack[i].length == 1 && stack[i][0].typ == syntax.typ) {
818
+ s = stack[i][0];
819
+ stack.splice(i--, 1, s.l, s.r);
820
+ }
821
+ else {
822
+ data.push(stack[i]);
823
+ }
1504
824
  }
1505
- // @ts-ignore
1506
- return result;
825
+ return data;
1507
826
  }
1508
827
 
1509
- export { validateSyntax };
828
+ export { createContext, doEvaluateSyntax, evaluateSyntax };