@tbela99/css-parser 1.3.3 → 1.4.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 (56) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +64 -48
  3. package/dist/config.json.js +3 -0
  4. package/dist/index-umd-web.js +2266 -631
  5. package/dist/index.cjs +2271 -620
  6. package/dist/index.d.ts +522 -181
  7. package/dist/lib/ast/expand.js +5 -10
  8. package/dist/lib/ast/features/calc.js +3 -2
  9. package/dist/lib/ast/features/inlinecssvariables.js +5 -3
  10. package/dist/lib/ast/features/prefix.js +1 -1
  11. package/dist/lib/ast/features/shorthand.js +1 -0
  12. package/dist/lib/ast/features/transform.js +13 -19
  13. package/dist/lib/ast/features/type.js +1 -1
  14. package/dist/lib/ast/minify.js +6 -3
  15. package/dist/lib/ast/transform/compute.js +2 -4
  16. package/dist/lib/ast/transform/matrix.js +20 -20
  17. package/dist/lib/ast/transform/minify.js +105 -12
  18. package/dist/lib/ast/transform/rotate.js +11 -11
  19. package/dist/lib/ast/transform/scale.js +6 -6
  20. package/dist/lib/ast/transform/skew.js +4 -4
  21. package/dist/lib/ast/transform/translate.js +3 -3
  22. package/dist/lib/ast/transform/utils.js +30 -37
  23. package/dist/lib/ast/types.js +76 -5
  24. package/dist/lib/ast/walk.js +77 -58
  25. package/dist/lib/fs/resolve.js +69 -10
  26. package/dist/lib/parser/declaration/list.js +6 -1
  27. package/dist/lib/parser/parse.js +1169 -312
  28. package/dist/lib/parser/tokenize.js +33 -20
  29. package/dist/lib/parser/utils/declaration.js +54 -0
  30. package/dist/lib/parser/utils/hash.js +86 -0
  31. package/dist/lib/parser/utils/text.js +8 -0
  32. package/dist/lib/renderer/render.js +26 -7
  33. package/dist/lib/syntax/color/relativecolor.js +0 -3
  34. package/dist/lib/syntax/syntax.js +36 -18
  35. package/dist/lib/validation/at-rules/container.js +11 -0
  36. package/dist/lib/validation/at-rules/counter-style.js +11 -0
  37. package/dist/lib/validation/at-rules/font-feature-values.js +11 -0
  38. package/dist/lib/validation/at-rules/keyframes.js +11 -0
  39. package/dist/lib/validation/at-rules/layer.js +11 -0
  40. package/dist/lib/validation/at-rules/media.js +11 -0
  41. package/dist/lib/validation/at-rules/page-margin-box.js +11 -0
  42. package/dist/lib/validation/at-rules/page.js +11 -0
  43. package/dist/lib/validation/at-rules/supports.js +11 -0
  44. package/dist/lib/validation/at-rules/when.js +11 -0
  45. package/dist/lib/validation/config.js +0 -2
  46. package/dist/lib/validation/config.json.js +36 -4
  47. package/dist/lib/validation/parser/parse.js +53 -2
  48. package/dist/lib/validation/syntax.js +204 -36
  49. package/dist/lib/validation/syntaxes/compound-selector.js +1 -2
  50. package/dist/lib/validation/syntaxes/relative-selector-list.js +2 -5
  51. package/dist/node.js +60 -18
  52. package/dist/types.d.ts +17 -0
  53. package/dist/types.js +20 -0
  54. package/dist/web.js +43 -17
  55. package/package.json +20 -17
  56. package/dist/lib/validation/parser/types.js +0 -54
@@ -138,23 +138,27 @@ function* consumeString(quoteStr, buffer, parseInfo) {
138
138
  }
139
139
  }
140
140
  function match(parseInfo, input) {
141
- return parseInfo.stream.slice(parseInfo.currentPosition.ind + 1, parseInfo.currentPosition.ind + input.length + 1) == input;
141
+ const position = parseInfo.currentPosition.ind - parseInfo.offset;
142
+ return parseInfo.stream.slice(position + 1, position + input.length + 1) == input;
142
143
  }
143
144
  function peek(parseInfo, count = 1) {
144
145
  if (count == 1) {
145
- return parseInfo.stream.charAt(parseInfo.currentPosition.ind + 1);
146
+ return parseInfo.stream.charAt(parseInfo.currentPosition.ind - parseInfo.offset + 1);
146
147
  }
147
- return parseInfo.stream.slice(parseInfo.currentPosition.ind + 1, parseInfo.currentPosition.ind + count + 1);
148
+ const position = parseInfo.currentPosition.ind - parseInfo.offset;
149
+ return parseInfo.stream.slice(position + 1, position + count + 1);
148
150
  }
149
151
  function prev(parseInfo) {
150
- return parseInfo.stream.charAt(parseInfo.currentPosition.ind - 1);
152
+ return parseInfo.offset == parseInfo.currentPosition.ind ? parseInfo.buffer.slice(-1) : parseInfo.stream.charAt(parseInfo.currentPosition.ind - parseInfo.offset - 1);
151
153
  }
152
154
  function next(parseInfo, count = 1) {
153
155
  let char = '';
154
156
  let chr = '';
155
- while (count-- && (chr = parseInfo.stream.charAt(parseInfo.currentPosition.ind + 1))) {
157
+ let position = parseInfo.currentPosition.ind - parseInfo.offset;
158
+ while (count-- && (chr = parseInfo.stream.charAt(position + 1))) {
156
159
  char += chr;
157
- const codepoint = parseInfo.stream.charCodeAt(++parseInfo.currentPosition.ind);
160
+ const codepoint = parseInfo.stream.charCodeAt(++position);
161
+ ++parseInfo.currentPosition.ind;
158
162
  if (isNewLine(codepoint)) {
159
163
  parseInfo.currentPosition.lin++;
160
164
  parseInfo.currentPosition.col = 0;
@@ -182,13 +186,15 @@ function* tokenize(parseInfo, yieldEOFToken = true) {
182
186
  yield pushToken(buffer, parseInfo);
183
187
  buffer = '';
184
188
  }
189
+ buffer += value;
185
190
  while (value = next(parseInfo)) {
186
191
  charCode = value.charCodeAt(0);
187
192
  if (!isWhiteSpace(charCode)) {
188
193
  break;
189
194
  }
195
+ buffer += value;
190
196
  }
191
- yield pushToken('', parseInfo, EnumToken.WhitespaceTokenType);
197
+ yield pushToken(buffer, parseInfo, EnumToken.WhitespaceTokenType);
192
198
  buffer = '';
193
199
  }
194
200
  switch (charCode) {
@@ -236,8 +242,7 @@ function* tokenize(parseInfo, yieldEOFToken = true) {
236
242
  buffer = '';
237
243
  }
238
244
  if (match(parseInfo, '=')) {
239
- yield pushToken('', parseInfo, EnumToken.LteTokenType);
240
- next(parseInfo);
245
+ yield pushToken(value + next(parseInfo), parseInfo, EnumToken.LteTokenType);
241
246
  break;
242
247
  }
243
248
  buffer += value;
@@ -292,8 +297,7 @@ function* tokenize(parseInfo, yieldEOFToken = true) {
292
297
  }
293
298
  if (charCode == 124 /* TokenMap.PIPE */) {
294
299
  if (match(parseInfo, '|')) {
295
- next(parseInfo);
296
- yield pushToken('', parseInfo, EnumToken.ColumnCombinatorTokenType);
300
+ yield pushToken(value + next(parseInfo), parseInfo, EnumToken.ColumnCombinatorTokenType);
297
301
  }
298
302
  else if (match(parseInfo, '=')) {
299
303
  buffer += next(parseInfo);
@@ -347,11 +351,10 @@ function* tokenize(parseInfo, yieldEOFToken = true) {
347
351
  buffer = '';
348
352
  }
349
353
  if (match(parseInfo, '=')) {
350
- yield pushToken('', parseInfo, EnumToken.GteTokenType);
351
- next(parseInfo);
354
+ yield pushToken(value + next(parseInfo), parseInfo, EnumToken.GteTokenType);
352
355
  }
353
356
  else {
354
- yield pushToken('', parseInfo, EnumToken.GtTokenType);
357
+ yield pushToken(value, parseInfo, EnumToken.GtTokenType);
355
358
  }
356
359
  consumeWhiteSpace(parseInfo);
357
360
  break;
@@ -402,7 +405,7 @@ function* tokenize(parseInfo, yieldEOFToken = true) {
402
405
  yield pushToken(buffer, parseInfo);
403
406
  buffer = '';
404
407
  }
405
- yield pushToken('', parseInfo, EnumToken.EndParensTokenType);
408
+ yield pushToken(value, parseInfo, EnumToken.EndParensTokenType);
406
409
  break;
407
410
  case 40 /* TokenMap.OPEN_PAREN */:
408
411
  if (buffer.length == 0) {
@@ -468,7 +471,7 @@ function* tokenize(parseInfo, yieldEOFToken = true) {
468
471
  // ')'
469
472
  if (charCode == 0x29) {
470
473
  yield pushToken(buffer, parseInfo, hasNewLine ? EnumToken.BadStringTokenType : EnumToken.StringTokenType);
471
- yield pushToken('', parseInfo, EnumToken.EndParensTokenType);
474
+ yield pushToken(value, parseInfo, EnumToken.EndParensTokenType);
472
475
  buffer = '';
473
476
  break;
474
477
  }
@@ -492,7 +495,7 @@ function* tokenize(parseInfo, yieldEOFToken = true) {
492
495
  charCode = value.charCodeAt(0);
493
496
  if (charCode == 0x29) { // ')'
494
497
  yield pushToken(buffer, parseInfo, EnumToken.UrlTokenTokenType);
495
- yield pushToken('', parseInfo, EnumToken.EndParensTokenType);
498
+ yield pushToken(value, parseInfo, EnumToken.EndParensTokenType);
496
499
  buffer = '';
497
500
  break;
498
501
  }
@@ -526,8 +529,7 @@ function* tokenize(parseInfo, yieldEOFToken = true) {
526
529
  buffer = '';
527
530
  }
528
531
  if (match(parseInfo, 'important')) {
529
- yield pushToken('', parseInfo, EnumToken.ImportantTokenType);
530
- next(parseInfo, 9);
532
+ yield pushToken(value + next(parseInfo, 9), parseInfo, EnumToken.ImportantTokenType);
531
533
  buffer = '';
532
534
  break;
533
535
  }
@@ -572,6 +574,7 @@ async function* tokenizeStream(input) {
572
574
  const parseInfo = {
573
575
  stream: '',
574
576
  buffer: '',
577
+ offset: 0,
575
578
  position: { ind: 0, lin: 1, col: 1 },
576
579
  currentPosition: { ind: -1, lin: 1, col: 0 }
577
580
  };
@@ -579,7 +582,17 @@ async function* tokenizeStream(input) {
579
582
  const reader = input.getReader();
580
583
  while (true) {
581
584
  const { done, value } = await reader.read();
582
- parseInfo.stream += ArrayBuffer.isView(value) ? decoder.decode(value, { stream: true }) : value;
585
+ const stream = ArrayBuffer.isView(value) ? decoder.decode(value, { stream: true }) : value;
586
+ if (!done) {
587
+ if (parseInfo.stream.length > 2) {
588
+ parseInfo.stream = parseInfo.stream.slice(-2) + stream;
589
+ parseInfo.offset = parseInfo.currentPosition.ind - 1;
590
+ }
591
+ else {
592
+ parseInfo.stream = stream;
593
+ parseInfo.offset = Math.max(0, parseInfo.currentPosition.ind);
594
+ }
595
+ }
583
596
  yield* tokenize(parseInfo, done);
584
597
  if (done) {
585
598
  break;
@@ -20,6 +20,60 @@ function parseDeclarationNode(node, errors, location) {
20
20
  });
21
21
  return null;
22
22
  }
23
+ if ('composes' == node.nam.toLowerCase()) {
24
+ let left = [];
25
+ let right = [];
26
+ let current = 0;
27
+ let hasFrom = 0;
28
+ for (; current < node.val.length; current++) {
29
+ if (EnumToken.WhitespaceTokenType == node.val[current].typ || EnumToken.CommentTokenType == node.val[current].typ) {
30
+ if (!hasFrom) {
31
+ left.push(node.val[current]);
32
+ }
33
+ else {
34
+ right.push(node.val[current]);
35
+ }
36
+ continue;
37
+ }
38
+ if (EnumToken.IdenTokenType == node.val[current].typ || EnumToken.DashedIdenTokenType == node.val[current].typ || EnumToken.StringTokenType == node.val[current].typ) {
39
+ if (EnumToken.IdenTokenType == node.val[current].typ) {
40
+ if ('from' == node.val[current].val) {
41
+ if (hasFrom) {
42
+ return null;
43
+ }
44
+ hasFrom++;
45
+ continue;
46
+ }
47
+ }
48
+ if (hasFrom) {
49
+ right.push(node.val[current]);
50
+ }
51
+ else {
52
+ left.push(node.val[current]);
53
+ }
54
+ continue;
55
+ }
56
+ break;
57
+ }
58
+ if (hasFrom <= 1 && current > 0) {
59
+ if (hasFrom == 0) {
60
+ node.val.splice(0, left.length, {
61
+ typ: EnumToken.ComposesSelectorNodeType,
62
+ l: left,
63
+ r: null
64
+ });
65
+ }
66
+ else {
67
+ node.val.splice(0, current, {
68
+ typ: EnumToken.ComposesSelectorNodeType,
69
+ l: left,
70
+ r: right.reduce((a, b) => {
71
+ return a == null ? b : b.typ == EnumToken.WhitespaceTokenType || b.typ == EnumToken.CommentTokenType ? a : b;
72
+ }, null)
73
+ });
74
+ }
75
+ }
76
+ }
23
77
  for (const { value: val, parent } of walkValues(node.val, node)) {
24
78
  if (val.typ == EnumToken.AttrTokenType && val.chi.every((t) => [EnumToken.IdenTokenType, EnumToken.WhitespaceTokenType, EnumToken.CommentTokenType].includes(t.typ))) {
25
79
  // @ts-ignore
@@ -0,0 +1,86 @@
1
+ // Alphabet: a-z, A-Z, 0-9, _, -
2
+ const LOWER = "abcdefghijklmnopqrstuvwxyz";
3
+ const DIGITS = "0123456789";
4
+ const FULL_ALPHABET = (LOWER + DIGITS).split(""); // 64 chars
5
+ const FIRST_ALPHABET = (LOWER).split(""); // 54 chars (no digits)
6
+ /**
7
+ * supported hash algorithms
8
+ */
9
+ const hashAlgorithms = ['hex', 'base64', 'base64url', 'sha1', 'sha256', 'sha384', 'sha512'];
10
+ // simple deterministic hash → number
11
+ function hashCode(str) {
12
+ let hash = 0;
13
+ let l = str.length;
14
+ let i = 0;
15
+ while (i < l) {
16
+ hash = (hash * 31 + str.charCodeAt(i++)) >>> 0;
17
+ }
18
+ return hash;
19
+ }
20
+ /**
21
+ * generate a hash id
22
+ * @param input
23
+ * @param length
24
+ */
25
+ function hashId(input, length = 6) {
26
+ let n = hashCode(input);
27
+ const chars = [];
28
+ // First character: must not be a digit
29
+ chars.push(FIRST_ALPHABET[n % FIRST_ALPHABET.length]);
30
+ // Remaining characters
31
+ for (let i = 1; i < length; i++) {
32
+ n = (n + chars.length + i) % FULL_ALPHABET.length;
33
+ chars.push(FULL_ALPHABET[n]);
34
+ }
35
+ return chars.join("");
36
+ }
37
+ /**
38
+ * convert input to hex
39
+ * @param input
40
+ */
41
+ function toHex(input) {
42
+ let result = '';
43
+ if (input instanceof ArrayBuffer || ArrayBuffer.isView(input)) {
44
+ for (const byte of Array.from(new Uint8Array(input))) {
45
+ result += byte.toString(16).padStart(2, '0');
46
+ }
47
+ }
48
+ else {
49
+ for (const char of String(input)) {
50
+ result += char.charCodeAt(0).toString(16).padStart(2, '0');
51
+ }
52
+ }
53
+ return result;
54
+ }
55
+ /**
56
+ * generate a hash
57
+ * @param input
58
+ * @param length
59
+ * @param algo
60
+ */
61
+ async function hash(input, length = 6, algo) {
62
+ let result;
63
+ if (algo != null) {
64
+ switch (algo) {
65
+ case 'hex':
66
+ return toHex(input).slice(0, length);
67
+ case 'base64':
68
+ case 'base64url':
69
+ result = btoa(input);
70
+ if (algo == 'base64url') {
71
+ result = result.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
72
+ }
73
+ return result.slice(0, length);
74
+ case 'sha1':
75
+ case 'sha256':
76
+ case 'sha384':
77
+ case 'sha512':
78
+ return toHex(await crypto.subtle.digest(algo.replace('sha', 'SHA-'), new TextEncoder().encode(input))).slice(0, length);
79
+ default:
80
+ throw new Error(`Unsupported hash algorithm: ${algo}`);
81
+ }
82
+ }
83
+ return hashId(input, length);
84
+ }
85
+
86
+ export { hash, hashAlgorithms, hashId };
@@ -0,0 +1,8 @@
1
+ function dasherize(value) {
2
+ return value.replace(/([A-Z])/g, (all, one) => `-${one.toLowerCase()}`);
3
+ }
4
+ function camelize(value) {
5
+ return value.replace(/-([a-z])/g, (all, one) => one.toUpperCase());
6
+ }
7
+
8
+ export { camelize, dasherize };
@@ -29,9 +29,10 @@ function update(position, str) {
29
29
  * render ast
30
30
  * @param data
31
31
  * @param options
32
+ * @param mapping
32
33
  * @private
33
34
  */
34
- function doRender(data, options = {}) {
35
+ function doRender(data, options = {}, mapping) {
35
36
  const minify = options.minify ?? true;
36
37
  const beautify = options.beautify ?? !minify;
37
38
  options = {
@@ -68,12 +69,23 @@ function doRender(data, options = {}) {
68
69
  const errors = [];
69
70
  const sourcemap = options.sourcemap ? new SourceMap : null;
70
71
  const cache = Object.create(null);
72
+ const position = {
73
+ ind: 0,
74
+ lin: 1,
75
+ col: 1
76
+ };
77
+ let code = '';
78
+ if (mapping != null) {
79
+ if (mapping.importMapping != null) {
80
+ for (const [key, value] of Object.entries(mapping.importMapping)) {
81
+ code += `:import("${key}")${options.indent}{${options.newLine}${Object.entries(value).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : '') + `${options.indent}${v}:${options.indent}${k};`, '')}${options.newLine}}${options.newLine}`;
82
+ }
83
+ }
84
+ code += `:export${options.indent}{${options.newLine}${Object.entries(mapping.mapping).reduce((acc, [k, v]) => acc + (acc.length > 0 ? options.newLine : '') + `${options.indent}${k}:${options.indent}${v};`, '')}${options.newLine}}${options.newLine}`;
85
+ update(position, code);
86
+ }
71
87
  const result = {
72
- code: renderAstNode(options.expandNestingRules ? expand(data) : data, options, sourcemap, {
73
- ind: 0,
74
- lin: 1,
75
- col: 1
76
- }, errors, function reducer(acc, curr) {
88
+ code: code + renderAstNode(options.expandNestingRules && [EnumToken.StyleSheetNodeType, EnumToken.AtRuleNodeType, EnumToken.RuleNodeType].includes(data.typ) && 'chi' in data ? expand(data) : data, options, sourcemap, position, errors, function reducer(acc, curr) {
77
89
  if (curr.typ == EnumToken.CommentTokenType && options.removeComments) {
78
90
  if (!options.preserveLicense || !curr.val.startsWith('/*!')) {
79
91
  return acc;
@@ -209,7 +221,7 @@ function renderAstNode(data, options, sourcemap, position, errors, reducer, cach
209
221
  str = `${node.nam}:${options.indent}${(options.minify ? filterValues(node.val) : node.val).reduce(reducer, '').trimEnd()};`;
210
222
  }
211
223
  else if (node.typ == EnumToken.AtRuleNodeType && !('chi' in node)) {
212
- str = `${data.val === '' ? '' : options.indent || ' '}${data.val};`;
224
+ str = `${node.val === '' ? '' : options.indent || ' '}${node.val};`;
213
225
  }
214
226
  else {
215
227
  str = renderAstNode(node, options, sourcemap, { ...position }, errors, reducer, cache, level + 1, indents);
@@ -232,6 +244,11 @@ function renderAstNode(data, options, sourcemap, position, errors, reducer, cach
232
244
  return `@${data.nam}${data.val === '' ? '' : options.indent || ' '}${data.val}${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
233
245
  }
234
246
  return data.sel + `${options.indent}{${options.newLine}` + (children === '' ? '' : indentSub + children + options.newLine) + indent + `}`;
247
+ case EnumToken.CssVariableTokenType:
248
+ case EnumToken.CssVariableImportTokenType:
249
+ return `@value ${data.nam}:${options.indent}${filterValues((options.minify ? data.val : data.val)).reduce(reducer, '').trim()};`;
250
+ case EnumToken.CssVariableDeclarationMapTokenType:
251
+ return `@value ${filterValues(data.vars).reduce((acc, curr) => acc + renderToken(curr), '').trim()} from ${filterValues(data.from).reduce((acc, curr) => acc + renderToken(curr), '').trim()};`;
235
252
  case EnumToken.InvalidDeclarationNodeType:
236
253
  case EnumToken.InvalidRuleTokenType:
237
254
  case EnumToken.InvalidAtRuleTokenType:
@@ -386,6 +403,8 @@ function renderToken(token, options = {}, cache = Object.create(null), reducer,
386
403
  case EnumToken.NameSpaceAttributeTokenType:
387
404
  return (token.l == null ? '' : renderToken(token.l, options, cache, reducer, errors)) + '|' +
388
405
  renderToken(token.r, options, cache, reducer, errors);
406
+ case EnumToken.ComposesSelectorNodeType:
407
+ return token.l.reduce((acc, curr) => acc + renderToken(curr, options, cache), '') + (token.r == null ? '' : ' from ' + renderToken(token.r, options, cache, reducer, errors));
389
408
  case EnumToken.BlockStartTokenType:
390
409
  return '{';
391
410
  case EnumToken.BlockEndTokenType:
@@ -77,10 +77,7 @@ function computeComponentValue(expr, converted, values) {
77
77
  // normalize hue
78
78
  for (const k of walkValues([object.h])) {
79
79
  if (k.value.typ == EnumToken.AngleTokenType && k.value.unit == 'deg') {
80
- // @ts-ignore
81
80
  k.value.typ = EnumToken.NumberTokenType;
82
- // @ts-ignore
83
- delete k.value.unit;
84
81
  }
85
82
  }
86
83
  }
@@ -528,7 +528,7 @@ function isColor(token) {
528
528
  }
529
529
  if (token.typ == EnumToken.IdenTokenType) {
530
530
  // named color
531
- return token.val.toLowerCase() in COLORS_NAMES;
531
+ return token.val.toLowerCase() in COLORS_NAMES || 'currentcolor' === token.val.toLowerCase() || 'transparent' === token.val.toLowerCase();
532
532
  }
533
533
  let isLegacySyntax = false;
534
534
  if (token.typ == EnumToken.FunctionTokenType) {
@@ -581,8 +581,13 @@ function isColor(token) {
581
581
  return false;
582
582
  }
583
583
  }
584
- if (children[i].typ == EnumToken.FunctionTokenType && !mathFuncs.includes(children[i].val)) {
585
- return false;
584
+ if (children[i].typ == EnumToken.FunctionTokenType) {
585
+ if ('var' == children[i].val.toLowerCase()) {
586
+ continue;
587
+ }
588
+ if (!mathFuncs.includes(children[i].val)) {
589
+ return false;
590
+ }
586
591
  }
587
592
  }
588
593
  if (children.length == 4 || (isRelative && children.length == 6)) {
@@ -869,23 +874,29 @@ function isNumber(name) {
869
874
  }
870
875
  return true;
871
876
  }
872
- function isDimension(name) {
873
- let index = name.length;
874
- while (index--) {
875
- if (isLetter(name.charCodeAt(index))) {
876
- continue;
877
- }
878
- index++;
879
- break;
880
- }
881
- const number = name.slice(0, index);
882
- return number.length > 0 && isIdentStart(name.charCodeAt(index)) && isNumber(number);
883
- }
877
+ // export function isDimension(name: string) {
878
+ //
879
+ // let index: number = name.length;
880
+ //
881
+ // while (index--) {
882
+ //
883
+ // if (isLetter(<number>name.charCodeAt(index))) {
884
+ //
885
+ // continue
886
+ // }
887
+ //
888
+ // index++;
889
+ // break;
890
+ // }
891
+ //
892
+ // const number: string = name.slice(0, index);
893
+ // return number.length > 0 && isIdentStart(name.charCodeAt(index)) && isNumber(number);
894
+ // }
884
895
  function isPercentage(name) {
885
896
  return name.endsWith('%') && isNumber(name.slice(0, -1));
886
897
  }
887
- function isFlex(name) {
888
- return name.endsWith('fr') && isNumber(name.slice(0, -2));
898
+ function isFlex(dimension) {
899
+ return 'unit' in dimension && 'fr' == dimension.unit.toLowerCase();
889
900
  }
890
901
  function parseDimension(name) {
891
902
  let index = name.length;
@@ -901,6 +912,9 @@ function parseDimension(name) {
901
912
  val: +name.slice(0, index),
902
913
  unit: name.slice(index)
903
914
  };
915
+ if (index < 0 || Number.isNaN(dimension.val)) {
916
+ return null;
917
+ }
904
918
  if (isAngle(dimension)) {
905
919
  // @ts-ignore
906
920
  dimension.typ = EnumToken.AngleTokenType;
@@ -924,6 +938,10 @@ function parseDimension(name) {
924
938
  // @ts-ignore
925
939
  dimension.typ = EnumToken.FrequencyTokenType;
926
940
  }
941
+ else if (isFlex(dimension)) {
942
+ // @ts-ignore
943
+ dimension.typ = EnumToken.FlexTokenType;
944
+ }
927
945
  return dimension;
928
946
  }
929
947
  function isHexColor(name) {
@@ -958,4 +976,4 @@ function isWhiteSpace(codepoint) {
958
976
  codepoint == 0xa || codepoint == 0xc || codepoint == 0xd;
959
977
  }
960
978
 
961
- export { colorFontTech, fontFeaturesTech, fontFormat, isAngle, isAtKeyword, isColor, isColorspace, isDigit, isDimension, isFlex, isFrequency, isFunction, isHash, isHexColor, isHueInterpolationMethod, isIdent, isIdentCodepoint, isIdentColor, isIdentStart, isLength, isNewLine, isNumber, isPercentage, isPercentageToken, isPolarColorspace, isPseudo, isRectangularOrthogonalColorspace, isResolution, isTime, isWhiteSpace, mathFuncs, mediaTypes, mozExtensions, parseColor, parseDimension, pseudoAliasMap, pseudoElements, transformFunctions, webkitExtensions, wildCardFuncs };
979
+ export { colorFontTech, fontFeaturesTech, fontFormat, isAngle, isAtKeyword, isColor, isColorspace, isDigit, isFlex, isFrequency, isFunction, isHash, isHexColor, isHueInterpolationMethod, isIdent, isIdentCodepoint, isIdentColor, isIdentStart, isLength, isNewLine, isNumber, isPercentage, isPercentageToken, isPolarColorspace, isPseudo, isRectangularOrthogonalColorspace, isResolution, isTime, isWhiteSpace, mathFuncs, mediaTypes, mozExtensions, parseColor, parseDimension, pseudoAliasMap, pseudoElements, transformFunctions, webkitExtensions, wildCardFuncs };
@@ -11,6 +11,17 @@ import { splitTokenList } from '../utils/list.js';
11
11
 
12
12
  const validateContainerScrollStateFeature = validateContainerSizeFeature;
13
13
  function validateAtRuleContainer(atRule, options, root) {
14
+ if (!Array.isArray(atRule.chi)) {
15
+ // @ts-ignore
16
+ return {
17
+ valid: SyntaxValidationResult.Drop,
18
+ matches: [],
19
+ node: atRule,
20
+ syntax: '@' + atRule.nam,
21
+ error: 'expected supports body',
22
+ tokens: []
23
+ };
24
+ }
14
25
  // media-query-list
15
26
  if (!Array.isArray(atRule.tokens) || atRule.tokens.length == 0) {
16
27
  // @ts-ignore
@@ -8,6 +8,17 @@ import '../../syntax/color/utils/constants.js';
8
8
  import '../../renderer/sourcemap/lib/encode.js';
9
9
 
10
10
  function validateAtRuleCounterStyle(atRule, options, root) {
11
+ if (!Array.isArray(atRule.chi)) {
12
+ // @ts-ignore
13
+ return {
14
+ valid: SyntaxValidationResult.Drop,
15
+ matches: [],
16
+ node: atRule,
17
+ syntax: '@' + atRule.nam,
18
+ error: 'expected supports body',
19
+ tokens: []
20
+ };
21
+ }
11
22
  // media-query-list
12
23
  if (!Array.isArray(atRule.tokens) || atRule.tokens.length == 0) {
13
24
  // @ts-ignore
@@ -12,6 +12,17 @@ import '../syntax.js';
12
12
  import '../config.js';
13
13
 
14
14
  function validateAtRuleFontFeatureValues(atRule, options, root) {
15
+ if (!Array.isArray(atRule.chi)) {
16
+ // @ts-ignore
17
+ return {
18
+ valid: SyntaxValidationResult.Drop,
19
+ matches: [],
20
+ node: atRule,
21
+ syntax: '@' + atRule.nam,
22
+ error: 'expected supports body',
23
+ tokens: []
24
+ };
25
+ }
15
26
  if (!Array.isArray(atRule.tokens) || atRule.tokens.length == 0) {
16
27
  // @ts-ignore
17
28
  return {
@@ -9,6 +9,17 @@ import '../../renderer/sourcemap/lib/encode.js';
9
9
  import { consumeWhitespace } from '../utils/whitespace.js';
10
10
 
11
11
  function validateAtRuleKeyframes(atRule, options, root) {
12
+ if (!Array.isArray(atRule.chi)) {
13
+ // @ts-ignore
14
+ return {
15
+ valid: SyntaxValidationResult.Drop,
16
+ matches: [],
17
+ node: atRule,
18
+ syntax: '@' + atRule.nam,
19
+ error: 'expected supports body',
20
+ tokens: []
21
+ };
22
+ }
12
23
  if (!Array.isArray(atRule.tokens) || atRule.tokens.length == 0) {
13
24
  // @ts-ignore
14
25
  return {
@@ -12,6 +12,17 @@ import '../syntax.js';
12
12
  import '../config.js';
13
13
 
14
14
  function validateAtRuleLayer(atRule, options, root) {
15
+ if (!Array.isArray(atRule.chi)) {
16
+ // @ts-ignore
17
+ return {
18
+ valid: SyntaxValidationResult.Drop,
19
+ matches: [],
20
+ node: atRule,
21
+ syntax: '@' + atRule.nam,
22
+ error: 'expected supports body',
23
+ tokens: []
24
+ };
25
+ }
15
26
  // media-query-list
16
27
  if (!Array.isArray(atRule.tokens) || atRule.tokens.length == 0) {
17
28
  // @ts-ignore
@@ -10,6 +10,17 @@ import { consumeWhitespace } from '../utils/whitespace.js';
10
10
  import { splitTokenList } from '../utils/list.js';
11
11
 
12
12
  function validateAtRuleMedia(atRule, options, root) {
13
+ if (!Array.isArray(atRule.chi)) {
14
+ // @ts-ignore
15
+ return {
16
+ valid: SyntaxValidationResult.Drop,
17
+ matches: [],
18
+ node: atRule,
19
+ syntax: '@' + atRule.nam,
20
+ error: 'expected supports body',
21
+ tokens: []
22
+ };
23
+ }
13
24
  // media-query-list
14
25
  if (!Array.isArray(atRule.tokens) || atRule.tokens.length == 0) {
15
26
  // @ts-ignore
@@ -8,6 +8,17 @@ import '../../syntax/color/utils/constants.js';
8
8
  import '../../renderer/sourcemap/lib/encode.js';
9
9
 
10
10
  function validateAtRulePageMarginBox(atRule, options, root) {
11
+ if (!Array.isArray(atRule.chi)) {
12
+ // @ts-ignore
13
+ return {
14
+ valid: SyntaxValidationResult.Drop,
15
+ matches: [],
16
+ node: atRule,
17
+ syntax: '@' + atRule.nam,
18
+ error: 'expected supports body',
19
+ tokens: []
20
+ };
21
+ }
11
22
  if (Array.isArray(atRule.tokens) && atRule.tokens.length > 0) {
12
23
  // @ts-ignore
13
24
  return {
@@ -9,6 +9,17 @@ import '../../renderer/sourcemap/lib/encode.js';
9
9
  import { splitTokenList } from '../utils/list.js';
10
10
 
11
11
  function validateAtRulePage(atRule, options, root) {
12
+ if (!Array.isArray(atRule.chi)) {
13
+ // @ts-ignore
14
+ return {
15
+ valid: SyntaxValidationResult.Drop,
16
+ matches: [],
17
+ node: atRule,
18
+ syntax: '@' + atRule.nam,
19
+ error: 'expected supports body',
20
+ tokens: []
21
+ };
22
+ }
12
23
  // media-query-list
13
24
  if (!Array.isArray(atRule.tokens) || atRule.tokens.length == 0) {
14
25
  // @ts-ignore
@@ -23,6 +23,17 @@ function validateAtRuleSupports(atRule, options, root) {
23
23
  tokens: []
24
24
  };
25
25
  }
26
+ if (!Array.isArray(atRule.chi)) {
27
+ // @ts-ignore
28
+ return {
29
+ valid: SyntaxValidationResult.Drop,
30
+ matches: [],
31
+ node: atRule,
32
+ syntax: '@' + atRule.nam,
33
+ error: 'expected supports body',
34
+ tokens: []
35
+ };
36
+ }
26
37
  const result = validateAtRuleSupportsConditions(atRule, atRule.tokens);
27
38
  if (result) {
28
39
  if (result.node == null) {