@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.
Files changed (59) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +59 -20
  3. package/dist/index-umd-web.js +1846 -1075
  4. package/dist/index.cjs +1941 -1202
  5. package/dist/index.d.ts +914 -181
  6. package/dist/lib/ast/expand.js +5 -10
  7. package/dist/lib/ast/features/calc.js +8 -8
  8. package/dist/lib/ast/features/inlinecssvariables.js +9 -8
  9. package/dist/lib/ast/features/prefix.js +5 -15
  10. package/dist/lib/ast/features/shorthand.js +5 -6
  11. package/dist/lib/ast/features/transform.js +18 -25
  12. package/dist/lib/ast/features/type.js +4 -2
  13. package/dist/lib/ast/minify.js +56 -112
  14. package/dist/lib/ast/transform/compute.js +2 -4
  15. package/dist/lib/ast/transform/matrix.js +20 -20
  16. package/dist/lib/ast/transform/minify.js +105 -12
  17. package/dist/lib/ast/transform/rotate.js +11 -11
  18. package/dist/lib/ast/transform/scale.js +6 -6
  19. package/dist/lib/ast/transform/skew.js +4 -4
  20. package/dist/lib/ast/transform/translate.js +3 -3
  21. package/dist/lib/ast/transform/utils.js +30 -37
  22. package/dist/lib/ast/types.js +16 -4
  23. package/dist/lib/ast/walk.js +172 -70
  24. package/dist/lib/fs/resolve.js +12 -7
  25. package/dist/lib/parser/declaration/list.js +3 -1
  26. package/dist/lib/parser/parse.js +441 -161
  27. package/dist/lib/parser/tokenize.js +12 -14
  28. package/dist/lib/renderer/render.js +7 -7
  29. package/dist/lib/syntax/color/cmyk.js +6 -3
  30. package/dist/lib/syntax/color/color-mix.js +2 -3
  31. package/dist/lib/syntax/color/color.js +28 -6
  32. package/dist/lib/syntax/color/hex.js +3 -0
  33. package/dist/lib/syntax/color/hsl.js +18 -7
  34. package/dist/lib/syntax/color/hwb.js +3 -3
  35. package/dist/lib/syntax/color/lab.js +4 -4
  36. package/dist/lib/syntax/color/lch.js +7 -4
  37. package/dist/lib/syntax/color/oklab.js +4 -4
  38. package/dist/lib/syntax/color/oklch.js +18 -6
  39. package/dist/lib/syntax/color/relativecolor.js +9 -56
  40. package/dist/lib/syntax/color/srgb.js +1 -1
  41. package/dist/lib/syntax/syntax.js +36 -18
  42. package/dist/lib/validation/at-rules/container.js +11 -0
  43. package/dist/lib/validation/at-rules/counter-style.js +11 -0
  44. package/dist/lib/validation/at-rules/font-feature-values.js +11 -0
  45. package/dist/lib/validation/at-rules/keyframes.js +11 -0
  46. package/dist/lib/validation/at-rules/layer.js +11 -0
  47. package/dist/lib/validation/at-rules/media.js +11 -0
  48. package/dist/lib/validation/at-rules/page-margin-box.js +11 -0
  49. package/dist/lib/validation/at-rules/page.js +11 -0
  50. package/dist/lib/validation/at-rules/supports.js +11 -0
  51. package/dist/lib/validation/at-rules/when.js +11 -0
  52. package/dist/lib/validation/config.js +0 -2
  53. package/dist/lib/validation/config.json.js +21 -9
  54. package/dist/lib/validation/parser/parse.js +53 -2
  55. package/dist/lib/validation/syntax.js +199 -36
  56. package/dist/node.js +63 -36
  57. package/dist/web.js +84 -25
  58. package/package.json +7 -5
  59. package/dist/lib/validation/parser/types.js +0 -54
@@ -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"] = 0] = "Ignore";
9
+ WalkerOptionEnum[WalkerOptionEnum["Ignore"] = 1] = "Ignore";
7
10
  /**
8
11
  * stop walking the tree
9
12
  */
10
- WalkerOptionEnum[WalkerOptionEnum["Stop"] = 1] = "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"] = 2] = "Children";
17
+ WalkerOptionEnum[WalkerOptionEnum["Children"] = 4] = "Children";
15
18
  /**
16
- * ignore children
19
+ * ignore the current node children
17
20
  */
18
- WalkerOptionEnum[WalkerOptionEnum["IgnoreChildren"] = 3] = "IgnoreChildren";
21
+ WalkerOptionEnum[WalkerOptionEnum["IgnoreChildren"] = 8] = "IgnoreChildren";
19
22
  })(WalkerOptionEnum || (WalkerOptionEnum = {}));
20
- var WalkerValueEvent;
21
- (function (WalkerValueEvent) {
23
+ /**
24
+ * event types for the walkValues function
25
+ */
26
+ var WalkerEvent;
27
+ (function (WalkerEvent) {
22
28
  /**
23
29
  * enter node
24
30
  */
25
- WalkerValueEvent[WalkerValueEvent["Enter"] = 0] = "Enter";
31
+ WalkerEvent[WalkerEvent["Enter"] = 1] = "Enter";
26
32
  /**
27
33
  * leave node
28
34
  */
29
- WalkerValueEvent[WalkerValueEvent["Leave"] = 1] = "Leave";
30
- })(WalkerValueEvent || (WalkerValueEvent = {}));
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
- while ((node = parents.shift())) {
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
- if (option === WalkerOptionEnum.Ignore) {
45
- continue;
46
- }
47
- if (option === WalkerOptionEnum.Stop) {
48
- break;
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
- // @ts-ignore
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 !== WalkerOptionEnum.IgnoreChildren && 'chi' in node) {
57
- parents.unshift(...node.chi);
58
- for (const child of node.chi.slice()) {
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: WalkerValueEvent.Enter,
196
+ event: WalkerEvent.Enter,
80
197
  fn: filter
81
198
  };
82
199
  }
83
200
  else if (filter == null) {
84
201
  filter = {
85
- event: WalkerValueEvent.Enter
202
+ event: WalkerEvent.Enter
86
203
  };
87
204
  }
88
- const eventType = filter.event ?? WalkerValueEvent.Enter;
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 == WalkerValueEvent.Enter) {
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
- if (option === WalkerOptionEnum.Ignore) {
99
- continue;
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 === WalkerOptionEnum.Stop) {
102
- break;
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
- if (eventType == WalkerValueEvent.Enter && option !== WalkerOptionEnum.Children) {
111
- yield {
112
- value,
113
- parent: map.get(value) ?? root,
114
- previousValue: previous,
115
- nextValue: stack[0] ?? null,
116
- // @ts-ignore
117
- root: root ?? null
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
- if (reverse) {
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.push(value.l);
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.push(value.op);
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.push(r);
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.push(value.r);
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.unshift(...values);
274
+ stack[reverse ? 'push' : 'unshift'](...values);
163
275
  }
164
276
  }
165
- if (eventType == WalkerValueEvent.Leave && filter.fn != null) {
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 { WalkerOptionEnum, WalkerValueEvent, walk, walkValues };
293
+ export { WalkerEvent, WalkerOptionEnum, walk, walkValues };
@@ -2,12 +2,14 @@ const matchUrl = /^(https?:)?\/\//;
2
2
  /**
3
3
  * return the directory name of a path
4
4
  * @param path
5
- * @internal
5
+ *
6
+ * @private
6
7
  */
7
8
  function dirname(path) {
8
- if (path == '/' || path === '') {
9
- return path;
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
- break;
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 || !this.options.removeDuplicateDeclarations) {
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
  }