@tbela99/css-parser 1.1.0 → 1.1.1-alpha4

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.
@@ -36,12 +36,25 @@ function createContext(input) {
36
36
  update(context) {
37
37
  // @ts-ignore
38
38
  const newIndex = result.indexOf(context.current());
39
- if (newIndex != -1) {
40
- // console.error({newIndex, v: result[newIndex]});
41
- // console.error(new Error('update'))
39
+ if (newIndex > this.index) {
42
40
  this.index = newIndex;
43
41
  }
44
42
  },
43
+ consume(token, howMany) {
44
+ let newIndex = result.indexOf(token, this.index + 1);
45
+ if (newIndex == -1 || newIndex < this.index) {
46
+ return false;
47
+ }
48
+ howMany ??= 0;
49
+ let splice = 1;
50
+ if (result[newIndex - 1]?.typ == EnumToken.WhitespaceTokenType) {
51
+ splice++;
52
+ newIndex--;
53
+ }
54
+ result.splice(this.index + 1, 0, ...result.splice(newIndex, splice + howMany));
55
+ this.index += howMany + splice;
56
+ return true;
57
+ },
45
58
  done() {
46
59
  return this.index + 1 >= result.length;
47
60
  },
@@ -66,13 +79,21 @@ function createContext(input) {
66
79
  return result.slice(this.index + 1);
67
80
  },
68
81
  clone() {
69
- const context = createContext(input.slice());
82
+ const context = createContext(result.slice());
70
83
  context.index = this.index;
71
84
  return context;
85
+ },
86
+ // @ts-ignore
87
+ toJSON() {
88
+ return {
89
+ index: this.index,
90
+ slice: this.slice(),
91
+ tokens: this.tokens()
92
+ };
72
93
  }
73
94
  };
74
95
  }
75
- function evaluateSyntax(node, options, parent) {
96
+ function evaluateSyntax(node, options) {
76
97
  let ast;
77
98
  let result;
78
99
  switch (node.typ) {
@@ -81,6 +102,7 @@ function evaluateSyntax(node, options, parent) {
81
102
  break;
82
103
  }
83
104
  ast = getParsedSyntax("declarations" /* ValidationSyntaxGroupEnum.Declarations */, node.nam);
105
+ // console.error({ast: ast.reduce((acc, curr) => acc + renderSyntax(curr), '')});
84
106
  if (ast != null) {
85
107
  let token = null;
86
108
  const values = node.val.slice();
@@ -100,6 +122,7 @@ function evaluateSyntax(node, options, parent) {
100
122
  }
101
123
  }
102
124
  result = doEvaluateSyntax(ast, createContext(values), { ...options, visited: new WeakMap() });
125
+ // console.error(JSON.stringify({ast, values, result}, null, 1));
103
126
  if (result.valid == SyntaxValidationResult.Valid && !result.context.done()) {
104
127
  let token = null;
105
128
  while ((token = result.context.next()) != null) {
@@ -158,6 +181,7 @@ function doEvaluateSyntax(syntaxes, context, options) {
158
181
  let i = 0;
159
182
  let result;
160
183
  let token = null;
184
+ // console.error(`>> doEvaluateSyntax: ${syntaxes.reduce((acc, curr) => acc + renderSyntax(curr), '')}\n>> ${JSON.stringify({syntaxes}, null, 1)}>> context: ${context.slice<Token>().reduce((acc, curr) => acc + renderToken(curr), '')}`);
161
185
  for (; i < syntaxes.length; i++) {
162
186
  syntax = syntaxes[i];
163
187
  if (context.done()) {
@@ -187,6 +211,7 @@ function doEvaluateSyntax(syntaxes, context, options) {
187
211
  }
188
212
  else {
189
213
  if (isVisited(token, syntax, 'doEvaluateSyntax', options)) {
214
+ // console.error(`cyclic dependency: ${renderSyntax(syntax)}`);
190
215
  return {
191
216
  valid: SyntaxValidationResult.Drop,
192
217
  node: token,
@@ -208,7 +233,8 @@ function doEvaluateSyntax(syntaxes, context, options) {
208
233
  }
209
234
  context.update(result.context);
210
235
  }
211
- return {
236
+ // @ts-ignore
237
+ return result ?? {
212
238
  valid: SyntaxValidationResult.Valid,
213
239
  node: null,
214
240
  syntax: syntaxes[i - 1],
@@ -333,6 +359,7 @@ function matchOccurence(syntax, context, options) {
333
359
  };
334
360
  }
335
361
  function match(syntax, context, options) {
362
+ // console.error(`>> match(): ${renderSyntax(syntax)}\n>> ${JSON.stringify({syntax}, null, 1)}>> context: ${context.slice<Token>().reduce((acc, curr) => acc + renderToken(curr), '')}`);
336
363
  let success = false;
337
364
  let result;
338
365
  let token = context.peek();
@@ -350,7 +377,7 @@ function match(syntax, context, options) {
350
377
  }
351
378
  return {
352
379
  valid: SyntaxValidationResult.Drop,
353
- node: context.next(),
380
+ node: context.current(),
354
381
  syntax,
355
382
  error: `expected '${ValidationTokenEnum[syntax.typ].toLowerCase()}', got '${context.done() ? null : renderToken(context.peek())}'`,
356
383
  context
@@ -385,7 +412,7 @@ function match(syntax, context, options) {
385
412
  }
386
413
  switch (syntax.typ) {
387
414
  case ValidationTokenEnum.Keyword:
388
- success = (token.typ == EnumToken.IdenTokenType || token.typ == EnumToken.DashedIdenTokenType) &&
415
+ success = (token.typ == EnumToken.IdenTokenType || token.typ == EnumToken.DashedIdenTokenType || isIdentColor(token)) &&
389
416
  (token.val == syntax.val ||
390
417
  syntax.val.localeCompare(token.val, undefined, { sensitivity: 'base' }) == 0 ||
391
418
  // config.declarations.all
@@ -414,20 +441,17 @@ function match(syntax, context, options) {
414
441
  context
415
442
  };
416
443
  }
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
424
- });
425
- if (result.valid == SyntaxValidationResult.Valid) {
426
- context.next();
427
- result.context = context;
428
- return result;
429
- }
444
+ // {
445
+ // console.error(JSON.stringify({funcDef: (getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, (syntax as ValidationFunctionDefinitionToken).val + '()')?.[0] as ValidationFunctionToken).chi}, null, 1))
446
+ //
447
+ // const child = getParsedSyntax(ValidationSyntaxGroupEnum.Syntaxes, (syntax as ValidationFunctionDefinitionToken).val + '()')?.[0] as ValidationFunctionToken;
448
+ result = match(getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, syntax.val + '()')?.[0], context, options);
449
+ if (result.valid == SyntaxValidationResult.Valid) {
450
+ context.next();
451
+ result.context = context;
452
+ return result;
430
453
  }
454
+ // }
431
455
  break;
432
456
  case ValidationTokenEnum.DeclarationType:
433
457
  return doEvaluateSyntax(getParsedSyntax("declarations" /* ValidationSyntaxGroupEnum.Declarations */, syntax.val), context, {
@@ -524,6 +548,7 @@ function match(syntax, context, options) {
524
548
  }
525
549
  function matchPropertyType(syntax, context, options) {
526
550
  if (![
551
+ 'bg-position',
527
552
  'length-percentage', 'flex', 'calc-sum', 'color', 'color-base', 'system-color', 'deprecated-system-color',
528
553
  'pseudo-class-selector', 'pseudo-element-selector'
529
554
  ].includes(syntax.val)) {
@@ -553,6 +578,96 @@ function matchPropertyType(syntax, context, options) {
553
578
  return { ...result, context };
554
579
  }
555
580
  switch (syntax.val) {
581
+ case 'bg-position': {
582
+ let val;
583
+ let keyworkMatchCount = 0;
584
+ let lengthMatchCount = 0;
585
+ let functionMatchCount = 0;
586
+ let isBGX = false;
587
+ let isBGY = false;
588
+ while (token != null && keyworkMatchCount + lengthMatchCount + functionMatchCount < 3) {
589
+ // match one value: keyword or length
590
+ success = (token.typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(token.val));
591
+ if (success) {
592
+ functionMatchCount++;
593
+ context.next();
594
+ token = context.peek();
595
+ continue;
596
+ }
597
+ if (token.typ == EnumToken.WhitespaceTokenType) {
598
+ context.next();
599
+ token = context.peek();
600
+ continue;
601
+ }
602
+ if (token.typ == EnumToken.IdenTokenType) {
603
+ val = token.val.toLowerCase();
604
+ success = ['left', 'center', 'right', 'top', 'center', 'bottom'].includes(val);
605
+ if (!success) {
606
+ break;
607
+ }
608
+ keyworkMatchCount++;
609
+ if (keyworkMatchCount > 2) {
610
+ return {
611
+ valid: SyntaxValidationResult.Drop,
612
+ node: token,
613
+ syntax,
614
+ error: `expected <length>`,
615
+ context
616
+ };
617
+ }
618
+ if (val == 'left' || val == 'right') {
619
+ if (isBGX) {
620
+ return {
621
+ valid: SyntaxValidationResult.Drop,
622
+ node: token,
623
+ syntax,
624
+ error: `top | bottom | <length-percentage>`,
625
+ context
626
+ };
627
+ }
628
+ isBGX = true;
629
+ }
630
+ if (val == 'top' || val == 'bottom') {
631
+ if (isBGY) {
632
+ return {
633
+ valid: SyntaxValidationResult.Drop,
634
+ node: token,
635
+ syntax,
636
+ error: `expected left | right | <length-percentage>`,
637
+ context
638
+ };
639
+ }
640
+ isBGY = true;
641
+ }
642
+ context.next();
643
+ token = context.peek();
644
+ continue;
645
+ }
646
+ success = token.typ == EnumToken.LengthTokenType || token.typ == EnumToken.PercentageTokenType || (token.typ == EnumToken.NumberTokenType && token.val == '0');
647
+ if (!success) {
648
+ break;
649
+ }
650
+ lengthMatchCount++;
651
+ context.next();
652
+ token = context.peek();
653
+ }
654
+ if (keyworkMatchCount + lengthMatchCount + functionMatchCount == 0) {
655
+ return {
656
+ valid: SyntaxValidationResult.Drop,
657
+ node: token,
658
+ syntax,
659
+ error: `expected <bg-position>`,
660
+ context
661
+ };
662
+ }
663
+ return {
664
+ valid: SyntaxValidationResult.Valid,
665
+ node: token,
666
+ syntax,
667
+ error: '',
668
+ context
669
+ };
670
+ }
556
671
  case 'calc-sum':
557
672
  success = (token.typ == EnumToken.FunctionTokenType && mathFuncs.includes(token.val)) ||
558
673
  // @ts-ignore
@@ -621,8 +736,8 @@ function matchPropertyType(syntax, context, options) {
621
736
  case 'number':
622
737
  case 'number-token':
623
738
  success = token.typ == EnumToken.NumberTokenType;
624
- if ('range' in syntax) {
625
- success = success && +token.val >= +syntax.range[0] && +token.val <= +syntax.range[1];
739
+ if (success && 'range' in syntax) {
740
+ success = +token.val >= +syntax.range[0] && (syntax.range[1] == null || +token.val <= +syntax.range[1]);
626
741
  }
627
742
  break;
628
743
  case 'angle':
@@ -760,24 +875,28 @@ function anyOf(syntaxes, context, options) {
760
875
  function allOf(syntax, context, options) {
761
876
  let result;
762
877
  let i;
763
- // sort tokens -> wildCard -> last
764
- // 1px var(...) 2px => 1px 2px var(...)
765
- const slice = context.slice();
878
+ let slice = context.slice();
766
879
  const vars = [];
767
880
  const tokens = [];
881
+ const repeatable = [];
882
+ // match optional syntax first
883
+ // <length>{2,3}&&<color>? => <color>?&&<length>{2,3}
884
+ for (i = 0; i < syntax.length; i++) {
885
+ if (syntax[i].length == 1 && syntax[i][0].occurence != null) {
886
+ repeatable.push(syntax[i]);
887
+ syntax.splice(i--, 1);
888
+ }
889
+ }
890
+ if (repeatable.length > 0) {
891
+ syntax.push(...repeatable);
892
+ }
893
+ // sort tokens -> wildCard -> last
894
+ // 1px var(...) 2px => 1px 2px var(...)
768
895
  for (i = 0; i < slice.length; i++) {
769
896
  if (slice[i].typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(slice[i].val.toLowerCase())) {
770
897
  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--;
898
+ if (slice[i + 1]?.typ == EnumToken.WhitespaceTokenType) {
899
+ vars.push(slice[++i]);
781
900
  }
782
901
  continue;
783
902
  }
@@ -791,7 +910,44 @@ function allOf(syntax, context, options) {
791
910
  tokens.push(...vars);
792
911
  }
793
912
  const con = createContext(tokens);
913
+ let cp;
914
+ let j;
794
915
  for (i = 0; i < syntax.length; i++) {
916
+ if (syntax[i].length == 1 && syntax[i][0].isOptional) {
917
+ syntax[i][0].isOptional = false;
918
+ j = 0;
919
+ cp = con.clone();
920
+ slice = cp.slice();
921
+ if (cp.done()) {
922
+ syntax[i][0].isOptional = true;
923
+ syntax.splice(i, 1);
924
+ i = -1;
925
+ continue;
926
+ }
927
+ while (!cp.done()) {
928
+ result = doEvaluateSyntax(syntax[i], cp.clone(), options);
929
+ if (result.valid == SyntaxValidationResult.Valid) {
930
+ let end = slice.indexOf(cp.current());
931
+ if (end == -1) {
932
+ end = 0;
933
+ }
934
+ else {
935
+ end -= j - 1;
936
+ }
937
+ con.consume(slice[j], end < 0 ? 0 : end);
938
+ break;
939
+ }
940
+ cp.next();
941
+ j++;
942
+ }
943
+ syntax[i][0].isOptional = true;
944
+ // @ts-ignore
945
+ if (result?.valid == SyntaxValidationResult.Valid) {
946
+ syntax.splice(i, 1);
947
+ i = -1;
948
+ }
949
+ continue;
950
+ }
795
951
  result = doEvaluateSyntax(syntax[i], con.clone(), options);
796
952
  if (result.valid == SyntaxValidationResult.Valid) {
797
953
  con.update(result.context);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tbela99/css-parser",
3
3
  "description": "CSS parser for node and the browser",
4
- "version": "v1.1.0",
4
+ "version": "v1.1.1-alpha4",
5
5
  "exports": {
6
6
  ".": "./dist/node/index.js",
7
7
  "./node": "./dist/node/index.js",
@@ -16,10 +16,10 @@
16
16
  },
17
17
  "scripts": {
18
18
  "build": "rollup -c;./build.sh dist/index.d.ts 'declare interface' 'declare type'",
19
- "test": "web-test-runner \"test/**/web.spec.js\" --node-resolve --playwright --browsers chromium firefox webkit --root-dir=.; mocha --reporter-options='maxDiffSize=1801920' \"test/**/node.spec.js\"",
19
+ "test": "web-test-runner \"test/**/web.spec.js\" --node-resolve --playwright --browsers chromium firefox webkit --root-dir=.; mocha --reporter-options='maxDiffSize=1801920' --timeout=10000 \"test/**/node.spec.js\"",
20
20
  "test:web": "web-test-runner \"test/**/web.spec.js\" --node-resolve --playwright --browsers chromium firefox webkit --root-dir=.",
21
21
  "test:node": "mocha --reporter-options='maxDiffSize=1801920' \"test/**/node.spec.js\"",
22
- "test:cov": "c8 -x 'test/specs/**/*.js' -x dist/lib/validation/syntax.js -x 'dist/lib/validation/parser/*.js' --reporter=html --reporter=text --reporter=json-summary mocha --reporter-options='maxDiffSize=1801920' \"test/**/node.spec.js\"",
22
+ "test:cov": "c8 -x 'test/specs/**/*.js' -x dist/lib/validation/syntax.js -x 'dist/lib/validation/parser/*.js' --reporter=html --reporter=text --reporter=json-summary mocha --reporter-options='maxDiffSize=1801920' --timeout=10000 \"test/**/node.spec.js\"",
23
23
  "test:web-cov": "web-test-runner -x 'test/specs/**/*.js' -x dist/lib/validation/syntax.js,dist/lib/validation/parser \"test/**/web.spec.js\" --node-resolve --playwright --browsers chromium firefox webkit --root-dir=. --coverage",
24
24
  "profile": "node --enable-source-maps --inspect-brk test/inspect.js",
25
25
  "syntax-update": "esno tools/validation.ts",
@@ -65,8 +65,8 @@
65
65
  "c8": "^10.1.3",
66
66
  "esno": "^4.8.0",
67
67
  "mocha": "^11.7.1",
68
- "playwright": "^1.53.2",
69
- "rollup": "^4.44.2",
68
+ "playwright": "^1.54.1",
69
+ "rollup": "^4.45.1",
70
70
  "rollup-plugin-dts": "^6.2.1",
71
71
  "tslib": "^2.8.1"
72
72
  }