All files index.ts

98.21% Statements 55/56
90.62% Branches 29/32
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 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213                                  924x     924x 8x     916x 916x 916x   916x     3863x           666x 537x     129x           1322x           1322x 818x       410x 410x     410x           2237x 127x   127x             17x                                           17x                   14x                                           14x                   319x 247x 4x 8511x 108x 74x 185x 15x 363x 127x 28x   23x   9567x 12x     9555x     8662x 7725x       101x 624x           91x             12917x 12917x     2428x     4448x     237x 237x     240x       916x     50x     537x       666x    
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: [],
        id:
          babelNode.key.type === "Identifier"
            ? {
                type: "Identifier",
                name: babelNode.key.name,
                start: babelNode.key.start!,
                end: babelNode.key.end!,
              }
            : null,
      };
 
      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: [],
        id:
          babelNode.key.type === "Identifier"
            ? {
                type: "Identifier",
                name: babelNode.key.name,
                start: babelNode.key.start!,
                end: babelNode.key.end!,
              }
            : null,
      };
 
      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);
}