@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
package/dist/lib/ast/walk.js
CHANGED
|
@@ -1,61 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* options for the walk function
|
|
3
|
+
*/
|
|
1
4
|
var WalkerOptionEnum;
|
|
2
5
|
(function (WalkerOptionEnum) {
|
|
3
6
|
/**
|
|
4
7
|
* ignore the current node and its children
|
|
5
8
|
*/
|
|
6
|
-
WalkerOptionEnum[WalkerOptionEnum["Ignore"] =
|
|
9
|
+
WalkerOptionEnum[WalkerOptionEnum["Ignore"] = 1] = "Ignore";
|
|
7
10
|
/**
|
|
8
11
|
* stop walking the tree
|
|
9
12
|
*/
|
|
10
|
-
WalkerOptionEnum[WalkerOptionEnum["Stop"] =
|
|
13
|
+
WalkerOptionEnum[WalkerOptionEnum["Stop"] = 2] = "Stop";
|
|
11
14
|
/**
|
|
12
|
-
* ignore node and process children
|
|
15
|
+
* ignore the current node and process its children
|
|
13
16
|
*/
|
|
14
|
-
WalkerOptionEnum[WalkerOptionEnum["Children"] =
|
|
17
|
+
WalkerOptionEnum[WalkerOptionEnum["Children"] = 4] = "Children";
|
|
15
18
|
/**
|
|
16
|
-
* ignore children
|
|
19
|
+
* ignore the current node children
|
|
17
20
|
*/
|
|
18
|
-
WalkerOptionEnum[WalkerOptionEnum["IgnoreChildren"] =
|
|
21
|
+
WalkerOptionEnum[WalkerOptionEnum["IgnoreChildren"] = 8] = "IgnoreChildren";
|
|
19
22
|
})(WalkerOptionEnum || (WalkerOptionEnum = {}));
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
/**
|
|
24
|
+
* event types for the walkValues function
|
|
25
|
+
*/
|
|
26
|
+
var WalkerEvent;
|
|
27
|
+
(function (WalkerEvent) {
|
|
22
28
|
/**
|
|
23
29
|
* enter node
|
|
24
30
|
*/
|
|
25
|
-
|
|
31
|
+
WalkerEvent[WalkerEvent["Enter"] = 1] = "Enter";
|
|
26
32
|
/**
|
|
27
33
|
* leave node
|
|
28
34
|
*/
|
|
29
|
-
|
|
30
|
-
})(
|
|
35
|
+
WalkerEvent[WalkerEvent["Leave"] = 2] = "Leave";
|
|
36
|
+
})(WalkerEvent || (WalkerEvent = {}));
|
|
31
37
|
/**
|
|
32
38
|
* walk ast nodes
|
|
33
|
-
* @param node
|
|
34
|
-
* @param filter
|
|
39
|
+
* @param node initial node
|
|
40
|
+
* @param filter control the walk process
|
|
41
|
+
* @param reverse walk in reverse order
|
|
42
|
+
*
|
|
43
|
+
* ```ts
|
|
44
|
+
*
|
|
45
|
+
* import {walk} from '@tbela99/css-parser';
|
|
46
|
+
*
|
|
47
|
+
* const css = `
|
|
48
|
+
* body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); }
|
|
49
|
+
*
|
|
50
|
+
* html,
|
|
51
|
+
* body {
|
|
52
|
+
* line-height: 1.474;
|
|
53
|
+
* }
|
|
54
|
+
*
|
|
55
|
+
* .ruler {
|
|
56
|
+
*
|
|
57
|
+
* height: 10px;
|
|
58
|
+
* }
|
|
59
|
+
* `;
|
|
60
|
+
*
|
|
61
|
+
* for (const {node, parent, root} of walk(ast)) {
|
|
62
|
+
*
|
|
63
|
+
* // do something with node
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* Using a {@link filter} function to control the ast traversal. the filter function returns a value of type {@link WalkerOption}.
|
|
68
|
+
*
|
|
69
|
+
* ```ts
|
|
70
|
+
* import {EnumToken, transform, walk, WalkerOptionEnum} from '@tbela99/css-parser';
|
|
71
|
+
*
|
|
72
|
+
* const css = `
|
|
73
|
+
* body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); }
|
|
74
|
+
*
|
|
75
|
+
* html,
|
|
76
|
+
* body {
|
|
77
|
+
* line-height: 1.474;
|
|
78
|
+
* }
|
|
79
|
+
*
|
|
80
|
+
* .ruler {
|
|
81
|
+
*
|
|
82
|
+
* height: 10px;
|
|
83
|
+
* }
|
|
84
|
+
* `;
|
|
85
|
+
*
|
|
86
|
+
* function filter(node) {
|
|
87
|
+
*
|
|
88
|
+
* if (node.typ == EnumToken.AstRule && node.sel.includes('html')) {
|
|
89
|
+
*
|
|
90
|
+
* // skip the children of the current node
|
|
91
|
+
* return WalkerOptionEnum.IgnoreChildren;
|
|
92
|
+
* }
|
|
93
|
+
* }
|
|
94
|
+
*
|
|
95
|
+
* const result = await transform(css);
|
|
96
|
+
* for (const {node} of walk(result.ast, filter)) {
|
|
97
|
+
*
|
|
98
|
+
* console.error([EnumToken[node.typ]]);
|
|
99
|
+
* }
|
|
100
|
+
*
|
|
101
|
+
* // [ "StyleSheetNodeType" ]
|
|
102
|
+
* // [ "RuleNodeType" ]
|
|
103
|
+
* // [ "DeclarationNodeType" ]
|
|
104
|
+
* // [ "RuleNodeType" ]
|
|
105
|
+
* // [ "DeclarationNodeType" ]
|
|
106
|
+
* // [ "RuleNodeType" ]
|
|
107
|
+
* // [ "DeclarationNodeType" ]
|
|
108
|
+
* ```
|
|
35
109
|
*/
|
|
36
|
-
function* walk(node, filter) {
|
|
110
|
+
function* walk(node, filter, reverse) {
|
|
37
111
|
const parents = [node];
|
|
38
112
|
const root = node;
|
|
39
113
|
const map = new Map;
|
|
40
|
-
|
|
114
|
+
let isNumeric = false;
|
|
115
|
+
let i = 0;
|
|
116
|
+
while ((node = parents[i++])) {
|
|
41
117
|
let option = null;
|
|
42
118
|
if (filter != null) {
|
|
43
119
|
option = filter(node);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
120
|
+
isNumeric = typeof option == 'number';
|
|
121
|
+
if (isNumeric) {
|
|
122
|
+
if ((option & WalkerOptionEnum.Ignore)) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if ((option & WalkerOptionEnum.Stop)) {
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
49
128
|
}
|
|
50
129
|
}
|
|
51
|
-
|
|
52
|
-
if (option !== 'children') {
|
|
130
|
+
if (!isNumeric || (option & WalkerOptionEnum.Children) === 0) {
|
|
53
131
|
// @ts-ignore
|
|
54
132
|
yield { node, parent: map.get(node), root };
|
|
55
133
|
}
|
|
56
|
-
if (option
|
|
57
|
-
parents.
|
|
58
|
-
for (const child of node.chi
|
|
134
|
+
if ('chi' in node && (!isNumeric || ((option & WalkerOptionEnum.IgnoreChildren) === 0))) {
|
|
135
|
+
parents.splice(i, 0, ...node.chi[reverse ? 'reverse' : 'slice']());
|
|
136
|
+
for (const child of node.chi) {
|
|
59
137
|
map.set(child, node);
|
|
60
138
|
}
|
|
61
139
|
}
|
|
@@ -67,39 +145,80 @@ function* walk(node, filter) {
|
|
|
67
145
|
* @param root
|
|
68
146
|
* @param filter
|
|
69
147
|
* @param reverse
|
|
148
|
+
*
|
|
149
|
+
* Example:
|
|
150
|
+
*
|
|
151
|
+
* ```ts
|
|
152
|
+
*
|
|
153
|
+
* import {AstDeclaration, EnumToken, transform, walkValues} from '@tbela99/css-parser';
|
|
154
|
+
*
|
|
155
|
+
* const css = `
|
|
156
|
+
* body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); }
|
|
157
|
+
* `;
|
|
158
|
+
*
|
|
159
|
+
* const result = await transform(css);
|
|
160
|
+
* const declaration = result.ast.chi[0].chi[0] as AstDeclaration;
|
|
161
|
+
*
|
|
162
|
+
* // walk the node attribute's tokens in reverse order
|
|
163
|
+
* for (const {value} of walkValues(declaration.val, null, null,true)) {
|
|
164
|
+
*
|
|
165
|
+
* console.error([EnumToken[value.typ], value.val]);
|
|
166
|
+
* }
|
|
167
|
+
*
|
|
168
|
+
* // [ "Color", "color" ]
|
|
169
|
+
* // [ "FunctionTokenType", "calc" ]
|
|
170
|
+
* // [ "Number", 0.15 ]
|
|
171
|
+
* // [ "Add", undefined ]
|
|
172
|
+
* // [ "Iden", "b" ]
|
|
173
|
+
* // [ "Whitespace", undefined ]
|
|
174
|
+
* // [ "FunctionTokenType", "calc" ]
|
|
175
|
+
* // [ "Number", 0.24 ]
|
|
176
|
+
* // [ "Add", undefined ]
|
|
177
|
+
* // [ "Iden", "g" ]
|
|
178
|
+
* // [ "Whitespace", undefined ]
|
|
179
|
+
* // [ "Iden", "r" ]
|
|
180
|
+
* // [ "Whitespace", undefined ]
|
|
181
|
+
* // [ "Iden", "display-p3" ]
|
|
182
|
+
* // [ "Whitespace", undefined ]
|
|
183
|
+
* // [ "FunctionTokenType", "var" ]
|
|
184
|
+
* // [ "DashedIden", "--base-color" ]
|
|
185
|
+
* // [ "Whitespace", undefined ]
|
|
186
|
+
* // [ "Iden", "from" ]
|
|
187
|
+
* ```
|
|
70
188
|
*/
|
|
71
189
|
function* walkValues(values, root = null, filter, reverse) {
|
|
72
190
|
// const set = new Set<Token>();
|
|
73
191
|
const stack = values.slice();
|
|
74
192
|
const map = new Map;
|
|
75
193
|
let previous = null;
|
|
76
|
-
// let parent: FunctionToken | ParensToken | BinaryExpressionToken | null = null;
|
|
77
194
|
if (filter != null && typeof filter == 'function') {
|
|
78
195
|
filter = {
|
|
79
|
-
event:
|
|
196
|
+
event: WalkerEvent.Enter,
|
|
80
197
|
fn: filter
|
|
81
198
|
};
|
|
82
199
|
}
|
|
83
200
|
else if (filter == null) {
|
|
84
201
|
filter = {
|
|
85
|
-
event:
|
|
202
|
+
event: WalkerEvent.Enter
|
|
86
203
|
};
|
|
87
204
|
}
|
|
88
|
-
|
|
205
|
+
let isNumeric = false;
|
|
206
|
+
const eventType = filter.event ?? WalkerEvent.Enter;
|
|
89
207
|
while (stack.length > 0) {
|
|
90
208
|
let value = reverse ? stack.pop() : stack.shift();
|
|
91
209
|
let option = null;
|
|
92
|
-
if (filter.fn != null && eventType
|
|
210
|
+
if (filter.fn != null && (eventType & WalkerEvent.Enter)) {
|
|
93
211
|
const isValid = filter.type == null || value.typ == filter.type ||
|
|
94
212
|
(Array.isArray(filter.type) && filter.type.includes(value.typ)) ||
|
|
95
213
|
(typeof filter.type == 'function' && filter.type(value));
|
|
96
214
|
if (isValid) {
|
|
97
|
-
option = filter.fn(value, map.get(value) ?? root);
|
|
98
|
-
|
|
99
|
-
|
|
215
|
+
option = filter.fn(value, map.get(value) ?? root, WalkerEvent.Enter);
|
|
216
|
+
isNumeric = typeof option == 'number';
|
|
217
|
+
if (isNumeric && (option & WalkerOptionEnum.Stop)) {
|
|
218
|
+
return;
|
|
100
219
|
}
|
|
101
|
-
if (option
|
|
102
|
-
|
|
220
|
+
if (isNumeric && (option & WalkerOptionEnum.Ignore)) {
|
|
221
|
+
continue;
|
|
103
222
|
}
|
|
104
223
|
// @ts-ignore
|
|
105
224
|
if (option != null && typeof option == 'object' && 'typ' in option) {
|
|
@@ -107,38 +226,31 @@ function* walkValues(values, root = null, filter, reverse) {
|
|
|
107
226
|
}
|
|
108
227
|
}
|
|
109
228
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
if (option !== WalkerOptionEnum.IgnoreChildren && 'chi' in value) {
|
|
229
|
+
yield {
|
|
230
|
+
value,
|
|
231
|
+
parent: map.get(value) ?? root,
|
|
232
|
+
previousValue: previous,
|
|
233
|
+
nextValue: stack[0] ?? null,
|
|
234
|
+
// @ts-ignore
|
|
235
|
+
root: root ?? null
|
|
236
|
+
};
|
|
237
|
+
if ('chi' in value && (!isNumeric || (option & WalkerOptionEnum.IgnoreChildren) === 0)) {
|
|
121
238
|
const sliced = value.chi.slice();
|
|
122
239
|
for (const child of sliced) {
|
|
123
240
|
map.set(child, value);
|
|
124
241
|
}
|
|
125
|
-
|
|
126
|
-
stack.push(...sliced);
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
stack.unshift(...sliced);
|
|
130
|
-
}
|
|
242
|
+
stack[reverse ? 'push' : 'unshift'](...sliced);
|
|
131
243
|
}
|
|
132
244
|
else {
|
|
133
245
|
const values = [];
|
|
134
246
|
if ('l' in value && value.l != null) {
|
|
135
247
|
// @ts-ignore
|
|
136
|
-
values
|
|
248
|
+
values[reverse ? 'push' : 'unshift'](value.l);
|
|
137
249
|
// @ts-ignore
|
|
138
250
|
map.set(value.l, value);
|
|
139
251
|
}
|
|
140
252
|
if ('op' in value && typeof value.op == 'object') {
|
|
141
|
-
values
|
|
253
|
+
values[reverse ? 'push' : 'unshift'](value.op);
|
|
142
254
|
// @ts-ignore
|
|
143
255
|
map.set(value.op, value);
|
|
144
256
|
}
|
|
@@ -146,46 +258,36 @@ function* walkValues(values, root = null, filter, reverse) {
|
|
|
146
258
|
if (Array.isArray(value.r)) {
|
|
147
259
|
for (const r of value.r) {
|
|
148
260
|
// @ts-ignore
|
|
149
|
-
values
|
|
261
|
+
values[reverse ? 'push' : 'unshift'](r);
|
|
150
262
|
// @ts-ignore
|
|
151
263
|
map.set(r, value);
|
|
152
264
|
}
|
|
153
265
|
}
|
|
154
266
|
else {
|
|
155
267
|
// @ts-ignore
|
|
156
|
-
values
|
|
268
|
+
values[reverse ? 'push' : 'unshift'](value.r);
|
|
157
269
|
// @ts-ignore
|
|
158
270
|
map.set(value.r, value);
|
|
159
271
|
}
|
|
160
272
|
}
|
|
161
273
|
if (values.length > 0) {
|
|
162
|
-
stack
|
|
274
|
+
stack[reverse ? 'push' : 'unshift'](...values);
|
|
163
275
|
}
|
|
164
276
|
}
|
|
165
|
-
if (eventType
|
|
277
|
+
if ((eventType & WalkerEvent.Leave) && filter.fn != null) {
|
|
166
278
|
const isValid = filter.type == null || value.typ == filter.type ||
|
|
167
279
|
(Array.isArray(filter.type) && filter.type.includes(value.typ)) ||
|
|
168
280
|
(typeof filter.type == 'function' && filter.type(value));
|
|
169
281
|
if (isValid) {
|
|
170
|
-
option = filter.fn(value, map.get(value));
|
|
282
|
+
option = filter.fn(value, map.get(value), WalkerEvent.Leave);
|
|
171
283
|
// @ts-ignore
|
|
172
284
|
if (option != null && 'typ' in option) {
|
|
173
285
|
map.set(option, map.get(value) ?? root);
|
|
174
286
|
}
|
|
175
287
|
}
|
|
176
288
|
}
|
|
177
|
-
if (eventType == WalkerValueEvent.Leave && option !== WalkerOptionEnum.Children) {
|
|
178
|
-
yield {
|
|
179
|
-
value,
|
|
180
|
-
parent: map.get(value) ?? root,
|
|
181
|
-
previousValue: previous,
|
|
182
|
-
nextValue: stack[0] ?? null,
|
|
183
|
-
// @ts-ignore
|
|
184
|
-
root: root ?? null
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
289
|
previous = value;
|
|
188
290
|
}
|
|
189
291
|
}
|
|
190
292
|
|
|
191
|
-
export {
|
|
293
|
+
export { WalkerEvent, WalkerOptionEnum, walk, walkValues };
|
package/dist/lib/fs/resolve.js
CHANGED
|
@@ -2,12 +2,14 @@ const matchUrl = /^(https?:)?\/\//;
|
|
|
2
2
|
/**
|
|
3
3
|
* return the directory name of a path
|
|
4
4
|
* @param path
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
|
+
* @private
|
|
6
7
|
*/
|
|
7
8
|
function dirname(path) {
|
|
8
|
-
if (path == '/' || path === '') {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
// if (path == '/' || path === '') {
|
|
10
|
+
//
|
|
11
|
+
// return path;
|
|
12
|
+
// }
|
|
11
13
|
let i = 0;
|
|
12
14
|
let parts = [''];
|
|
13
15
|
for (; i < path.length; i++) {
|
|
@@ -15,9 +17,10 @@ function dirname(path) {
|
|
|
15
17
|
if (chr == '/') {
|
|
16
18
|
parts.push('');
|
|
17
19
|
}
|
|
18
|
-
else if (chr == '?' || chr == '#') {
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
// else if (chr == '?' || chr == '#') {
|
|
21
|
+
//
|
|
22
|
+
// break;
|
|
23
|
+
// }
|
|
21
24
|
else {
|
|
22
25
|
parts[parts.length - 1] += chr;
|
|
23
26
|
}
|
|
@@ -72,6 +75,8 @@ function resolve(url, currentDirectory, cwd) {
|
|
|
72
75
|
relative: url
|
|
73
76
|
};
|
|
74
77
|
}
|
|
78
|
+
cwd ??= '';
|
|
79
|
+
currentDirectory ??= '';
|
|
75
80
|
if (matchUrl.test(currentDirectory)) {
|
|
76
81
|
const path = new URL(url, currentDirectory).href;
|
|
77
82
|
return {
|
|
@@ -26,7 +26,9 @@ class PropertyList {
|
|
|
26
26
|
}
|
|
27
27
|
add(...declarations) {
|
|
28
28
|
for (const declaration of declarations) {
|
|
29
|
-
if (declaration.typ != EnumToken.DeclarationNodeType ||
|
|
29
|
+
if (declaration.typ != EnumToken.DeclarationNodeType ||
|
|
30
|
+
(typeof this.options.removeDuplicateDeclarations === 'string' && this.options.removeDuplicateDeclarations === declaration.nam.toLowerCase()) ||
|
|
31
|
+
(Array.isArray(this.options.removeDuplicateDeclarations) ? this.options.removeDuplicateDeclarations.includes(declaration.nam) : !this.options.removeDuplicateDeclarations)) {
|
|
30
32
|
this.declarations.set(Number(Math.random().toString().slice(2)).toString(36), declaration);
|
|
31
33
|
continue;
|
|
32
34
|
}
|