@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.
- package/CHANGELOG.md +5 -0
- package/README.md +1 -1
- package/dist/index-umd-web.js +288 -173
- package/dist/index.cjs +288 -173
- package/dist/index.d.ts +24 -7
- package/dist/lib/ast/minify.js +1 -2
- package/dist/lib/ast/types.js +9 -0
- package/dist/lib/ast/utils/utils.js +104 -0
- package/dist/lib/parser/parse.js +24 -41
- package/dist/lib/renderer/color/utils/constants.js +1 -1
- package/dist/lib/syntax/syntax.js +4 -0
- package/dist/lib/validation/config.json.js +63 -63
- package/dist/lib/validation/parser/parse.js +14 -31
- package/dist/lib/validation/syntax.js +182 -37
- package/package.json +5 -5
|
@@ -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
|
|
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(
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
772
|
-
|
|
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.
|
|
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.
|
|
69
|
-
"rollup": "^4.
|
|
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
|
}
|