All files script-coverage.ts

100% Statements 78/78
97.82% Branches 45/46
100% Functions 9/9
100% Lines 73/73

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170            54x           2956x 42x     2914x 2914x 42699x                 2914x 2914x 42699x 5902x       2914x 1910x     1004x         1910x 104136x 104136x   65333x     1910x   1910x   27575x     1910x   1910x   1910x 9687827x 9687827x 9687827x   9687827x 46304x         46304x       1910x       1004x   1004x 15124x 15044x 15044x       58080x   1004x 1004x 1004x 1004x   1004x 29604x   29604x 27852x 27852x   27852x 5888x   21964x       29604x 30088x   30088x 15044x   15044x     30088x     29604x     1004x         27852x   27852x 41752x   41752x         34200x       27852x             9025317x 9025317x   9025317x 98451028x 98451028x   98451028x 8730107x 89720921x 15366695x   74354226x       295210x    
import { type Profiler } from "node:inspector";
 
type Normalized = { start: number; end: number; count: number };
type RawRange = Normalized & { area: number; order: number };
 
/** Files larger than this use event sweep to avoid allocating huge hits arrays */
const HITS_ARRAY_MAX_SIZE = 2_000_000;
 
export function normalize(
  scriptCoverage: Pick<Profiler.ScriptCoverage, "functions">,
  hitsArrayMaxSize = HITS_ARRAY_MAX_SIZE,
): Normalized[] {
  if (scriptCoverage.functions.length === 0) {
    return [];
  }
 
  let order = 0;
  const ranges: RawRange[] = scriptCoverage.functions.flatMap((fn) =>
    fn.ranges.map((range) => ({
      start: range.startOffset,
      end: range.endOffset,
      count: range.count,
      area: range.endOffset - range.startOffset,
      order: order++,
    })),
  );
 
  let maxEnd = 0;
  for (const r of ranges) {
    if (r.end > maxEnd) {
      maxEnd = r.end;
    }
  }
 
  if (maxEnd <= hitsArrayMaxSize) {
    return normalizeWithHitsArray(ranges, maxEnd);
  }
 
  return normalizeWithEventSweep(ranges);
}
 
function normalizeWithHitsArray(ranges: RawRange[], maxEnd: number): Normalized[] {
  // Paint ranges into hits array so that the most specific range is applied last
  ranges.sort((a, b) => {
    const diff = b.area - a.area;
    if (diff !== 0) return diff;
 
    return a.end - b.end;
  });
 
  const hits = new Uint32Array(maxEnd);
 
  for (const range of ranges) {
    // V8's endOffset is exclusive - the offset at range's end belongs to the parent range
    hits.fill(range.count, range.start, range.end);
  }
 
  const normalized: Normalized[] = [];
 
  let start = 0;
 
  for (let end = 1; end <= hits.length; end++) {
    const isLast = end === hits.length;
    const current = isLast ? null : hits[end];
    const previous = hits[start];
 
    if (current !== previous || isLast) {
      normalized.push({
        start,
        end: end - 1,
        count: previous,
      });
      start = end;
    }
  }
 
  return normalized;
}
 
function normalizeWithEventSweep(ranges: RawRange[]): Normalized[] {
  const events: { offset: number; range: RawRange; isStart: boolean }[] = [];
 
  for (const range of ranges) {
    if (range.start < range.end) {
      events.push({ offset: range.start, range, isStart: true });
      events.push({ offset: range.end, range, isStart: false });
    }
  }
 
  events.sort((a, b) => a.offset - b.offset);
 
  const active: RawRange[] = [];
  const normalized: Normalized[] = [];
  let cursor = events.length > 0 ? events[0].offset : 0;
  let index = 0;
 
  while (index < events.length) {
    const offset = events[index].offset;
 
    if (active.length > 0 && cursor < offset) {
      const count = getMostSpecificRange(active).count;
      const previous = normalized.at(-1);
 
      if (previous && previous.end + 1 === cursor && previous.count === count) {
        previous.end = offset - 1;
      } else {
        normalized.push({ start: cursor, end: offset - 1, count });
      }
    }
 
    while (index < events.length && events[index].offset === offset) {
      const event = events[index];
 
      if (event.isStart) {
        active.push(event.range);
      } else {
        active.splice(active.indexOf(event.range), 1);
      }
 
      index++;
    }
 
    cursor = offset;
  }
 
  return normalized;
}
 
/** Pick the range that painting would have applied last in `normalizeWithHitsArray` */
function getMostSpecificRange(ranges: RawRange[]): RawRange {
  let winner = ranges[0];
 
  for (let index = 1; index < ranges.length; index++) {
    const range = ranges[index];
 
    if (
      range.area < winner.area ||
      (range.area === winner.area && range.end > winner.end) ||
      (range.area === winner.area && range.end === winner.end && range.order > winner.order)
    ) {
      winner = range;
    }
  }
 
  return winner;
}
 
export function getCount(
  offset: Pick<Profiler.CoverageRange, "startOffset" | "endOffset">,
  coverages: Normalized[],
) {
  let low = 0;
  let high = coverages.length - 1;
 
  while (low <= high) {
    const mid = Math.floor((low + high) / 2);
    const coverage = coverages[mid];
 
    if (coverage.start <= offset.startOffset && offset.startOffset <= coverage.end) {
      return coverage.count;
    } else if (offset.startOffset < coverage.start) {
      high = mid - 1;
    } else {
      low = mid + 1;
    }
  }
 
  return 0;
}