@tbela99/css-parser 1.3.3 → 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 (46) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +20 -17
  3. package/dist/index-umd-web.js +1031 -571
  4. package/dist/index.cjs +1031 -566
  5. package/dist/index.d.ts +247 -171
  6. package/dist/lib/ast/expand.js +5 -10
  7. package/dist/lib/ast/features/calc.js +3 -2
  8. package/dist/lib/ast/features/inlinecssvariables.js +5 -3
  9. package/dist/lib/ast/features/prefix.js +1 -1
  10. package/dist/lib/ast/features/shorthand.js +1 -0
  11. package/dist/lib/ast/features/transform.js +13 -19
  12. package/dist/lib/ast/minify.js +5 -2
  13. package/dist/lib/ast/transform/compute.js +2 -4
  14. package/dist/lib/ast/transform/matrix.js +20 -20
  15. package/dist/lib/ast/transform/minify.js +105 -12
  16. package/dist/lib/ast/transform/rotate.js +11 -11
  17. package/dist/lib/ast/transform/scale.js +6 -6
  18. package/dist/lib/ast/transform/skew.js +4 -4
  19. package/dist/lib/ast/transform/translate.js +3 -3
  20. package/dist/lib/ast/transform/utils.js +30 -37
  21. package/dist/lib/ast/types.js +14 -2
  22. package/dist/lib/ast/walk.js +61 -49
  23. package/dist/lib/fs/resolve.js +6 -3
  24. package/dist/lib/parser/declaration/list.js +3 -1
  25. package/dist/lib/parser/parse.js +345 -305
  26. package/dist/lib/parser/tokenize.js +11 -13
  27. package/dist/lib/renderer/render.js +2 -2
  28. package/dist/lib/syntax/syntax.js +36 -18
  29. package/dist/lib/validation/at-rules/container.js +11 -0
  30. package/dist/lib/validation/at-rules/counter-style.js +11 -0
  31. package/dist/lib/validation/at-rules/font-feature-values.js +11 -0
  32. package/dist/lib/validation/at-rules/keyframes.js +11 -0
  33. package/dist/lib/validation/at-rules/layer.js +11 -0
  34. package/dist/lib/validation/at-rules/media.js +11 -0
  35. package/dist/lib/validation/at-rules/page-margin-box.js +11 -0
  36. package/dist/lib/validation/at-rules/page.js +11 -0
  37. package/dist/lib/validation/at-rules/supports.js +11 -0
  38. package/dist/lib/validation/at-rules/when.js +11 -0
  39. package/dist/lib/validation/config.js +0 -2
  40. package/dist/lib/validation/config.json.js +21 -1
  41. package/dist/lib/validation/parser/parse.js +53 -2
  42. package/dist/lib/validation/syntax.js +199 -36
  43. package/dist/node.js +17 -12
  44. package/dist/web.js +11 -11
  45. package/package.json +6 -3
  46. package/dist/lib/validation/parser/types.js +0 -54
@@ -2,9 +2,6 @@ const epsilon = 1e-5;
2
2
  function identity() {
3
3
  return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
4
4
  }
5
- function pLength(point) {
6
- return Math.sqrt(point[0] * point[0] + point[1] * point[1] + point[2] * point[2]);
7
- }
8
5
  function normalize(point) {
9
6
  const [x, y, z] = point;
10
7
  const norm = Math.sqrt(point[0] * point[0] + point[1] * point[1] + point[2] * point[2]);
@@ -32,8 +29,14 @@ function multiply(matrixA, matrixB) {
32
29
  function inverse(matrix) {
33
30
  // Create augmented matrix [matrix | identity]
34
31
  let augmented = [
35
- 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
36
- 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1
32
+ ...matrix.slice(0, 4),
33
+ 1, 0, 0, 0,
34
+ ...matrix.slice(4, 8),
35
+ 0, 1, 0, 0,
36
+ ...matrix.slice(8, 12),
37
+ 0, 0, 1, 0,
38
+ ...matrix.slice(12, 16),
39
+ 0, 0, 0, 1
37
40
  ];
38
41
  // Gaussian elimination with partial pivoting
39
42
  for (let col = 0; col < 4; col++) {
@@ -73,24 +76,9 @@ function inverse(matrix) {
73
76
  // Extract the inverse from the right side of the augmented matrix
74
77
  return augmented.slice(0, 16);
75
78
  }
76
- // function transpose(matrix: Matrix): Matrix {
77
- // // Crée une nouvelle matrice vide 4x4
78
- // // @ts-ignore
79
- // let transposed: Matrix = [[], [], [], []] as Matrix;
80
- //
81
- // // Parcourt chaque ligne et colonne pour transposer
82
- // for (let i = 0; i < 4; i++) {
83
- //
84
- // for (let j = 0; j < 4; j++) {
85
- //
86
- // transposed[j][i] = matrix[i][j];
87
- // }
88
- // }
89
- //
90
- // return transposed;
91
- // }
92
79
  function round(number) {
93
- return Math.abs(number) < epsilon ? 0 : +number.toPrecision(6);
80
+ const rounded = Math.round(number);
81
+ return Math.abs(rounded - number) <= epsilon ? rounded : +number.toPrecision(6);
94
82
  }
95
83
  // translate3d(25.9808px, 0, 15px ) rotateY(60deg) skewX(49.9999deg) scale(1, 1.2)
96
84
  // translate → rotate → skew → scale
@@ -113,7 +101,7 @@ function decompose(original) {
113
101
  perspectiveMatrix[15] = 1;
114
102
  // @ts-ignore
115
103
  const inverted = inverse(original.slice());
116
- if (!inverted) {
104
+ if (inverted === null) {
117
105
  return null;
118
106
  }
119
107
  const transposedInverse = transposeMatrix4(inverted);
@@ -134,8 +122,13 @@ function decompose(original) {
134
122
  const row0 = [matrix[0], matrix[1], matrix[2]];
135
123
  const row1 = [matrix[4], matrix[5], matrix[6]];
136
124
  const row2 = [matrix[8], matrix[9], matrix[10]];
125
+ const cross = [
126
+ row1[1] * row2[2] - row1[2] * row2[1],
127
+ row1[2] * row2[0] - row1[0] * row2[2],
128
+ row1[0] * row2[1] - row1[1] * row2[0],
129
+ ];
137
130
  // Compute scale
138
- const scaleX = pLength(row0);
131
+ const scaleX = Math.hypot(...row0);
139
132
  const row0Norm = normalize(row0);
140
133
  const skewXY = dot(row0Norm, row1);
141
134
  const row1Proj = [
@@ -143,7 +136,7 @@ function decompose(original) {
143
136
  row1[1] - skewXY * row0Norm[1],
144
137
  row1[2] - skewXY * row0Norm[2]
145
138
  ];
146
- const scaleY = pLength(row1Proj);
139
+ const scaleY = Math.hypot(...row1Proj);
147
140
  const row1Norm = normalize(row1Proj);
148
141
  const skewXZ = dot(row0Norm, row2);
149
142
  const skewYZ = dot(row1Norm, row2);
@@ -152,8 +145,9 @@ function decompose(original) {
152
145
  row2[1] - skewXZ * row0Norm[1] - skewYZ * row1Norm[1],
153
146
  row2[2] - skewXZ * row0Norm[2] - skewYZ * row1Norm[2]
154
147
  ];
155
- const scaleZ = pLength(row2Proj);
156
148
  const row2Norm = normalize(row2Proj);
149
+ const determinant = row0[0] * cross[0] + row0[1] * cross[1] + row0[2] * cross[2];
150
+ const scaleZ = Math.hypot(...row2Proj) * (determinant < 0 ? -1 : 1);
157
151
  // Build rotation matrix from orthonormalized vectors
158
152
  const r00 = row0Norm[0], r01 = row1Norm[0], r02 = row2Norm[0];
159
153
  const r10 = row0Norm[1], r11 = row1Norm[1], r12 = row2Norm[1];
@@ -190,7 +184,6 @@ function decompose(original) {
190
184
  qz = 0.25 * s;
191
185
  }
192
186
  [qx, qy, qz] = toZero([qx, qy, qz]);
193
- // const q = gcd(qx, gcd(qy, qz));
194
187
  let q = [Math.abs(qx), Math.abs(qy), Math.abs(qz)].reduce((acc, curr) => {
195
188
  if (acc == 0 || (curr > 0 && curr < acc)) {
196
189
  acc = curr;
@@ -235,16 +228,16 @@ function toZero(v) {
235
228
  // https://drafts.csswg.org/css-transforms-1/#2d-matrix
236
229
  function is2DMatrix(matrix) {
237
230
  // m13,m14, m23, m24, m31, m32, m34, m43 are all 0
238
- return matrix[0 * 4 + 2] === 0 &&
239
- matrix[0 * 4 + 3] === 0 &&
240
- matrix[1 * 4 + 2] === 0 &&
241
- matrix[1 * 4 + 3] === 0 &&
242
- matrix[2 * 4 + 0] === 0 &&
243
- matrix[2 * 4 + 1] === 0 &&
244
- matrix[2 * 4 + 3] === 0 &&
245
- matrix[3 * 4 + 2] === 0 &&
246
- matrix[2 * 4 + 2] === 1 &&
247
- matrix[3 * 4 + 3] === 1;
231
+ return matrix[2] === 0 &&
232
+ matrix[3] === 0 &&
233
+ matrix[6] === 0 &&
234
+ matrix[7] === 0 &&
235
+ matrix[8] === 0 &&
236
+ matrix[9] === 0 &&
237
+ matrix[11] === 0 &&
238
+ matrix[14] === 0 &&
239
+ matrix[10] === 1 &&
240
+ matrix[15] === 1;
248
241
  }
249
242
 
250
243
  export { decompose, epsilon, identity, is2DMatrix, multiply, round, toZero };
@@ -19,14 +19,26 @@ var ValidationLevel;
19
19
  * disable validation
20
20
  */
21
21
  ValidationLevel[ValidationLevel["None"] = 0] = "None";
22
+ /**
23
+ * validate selectors
24
+ */
25
+ ValidationLevel[ValidationLevel["Selector"] = 1] = "Selector";
26
+ /**
27
+ * validate at-rules
28
+ */
29
+ ValidationLevel[ValidationLevel["AtRule"] = 2] = "AtRule";
30
+ /**
31
+ * validate declarations
32
+ */
33
+ ValidationLevel[ValidationLevel["Declaration"] = 4] = "Declaration";
22
34
  /**
23
35
  * validate selectors and at-rules
24
36
  */
25
- ValidationLevel[ValidationLevel["Default"] = 1] = "Default";
37
+ ValidationLevel[ValidationLevel["Default"] = 3] = "Default";
26
38
  /**
27
39
  * validate selectors, at-rules and declarations
28
40
  */
29
- ValidationLevel[ValidationLevel["All"] = 2] = "All"; // selectors + at-rules + declarations
41
+ ValidationLevel[ValidationLevel["All"] = 7] = "All"; // selectors + at-rules + declarations
30
42
  })(ValidationLevel || (ValidationLevel = {}));
31
43
  /**
32
44
  * enum of all token types
@@ -12,28 +12,28 @@ var WalkerOptionEnum;
12
12
  */
13
13
  WalkerOptionEnum[WalkerOptionEnum["Stop"] = 2] = "Stop";
14
14
  /**
15
- * ignore node and process children
15
+ * ignore the current node and process its children
16
16
  */
17
17
  WalkerOptionEnum[WalkerOptionEnum["Children"] = 4] = "Children";
18
18
  /**
19
- * ignore children
19
+ * ignore the current node children
20
20
  */
21
21
  WalkerOptionEnum[WalkerOptionEnum["IgnoreChildren"] = 8] = "IgnoreChildren";
22
22
  })(WalkerOptionEnum || (WalkerOptionEnum = {}));
23
23
  /**
24
24
  * event types for the walkValues function
25
25
  */
26
- var WalkerValueEvent;
27
- (function (WalkerValueEvent) {
26
+ var WalkerEvent;
27
+ (function (WalkerEvent) {
28
28
  /**
29
29
  * enter node
30
30
  */
31
- WalkerValueEvent[WalkerValueEvent["Enter"] = 1] = "Enter";
31
+ WalkerEvent[WalkerEvent["Enter"] = 1] = "Enter";
32
32
  /**
33
33
  * leave node
34
34
  */
35
- WalkerValueEvent[WalkerValueEvent["Leave"] = 2] = "Leave";
36
- })(WalkerValueEvent || (WalkerValueEvent = {}));
35
+ WalkerEvent[WalkerEvent["Leave"] = 2] = "Leave";
36
+ })(WalkerEvent || (WalkerEvent = {}));
37
37
  /**
38
38
  * walk ast nodes
39
39
  * @param node initial node
@@ -64,11 +64,10 @@ var WalkerValueEvent;
64
64
  * }
65
65
  * ```
66
66
  *
67
- * Using a filter to control the walk process:
67
+ * Using a {@link filter} function to control the ast traversal. the filter function returns a value of type {@link WalkerOption}.
68
68
  *
69
69
  * ```ts
70
- *
71
- * import {walk} from '@tbela99/css-parser';
70
+ * import {EnumToken, transform, walk, WalkerOptionEnum} from '@tbela99/css-parser';
72
71
  *
73
72
  * const css = `
74
73
  * body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); }
@@ -84,17 +83,28 @@ var WalkerValueEvent;
84
83
  * }
85
84
  * `;
86
85
  *
87
- * for (const {node, parent, root} of walk(ast, (node) => {
86
+ * function filter(node) {
88
87
  *
89
88
  * if (node.typ == EnumToken.AstRule && node.sel.includes('html')) {
90
89
  *
91
90
  * // skip the children of the current node
92
91
  * return WalkerOptionEnum.IgnoreChildren;
93
92
  * }
94
- * })) {
93
+ * }
95
94
  *
96
- * // do something with node
95
+ * const result = await transform(css);
96
+ * for (const {node} of walk(result.ast, filter)) {
97
+ *
98
+ * console.error([EnumToken[node.typ]]);
97
99
  * }
100
+ *
101
+ * // [ "StyleSheetNodeType" ]
102
+ * // [ "RuleNodeType" ]
103
+ * // [ "DeclarationNodeType" ]
104
+ * // [ "RuleNodeType" ]
105
+ * // [ "DeclarationNodeType" ]
106
+ * // [ "RuleNodeType" ]
107
+ * // [ "DeclarationNodeType" ]
98
108
  * ```
99
109
  */
100
110
  function* walk(node, filter, reverse) {
@@ -102,7 +112,8 @@ function* walk(node, filter, reverse) {
102
112
  const root = node;
103
113
  const map = new Map;
104
114
  let isNumeric = false;
105
- while ((node = parents.shift())) {
115
+ let i = 0;
116
+ while ((node = parents[i++])) {
106
117
  let option = null;
107
118
  if (filter != null) {
108
119
  option = filter(node);
@@ -121,8 +132,8 @@ function* walk(node, filter, reverse) {
121
132
  yield { node, parent: map.get(node), root };
122
133
  }
123
134
  if ('chi' in node && (!isNumeric || ((option & WalkerOptionEnum.IgnoreChildren) === 0))) {
124
- parents.unshift(...node.chi[reverse ? 'reverse' : 'slice']());
125
- for (const child of node.chi.slice()) {
135
+ parents.splice(i, 0, ...node.chi[reverse ? 'reverse' : 'slice']());
136
+ for (const child of node.chi) {
126
137
  map.set(child, node);
127
138
  }
128
139
  }
@@ -139,27 +150,41 @@ function* walk(node, filter, reverse) {
139
150
  *
140
151
  * ```ts
141
152
  *
142
- * import {EnumToken, walk} from '@tbela99/css-parser';
153
+ * import {AstDeclaration, EnumToken, transform, walkValues} from '@tbela99/css-parser';
143
154
  *
144
155
  * const css = `
145
156
  * body { color: color(from var(--base-color) display-p3 r calc(g + 0.24) calc(b + 0.15)); }
146
- *
147
- * html,
148
- * body {
149
- * line-height: 1.474;
150
- * }
151
- *
152
- * .ruler {
153
- *
154
- * height: 10px;
155
- * }
156
157
  * `;
157
158
  *
158
- * for (const {value} of walkValues(result.ast.chi[0].chi[0].val, null, null,true)) {
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)) {
159
164
  *
160
165
  * console.error([EnumToken[value.typ], value.val]);
161
166
  * }
162
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
+ * ```
163
188
  */
164
189
  function* walkValues(values, root = null, filter, reverse) {
165
190
  // const set = new Set<Token>();
@@ -168,26 +193,26 @@ function* walkValues(values, root = null, filter, reverse) {
168
193
  let previous = null;
169
194
  if (filter != null && typeof filter == 'function') {
170
195
  filter = {
171
- event: WalkerValueEvent.Enter,
196
+ event: WalkerEvent.Enter,
172
197
  fn: filter
173
198
  };
174
199
  }
175
200
  else if (filter == null) {
176
201
  filter = {
177
- event: WalkerValueEvent.Enter
202
+ event: WalkerEvent.Enter
178
203
  };
179
204
  }
180
205
  let isNumeric = false;
181
- const eventType = filter.event ?? WalkerValueEvent.Enter;
206
+ const eventType = filter.event ?? WalkerEvent.Enter;
182
207
  while (stack.length > 0) {
183
208
  let value = reverse ? stack.pop() : stack.shift();
184
209
  let option = null;
185
- if (filter.fn != null && (eventType & WalkerValueEvent.Enter)) {
210
+ if (filter.fn != null && (eventType & WalkerEvent.Enter)) {
186
211
  const isValid = filter.type == null || value.typ == filter.type ||
187
212
  (Array.isArray(filter.type) && filter.type.includes(value.typ)) ||
188
213
  (typeof filter.type == 'function' && filter.type(value));
189
214
  if (isValid) {
190
- option = filter.fn(value, map.get(value) ?? root, WalkerValueEvent.Enter);
215
+ option = filter.fn(value, map.get(value) ?? root, WalkerEvent.Enter);
191
216
  isNumeric = typeof option == 'number';
192
217
  if (isNumeric && (option & WalkerOptionEnum.Stop)) {
193
218
  return;
@@ -201,7 +226,6 @@ function* walkValues(values, root = null, filter, reverse) {
201
226
  }
202
227
  }
203
228
  }
204
- // if ((eventType & WalkerValueEvent.Enter) && (!isNumeric || ((option as number) & WalkerOptionEnum.Children) === 0)) {
205
229
  yield {
206
230
  value,
207
231
  parent: map.get(value) ?? root,
@@ -210,7 +234,6 @@ function* walkValues(values, root = null, filter, reverse) {
210
234
  // @ts-ignore
211
235
  root: root ?? null
212
236
  };
213
- // }
214
237
  if ('chi' in value && (!isNumeric || (option & WalkerOptionEnum.IgnoreChildren) === 0)) {
215
238
  const sliced = value.chi.slice();
216
239
  for (const child of sliced) {
@@ -251,31 +274,20 @@ function* walkValues(values, root = null, filter, reverse) {
251
274
  stack[reverse ? 'push' : 'unshift'](...values);
252
275
  }
253
276
  }
254
- if ((eventType & WalkerValueEvent.Leave) && filter.fn != null) {
277
+ if ((eventType & WalkerEvent.Leave) && filter.fn != null) {
255
278
  const isValid = filter.type == null || value.typ == filter.type ||
256
279
  (Array.isArray(filter.type) && filter.type.includes(value.typ)) ||
257
280
  (typeof filter.type == 'function' && filter.type(value));
258
281
  if (isValid) {
259
- option = filter.fn(value, map.get(value), WalkerValueEvent.Leave);
282
+ option = filter.fn(value, map.get(value), WalkerEvent.Leave);
260
283
  // @ts-ignore
261
284
  if (option != null && 'typ' in option) {
262
285
  map.set(option, map.get(value) ?? root);
263
286
  }
264
287
  }
265
288
  }
266
- // if ((eventType & WalkerValueEvent.Leave) && (!isNumeric && ((option as number) & WalkerOptionEnum.Children) === 0)) {
267
- //
268
- // yield {
269
- // value,
270
- // parent: <FunctionToken | ParensToken>map.get(value) ?? root,
271
- // previousValue: previous,
272
- // nextValue: <Token>stack[0] ?? null,
273
- // // @ts-ignore
274
- // root: root ?? null
275
- // };
276
- // }
277
289
  previous = value;
278
290
  }
279
291
  }
280
292
 
281
- export { WalkerOptionEnum, WalkerValueEvent, walk, walkValues };
293
+ export { WalkerEvent, WalkerOptionEnum, walk, walkValues };
@@ -17,9 +17,10 @@ function dirname(path) {
17
17
  if (chr == '/') {
18
18
  parts.push('');
19
19
  }
20
- else if (chr == '?' || chr == '#') {
21
- break;
22
- }
20
+ // else if (chr == '?' || chr == '#') {
21
+ //
22
+ // break;
23
+ // }
23
24
  else {
24
25
  parts[parts.length - 1] += chr;
25
26
  }
@@ -74,6 +75,8 @@ function resolve(url, currentDirectory, cwd) {
74
75
  relative: url
75
76
  };
76
77
  }
78
+ cwd ??= '';
79
+ currentDirectory ??= '';
77
80
  if (matchUrl.test(currentDirectory)) {
78
81
  const path = new URL(url, currentDirectory).href;
79
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 || (Array.isArray(this.options.removeDuplicateDeclarations) ? this.options.removeDuplicateDeclarations.includes(declaration.nam) : !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
  }