@tbela99/css-parser 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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) {
@@ -208,7 +229,8 @@ function doEvaluateSyntax(syntaxes, context, options) {
208
229
  }
209
230
  context.update(result.context);
210
231
  }
211
- return {
232
+ // @ts-ignore
233
+ return result ?? {
212
234
  valid: SyntaxValidationResult.Valid,
213
235
  node: null,
214
236
  syntax: syntaxes[i - 1],
@@ -350,7 +372,7 @@ function match(syntax, context, options) {
350
372
  }
351
373
  return {
352
374
  valid: SyntaxValidationResult.Drop,
353
- node: context.next(),
375
+ node: context.current(),
354
376
  syntax,
355
377
  error: `expected '${ValidationTokenEnum[syntax.typ].toLowerCase()}', got '${context.done() ? null : renderToken(context.peek())}'`,
356
378
  context
@@ -385,7 +407,7 @@ function match(syntax, context, options) {
385
407
  }
386
408
  switch (syntax.typ) {
387
409
  case ValidationTokenEnum.Keyword:
388
- success = (token.typ == EnumToken.IdenTokenType || token.typ == EnumToken.DashedIdenTokenType) &&
410
+ success = (token.typ == EnumToken.IdenTokenType || token.typ == EnumToken.DashedIdenTokenType || isIdentColor(token)) &&
389
411
  (token.val == syntax.val ||
390
412
  syntax.val.localeCompare(token.val, undefined, { sensitivity: 'base' }) == 0 ||
391
413
  // config.declarations.all
@@ -414,19 +436,11 @@ function match(syntax, context, options) {
414
436
  context
415
437
  };
416
438
  }
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
- }
439
+ result = match(getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, syntax.val + '()')?.[0], context, options);
440
+ if (result.valid == SyntaxValidationResult.Valid) {
441
+ context.next();
442
+ result.context = context;
443
+ return result;
430
444
  }
431
445
  break;
432
446
  case ValidationTokenEnum.DeclarationType:
@@ -524,6 +538,7 @@ function match(syntax, context, options) {
524
538
  }
525
539
  function matchPropertyType(syntax, context, options) {
526
540
  if (![
541
+ 'bg-position',
527
542
  'length-percentage', 'flex', 'calc-sum', 'color', 'color-base', 'system-color', 'deprecated-system-color',
528
543
  'pseudo-class-selector', 'pseudo-element-selector'
529
544
  ].includes(syntax.val)) {
@@ -553,6 +568,96 @@ function matchPropertyType(syntax, context, options) {
553
568
  return { ...result, context };
554
569
  }
555
570
  switch (syntax.val) {
571
+ case 'bg-position': {
572
+ let val;
573
+ let keyworkMatchCount = 0;
574
+ let lengthMatchCount = 0;
575
+ let functionMatchCount = 0;
576
+ let isBGX = false;
577
+ let isBGY = false;
578
+ while (token != null && keyworkMatchCount + lengthMatchCount + functionMatchCount < 3) {
579
+ // match one value: keyword or length
580
+ success = (token.typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(token.val));
581
+ if (success) {
582
+ functionMatchCount++;
583
+ context.next();
584
+ token = context.peek();
585
+ continue;
586
+ }
587
+ if (token.typ == EnumToken.WhitespaceTokenType) {
588
+ context.next();
589
+ token = context.peek();
590
+ continue;
591
+ }
592
+ if (token.typ == EnumToken.IdenTokenType) {
593
+ val = token.val.toLowerCase();
594
+ success = ['left', 'center', 'right', 'top', 'center', 'bottom'].includes(val);
595
+ if (!success) {
596
+ break;
597
+ }
598
+ keyworkMatchCount++;
599
+ if (keyworkMatchCount > 2) {
600
+ return {
601
+ valid: SyntaxValidationResult.Drop,
602
+ node: token,
603
+ syntax,
604
+ error: `expected <length-percentage>`,
605
+ context
606
+ };
607
+ }
608
+ if (val == 'left' || val == 'right') {
609
+ if (isBGX) {
610
+ return {
611
+ valid: SyntaxValidationResult.Drop,
612
+ node: token,
613
+ syntax,
614
+ error: `top | bottom | <length-percentage>`,
615
+ context
616
+ };
617
+ }
618
+ isBGX = true;
619
+ }
620
+ if (val == 'top' || val == 'bottom') {
621
+ if (isBGY) {
622
+ return {
623
+ valid: SyntaxValidationResult.Drop,
624
+ node: token,
625
+ syntax,
626
+ error: `expected left | right | <length-percentage>`,
627
+ context
628
+ };
629
+ }
630
+ isBGY = true;
631
+ }
632
+ context.next();
633
+ token = context.peek();
634
+ continue;
635
+ }
636
+ success = token.typ == EnumToken.LengthTokenType || token.typ == EnumToken.PercentageTokenType || (token.typ == EnumToken.NumberTokenType && token.val == '0');
637
+ if (!success) {
638
+ break;
639
+ }
640
+ lengthMatchCount++;
641
+ context.next();
642
+ token = context.peek();
643
+ }
644
+ if (keyworkMatchCount + lengthMatchCount + functionMatchCount == 0) {
645
+ return {
646
+ valid: SyntaxValidationResult.Drop,
647
+ node: token,
648
+ syntax,
649
+ error: `expected <bg-position>`,
650
+ context
651
+ };
652
+ }
653
+ return {
654
+ valid: SyntaxValidationResult.Valid,
655
+ node: token,
656
+ syntax,
657
+ error: '',
658
+ context
659
+ };
660
+ }
556
661
  case 'calc-sum':
557
662
  success = (token.typ == EnumToken.FunctionTokenType && mathFuncs.includes(token.val)) ||
558
663
  // @ts-ignore
@@ -621,8 +726,8 @@ function matchPropertyType(syntax, context, options) {
621
726
  case 'number':
622
727
  case 'number-token':
623
728
  success = token.typ == EnumToken.NumberTokenType;
624
- if ('range' in syntax) {
625
- success = success && +token.val >= +syntax.range[0] && +token.val <= +syntax.range[1];
729
+ if (success && 'range' in syntax) {
730
+ success = +token.val >= +syntax.range[0] && (syntax.range[1] == null || +token.val <= +syntax.range[1]);
626
731
  }
627
732
  break;
628
733
  case 'angle':
@@ -679,7 +784,6 @@ function matchPropertyType(syntax, context, options) {
679
784
  ['length-percentage', 'length', 'number', 'number-token', 'angle', 'percentage', 'dimension'].includes(syntax.val)) {
680
785
  if (!success) {
681
786
  success = mathFuncs.includes(token.val.toLowerCase()) &&
682
- // (token as FunctionToken).val + '()' in config[ValidationSyntaxGroupEnum.Syntaxes] &&
683
787
  doEvaluateSyntax(getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, token.val + '()')?.[0]?.chi ?? [], createContext(token.chi), {
684
788
  ...options,
685
789
  isRepeatable: null,
@@ -760,24 +864,28 @@ function anyOf(syntaxes, context, options) {
760
864
  function allOf(syntax, context, options) {
761
865
  let result;
762
866
  let i;
763
- // sort tokens -> wildCard -> last
764
- // 1px var(...) 2px => 1px 2px var(...)
765
- const slice = context.slice();
867
+ let slice = context.slice();
766
868
  const vars = [];
767
869
  const tokens = [];
870
+ const repeatable = [];
871
+ // match optional syntax first
872
+ // <length>{2,3}&&<color>? => <color>?&&<length>{2,3}
873
+ for (i = 0; i < syntax.length; i++) {
874
+ if (syntax[i].length == 1 && syntax[i][0].occurence != null) {
875
+ repeatable.push(syntax[i]);
876
+ syntax.splice(i--, 1);
877
+ }
878
+ }
879
+ if (repeatable.length > 0) {
880
+ syntax.push(...repeatable);
881
+ }
882
+ // sort tokens -> wildCard -> last
883
+ // 1px var(...) 2px => 1px 2px var(...)
768
884
  for (i = 0; i < slice.length; i++) {
769
885
  if (slice[i].typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(slice[i].val.toLowerCase())) {
770
886
  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--;
887
+ if (slice[i + 1]?.typ == EnumToken.WhitespaceTokenType) {
888
+ vars.push(slice[++i]);
781
889
  }
782
890
  continue;
783
891
  }
@@ -791,7 +899,44 @@ function allOf(syntax, context, options) {
791
899
  tokens.push(...vars);
792
900
  }
793
901
  const con = createContext(tokens);
902
+ let cp;
903
+ let j;
794
904
  for (i = 0; i < syntax.length; i++) {
905
+ if (syntax[i].length == 1 && syntax[i][0].isOptional) {
906
+ syntax[i][0].isOptional = false;
907
+ j = 0;
908
+ cp = con.clone();
909
+ slice = cp.slice();
910
+ if (cp.done()) {
911
+ syntax[i][0].isOptional = true;
912
+ syntax.splice(i, 1);
913
+ i = -1;
914
+ continue;
915
+ }
916
+ while (!cp.done()) {
917
+ result = doEvaluateSyntax(syntax[i], cp.clone(), options);
918
+ if (result.valid == SyntaxValidationResult.Valid) {
919
+ let end = slice.indexOf(cp.current());
920
+ if (end == -1) {
921
+ end = 0;
922
+ }
923
+ else {
924
+ end -= j - 1;
925
+ }
926
+ con.consume(slice[j], end < 0 ? 0 : end);
927
+ break;
928
+ }
929
+ cp.next();
930
+ j++;
931
+ }
932
+ syntax[i][0].isOptional = true;
933
+ // @ts-ignore
934
+ if (result?.valid == SyntaxValidationResult.Valid) {
935
+ syntax.splice(i, 1);
936
+ i = -1;
937
+ }
938
+ continue;
939
+ }
795
940
  result = doEvaluateSyntax(syntax[i], con.clone(), options);
796
941
  if (result.valid == SyntaxValidationResult.Valid) {
797
942
  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",
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
  }