LCOV - code coverage report
Current view: top level - src - utils.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 45 52 86.5 %
Date: 2025-06-29 02:18:36 Functions: 8 9 88.9 %

          Line data    Source code
       1             : // @ts-check
       2             : 
       3             : import { forEach, indexOf } from "lodash";
       4             : 
       5           1 : const DEFAULT_MAX_RESULTS = 250;
       6             : 
       7           1 : const renderableElements = new Set([
       8             :   "a", "circle", "ellipse", "foreignObject",
       9             :   "g", "image", "line", "path", "polygon", "polyline", "rect", "svg", "switch",
      10             :   "symbol", "text", "textPath", "tspan", "use"
      11             : ]);
      12             : 
      13             : /**
      14             :  * @param  {Element} element
      15             :  * @param  {{ maxNodes?: number, maxResults?: number }} [options]
      16             :  * @return {string[]}
      17             :  */
      18             : export function getDomSelectors(element, options) {
      19           5 :   const ret = getDomIdClass(element, options);
      20           5 :   if (ret.length < (options?.maxResults ?? DEFAULT_MAX_RESULTS)) {
      21           5 :     const maxResults = (options?.maxResults ?? DEFAULT_MAX_RESULTS) - ret.length;
      22           5 :     return ret.concat(getDirectPaths(element, { maxResults }));
      23             :   }
      24           0 :   return ret;
      25             : }
      26             : 
      27             : /**
      28             :  * @param  {Element} element
      29             :  * @param  {{ maxNodes?: number, maxResults?: number }} [options]
      30             :  * @return {string[]}
      31             :  */
      32             : export function getDomIdClass(element, options) {
      33           5 :   if (!element) { return []; }
      34             : 
      35           5 :   const stack = [ element ];
      36           5 :   const ret = new Set();
      37           5 :   let maxNodes = options?.maxNodes ?? 250;
      38           5 :   let maxResults = options?.maxResults ?? DEFAULT_MAX_RESULTS;
      39             : 
      40           5 :   while (maxNodes > 0 && maxResults > 0 && stack.length > 0) {
      41          65 :     const element = /** @type {Element} */ (stack.shift());
      42          65 :     forEach(element.children, (elt) => { /* eslint-disable-line no-loop-func */ /* jshint ignore:line */
      43          60 :       stack.push(elt);
      44          60 :       if (elt.id) {
      45           6 :         ret.add("#" + elt.id);
      46           6 :         if (--maxResults <= 0) { return false; }
      47             :       }
      48          60 :       forEach(elt.classList, (c) => {
      49           4 :         ret.add("." + c);
      50           4 :         if (--maxResults <= 0) { return false; }
      51             :       });
      52          60 :       if (--maxNodes <= 0) { return false; }
      53             :     });
      54             :   }
      55           5 :   return Array.from(ret);
      56             : }
      57             : 
      58             : /**
      59             :  * @param  {Element} element
      60             :  * @param  {{ maxResults?: number, maxDepth?: number, allowedNodes?: Set<string> }} [options]
      61             :  * @return {string[]}
      62             :  */
      63             : export function getDirectPaths(element, options) {
      64           5 :   if (!element) { return []; }
      65             : 
      66           5 :   const stack = [ { elt: element, path: ":scope", depth: 0 } ];
      67             :   /** @type {string[]} */
      68           5 :   const ret = [];
      69           5 :   let maxResults = options?.maxResults ?? DEFAULT_MAX_RESULTS;
      70           5 :   const maxDepth = options?.maxDepth ?? 3;
      71           5 :   const allowedNodes = options?.allowedNodes ?? renderableElements;
      72             : 
      73           5 :   while (maxResults > 0 && stack.length > 0) {
      74          11 :     const element = stack.shift();
      75             :     /** @type {{ [tag: string]: number }} */
      76          11 :     const childCountMap = {};
      77          11 :     forEach(element?.elt.children, (elt) => { /* eslint-disable-line no-loop-func */ /* jshint ignore:line */
      78          11 :       const type = elt.tagName;
      79          11 :       if (!allowedNodes.has(type) || !element) { return; }
      80             : 
      81           6 :       childCountMap[type] = (childCountMap[type] ?? 0) + 1;
      82             : 
      83           6 :       const path = `${element?.path}>${type}:nth-of-type(${childCountMap[type]})`;
      84           6 :       if (element.depth < maxDepth) {
      85           6 :         stack.push({ elt, path, depth: element.depth + 1 });
      86             :       }
      87           6 :       ret.push(path);
      88           6 :       if (--maxResults <= 0) { return false; }
      89             :     });
      90             :   }
      91           5 :   return ret;
      92             : }
      93             : 
      94             : /**
      95             :  * @param {Element|null} root
      96             :  * @param {Element|null} element
      97             :  * @return {string|null}
      98             :  */
      99             : export function getElementPath(root, element) {
     100           0 :   if (element && element === root) {
     101           0 :     return ":scope";
     102             :   }
     103             :   else {
     104           0 :     if (!element || !element.parentElement) { return null; }
     105           0 :     const tagName = element.tagName;
     106             : 
     107           0 :     const nth = indexOf(element.parentElement.getElementsByTagName(tagName), element);
     108             : 
     109           0 :     return `${getElementPath(root, element.parentElement)}>${tagName}:nth-of-type(${nth})`;
     110             :   }
     111             : }
     112             : 
     113           1 : let id = 0;
     114             : 
     115             : /** @returns {string} */
     116             : export function genId() {
     117          36 :   return "x-" + (++id);
     118             : }
     119             : 
     120             : /**
     121             :  * @brief function used to automatically and correctly infer types in mixins
     122             :  * @template V, Data, Methods, Computed, PropNames, SetupBindings, Mixin, Extends
     123             :  * @type {V.mixinMaker<V, Data, Methods, Computed, PropNames, SetupBindings, Mixin, Extends>}}
     124             :  */
     125           4 : export function mixinMaker(m) { return /** @type {any} */ (m); }

Generated by: LCOV version 1.16