All files index.ts

98.21% Statements 55/56
96.42% Branches 27/28
96.77% Functions 30/31
98.21% Lines 55/56

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 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195                                  916x     916x 8x     908x 908x 908x   908x     3847x           637x 504x     133x           1322x           1322x 818x       383x 383x     383x           2231x 121x   121x             8x                         8x                   12x                         12x                   319x 247x 4x 8487x 108x 74x 185x 15x 363x 127x 28x   23x   9547x 12x     9535x     8670x 7733x       89x 588x           91x             12913x 12913x     2416x     4448x     237x 237x     240x       908x     50x     504x       637x    
import { type Node } from "estree";
import type { CoverageMapData } from "istanbul-lib-coverage";
 
import { getWalker } from "./ast";
import { CoverageMapper } from "./coverage-mapper";
import { getIgnoreHints } from "./ignore-hints";
import { type Options } from "./types";
 
export { convert, Options };
 
/**
 * Maps V8 `ScriptCoverage` to Istanbul's `CoverageMap`.
 * Results are identical with `istanbul-lib-instrument`.
 */
export default async function convert<T = Node, Program = T & { type: "Program" }>(
  options: Options<T, Program>,
): Promise<CoverageMapData> {
  const ignoreHints = getIgnoreHints(options.code);
 
  // File ignore contains always only 1 entry
  if (ignoreHints.length === 1 && ignoreHints[0].type === "file") {
    return {};
  }
 
  const walker = getWalker();
  const mapper = await CoverageMapper.create<T, Program>(options, walker.onIgnore);
  const ast = await options.ast;
 
  await walker.walk(ast, ignoreHints, options.ignoreClassMethods, {
    // Functions
    onFunctionDeclaration(node) {
      mapper.onFunction(node, {
        loc: node.body,
        decl: node.id || { ...node, end: node.start + 1 },
      });
    },
    onFunctionExpression(node) {
      if (isCovered(node)) {
        return;
      }
 
      mapper.onFunction(node, {
        loc: node.body,
        decl: node.id || { ...node, end: node.start + 1 },
      });
    },
    onArrowFunctionExpression(node) {
      mapper.onFunction(node, {
        loc: node.body,
        decl: { ...node, end: node.start + 1 },
      });
 
      // Implicit return-statement of bodyless arrow function
      if (node.body.type !== "BlockStatement") {
        mapper.onStatement(node.body, node);
      }
    },
    onMethodDefinition(node) {
      Eif (node.value.type === "FunctionExpression") {
        setCovered(node.value);
      }
 
      mapper.onFunction(node, {
        loc: node.value.body,
        decl: node.key,
      });
    },
    onProperty(node) {
      if (node.value.type === "FunctionExpression") {
        setCovered(node.value);
 
        mapper.onFunction(node, {
          loc: node.value.body,
          decl: node.key,
        });
      }
    },
    onClassMethod(babelNode) {
      const node: Node = {
        type: "FunctionExpression",
        start: babelNode.start!,
        end: babelNode.end!,
        body: {
          type: "BlockStatement",
          start: babelNode.body.start!,
          end: babelNode.body.end!,
          body: [],
        },
        params: [],
      };
 
      mapper.onFunction(node, {
        loc: node.body,
        decl: {
          start: babelNode.key.start!,
          end: babelNode.key.end!,
        },
      });
    },
 
    onObjectMethod(babelNode) {
      const node: Node = {
        type: "FunctionExpression",
        start: babelNode.start!,
        end: babelNode.end!,
        body: {
          type: "BlockStatement",
          start: babelNode.body.start!,
          end: babelNode.body.end!,
          body: [],
        },
        params: [],
      };
 
      mapper.onFunction(node, {
        loc: node.body,
        decl: {
          start: babelNode.key.start!,
          end: babelNode.key.end!,
        },
      });
    },
 
    // Statements
    onBreakStatement: (node) => mapper.onStatement(node),
    onContinueStatement: (node) => mapper.onStatement(node),
    onDebuggerStatement: (node) => mapper.onStatement(node),
    onReturnStatement: (node) => mapper.onStatement(node),
    onThrowStatement: (node) => mapper.onStatement(node),
    onTryStatement: (node) => mapper.onStatement(node),
    onForStatement: (node) => mapper.onStatement(node),
    onForInStatement: (node) => mapper.onStatement(node),
    onForOfStatement: (node) => mapper.onStatement(node),
    onWhileStatement: (node) => mapper.onStatement(node),
    onDoWhileStatement: (node) => mapper.onStatement(node),
    onWithStatement: (node) => mapper.onStatement(node),
    onLabeledStatement: (node) => mapper.onStatement(node),
    onExpressionStatement(node) {
      if (node.expression.type === "Literal" && node.expression.value === "use strict") {
        return;
      }
 
      mapper.onStatement(node);
    },
    onVariableDeclarator(node) {
      if (node.init) {
        mapper.onStatement(node.init, node);
      }
    },
    onClassBody(node) {
      for (const child of node.body) {
        if (
          (child.type === "PropertyDefinition" ||
            child.type === "ClassProperty" ||
            child.type === "ClassPrivateProperty") &&
          child.value
        ) {
          mapper.onStatement(child.value as Node);
        }
      }
    },
 
    // Branches
    onIfStatement(node, branches) {
      mapper.onBranch("if", node, branches);
      mapper.onStatement(node);
    },
    onConditionalExpression(node, branches) {
      mapper.onBranch("cond-expr", node, branches);
    },
    onLogicalExpression(node, branches) {
      mapper.onBranch("binary-expr", node, branches);
    },
    onSwitchStatement(node, cases) {
      mapper.onBranch("switch", node, cases);
      mapper.onStatement(node);
    },
    onAssignmentPattern(node) {
      mapper.onBranch("default-arg", node, [node.right]);
    },
  });
 
  return mapper.generate();
}
 
const coveredNodes = new WeakSet<Node>();
 
function setCovered(node: Node) {
  coveredNodes.add(node);
}
 
function isCovered(node: Node) {
  return coveredNodes.has(node);
}