@tbela99/css-parser 1.3.2 → 1.3.4
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 +12 -0
- package/README.md +59 -20
- package/dist/index-umd-web.js +1846 -1075
- package/dist/index.cjs +1941 -1202
- package/dist/index.d.ts +914 -181
- package/dist/lib/ast/expand.js +5 -10
- package/dist/lib/ast/features/calc.js +8 -8
- package/dist/lib/ast/features/inlinecssvariables.js +9 -8
- package/dist/lib/ast/features/prefix.js +5 -15
- package/dist/lib/ast/features/shorthand.js +5 -6
- package/dist/lib/ast/features/transform.js +18 -25
- package/dist/lib/ast/features/type.js +4 -2
- package/dist/lib/ast/minify.js +56 -112
- package/dist/lib/ast/transform/compute.js +2 -4
- package/dist/lib/ast/transform/matrix.js +20 -20
- package/dist/lib/ast/transform/minify.js +105 -12
- package/dist/lib/ast/transform/rotate.js +11 -11
- package/dist/lib/ast/transform/scale.js +6 -6
- package/dist/lib/ast/transform/skew.js +4 -4
- package/dist/lib/ast/transform/translate.js +3 -3
- package/dist/lib/ast/transform/utils.js +30 -37
- package/dist/lib/ast/types.js +16 -4
- package/dist/lib/ast/walk.js +172 -70
- package/dist/lib/fs/resolve.js +12 -7
- package/dist/lib/parser/declaration/list.js +3 -1
- package/dist/lib/parser/parse.js +441 -161
- package/dist/lib/parser/tokenize.js +12 -14
- package/dist/lib/renderer/render.js +7 -7
- package/dist/lib/syntax/color/cmyk.js +6 -3
- package/dist/lib/syntax/color/color-mix.js +2 -3
- package/dist/lib/syntax/color/color.js +28 -6
- package/dist/lib/syntax/color/hex.js +3 -0
- package/dist/lib/syntax/color/hsl.js +18 -7
- package/dist/lib/syntax/color/hwb.js +3 -3
- package/dist/lib/syntax/color/lab.js +4 -4
- package/dist/lib/syntax/color/lch.js +7 -4
- package/dist/lib/syntax/color/oklab.js +4 -4
- package/dist/lib/syntax/color/oklch.js +18 -6
- package/dist/lib/syntax/color/relativecolor.js +9 -56
- package/dist/lib/syntax/color/srgb.js +1 -1
- package/dist/lib/syntax/syntax.js +36 -18
- package/dist/lib/validation/at-rules/container.js +11 -0
- package/dist/lib/validation/at-rules/counter-style.js +11 -0
- package/dist/lib/validation/at-rules/font-feature-values.js +11 -0
- package/dist/lib/validation/at-rules/keyframes.js +11 -0
- package/dist/lib/validation/at-rules/layer.js +11 -0
- package/dist/lib/validation/at-rules/media.js +11 -0
- package/dist/lib/validation/at-rules/page-margin-box.js +11 -0
- package/dist/lib/validation/at-rules/page.js +11 -0
- package/dist/lib/validation/at-rules/supports.js +11 -0
- package/dist/lib/validation/at-rules/when.js +11 -0
- package/dist/lib/validation/config.js +0 -2
- package/dist/lib/validation/config.json.js +21 -9
- package/dist/lib/validation/parser/parse.js +53 -2
- package/dist/lib/validation/syntax.js +199 -36
- package/dist/node.js +63 -36
- package/dist/web.js +84 -25
- package/package.json +7 -5
- package/dist/lib/validation/parser/types.js +0 -54
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { ValidationTokenEnum } from './parser/
|
|
2
|
-
import {
|
|
3
|
-
import { EnumToken, SyntaxValidationResult, ColorType } from '../ast/types.js';
|
|
1
|
+
import { ValidationTokenEnum, renderSyntax } from './parser/parse.js';
|
|
2
|
+
import { SyntaxValidationResult, EnumToken, ColorType } from '../ast/types.js';
|
|
4
3
|
import '../ast/minify.js';
|
|
5
4
|
import '../ast/walk.js';
|
|
6
5
|
import '../parser/parse.js';
|
|
7
6
|
import '../parser/tokenize.js';
|
|
8
7
|
import '../parser/utils/config.js';
|
|
9
|
-
import { wildCardFuncs, isIdentColor, mathFuncs } from '../syntax/syntax.js';
|
|
8
|
+
import { wildCardFuncs, isIdentColor, mathFuncs, isColor } from '../syntax/syntax.js';
|
|
10
9
|
import { renderToken } from '../renderer/render.js';
|
|
11
10
|
import '../renderer/sourcemap/lib/encode.js';
|
|
12
11
|
import { getSyntaxConfig, getParsedSyntax, getSyntax } from './config.js';
|
|
@@ -17,6 +16,116 @@ import '../ast/features/type.js';
|
|
|
17
16
|
const config = getSyntaxConfig();
|
|
18
17
|
// @ts-ignore
|
|
19
18
|
const allValues = getSyntaxConfig()["declarations" /* ValidationSyntaxGroupEnum.Declarations */].all.syntax.trim().split(/[\s|]+/g);
|
|
19
|
+
/**
|
|
20
|
+
* Check if a node is allowed as child in a given context
|
|
21
|
+
* @param node
|
|
22
|
+
* @param context
|
|
23
|
+
*/
|
|
24
|
+
function isNodeAllowedInContext(node, context) {
|
|
25
|
+
if (node.typ == EnumToken.CommentNodeType || context == null) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
switch (context?.typ) {
|
|
29
|
+
case EnumToken.StyleSheetNodeType:
|
|
30
|
+
case EnumToken.RuleNodeType:
|
|
31
|
+
return node.typ == EnumToken.RuleNodeType ||
|
|
32
|
+
node.typ == EnumToken.AtRuleNodeType ||
|
|
33
|
+
node.typ == EnumToken.KeyframesAtRuleNodeType ||
|
|
34
|
+
(node.typ == EnumToken.DeclarationNodeType && context.typ == EnumToken.RuleNodeType) ||
|
|
35
|
+
(node.typ == EnumToken.CDOCOMMNodeType && context.typ == EnumToken.StyleSheetNodeType);
|
|
36
|
+
case EnumToken.KeyframesAtRuleNodeType:
|
|
37
|
+
return node.typ == EnumToken.KeyFramesRuleNodeType;
|
|
38
|
+
case EnumToken.KeyFramesRuleNodeType:
|
|
39
|
+
return node.typ == EnumToken.DeclarationNodeType;
|
|
40
|
+
case EnumToken.AtRuleNodeType:
|
|
41
|
+
// @ts-ignore
|
|
42
|
+
const syntax = getParsedSyntax("atRules" /* ValidationSyntaxGroupEnum.AtRules */, '@' + context.nam)?.[0].chi ?? null;
|
|
43
|
+
//
|
|
44
|
+
if (syntax == null) {
|
|
45
|
+
// console.error(`syntax: Not found ${ValidationSyntaxGroupEnum.AtRules}@${(context as AstAtRule).nam}`);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
const stack = syntax.slice();
|
|
49
|
+
for (const child of stack) {
|
|
50
|
+
if (Array.isArray(child)) {
|
|
51
|
+
stack.push(...child);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if ('chi' in child && Array.isArray(child.chi)) {
|
|
55
|
+
stack.push(...child.chi);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
// @ts-ignore
|
|
59
|
+
if (child.l != null) {
|
|
60
|
+
// @ts-ignore
|
|
61
|
+
stack.push(child.l);
|
|
62
|
+
// @ts-ignore
|
|
63
|
+
if (child.r != null) {
|
|
64
|
+
// @ts-ignore
|
|
65
|
+
stack.push(...(Array.isArray(child.r) ? child.r : [child.r]));
|
|
66
|
+
}
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (node.typ == EnumToken.DeclarationNodeType) {
|
|
70
|
+
if (child.typ == ValidationTokenEnum.DeclarationDefinitionToken) {
|
|
71
|
+
if (node.nam == child.nam) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (child.typ == ValidationTokenEnum.PropertyType) {
|
|
77
|
+
if (['group-rule-body', 'block-contents', 'rule-list', 'stylesheet'].includes(child.val)) {
|
|
78
|
+
if ((node.typ == EnumToken.RuleNodeType ||
|
|
79
|
+
node.typ == EnumToken.AtRuleNodeType ||
|
|
80
|
+
node.typ == EnumToken.KeyframesAtRuleNodeType)) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
if (node.typ == EnumToken.DeclarationNodeType) {
|
|
84
|
+
let parent = node.parent;
|
|
85
|
+
while (parent != null) {
|
|
86
|
+
if (parent.parent?.typ == EnumToken.RuleNodeType) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
parent = parent.parent;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (['declaration-list', 'feature-value-declaration'].includes(child.val) && node.typ == EnumToken.DeclarationNodeType) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
if (child.val == 'page-body' && (node.typ == EnumToken.DeclarationNodeType || (node.typ == EnumToken.AtRuleNodeType && [
|
|
97
|
+
'top-left-corner', 'top-left', 'top-center', 'top-right', 'top-right-corner',
|
|
98
|
+
'bottom-left-corner', 'bottom-left', 'bottom-center', 'bottom-right', 'bottom-right-corner',
|
|
99
|
+
'left-top', 'left-middle', 'left-bottom', 'right-top', 'right-middle', 'right-bottom'
|
|
100
|
+
].includes(node.nam)))) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
if (child.val == 'feature-value-block-list' &&
|
|
104
|
+
(node.typ == EnumToken.AtRuleNodeType && ['stylistic', 'historical-forms', 'styleset', 'character-variant', 'swash', 'ornaments', 'annotation'].includes(node.nam))) {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
if (['feature-value-declaration-list', 'feature-value-declaration'].includes(child.val) && node.typ == EnumToken.DeclarationNodeType) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
if (child.val == 'page-body') {
|
|
111
|
+
if (node.typ == EnumToken.DeclarationNodeType) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// console.error(`isNodeAllowedInContext: Not found ${(child as ValidationPropertyToken).val}`, {
|
|
116
|
+
// child,
|
|
117
|
+
// node
|
|
118
|
+
// });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Create a syntax validation context from a list of tokens
|
|
127
|
+
* @param input
|
|
128
|
+
*/
|
|
20
129
|
function createContext(input) {
|
|
21
130
|
const values = input.slice();
|
|
22
131
|
const result = values.filter(token => token.typ != EnumToken.CommentTokenType).slice();
|
|
@@ -77,7 +186,22 @@ function createContext(input) {
|
|
|
77
186
|
}
|
|
78
187
|
};
|
|
79
188
|
}
|
|
80
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Evaluate the validity of the syntax of a node
|
|
191
|
+
* @param node
|
|
192
|
+
* @param parent
|
|
193
|
+
* @param options
|
|
194
|
+
*/
|
|
195
|
+
function evaluateSyntax(node, parent, options) {
|
|
196
|
+
if (node.validSyntax) {
|
|
197
|
+
return {
|
|
198
|
+
valid: SyntaxValidationResult.Valid,
|
|
199
|
+
node,
|
|
200
|
+
syntax: null,
|
|
201
|
+
error: '',
|
|
202
|
+
context: []
|
|
203
|
+
};
|
|
204
|
+
}
|
|
81
205
|
let ast;
|
|
82
206
|
let result;
|
|
83
207
|
switch (node.typ) {
|
|
@@ -85,25 +209,34 @@ function evaluateSyntax(node, options) {
|
|
|
85
209
|
if (node.nam.startsWith('--')) {
|
|
86
210
|
break;
|
|
87
211
|
}
|
|
212
|
+
let token = null;
|
|
213
|
+
let values = node.val.slice();
|
|
88
214
|
ast = getParsedSyntax("declarations" /* ValidationSyntaxGroupEnum.Declarations */, node.nam);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
215
|
+
while (values.length > 0) {
|
|
216
|
+
token = values.at(-1);
|
|
217
|
+
if (token.typ == EnumToken.WhitespaceTokenType || token.typ == EnumToken.CommentTokenType) {
|
|
218
|
+
values.pop();
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
if (token.typ == EnumToken.ImportantTokenType) {
|
|
95
222
|
values.pop();
|
|
96
|
-
|
|
97
|
-
else {
|
|
98
|
-
if (token.typ == EnumToken.ImportantTokenType) {
|
|
223
|
+
if (values.at(-1)?.typ == EnumToken.WhitespaceTokenType) {
|
|
99
224
|
values.pop();
|
|
100
|
-
if (values.at(-1)?.typ == EnumToken.WhitespaceTokenType) {
|
|
101
|
-
values.pop();
|
|
102
|
-
}
|
|
103
225
|
}
|
|
104
|
-
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (ast == null) {
|
|
231
|
+
if (parent?.typ == EnumToken.AtRuleNodeType) {
|
|
232
|
+
ast = (getParsedSyntax("atRules" /* ValidationSyntaxGroupEnum.AtRules */, ['@' + parent.nam, 'descriptors', node.nam]));
|
|
233
|
+
if (ast == null) {
|
|
234
|
+
ast = (getParsedSyntax("atRules" /* ValidationSyntaxGroupEnum.AtRules */, ['@' + parent.nam, 'descriptors', node.nam]) ?? getParsedSyntax("atRules" /* ValidationSyntaxGroupEnum.AtRules */, '@' + parent.nam))?.[0]?.chi;
|
|
235
|
+
values = [{ ...node, val: values }];
|
|
105
236
|
}
|
|
106
237
|
}
|
|
238
|
+
}
|
|
239
|
+
if (ast != null) {
|
|
107
240
|
result = doEvaluateSyntax(ast, createContext(values), { ...options, visited: new WeakMap() });
|
|
108
241
|
if (result.valid == SyntaxValidationResult.Valid && !result.context.done()) {
|
|
109
242
|
let token = null;
|
|
@@ -195,7 +328,7 @@ function doEvaluateSyntax(syntaxes, context, options) {
|
|
|
195
328
|
continue;
|
|
196
329
|
}
|
|
197
330
|
}
|
|
198
|
-
else if (options.
|
|
331
|
+
else if (options.occurrence !== false && syntax.occurence != null) {
|
|
199
332
|
result = matchOccurence(syntax, context, options);
|
|
200
333
|
}
|
|
201
334
|
else if (options.atLeastOnce !== false && syntax.atLeastOnce) {
|
|
@@ -284,7 +417,7 @@ function matchList(syntax, context, options) {
|
|
|
284
417
|
result = doEvaluateSyntax([syntax], createContext(tokens), {
|
|
285
418
|
...options,
|
|
286
419
|
isList: false,
|
|
287
|
-
|
|
420
|
+
occurrence: false
|
|
288
421
|
});
|
|
289
422
|
if (result.valid == SyntaxValidationResult.Valid) {
|
|
290
423
|
context = con.clone();
|
|
@@ -319,7 +452,7 @@ function matchOccurence(syntax, context, options) {
|
|
|
319
452
|
let counter = 0;
|
|
320
453
|
let result;
|
|
321
454
|
do {
|
|
322
|
-
result = match(syntax, context.clone(), { ...options,
|
|
455
|
+
result = match(syntax, context.clone(), { ...options, occurrence: false });
|
|
323
456
|
if (result.valid == SyntaxValidationResult.Drop) {
|
|
324
457
|
break;
|
|
325
458
|
}
|
|
@@ -370,7 +503,7 @@ function match(syntax, context, options) {
|
|
|
370
503
|
...options,
|
|
371
504
|
isRepeatable: null,
|
|
372
505
|
isList: null,
|
|
373
|
-
|
|
506
|
+
occurrence: null,
|
|
374
507
|
atLeastOnce: null
|
|
375
508
|
});
|
|
376
509
|
if (result.valid == SyntaxValidationResult.Valid) {
|
|
@@ -382,7 +515,7 @@ function match(syntax, context, options) {
|
|
|
382
515
|
case ValidationTokenEnum.Keyword:
|
|
383
516
|
success = (token.typ == EnumToken.IdenTokenType || token.typ == EnumToken.DashedIdenTokenType || isIdentColor(token)) &&
|
|
384
517
|
(token.val == syntax.val ||
|
|
385
|
-
syntax.val === token.val?.toLowerCase?.() ||
|
|
518
|
+
syntax.val.toLowerCase() === token.val?.toLowerCase?.() ||
|
|
386
519
|
// config.declarations.all
|
|
387
520
|
allValues.includes(token.val.toLowerCase()));
|
|
388
521
|
if (success) {
|
|
@@ -421,7 +554,7 @@ function match(syntax, context, options) {
|
|
|
421
554
|
...options,
|
|
422
555
|
isRepeatable: null,
|
|
423
556
|
isList: null,
|
|
424
|
-
|
|
557
|
+
occurrence: null,
|
|
425
558
|
atLeastOnce: null
|
|
426
559
|
});
|
|
427
560
|
case ValidationTokenEnum.Comma:
|
|
@@ -456,7 +589,7 @@ function match(syntax, context, options) {
|
|
|
456
589
|
...options,
|
|
457
590
|
isRepeatable: null,
|
|
458
591
|
isList: null,
|
|
459
|
-
|
|
592
|
+
occurrence: null,
|
|
460
593
|
atLeastOnce: null
|
|
461
594
|
}).valid == SyntaxValidationResult.Valid;
|
|
462
595
|
if (success) {
|
|
@@ -479,18 +612,19 @@ function match(syntax, context, options) {
|
|
|
479
612
|
}
|
|
480
613
|
function matchPropertyType(syntax, context, options) {
|
|
481
614
|
if (![
|
|
482
|
-
'
|
|
615
|
+
'color',
|
|
483
616
|
'integer',
|
|
617
|
+
'bg-position',
|
|
484
618
|
'length-percentage', 'flex', 'calc-sum', 'color',
|
|
485
619
|
'color-base', 'system-color', 'deprecated-system-color',
|
|
486
|
-
'pseudo-class-selector', 'pseudo-element-selector'
|
|
620
|
+
'pseudo-class-selector', 'pseudo-element-selector', 'feature-value-declaration'
|
|
487
621
|
].includes(syntax.val)) {
|
|
488
622
|
if (syntax.val in config["syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */]) {
|
|
489
623
|
return doEvaluateSyntax(getParsedSyntax("syntaxes" /* ValidationSyntaxGroupEnum.Syntaxes */, syntax.val), context, {
|
|
490
624
|
...options,
|
|
491
625
|
isRepeatable: null,
|
|
492
626
|
isList: null,
|
|
493
|
-
|
|
627
|
+
occurrence: null,
|
|
494
628
|
atLeastOnce: null
|
|
495
629
|
});
|
|
496
630
|
}
|
|
@@ -502,7 +636,7 @@ function matchPropertyType(syntax, context, options) {
|
|
|
502
636
|
...options,
|
|
503
637
|
isRepeatable: null,
|
|
504
638
|
isList: null,
|
|
505
|
-
|
|
639
|
+
occurrence: null,
|
|
506
640
|
atLeastOnce: null
|
|
507
641
|
});
|
|
508
642
|
if (result.valid == SyntaxValidationResult.Valid) {
|
|
@@ -607,6 +741,14 @@ function matchPropertyType(syntax, context, options) {
|
|
|
607
741
|
(token.typ == EnumToken.IdenTokenType && typeof Math[token.val.toUpperCase()] == 'number') ||
|
|
608
742
|
[EnumToken.BinaryExpressionTokenType, EnumToken.NumberTokenType, EnumToken.PercentageTokenType, EnumToken.DimensionTokenType, EnumToken.LengthTokenType, EnumToken.AngleTokenType, EnumToken.TimeTokenType, EnumToken.ResolutionTokenType, EnumToken.FrequencyTokenType].includes(token.typ);
|
|
609
743
|
break;
|
|
744
|
+
case 'declaration':
|
|
745
|
+
{
|
|
746
|
+
success = token.typ == EnumToken.DeclarationNodeType;
|
|
747
|
+
if (success) {
|
|
748
|
+
success = evaluateSyntax(token, null, options).valid == SyntaxValidationResult.Valid;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
break;
|
|
610
752
|
case 'declaration-value':
|
|
611
753
|
while (!context.done()) {
|
|
612
754
|
context.next();
|
|
@@ -633,13 +775,17 @@ function matchPropertyType(syntax, context, options) {
|
|
|
633
775
|
break;
|
|
634
776
|
case 'color':
|
|
635
777
|
case 'color-base':
|
|
636
|
-
success = token.typ == EnumToken.ColorTokenType ||
|
|
778
|
+
success = token.typ == EnumToken.ColorTokenType ||
|
|
779
|
+
(token.typ == EnumToken.IdenTokenType && 'currentcolor' === token.val.toLowerCase()) ||
|
|
780
|
+
(token.typ == EnumToken.IdenTokenType && 'transparent' === token.val.toLowerCase()) ||
|
|
781
|
+
(token.typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(token.val) ||
|
|
782
|
+
isColor(token));
|
|
637
783
|
if (!success && token.typ == EnumToken.FunctionTokenType && colorsFunc.includes(token.val)) {
|
|
638
784
|
success = doEvaluateSyntax(getParsedSyntax("functions" /* ValidationSyntaxGroupEnum.Functions */, token.val)?.[0]?.chi, createContext(token.chi), {
|
|
639
785
|
...options,
|
|
640
786
|
isRepeatable: null,
|
|
641
787
|
isList: null,
|
|
642
|
-
|
|
788
|
+
occurrence: null,
|
|
643
789
|
atLeastOnce: null
|
|
644
790
|
}).valid == SyntaxValidationResult.Valid;
|
|
645
791
|
}
|
|
@@ -647,8 +793,26 @@ function matchPropertyType(syntax, context, options) {
|
|
|
647
793
|
case 'hex-color':
|
|
648
794
|
success = (token.typ == EnumToken.ColorTokenType && token.kin == ColorType.HEX) || (token.typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(token.val));
|
|
649
795
|
break;
|
|
796
|
+
case 'feature-value-declaration':
|
|
797
|
+
{
|
|
798
|
+
let hasNumber = false;
|
|
799
|
+
success = token.typ == EnumToken.DeclarationNodeType && token.val.length > 0 && token.val.every((val) => {
|
|
800
|
+
if (val.typ == EnumToken.WhitespaceTokenType || val.typ == EnumToken.CommentTokenType) {
|
|
801
|
+
return true;
|
|
802
|
+
}
|
|
803
|
+
const success = (val.typ == EnumToken.NumberTokenType && Number.isInteger(+val.val) && val.val > 0) || (val.typ == EnumToken.FunctionTokenType && mathFuncs.includes(val.val.toLowerCase()) || (val.typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(val.val)));
|
|
804
|
+
if (success) {
|
|
805
|
+
hasNumber = true;
|
|
806
|
+
}
|
|
807
|
+
if ('range' in syntax) {
|
|
808
|
+
return success && +val.val >= +syntax.range[0] && +val.val <= +syntax.range[1];
|
|
809
|
+
}
|
|
810
|
+
return success;
|
|
811
|
+
}) && hasNumber;
|
|
812
|
+
}
|
|
813
|
+
break;
|
|
650
814
|
case 'integer':
|
|
651
|
-
success = (token.typ == EnumToken.NumberTokenType && Number.isInteger(+
|
|
815
|
+
success = (token.typ == EnumToken.NumberTokenType && Number.isInteger(+token.val) && token.val > 0) || (token.typ == EnumToken.FunctionTokenType && mathFuncs.includes(token.val.toLowerCase()) || (token.typ == EnumToken.FunctionTokenType && wildCardFuncs.includes(token.val)));
|
|
652
816
|
if ('range' in syntax) {
|
|
653
817
|
success = success && +token.val >= +syntax.range[0] && +token.val <= +syntax.range[1];
|
|
654
818
|
}
|
|
@@ -712,7 +876,7 @@ function matchPropertyType(syntax, context, options) {
|
|
|
712
876
|
...options,
|
|
713
877
|
isRepeatable: null,
|
|
714
878
|
isList: null,
|
|
715
|
-
|
|
879
|
+
occurrence: null,
|
|
716
880
|
atLeastOnce: null
|
|
717
881
|
}).valid == SyntaxValidationResult.Valid;
|
|
718
882
|
}
|
|
@@ -728,7 +892,7 @@ function matchPropertyType(syntax, context, options) {
|
|
|
728
892
|
...options,
|
|
729
893
|
isRepeatable: null,
|
|
730
894
|
isList: null,
|
|
731
|
-
|
|
895
|
+
occurrence: null,
|
|
732
896
|
atLeastOnce: null
|
|
733
897
|
}).valid == SyntaxValidationResult.Valid;
|
|
734
898
|
}
|
|
@@ -875,7 +1039,6 @@ function allOf(syntax, context, options) {
|
|
|
875
1039
|
i = -1;
|
|
876
1040
|
}
|
|
877
1041
|
}
|
|
878
|
-
// console.error()
|
|
879
1042
|
const success = syntax.length == 0;
|
|
880
1043
|
return {
|
|
881
1044
|
valid: success ? SyntaxValidationResult.Valid : SyntaxValidationResult.Drop,
|
|
@@ -902,4 +1065,4 @@ function flatten(syntax) {
|
|
|
902
1065
|
return data;
|
|
903
1066
|
}
|
|
904
1067
|
|
|
905
|
-
export { createContext, doEvaluateSyntax, evaluateSyntax };
|
|
1068
|
+
export { createContext, doEvaluateSyntax, evaluateSyntax, isNodeAllowedInContext };
|
package/dist/node.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import process from 'node:process';
|
|
2
2
|
export { ColorType, EnumToken, ValidationLevel } from './lib/ast/types.js';
|
|
3
3
|
export { minify } from './lib/ast/minify.js';
|
|
4
|
-
export {
|
|
4
|
+
export { WalkerEvent, WalkerOptionEnum, walk, walkValues } from './lib/ast/walk.js';
|
|
5
5
|
export { expand } from './lib/ast/expand.js';
|
|
6
6
|
import { doRender } from './lib/renderer/render.js';
|
|
7
7
|
export { renderToken } from './lib/renderer/render.js';
|
|
@@ -14,38 +14,50 @@ export { convertColor } from './lib/syntax/color/color.js';
|
|
|
14
14
|
import './lib/syntax/color/utils/constants.js';
|
|
15
15
|
export { isOkLabClose, okLabDistance } from './lib/syntax/color/utils/distance.js';
|
|
16
16
|
import './lib/validation/config.js';
|
|
17
|
-
import './lib/validation/parser/types.js';
|
|
18
17
|
import './lib/validation/parser/parse.js';
|
|
19
18
|
import './lib/validation/syntaxes/complex-selector.js';
|
|
20
19
|
import './lib/validation/syntax.js';
|
|
21
20
|
import { resolve, matchUrl, dirname } from './lib/fs/resolve.js';
|
|
22
21
|
import { Readable } from 'node:stream';
|
|
23
22
|
import { createReadStream } from 'node:fs';
|
|
23
|
+
import { readFile, lstat } from 'node:fs/promises';
|
|
24
24
|
export { FeatureWalkMode } from './lib/ast/features/type.js';
|
|
25
25
|
|
|
26
|
-
/**
|
|
27
|
-
* node module entry point
|
|
28
|
-
* @module node
|
|
29
|
-
*/
|
|
30
26
|
/**
|
|
31
27
|
* load file or url as stream
|
|
32
28
|
* @param url
|
|
33
29
|
* @param currentFile
|
|
30
|
+
* @param asStream
|
|
31
|
+
* @throws Error file not found
|
|
34
32
|
*
|
|
35
33
|
* @private
|
|
36
34
|
*/
|
|
37
|
-
async function
|
|
35
|
+
async function load(url, currentFile = '.', asStream = false) {
|
|
38
36
|
const resolved = resolve(url, currentFile);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
if (matchUrl.test(resolved.absolute)) {
|
|
38
|
+
return fetch(resolved.absolute).then(async (response) => {
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
throw new Error(`${response.status} ${response.statusText} ${response.url}`);
|
|
41
|
+
}
|
|
42
|
+
return asStream ? response.body : await response.text();
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
if (!asStream) {
|
|
47
|
+
return readFile(resolved.absolute, 'utf-8');
|
|
43
48
|
}
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
const stats = await lstat(resolved.absolute);
|
|
50
|
+
if (stats.isFile()) {
|
|
51
|
+
return Readable.toWeb(createReadStream(resolved.absolute, { encoding: 'utf-8', highWaterMark: 64 * 1024 }));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
console.warn(error);
|
|
56
|
+
}
|
|
57
|
+
throw new Error(`File not found: '${resolved.absolute || url}'`);
|
|
46
58
|
}
|
|
47
59
|
/**
|
|
48
|
-
* render ast tree
|
|
60
|
+
* render the ast tree
|
|
49
61
|
* @param data
|
|
50
62
|
* @param options
|
|
51
63
|
*
|
|
@@ -55,22 +67,33 @@ async function getStream(url, currentFile = '.') {
|
|
|
55
67
|
*
|
|
56
68
|
* import {render, ColorType} from '@tbela99/css-parser';
|
|
57
69
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* console.log(result.code);
|
|
70
|
+
* const css = 'body { color: color(from hsl(0 100% 50%) xyz x y z); }';
|
|
71
|
+
* const parseResult = await parse(css);
|
|
61
72
|
*
|
|
62
|
-
*
|
|
63
|
-
* result = await parseFile(ast, {beatify: true, convertColor: ColorType.SRGB});
|
|
73
|
+
* let renderResult = render(parseResult.ast);
|
|
64
74
|
* console.log(result.code);
|
|
75
|
+
*
|
|
76
|
+
* // body{color:red}
|
|
77
|
+
*
|
|
78
|
+
*
|
|
79
|
+
* renderResult = render(parseResult.ast, {beautify: true, convertColor: ColorType.SRGB});
|
|
80
|
+
* console.log(renderResult.code);
|
|
81
|
+
*
|
|
82
|
+
* // body {
|
|
83
|
+
* // color: color(srgb 1 0 0)
|
|
84
|
+
* // }
|
|
65
85
|
* ```
|
|
66
86
|
*/
|
|
67
87
|
function render(data, options = {}) {
|
|
68
|
-
return doRender(data, Object.assign(options, {
|
|
88
|
+
return doRender(data, Object.assign(options, { resolve, dirname, cwd: options.cwd ?? process.cwd() }));
|
|
69
89
|
}
|
|
70
90
|
/**
|
|
71
91
|
* parse css file
|
|
72
92
|
* @param file url or path
|
|
73
93
|
* @param options
|
|
94
|
+
* @param asStream load file as stream
|
|
95
|
+
*
|
|
96
|
+
* @throws Error file not found
|
|
74
97
|
*
|
|
75
98
|
* Example:
|
|
76
99
|
*
|
|
@@ -87,23 +110,23 @@ function render(data, options = {}) {
|
|
|
87
110
|
* console.log(result.ast);
|
|
88
111
|
* ```
|
|
89
112
|
*/
|
|
90
|
-
async function parseFile(file, options = {}) {
|
|
91
|
-
return
|
|
113
|
+
async function parseFile(file, options = {}, asStream = false) {
|
|
114
|
+
return Promise.resolve((options.load ?? load)(file, '.', asStream)).then(stream => parse(stream, { src: file, ...options }));
|
|
92
115
|
}
|
|
93
116
|
/**
|
|
94
117
|
* parse css
|
|
95
118
|
* @param stream
|
|
96
|
-
* @param
|
|
119
|
+
* @param options
|
|
97
120
|
*
|
|
98
121
|
* Example:
|
|
99
122
|
*
|
|
100
123
|
* ```ts
|
|
101
124
|
*
|
|
102
|
-
* import {
|
|
125
|
+
* import {parse} from '@tbela99/css-parser';
|
|
103
126
|
*
|
|
104
127
|
* // css string
|
|
105
|
-
* let result = await
|
|
106
|
-
* console.log(result.
|
|
128
|
+
* let result = await parse(css);
|
|
129
|
+
* console.log(result.ast);
|
|
107
130
|
* ```
|
|
108
131
|
*
|
|
109
132
|
* Example using stream
|
|
@@ -116,35 +139,38 @@ async function parseFile(file, options = {}) {
|
|
|
116
139
|
* // usage: node index.ts < styles.css or cat styles.css | node index.ts
|
|
117
140
|
*
|
|
118
141
|
* const readableStream = Readable.toWeb(process.stdin);
|
|
119
|
-
*
|
|
142
|
+
* let result = await parse(readableStream, {beautify: true});
|
|
120
143
|
*
|
|
121
144
|
* console.log(result.ast);
|
|
122
145
|
* ```
|
|
123
146
|
*
|
|
124
|
-
* Example using fetch
|
|
147
|
+
* Example using fetch and readable stream
|
|
125
148
|
*
|
|
126
149
|
* ```ts
|
|
127
150
|
*
|
|
128
151
|
* import {parse} from '@tbela99/css-parser';
|
|
129
152
|
*
|
|
130
153
|
* const response = await fetch('https://docs.deno.com/styles.css');
|
|
131
|
-
* result = await parse(response.body, {beautify: true});
|
|
154
|
+
* const result = await parse(response.body, {beautify: true});
|
|
132
155
|
*
|
|
133
156
|
* console.log(result.ast);
|
|
134
157
|
* ```
|
|
135
158
|
*/
|
|
136
|
-
async function parse(stream,
|
|
159
|
+
async function parse(stream, options = {}) {
|
|
137
160
|
return doParse(stream instanceof ReadableStream ? tokenizeStream(stream) : tokenize({
|
|
138
161
|
stream,
|
|
139
162
|
buffer: '',
|
|
140
163
|
position: { ind: 0, lin: 1, col: 1 },
|
|
141
164
|
currentPosition: { ind: -1, lin: 1, col: 0 }
|
|
142
|
-
}), Object.assign(
|
|
165
|
+
}), Object.assign(options, { load, resolve, dirname, cwd: options.cwd ?? process.cwd() }));
|
|
143
166
|
}
|
|
144
167
|
/**
|
|
145
168
|
* transform css file
|
|
146
169
|
* @param file url or path
|
|
147
170
|
* @param options
|
|
171
|
+
* @param asStream load file as stream
|
|
172
|
+
*
|
|
173
|
+
* @throws Error file not found
|
|
148
174
|
*
|
|
149
175
|
* Example:
|
|
150
176
|
*
|
|
@@ -161,8 +187,8 @@ async function parse(stream, opt = {}) {
|
|
|
161
187
|
* console.log(result.code);
|
|
162
188
|
* ```
|
|
163
189
|
*/
|
|
164
|
-
async function transformFile(file, options = {}) {
|
|
165
|
-
return
|
|
190
|
+
async function transformFile(file, options = {}, asStream = false) {
|
|
191
|
+
return Promise.resolve((options.load ?? load)(file, '.', asStream)).then(stream => transform(stream, { src: file, ...options }));
|
|
166
192
|
}
|
|
167
193
|
/**
|
|
168
194
|
* transform css
|
|
@@ -176,7 +202,7 @@ async function transformFile(file, options = {}) {
|
|
|
176
202
|
* import {transform} from '@tbela99/css-parser';
|
|
177
203
|
*
|
|
178
204
|
* // css string
|
|
179
|
-
*
|
|
205
|
+
* const result = await transform(css);
|
|
180
206
|
* console.log(result.code);
|
|
181
207
|
* ```
|
|
182
208
|
*
|
|
@@ -211,7 +237,8 @@ async function transform(css, options = {}) {
|
|
|
211
237
|
options = { minify: true, removeEmpty: true, removeCharset: true, ...options };
|
|
212
238
|
const startTime = performance.now();
|
|
213
239
|
return parse(css, options).then((parseResult) => {
|
|
214
|
-
|
|
240
|
+
// ast already expanded by parse
|
|
241
|
+
const rendered = render(parseResult.ast, { ...options, expandNestingRules: false });
|
|
215
242
|
return {
|
|
216
243
|
...parseResult,
|
|
217
244
|
...rendered,
|
|
@@ -226,4 +253,4 @@ async function transform(css, options = {}) {
|
|
|
226
253
|
});
|
|
227
254
|
}
|
|
228
255
|
|
|
229
|
-
export { dirname,
|
|
256
|
+
export { dirname, load, parse, parseFile, render, resolve, transform, transformFile };
|