@projectwallace/css-analyzer 9.4.0 → 9.5.0

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/dist/index.js CHANGED
@@ -1,595 +1,8 @@
1
- import { ATTRIBUTE_SELECTOR, AT_RULE, BLOCK, BREAK, CLASS_SELECTOR, COMBINATOR, CONTAINER_QUERY, DECLARATION, DIMENSION, FUNCTION, HASH, IDENTIFIER, ID_SELECTOR, LAYER_NAME, MEDIA_FEATURE, MEDIA_TYPE, NTH_OF_SELECTOR, NTH_SELECTOR, NUMBER, OPERATOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, SELECTOR, SELECTOR_LIST, SKIP, STYLE_RULE, SUPPORTS_QUERY, TYPE_SELECTOR, URL, is_custom, is_vendor_prefixed, parse, str_equals, str_starts_with, walk } from "@projectwallace/css-parser";
2
- import { parse_selector } from "@projectwallace/css-parser/parse-selector";
3
- //#region src/atrules/atrules.ts
4
- /**
5
- * Check if an @supports atRule is a browserhack (Wallace parser version)
6
- * @param node - The Atrule CSSNode from Wallace parser
7
- */
8
- function isSupportsBrowserhack(node, on_hack) {
9
- walk(node, function(n) {
10
- if (n.type === SUPPORTS_QUERY) {
11
- const normalizedPrelude = (n.prelude || n.value || "").toString().toLowerCase().replaceAll(/\s+/g, "");
12
- if (normalizedPrelude.includes("-webkit-appearance:none")) {
13
- on_hack("-webkit-appearance: none");
14
- return BREAK;
15
- }
16
- if (normalizedPrelude.includes("-moz-appearance:meterbar")) {
17
- on_hack("-moz-appearance: meterbar");
18
- return BREAK;
19
- }
20
- }
21
- });
22
- }
23
- /**
24
- * Check if a @media atRule is a browserhack (Wallace parser version)
25
- * @param node - The Atrule CSSNode from Wallace parser
26
- * @returns true if the atrule is a browserhack
27
- */
28
- function isMediaBrowserhack(node, on_hack) {
29
- walk(node, function(n) {
30
- if (n.type === MEDIA_TYPE) {
31
- const text = n.text || "";
32
- if (text.startsWith("\\0")) {
33
- on_hack("\\0");
34
- return BREAK;
35
- }
36
- if (text.includes("\\9")) {
37
- on_hack("\\9");
38
- return BREAK;
39
- }
40
- }
41
- if (n.type === MEDIA_FEATURE) {
42
- const name = n.property || "";
43
- if (str_equals("-moz-images-in-menus", name)) {
44
- on_hack("-moz-images-in-menus");
45
- return BREAK;
46
- }
47
- if (str_equals("min--moz-device-pixel-ratio", name)) {
48
- on_hack("min--moz-device-pixel-ratio");
49
- return BREAK;
50
- }
51
- if (str_equals("-ms-high-contrast", name)) {
52
- on_hack("-ms-high-contrast");
53
- return BREAK;
54
- }
55
- if (str_equals("min-resolution", name) && n.has_children) {
56
- for (const child of n) if (child.type === DIMENSION && child.value === .001 && str_equals("dpcm", child.unit || "")) {
57
- on_hack("min-resolution: .001dpcm");
58
- return BREAK;
59
- }
60
- }
61
- if (str_equals("-webkit-min-device-pixel-ratio", name) && n.has_children) {
62
- for (const child of n) if (child.type === NUMBER && (child.value === 0 || child.value === 1e4)) {
63
- on_hack("-webkit-min-device-pixel-ratio");
64
- return BREAK;
65
- }
66
- }
67
- if (n.has_children) {
68
- for (const child of n) if (child.type === IDENTIFIER && child.text === "\\0") {
69
- on_hack("\\0");
70
- return BREAK;
71
- }
72
- }
73
- }
74
- });
75
- }
76
- //#endregion
77
- //#region src/keyword-set.ts
78
- /**
79
- * @description A Set-like construct to search CSS keywords in a case-insensitive way
80
- */
81
- var KeywordSet = class {
82
- set;
83
- constructor(items) {
84
- /** @type {Set<string>} */
85
- this.set = new Set(items);
86
- }
87
- has(item) {
88
- return this.set.has(item.toLowerCase());
89
- }
90
- };
91
- //#endregion
92
- //#region src/string-utils.ts
93
- function unquote(str) {
94
- return str.replaceAll(/(?:^['"])|(?:['"]$)/g, "");
95
- }
96
- /**
97
- * Case-insensitive compare two character codes
98
- * @see https://github.com/csstree/csstree/blob/41f276e8862d8223eeaa01a3d113ab70bb13d2d9/lib/tokenizer/utils.js#L22
99
- */
100
- function compareChar(referenceCode, testCode) {
101
- if (testCode >= 65 && testCode <= 90) testCode = testCode | 32;
102
- return referenceCode === testCode;
103
- }
104
- /**
105
- * Case-insensitive testing whether a string ends with a given substring
106
- *
107
- * @example
108
- * endsWith('test', 'my-test') // true
109
- * endsWith('test', 'est') // false
110
- *
111
- * @param base e.g. '-webkit-transform'
112
- * @param maybe e.g. 'transform'
113
- * @returns true if `test` ends with `base`, false otherwise
114
- */
115
- function endsWith(base, maybe) {
116
- if (base === maybe) return true;
117
- let len = maybe.length;
118
- let offset = len - base.length;
119
- if (offset < 0) return false;
120
- for (let i = len - 1; i >= offset; i--) if (compareChar(base.charCodeAt(i - offset), maybe.charCodeAt(i)) === false) return false;
121
- return true;
122
- }
123
- //#endregion
124
- //#region src/selectors/utils.ts
125
- const PSEUDO_FUNCTIONS = new KeywordSet([
126
- "nth-child",
127
- "where",
128
- "not",
129
- "is",
130
- "has",
131
- "nth-last-child",
132
- "matches",
133
- "-webkit-any",
134
- "-moz-any"
135
- ]);
136
- function isPrefixed(selector, on_selector) {
137
- walk(selector, function(node) {
138
- if (node.type === PSEUDO_ELEMENT_SELECTOR || node.type === PSEUDO_CLASS_SELECTOR || node.type === TYPE_SELECTOR) {
139
- if (node.is_vendor_prefixed) {
140
- let prefix = "";
141
- if (node.type === PSEUDO_CLASS_SELECTOR) prefix = ":";
142
- else if (node.type === PSEUDO_ELEMENT_SELECTOR) prefix = "::";
143
- on_selector(prefix + (node.name || node.text));
144
- }
145
- }
146
- });
147
- }
148
- /**
149
- * Check if a Wallace selector is an accessibility selector (has aria-* or role attribute)
150
- */
151
- function isAccessibility(selector, on_selector) {
152
- function normalize(node) {
153
- let clone = node.clone();
154
- if (clone.value) return "[" + clone.name?.toLowerCase() + clone.attr_operator + "\"" + unquote(clone.value.toString()) + "\"]";
155
- return "[" + clone.name?.toLowerCase() + "]";
156
- }
157
- walk(selector, function(node) {
158
- if (node.type === ATTRIBUTE_SELECTOR) {
159
- const name = node.name || "";
160
- if (str_equals("role", name) || str_starts_with(name, "aria-")) on_selector(normalize(node));
161
- }
162
- });
163
- }
164
- /**
165
- * Get the Complexity for a Wallace Selector Node
166
- * @param selector - Wallace CSSNode for a Selector
167
- * @return The numeric complexity of the Selector
168
- */
169
- function getComplexity(selector) {
170
- let complexity = 0;
171
- function findSelectors(node, complexities) {
172
- walk(node, function(n) {
173
- if (n.type === SELECTOR) complexities.push(getComplexity(n));
174
- });
175
- }
176
- walk(selector, function(node) {
177
- const type = node.type;
178
- if (type === SELECTOR) return;
179
- if (type === NTH_SELECTOR) {
180
- if (node.text && node.text.trim()) complexity++;
181
- return;
182
- }
183
- complexity++;
184
- if (type === PSEUDO_ELEMENT_SELECTOR || type === TYPE_SELECTOR || type === PSEUDO_CLASS_SELECTOR) {
185
- if (node.is_vendor_prefixed) complexity++;
186
- }
187
- if (type === ATTRIBUTE_SELECTOR) {
188
- if (node.value) complexity++;
189
- return SKIP;
190
- }
191
- if (type === PSEUDO_CLASS_SELECTOR) {
192
- const name = node.name || "";
193
- if (PSEUDO_FUNCTIONS.has(name.toLowerCase())) {
194
- const childComplexities = [];
195
- if (node.has_children) for (const child of node) if (child.type === SELECTOR) childComplexities.push(getComplexity(child));
196
- else findSelectors(child, childComplexities);
197
- if (childComplexities.length > 0) {
198
- for (const c of childComplexities) complexity += c;
199
- return SKIP;
200
- }
201
- }
202
- }
203
- });
204
- return complexity;
205
- }
206
- /**
207
- * Walk a selector node and trigger a callback every time a Combinator was found
208
- */
209
- function getCombinators(selector, onMatch) {
210
- walk(selector, function(node) {
211
- if (node.type === COMBINATOR) onMatch({
212
- name: node.name?.trim() === "" ? " " : node.name,
213
- loc: {
214
- offset: node.start,
215
- line: node.line,
216
- column: node.column,
217
- length: 1
218
- }
219
- });
220
- });
221
- }
222
- //#endregion
223
- //#region src/selectors/specificity.ts
224
- function compare(s1, s2) {
225
- if (s1[0] === s2[0]) {
226
- if (s1[1] === s2[1]) return s1[2] - s2[2];
227
- return s1[1] - s2[1];
228
- }
229
- return s1[0] - s2[0];
230
- }
231
- function max(list) {
232
- return list.sort(compare).at(-1);
233
- }
234
- const calculateForAST = (selectorAST) => {
235
- let a = 0;
236
- let b = 0;
237
- let c = 0;
238
- let current = selectorAST.first_child;
239
- while (current) {
240
- switch (current.type) {
241
- case ID_SELECTOR:
242
- a += 1;
243
- break;
244
- case ATTRIBUTE_SELECTOR:
245
- case CLASS_SELECTOR:
246
- b += 1;
247
- break;
248
- case PSEUDO_CLASS_SELECTOR:
249
- switch (current.name?.toLowerCase()) {
250
- case "where": break;
251
- case "-webkit-any":
252
- case "any":
253
- if (current.first_child) b += 1;
254
- break;
255
- case "-moz-any":
256
- case "is":
257
- case "matches":
258
- case "not":
259
- case "has":
260
- if (current.has_children) {
261
- const childSelectorList = current.first_child;
262
- if (childSelectorList?.type === SELECTOR_LIST) {
263
- const max1 = max(calculate(childSelectorList));
264
- a += max1[0];
265
- b += max1[1];
266
- c += max1[2];
267
- }
268
- }
269
- break;
270
- case "nth-child":
271
- case "nth-last-child":
272
- b += 1;
273
- const nthOf = current.first_child;
274
- if (nthOf?.type === NTH_OF_SELECTOR && nthOf.selector) {
275
- const max2 = max(calculate(nthOf.selector));
276
- a += max2[0];
277
- b += max2[1];
278
- c += max2[2];
279
- }
280
- break;
281
- case "host-context":
282
- case "host":
283
- b += 1;
284
- const childSelector = current.first_child?.first_child;
285
- if (childSelector?.type === SELECTOR) {
286
- let childPart = childSelector.first_child;
287
- while (childPart) {
288
- if (childPart.type === COMBINATOR) break;
289
- const partSpecificity = calculateForAST({
290
- type_name: "Selector",
291
- first_child: childPart,
292
- has_children: true
293
- });
294
- a += partSpecificity[0] ?? 0;
295
- b += partSpecificity[1] ?? 0;
296
- c += partSpecificity[2] ?? 0;
297
- childPart = childPart.next_sibling;
298
- }
299
- }
300
- break;
301
- case "after":
302
- case "before":
303
- case "first-letter":
304
- case "first-line":
305
- c += 1;
306
- break;
307
- default:
308
- b += 1;
309
- break;
310
- }
311
- break;
312
- case PSEUDO_ELEMENT_SELECTOR:
313
- switch (current.name?.toLowerCase()) {
314
- case "slotted":
315
- c += 1;
316
- const childSelector = current.first_child?.first_child;
317
- if (childSelector?.type === SELECTOR) {
318
- let childPart = childSelector.first_child;
319
- while (childPart) {
320
- if (childPart.type === COMBINATOR) break;
321
- const partSpecificity = calculateForAST({
322
- type_name: "Selector",
323
- first_child: childPart,
324
- has_children: true
325
- });
326
- a += partSpecificity[0] ?? 0;
327
- b += partSpecificity[1] ?? 0;
328
- c += partSpecificity[2] ?? 0;
329
- childPart = childPart.next_sibling;
330
- }
331
- }
332
- break;
333
- case "view-transition-group":
334
- case "view-transition-image-pair":
335
- case "view-transition-old":
336
- case "view-transition-new":
337
- if (current.first_child?.text === "*") break;
338
- c += 1;
339
- break;
340
- default:
341
- c += 1;
342
- break;
343
- }
344
- break;
345
- case TYPE_SELECTOR:
346
- let typeSelector = current.name ?? "";
347
- if (typeSelector.includes("|")) typeSelector = typeSelector.split("|")[1] ?? "";
348
- if (typeSelector !== "*") c += 1;
349
- break;
350
- default: break;
351
- }
352
- current = current.next_sibling;
353
- }
354
- return [
355
- a,
356
- b,
357
- c
358
- ];
359
- };
360
- const convertToAST = (source) => {
361
- if (typeof source === "string") try {
362
- return parse_selector(source);
363
- } catch (e) {
364
- const message = e instanceof Error ? e.message : String(e);
365
- throw new TypeError(`Could not convert passed in source '${source}' to SelectorList: ${message}`);
366
- }
367
- if (source instanceof Object) {
368
- if (source.type === SELECTOR_LIST) return source;
369
- throw new TypeError(`Passed in source is an Object but no AST / AST of the type SelectorList`);
370
- }
371
- throw new TypeError(`Passed in source is not a String nor an Object. I don't know what to do with it.`);
372
- };
373
- const calculate = (selector) => {
374
- if (!selector) return [];
375
- const ast = convertToAST(selector);
376
- const specificities = [];
377
- let selectorNode = ast.first_child;
378
- while (selectorNode) {
379
- specificities.push(calculateForAST(selectorNode));
380
- selectorNode = selectorNode.next_sibling;
381
- }
382
- return specificities;
383
- };
384
- //#endregion
385
- //#region src/values/colors.ts
386
- const namedColors = new KeywordSet([
387
- "white",
388
- "black",
389
- "red",
390
- "gray",
391
- "silver",
392
- "grey",
393
- "green",
394
- "orange",
395
- "blue",
396
- "dimgray",
397
- "whitesmoke",
398
- "lightgray",
399
- "lightgrey",
400
- "yellow",
401
- "gold",
402
- "pink",
403
- "gainsboro",
404
- "magenta",
405
- "purple",
406
- "darkgray",
407
- "navy",
408
- "darkred",
409
- "teal",
410
- "maroon",
411
- "darkgrey",
412
- "tomato",
413
- "darkorange",
414
- "brown",
415
- "crimson",
416
- "lightyellow",
417
- "slategray",
418
- "salmon",
419
- "lightgreen",
420
- "lightblue",
421
- "orangered",
422
- "aliceblue",
423
- "dodgerblue",
424
- "lime",
425
- "darkblue",
426
- "darkgoldenrod",
427
- "skyblue",
428
- "royalblue",
429
- "darkgreen",
430
- "ivory",
431
- "olive",
432
- "aqua",
433
- "turquoise",
434
- "cyan",
435
- "khaki",
436
- "beige",
437
- "snow",
438
- "ghostwhite",
439
- "limegreen",
440
- "coral",
441
- "dimgrey",
442
- "hotpink",
443
- "midnightblue",
444
- "firebrick",
445
- "indigo",
446
- "wheat",
447
- "mediumblue",
448
- "lightpink",
449
- "plum",
450
- "azure",
451
- "violet",
452
- "lavender",
453
- "deepskyblue",
454
- "darkslategrey",
455
- "goldenrod",
456
- "cornflowerblue",
457
- "lightskyblue",
458
- "indianred",
459
- "yellowgreen",
460
- "saddlebrown",
461
- "palegreen",
462
- "bisque",
463
- "tan",
464
- "antiquewhite",
465
- "steelblue",
466
- "forestgreen",
467
- "fuchsia",
468
- "mediumaquamarine",
469
- "seagreen",
470
- "sienna",
471
- "deeppink",
472
- "mediumseagreen",
473
- "peru",
474
- "greenyellow",
475
- "lightgoldenrodyellow",
476
- "orchid",
477
- "cadetblue",
478
- "navajowhite",
479
- "lightsteelblue",
480
- "slategrey",
481
- "linen",
482
- "lightseagreen",
483
- "darkcyan",
484
- "lightcoral",
485
- "aquamarine",
486
- "blueviolet",
487
- "cornsilk",
488
- "lightsalmon",
489
- "chocolate",
490
- "lightslategray",
491
- "floralwhite",
492
- "darkturquoise",
493
- "darkslategray",
494
- "rebeccapurple",
495
- "burlywood",
496
- "chartreuse",
497
- "lightcyan",
498
- "lemonchiffon",
499
- "palevioletred",
500
- "darkslateblue",
501
- "mediumpurple",
502
- "lawngreen",
503
- "slateblue",
504
- "darkseagreen",
505
- "blanchedalmond",
506
- "mistyrose",
507
- "darkolivegreen",
508
- "seashell",
509
- "olivedrab",
510
- "peachpuff",
511
- "darkviolet",
512
- "powderblue",
513
- "darkmagenta",
514
- "lightslategrey",
515
- "honeydew",
516
- "palegoldenrod",
517
- "darkkhaki",
518
- "oldlace",
519
- "mintcream",
520
- "sandybrown",
521
- "mediumturquoise",
522
- "papayawhip",
523
- "paleturquoise",
524
- "mediumvioletred",
525
- "thistle",
526
- "springgreen",
527
- "moccasin",
528
- "rosybrown",
529
- "lavenderblush",
530
- "mediumslateblue",
531
- "darkorchid",
532
- "mediumorchid",
533
- "darksalmon",
534
- "mediumspringgreen"
535
- ]);
536
- const systemColors = new KeywordSet([
537
- "accentcolor",
538
- "accentcolortext",
539
- "activetext",
540
- "buttonborder",
541
- "buttonface",
542
- "buttontext",
543
- "canvas",
544
- "canvastext",
545
- "field",
546
- "fieldtext",
547
- "graytext",
548
- "highlight",
549
- "highlighttext",
550
- "linktext",
551
- "mark",
552
- "marktext",
553
- "selecteditem",
554
- "selecteditemtext",
555
- "visitedtext"
556
- ]);
557
- const colorFunctions = new KeywordSet([
558
- "rgba",
559
- "rgb",
560
- "hsla",
561
- "hsl",
562
- "oklch",
563
- "color",
564
- "hwb",
565
- "lch",
566
- "lab",
567
- "oklab"
568
- ]);
569
- const colorKeywords = new KeywordSet(["transparent", "currentcolor"]);
570
- //#endregion
571
- //#region src/values/values.ts
572
- const keywords = new KeywordSet([
573
- "auto",
574
- "none",
575
- "inherit",
576
- "initial",
577
- "unset",
578
- "revert",
579
- "revert-layer"
580
- ]);
581
- /**
582
- * Test whether a value is a reset (0, 0px, -0.0e0 etc.)
583
- */
584
- function isValueReset(node) {
585
- for (let child of node.children) {
586
- if (child.type === NUMBER && child.value === 0) continue;
587
- if (child.type === DIMENSION && child.value === 0) continue;
588
- return false;
589
- }
590
- return true;
591
- }
592
- //#endregion
1
+ import { n as isSupportsBrowserhack, t as isMediaBrowserhack } from "./atrules-CskmpIdJ.js";
2
+ import { n as unquote, r as KeywordSet, t as endsWith } from "./string-utils-olNNcOlY.js";
3
+ import { a as getComplexity, i as getCombinators, n as calculateForAST, o as isAccessibility, s as isPrefixed, t as calculate } from "./specificity-svLpcKkT.js";
4
+ import { a as colorKeywords, i as colorFunctions, n as isValueReset, o as namedColors, r as keywords, s as systemColors, t as isIe9Hack } from "./browserhacks-eP_e1D5u.js";
5
+ import { ATTRIBUTE_SELECTOR, AT_RULE, BLOCK, CONTAINER_QUERY, DECLARATION, DIMENSION, FUNCTION, HASH, IDENTIFIER, LAYER_NAME, MEDIA_FEATURE, NUMBER, OPERATOR, PSEUDO_CLASS_SELECTOR, PSEUDO_ELEMENT_SELECTOR, SELECTOR, SELECTOR_LIST, SKIP, STYLE_RULE, SUPPORTS_QUERY, TYPE_SELECTOR, URL, is_custom, is_vendor_prefixed, parse, str_starts_with, walk } from "@projectwallace/css-parser";
593
6
  //#region src/values/destructure-font-shorthand.ts
594
7
  const SYSTEM_FONTS = new KeywordSet([
595
8
  "caption",
@@ -902,16 +315,6 @@ function getEmbedType(embed) {
902
315
  return embed.substring(start, semicolon);
903
316
  }
904
317
  //#endregion
905
- //#region src/values/browserhacks.ts
906
- function isIe9Hack(node) {
907
- let children = node.children;
908
- if (children) {
909
- let last = children.at(-1);
910
- return last && last.type === IDENTIFIER && endsWith("\\9", last.text) ? true : false;
911
- }
912
- return false;
913
- }
914
- //#endregion
915
318
  //#region src/properties/property-utils.ts
916
319
  const SPACING_RESET_PROPERTIES = new Set([
917
320
  "margin",
@@ -1593,6 +996,7 @@ function analyzeInternal(css, options, useLocations) {
1593
996
  }
1594
997
  /**
1595
998
  * Compare specificity A to Specificity B
999
+ * @deprecated this one is the inverse of the one exported in /selectors/index.ts; wille be removed in next major version
1596
1000
  * @returns 0 when a==b, 1 when a<b, -1 when a>b
1597
1001
  */
1598
1002
  function compareSpecificity(a, b) {
@@ -0,0 +1,2 @@
1
+ import { a as calculate, i as isPrefixed, n as getComplexity, o as calculateForAST, r as isAccessibility, s as compare, t as getCombinators } from "../utils-BUeYqEL1.js";
2
+ export { compare as compareSpecificity, getCombinators, getComplexity, calculate as getSpecificity, calculateForAST as getSpecificityForAST, isAccessibility, isPrefixed };
@@ -0,0 +1,3 @@
1
+ import "../string-utils-olNNcOlY.js";
2
+ import { a as getComplexity, i as getCombinators, n as calculateForAST, o as isAccessibility, r as compare, s as isPrefixed, t as calculate } from "../specificity-svLpcKkT.js";
3
+ export { compare as compareSpecificity, getCombinators, getComplexity, calculate as getSpecificity, calculateForAST as getSpecificityForAST, isAccessibility, isPrefixed };